TIL 20231219

2023. 12. 19. 20:44스프링

DI(Dependency Injection) & IoC(Inversion of Control)

 

Dependency Injection (의존성 주입)

객체가 자체적으로 필요로 하는 의존성을 생성하는 것이 아니라, 외부에서 주입받는 디자인 패턴이다.
객체간의 결합도를 낮추기 위해 사용한다.
주로 Contructor 기반 주입, Field 기반 주입, Setter 기반 주입이 있다.
DI 가 적용되지 않은 예시다. 내부에서 의존성을 생성하고 있다.

 

class Dependent() {
    private val dependency = Dependency(arg1, arg2)
}

 

Constructor를 통해 주입받는 예시다.

 

class Dependent(private val dependency: Dependency) 

 

Field를 통해 주입 받는 예시다.

 

class Dependent() {
    lateinit var dependency: Dependency
}

 

Setter를 통해 주입받는 예시다.

 

class Dependent() {
    private lateinit var dependency: Dependency

    fun setDependency(dependency: Dependency) {
        this.dependency = dependency
    }

 

의존되는 객체의 불변성 확보,
순환 참조 방지,
테스트 코드 작성의 용이
 
이러한 이유로 Constructor 를 통한 주입을 현업에서 주로 사용한다.
 
 
 
 
Inversion of Control (제어의 역전)

 

Application 관점에서 라이브러리를 사용할 때는 우리가 직접 호출을 하지만, 프레임워크는 우리가 구성 요소를 등록하면, 알아서 Framework에서 호출을 한다. 이처럼 객체의 생성과 생명주기를 외부에서 제어하는 디자인 패턴이라 할 수 있다.


IoC는 굉장히 넓은 의미로, IoC를 구현하는 방법은 여러가지가 있다. 그 중 하나가 DI라 할 수 있고 의존성들에 대해 제어의 역전이 되는 거라 볼 수 있다.
Framework에서 IoC를 제공할 때, 이를 IoC Container라고 부른다. Spring에서는 IoC의 방식중 DI 를 주로 사용하기 때문에, 최근에는 DI Container라고 불리기도 한다.
결국 우리는 이 DI Container를 통해서, 우리가 작성한 Class의 관리를 크게 신경쓰지 않고, Spring에 맡길 수 있는 것이다.

 

 

 

 

조립을 하기 위해서 방향성만 정하고 di를 스프링에 맡기게 되면 최하위 객체부터 역으로 조립을 하기 시작한다.

즉, 이를 di 방향이 역전이 되었다. 고 보면 된다.

강한 결합 같은경우 a,b,c,d처럼 정방향으로 이동하지만 느슨한 결합일 경우 d,c,b,a처럼 역방향으로 이동한다.

이걸 쉽게 알 수 있는 경우는 느슨한 결합일때, 예를 들어 c에서 오류가 발생하면 이어서 b,a역시 에러가 생긴다고 한다.

 

Spring Bean

Spring IoC Container가 관리하는 객체를 Spring Bean 이라 한다.

 

Spring Bean을 등록하는 방법

기본적으로 @Component Annotation을 통해 이루어진다. Annotation은 Kotlin 및 Java에서 사용되는 메타데이터의 일종으로, @로 시작되며 프로그램 코드에 부가적인 정보를 제공해주는 역할을 한다. 아래와 같이 클래스를 Spring Bean으로 등록할 수 있다.​

 

@Component
class Dependency()

 

Bean Configuration 파일을 활용하는 방법을 통해서도 가능하다. @Configuration 과 @Bean Annotation을 통해 등록할 수 있다.

 

@Configuration
class DependencyConfiguration() {

