Spring day01 intro 봄이여 오라~🌻

alert("april");·2023년 7월 30일
0

spring

목록 보기
1/4
post-thumbnail

출처 https://www.codestates.com/blog/content/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8

Framework

라이브러리란, 개발자가 작성해놓은 코드 파일을 의미하며,
API란, 여러 라이브러리가 모여있는 패키지(JAR)를 의미한다.
프레임워크란, API가 굉장히 많이 모여져서 덩치가 커져있는 것을 의미한다.

Framework의 장점

개발에 필요한 구조를 이미 코드로 만들어 놓았기 때문에 실력이 부족한 개발자라 하더라도 반쯤 완성된 상태에서 필요한 부분을 조립하는 형태의 개발이 가능하다.
회사 입장에서는 프레임워크를 사용하면 일정한 품질이 보장되는 결과물을 얻을 수 있고, 개발자 입장에서는 완성된 구조에 자신이 맡은 서비스에 대한 코드를 개발해서 넣기 때문에 개발 시간을 단축할 수 있다.

Spring Framework

경량 프레임워크
예전 프레임워크는 다양한 경우를 처리하기 위해 여러 기능을 넣다 보니 하나의 기능을 위해서 아주 많은 구조가 필요했다. 기술이 너무나 복잡하고 방대했기 때문에, 전체를 이해하고 개발하기에는 어려움이 많았다. 그래서 Spring Framework가 등장했고, 특정 기능을 위주로 간단한 JAR파일 등을 선택하여 모든 개발이 가능하도록 구성되어 있다.

Spring Framework의 장점

  • 프로젝트 전체 구조를 설계할 때 유용하다(빠른 속도로 서버 제작 가능).
  • 다른 프레임워크들의 포용, 여러 프레임워크를 혼용해서 사용 가능하기 때문에 접착성이 좋다.
  • 개발 생산성과 개발도구의 지원

Spring Framework의 특징

🔴 POJO 기반의 구성
🟠 DI를 통한 객체간의 관계 구성
🟡 AOP 지원
🟢 Transaction 관리
🔵 편리한 MVC 구조
🟣 WAS에 종속적이지 않은 개발 환경

🔴 POJO 기반의 구성

오래된 방식의 간단한 자바 객체라는 의미이며, JAVA 코드에서 일반적으로 객체를 구성하는 방식을 Spring Framework에서 그대로 사용할 수 있다는 의미이다.

🟠 DI를 통한 객체간의 관계 구성

의존성(Dependency)이란 하나의 객체가 다른 객체 없이 제대로 된 역할을 할 수 없다는 것을 의미한다.
예를 들어 A객체가 B객체 없이 동작이 불가능한 상황을 "A가 B에 의존적이다"라고 표현한다.
하지만 직접 A필드에 B객체를 선언하면 결합도가 단단해지기 때문에 유연성이 떨어진다.


주입(Injection)은 외부에서 내부로 밀어 넣는 것을 의미한다.
필요한 객체를 외부에서 밀어 넣어 유연성을 높이고 결합도를 느슨하게 해준다.
주입을 받는 입장에서는 어떤 객체인지 신경 쓸 필요가 없고 어떤 객체에 의존하든 자신의 역할은 변하지 않는다.


의존성
A →→→→→→→→→→→→→ B
A객체에서 B객체를 필드로 직접 생성


의존성 주입
A ↔↔↔↔↔ ? ↔↔↔↔↔ B
A는 B가 필요하다고 신호를 보내고, ?가 B객체를 외부에서 생성하여 주입하게 된다.
Spring Framework에서는 ApplicationContext가 ?이며, 필요한 객체들을 생성 및 주입해주는 역할을 한다. 따라서 개발자들은 기존의 프로그래밍과는 달리 객체와 객체를 분리해서 생성하고, 이러한 객체를 엮는 wiring 작업의 형태로 개발하게 된다.


ApplicationContext가 관리하는 객체들을 빈(Bean)이라 부르고,
빈과 빈 사이의 의존 관계를 처리하는 방식으로는 XML, 어노테이션, JAVA 방식이 있다.

🟡 AOP 지원

