포스트

Spring의 IoC와 DI를 어떻게 이해할 것인가

IoC와 DI는 왜 중요한가

스프링을 배울 때 가장 먼저 듣는 개념이 IoC와 DI다. 그런데 용어만 외우면 실전에서 감이 잘 오지 않는다. 두 개념이 공통으로 말하는 것은 객체가 스스로 의존 대상을 만들고 실행 흐름을 통제하지 않는다는 점이다.


DI 먼저 이해하는 편이 쉽다

의존이란

한 클래스가 다른 클래스의 기능에 기대고 있으면 의존 관계가 있다고 본다. 문제는 이 의존이 구체 클래스에 직접 묶일 때다.

1
2
3
public class OrderService {
    private final PaymentClient paymentClient = new KakaoPayClient();
}

이 구조는 교체가 어렵고 테스트도 불편하다.

DI란

필요한 의존 객체를 직접 생성하지 않고 외부에서 주입받는 방식이다.

1
2
3
4
5
6
7
public class OrderService {
    private final PaymentClient paymentClient;

    public OrderService(PaymentClient paymentClient) {
        this.paymentClient = paymentClient;
    }
}

이렇게 하면 구현체를 바꾸기 쉬워지고, 테스트 더블(테스트용 가짜 구현체) 주입도 쉬워진다.


IoC는 더 큰 개념이다

IoC는 제어권이 애플리케이션 코드에서 컨테이너로 넘어가는 것을 말한다. Spring 문서는 DI를 IoC의 특수한 형태로 설명한다. 객체는 생성자 인자나 프로퍼티로 의존성을 선언만 하고 컨테이너가 빈을 만들 때 주입한다. 빈이 의존 대상을 직접 생성하거나 찾아오는 방식의 반대라서 제어의 역전이라는 이름이 붙었다(The IoC Container).

예전에는 main()에서 객체를 만들고 연결하고 호출 순서도 직접 정했다. 스프링에서는 컨테이너가 빈을 만들고 의존성을 연결하고 생명주기를 관리한다. 개발자는 어떤 빈이 필요하고 어떻게 협력해야 하는지만 선언한다.

정리하면 다음과 같다.

  • DI: 의존성을 주입하는 구체적 기법
  • IoC: 제어 흐름이 컨테이너에 역전된 더 큰 개념

왜 생성자 주입을 권장하는가

  • 필수 의존성을 강제할 수 있다.
  • 불변 필드 사용이 쉽다.
  • 테스트가 단순하다.
  • 순환 참조가 빨리 드러난다.

Spring 팀도 생성자 주입을 권한다. 컴포넌트를 불변 객체로 만들 수 있고, 필수 의존성이 null이 아님을 보장하기 때문이다(Constructor-based or setter-based DI?). 필드 주입보다 생성자 주입이 더 명시적이고 안전하다. 주입할 빈의 후보를 고르는 과정은 의존성 주입과 후보 선택에 있다.


실무에서 흔한 오해

  • 스프링 빈이면 자동으로 좋은 설계라고 생각함
  • @Autowired만 붙이면 DI를 이해한 것처럼 느껴짐
  • 프레임워크에 강하게 묶인 코드도 괜찮다고 여김

좋은 설계는 스프링 애너테이션을 떼어내도 역할이 설명되는 구조여야 한다.


정리

객체는 필요한 의존성을 생성자로 선언만 하고, 생성과 연결은 컨테이너가 맡는다. 그래서 OrderService는 KakaoPayClient를 몰라도 되고, 테스트에서는 가짜 PaymentClient를 넘길 수 있다.

참고한 자료외부 출처 2

외부 출처

Spring과 JVM 백엔드
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다