    @Bean
    fun exampleDependency(): Dependency {
        return Dependency()
    }
}

 

 


Annotation은 코드에 부가 정보를 추가하는 기능 역할로, @ 기호와 함께 나타나는 표기법으로 주로 컴파일러나 프로그램 실행 기간에서 사전 처리를 위해 사용한다.

 

선언

annotation class 애노테이션명

 

예시

@Test 유닛테스트

 

@ JvmStatic 자바 코드에서 컴패니언 객체를 접근

 

 

다른 Annotation은 어떤 것들이 있을까?

 

 

@ComponentScan
@ComponentScan은 Spring이 빈을 찾을 때 어느 패키지에서 검색할지를 지정하는데 사용된다.
이 Annotation을 사용하면 Spring은 해당 패키지와 하위 패키지에서 @Component, @Service, @Repository, @Controller 등이 붙은 클래스를 스캔하여 빈으로 등록한다.


@Component
@Component는 해당 클래스를 Spring 빈으로 등록하겠다는 것을 표시하는 데 사용된다.


@Autowired
@Autowired는 필드, 생성자, 메서드에 사용되며, 해당 타입의 빈을 자동으로 주입하겠다는 의미다.


@Qualifier
@Qualifier는 여러 빈 중에서 어떤 빈을 주입할지를 명시하는 데 사용된다.


@Configuration
@Configuration은 Java 클래스를 Spring의 설정 파일로 지정한다. 해당 클래스 내부에서 @Bean 어노테이션을 사용하여 빈을 정의할 수 있다.


@Bean
@Bean은 메서드가 반환하는 객체를 Spring 빈으로 등록한다. 주로 @Configuration 어노테이션이 붙은 클래스에서 사용된다.


@Service, @Repository, @Controller
@Service, @Repository, @Controller는 @Component의 특수한 형태로, 각각 비즈니스 로직, 데이터 액세스 로직, 웹 컨트롤러를 나타낸다.


@RestController
@Controller 와 @ResponseBody 가 합쳐진 형태로, 주로 JSON 혹은 XML과 같은 데이터만을 컨트롤러에서 응답할 때 사용한다.


@RequestMapping
@RequestMapping은 컨트롤러 메서드에 URL을 매핑하며, 어떤 요청이 들어왔을 때 해당 메서드를 실행할지를 설정한다.

 

@Target

애노테이션이 지정되어 사용할 종류(클래스, 함수, 프로퍼티 등)를 정의한다.


@Retention

애노테이션을 컴파일된 클래스 파일에 저장할 것인지 실행 시간에 반영할 것인지 결정한다.

 

@Repeatable

애노테이션을 같은 요소에 여러 번 사용 가능하게 할지를 결정한다.


@MustBeDocumented

애노테이션이 API의 일부분으로 문서화하기 위해 사용한다.

 

 


 

Bean은 어떻게 관리될까?

 
 

Bean Scope는 Bean의 생명주기를 말한다. Bean은 기본적으로 객체기 때문에, Instance가 생성된다. 이 Instance의 생명주기를 결정하는 것이 바로 Bean Scope라 할 수 있다.

Bean Scope는 기본적으로 Singleton으로 설정된다. Singleton은 하나의 디자인 패턴으로, 특정 클래스가 인스턴스화 될 때 해당 클래스의 인스턴스가 하나만 생성되도록 보장하는 패턴이다. 즉, Spring에서 기본적으로 Bean은 IoC Container당 하나만이 생성되고, IoC Container와 생명주기를 같이한다.

이렇게 Singleton이 기본 Bean Scope가 되는 이유가 무엇일까? 제일 큰 이유는 성능 향상과 자원 관리에 있다. Instance를 만들고 초기화 하는 것도 비용이기 때문에, 이미 만들어진 Instance를 재사용한다면 전체적인 성능이 향상된다. 또한, 재사용으로 인해 메모리 자원을 효율적으로 사용할 수 있다.

Singleton이기 때문에 주의해야하는 점도 있다. 기본적으로 Bean은 내부에 상태정보를 갖지 않는 Stateless 방식으로 생성되어야 한다. 상태를 갖게 되고 동시에 두 개 이상의 요청이 해당 상태를 변경하려 했을때 예측하지 못한 상태로 변할 수 있기 때문이다.

Singleton 이외에 다른 생명주기들 또한 개발자가 직접 설정이 가능하다.

Prototype: Instance를 요청할 때마다 새로운 Instance를 생성한다.
Request: Bean의 생명주기를 단일 HTTP request와 함께한다. 즉, 각 Http 요청이 올때마다 빈이 생성되고 사라진다.
Session: Bean의 생명주기를 Http session과 함께한다.

Application: Bean의 생명주기가 ServletContext와 함께한다. 언뜻보면 Singleton과 무엇이 다르냐 할 수 있지만, 하나의 Application에서 여러 DispatchServlet을 등록할 수 있기때문에 명확히 다르다.

 


import org.springframework.stereotype.Component
import org.springframework.context.annotation.Scope
import org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE


@Component
@Scope(SCOPE_PROTOTYPE)
class Dependency()

 

이처럼 Annotion을 통해 Scope를 설정할 수 있다.

 

 

 


DI 라는 개념이 확실히 와닿지 않는다. 대충 뭔지는 알겠는데,, 정확히 그걸 적용하고 안적용한 차이를 두드러지게 이해하지 못하겠다,, 내일 튜터님이나 잘 아는 동기분에게 물어봐서 해결해야겠다,, 편리한 부분이 있다는 것도 어느 정도 이해했다. 분명히 어려운 프레임워크지만 그래도 한 번 시작하다보면 나쁘지 않은 것 같다.. 

오늘로 강의를 세 번 들었고 아직도 100퍼센트 이해한건 아니더라도 그냥 이런 개념이 있구나 그렇구나 정도다.

그래도 좀이라도 이해되는 부분의 범위도 늘었으니 다행이랄까,, 첨에 들었을때 진짜 이해 1도 안됐는데 그래도 지금은 조금이라도 아하 그렇구나가 된다. 휴 사실 오늘도 몸도 안좋고 멘탈도 갈렸는데,, 다들 조급해하지말라고 해주고 천천히 해보자고 격려해주니 힘이 난다.. 좀 더 열심히 하면 언젠가 되겠지.. 이런 마인드로 공부 꾸준히 해볼 생각이다

코드카타도 아직 많이 풀지 못했는데 조금씩이라도 풀어 나가보려고 한다 ㅎㅎ 스터디 하기로 한 책 마저 읽고 모르는 거 정리해서 질문 해야지 ㅠ 파이팅 끝날때까지 끝난게 아니다

 

'스프링' 카테고리의 다른 글

TIL 20231226  (0) 2023.12.26
TIL 20231222  (0) 2023.12.22
TIL 20231221  (0) 2023.12.20
TIL 20231220  (0) 2023.12.20
TIL 20231218  (1) 2023.12.18