관점 지향 프로그래밍
좋은 개발 환경에서는 개발자가 비지니스 로직에만 집중할 수 있게 한다. Spring Framework는 반복적인 코드를 제거해줌으로써 핵심 비지니스 로직에만 집중할 수 있는 방법을 제공한다.
보안이나 로그, 트랜잭션, 예외처리와 같이 비지니스 로직은 아니지만,
반드시 처리가 필요한 부분을 주변 로직(횡단 관심사)라고 하고, 개발해야할 서비스는 메인 로직(종단 관심사)라고 한다.
Spring Framework는 이러한 횡단 관심사를 분리해서 설계하는 것이 가능하고, 횡단 관심사를 모듈로 분리하는 프로그래밍을 AOP라고 한다.
핵심 비지니스 로직에만 집중하여 코드 개발이 가능해지고, 각 프로젝트마다 다른 관심사 적용 시 코드 수정을 최소화 할 수 있으며, 원하는 관심사의 유지보수가 수월한 코드로 구성이 가능해진다.
※ 비지니스 로직(핵심 로직): 서비스를 개발하기 위한 소스코드 및 알고리즘

🟢 Transaction 관리

DB 작업 시 트랜잭션을 매번 상황에 맞게 코드로 작성하지 않고,
어노테이션이나 XML로 트랜잭션을 쉽게 관리할 수 있다.

🟣 WAS에 종속적이지 않은 개발 환경

전체 Application을 실행하지 않아도 기능별 단위 테스트가 용이하기 때문에 버그를 줄이고 개발 시간을 단축할 수 있다.

Spring Boot

Spring Framework를 사용함에 있어서 초기 설정 및 필요한 라이브러리에 대한 설정의 어려움이 많으며, 시간이 너무 오래 걸리기 때문에 자동 설정(AutoConfiguration)과 개발에 필요한 모든 것을 관리해주는 스프링 부트를 선호한다. 각 코어 및 라이브러리의 버전들도 맞추어야 하지만 스프링 부트를 사용하면 이러한 복잡성을 해결하기에도 좋다

프로젝트 기본 경로

1) src/main/java : 서버단 JAVA 파일
2) src/main/resources : 설정 파일 및 뷰단
3) src/main/resources/static : css, js, image 등의 경로
4) src/main/resources/templates : html 파일 경로
5) src/main/resources/application.yml : 서버, 경로 및 DB 설정 파일
6) src/test : 단위 테스트 JAVA 파일(jUnit, Assertions)
7) build.gradle : 라이브러리 의존성 관리

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.5'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'com.app'
version = '1.0'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-web-services'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    annotationProcessor 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

연습 1)
domain에 Knife, Food 클래스 선언
의존성 주입 후 단위 테스트에서 검사

연습 2)
Restaurant.java, Outback.java, Vips.java 세 객체를 선언한 뒤
상속관계를 판단하여 하나의 객체를 인터페이스로 선언한다.
각 레스토랑에 셀바 이용 가능 여부와 스테이크 가격을 필드로 구성한다.
스테이크 가격은 항상 똑같지만 각 레스토랑에서 변경 가능하다.
기본 레스토랑은 아웃백으로 설정한다.

Restaurant.java(interface)

package com.app.dependency.domain;

import org.springframework.stereotype.Component;

public interface Restaurant {
    public int steakPrice = 100000; // 상수이기 때문에 무조건 처음에 가격을 정해줘야함
    public boolean selfBar();   // +알파를 하고싶으면 다운케스팅을 하거나 static을 이용하거나 해야한다
    public int getSteakPrice();

}

Outback.java

package com.app.dependency.domain;

import lombok.Data;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Data
@Primary	// 기본 레스토랑 설정
public class Outback implements Restaurant{
    public int steakPrice = Restaurant.steakPrice + 50000;

    @Override   // 재정의는 메소드만 가능!
    public boolean selfBar() {
        return false;
    }

    @Override
    public int getSteakPrice() {
        return steakPrice;
    }
}

Vips.java

package com.app.dependency.domain;

import lombok.Data;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier(value = "vips")
@Data
public class Vips implements Restaurant{
    public int steakPrice = Restaurant.steakPrice + 20000;

    @Override
    public boolean selfBar() {
        return true;
    }

    @Override
    public int getSteakPrice() {
        return steakPrice;  // restaurant에 선언된 메소드를 사용할 수 있기때문에
    }
}

DomainTest.java

package com.app.dependency.domain;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Slf4j
public class DomainTests {

    @Autowired
    private Restaurant restaurant;  // 업케이스팅될때 +알파가 다 짤림

    @Test
    public void restaurantTest(){
        log.info("{}",restaurant.selfBar());
        log.info("{}",((Outback)restaurant).steakPrice); // 다운케스팅해서 steakPrice를 가져올 수 있다
        log.info("{}",restaurant.getSteakPrice());  // 아예 부모 객체에서 메소드로 정의해버리는 방법도 있다
    }
}
profile
Slowly but surely

0개의 댓글