Spring을 사용하는 이유이자, 가장 중요한 핵심 컨셉인 DI 와 IOC에 대해 알아봅시다.
- 한국말로 하면 의존관계 주입, 이게 무슨 말일까?
A가 B를 의존한다 = B가 변하게 되면, B를 의존하고 있던 A 또한 영향을 받게 된다.
예를 들어, 햄버거를 만든다고 해보자. 햄버거를 만드는 요리사가 햄버거 레시피에 의존하게 된다. 이때, 햄버거 레시피가 변하게 되면 요리사는 햄버거 만드는 과정을 수정해야 할 것이다. 이게 바로 의존관계이다.
하지만 이걸 직접적으로 코드로 나타나게 되면, 다양한 햄버거를 만들고 싶을 때, 햄버거 셰프 내부적으로 레시피를 수정해야 한다. 외부에서 이 레시피로 바꿔줘요! 주문이 불가능하고, 셰프가 직접 바꿔줘야 한다는 것이다.
이처럼, 외부에서도 이 의존관계를 변경할 수 있게 외부에서 의존관계를 결정하고 주입하는 것이 DI(Dependency Injection)인 것이다. 여기서 말하는 외부는 결국 개발자인 우리가 될 것이다.
토비의 스프링 책에서는 3가지 조건을 충족해야 DI를 만족한다고 한다.
- 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스에만 의존하고 있어야 한다.
- 런타임 시점의 의존관계는 Container 나 Factory 같은 제 3의 존재가 결정한다.
- 의존 관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 주입해줌으로써 만들어진다.
구현 방법, 즉 DI 의존관계를 외부에서 주입하는 방법 = 클래스 변수를 결정하는 방법이다.
이 방법에는 3가지가 존재한다.
- 생성자를 이용
- 수정자(Setter)를 이용
- Method를 이용 → Setter는 하나의 파라미터만 받을 수 있지만, Method는 여러 파라미터를 받을 수 있다.
사실 2,3번은 구분해도 되고 구분하지 않아도 될 것 같다. 어차피 Setter 또한 Method의 일종이니..보통 Setter를 가장 많이 사용하는 것 같다.
- 의존성이 줄어든다 → 주입 대상이 변하더라도 구현 자체가 크게 변할일이 없다.
- 재사용성의 증가 → 인터페이스를 만들어 놓으면, 다른 클래스에서 재사용할 수 있다.
- 테스트하기에 좋다 ( 단위 테스트 가능 )
- 가독성이 높아진다.
- 해석하면 제어의 역전 이라는 뜻인데, 이게 무슨 말일까?
- 제어의 흐름을 바꾼다라고 생각해보자
객체의 의존성을 역전시켜 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여, 중복성은 낮추고 가독성을 높이며 유지 보수를 편하게 해준다. Spring IoC Container가 이 기능을 제공한다.
구체적으로 어떻게 다른지 살펴보자면, 기존 객체 제어의 흐름은 다음과 같다.
- 객체 생성
- 의존성 객체 생성 - 클래스 내부에서 생성
- 의존성 객체 메소드 호출
하지만 스프링에서 객체 제어의 흐름은 다음과 같다.
- 객체 생성
- 의존성 객체 주입 - 제어권을 스프링에게 넘겨 스프링이 만들어 놓은 객체를 주입
- 의존성 객체 메소드 호출
그러니까, 모든 의존성 객체를 스프링이 실행될 때 IoC 컨테이너에 Bean으로 등록을 해놓고, 이것을 필요한 곳에 주입시켜주는 것이다. 또한, 이 때문에 Bean은 싱글턴 패턴의 특징을 갖게 된다.
Autowired도 이와 같은 흐름 때문에 가능한 것이다. Bean으로 등록되어 있으면 알맞는 Bean을 컨테이너에서 찾아서 주입시켜주는 방식.
- Spring을 사용한다면 xml로 빈을 등록하거나, Component Scan을 사용하거나, Configuration에서 @Bean 어노테이션을 사용하여 등록하거나 했어야 한다.
- 하지만 실제 코드에서는 우리가 이와 같은 설정을 한 기억이 없다!
- 왜냐하면 Spring Boot에서 Bean으로 등록되는 어노테이션들을 제공하기 때문
- @Controller, @Service, @Repository 등의 어노테이션들이 @Component 어노테이션을 포함하고 있으며, 그렇기 때문에 Bean으로 자동 등록이 되는 것이다.
https://tecoble.techcourse.co.kr/post/2021-04-27-dependency-injection/