Post

IoC와 DI

IoC와 DI

IoC (Inversion of Control)

직역하면 제어의 역전인데, 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부 프레임워크나 컨테이너에서 관리하는 것을 뜻한다.
스프링에서 빈의 생명 주기를 개발자가 아닌 프레임워크가 관리하는 것을 IoC라고 볼 수 있는데, 객체의 생성과 사용을 담당하는 주체가 개발자가 아니라 프레임워크로 바뀌면서 제어의 흐름이 반전되었다는 의미이다.

프레임워크 vs 라이브러리
스프링이나 JUnit과 같이 전체적인 흐름에 맞춰 개발자가 코드를 작성하면 (개발자의 코드를 제어하면) 프레임워크이다.
반대로 개발자의 코드가 직접 흐름을 제어한다면 라이브러리이다.



DI(Dependency Injection)

직역하면 의존성 주입인데, 혹자는 의존관계 주입이라고 표현한다.
DI는 IoC의 구현 방법 중 하나로, 객체가 사용할 의존 관계를 직접 설정하지 않고, 외부에서 주입받는 것을 뜻한다.

예를 들어, 주문을 처리하는 OrderService 클래스가 있다고 해보자.
이 클래스에서 주문 금액을 계산할 때, 할인 정책을 적용해야 한다.
이를 위해 OrderService는 DiscountPolicy를 의존해야 하는데, DiscountPolicy의 구현체는 다음과 같이 두 개가 있다.

다음은 DI를 적용하지 않은 코드이다.

1
2
3
4
5
public class OrderService {
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

    ...
}

위 코드를 보면, OrderService 객체가 의존할 DiscountPolicy의 객체를 직접 생성하고 사용하는 것을 볼 수 있다.
이때, FixDiscountPolicy를 사용하려 한다면, 직접 OrderService 코드를 수정해야 한다.
이는 OCP를 위반한 것이고, 이로 인해 결합도가 증가하여 유지보수나 단위 테스트가 어려워진다.
시스템 전체적으로 사용할 DiscountPolicy를 변경할 때, OrderService 코드만 변경하면 큰 문제가 없다.
하지만 DiscountPolicy를 의존하는 많은 클래스들이 있다면, 코드를 수정하는데 많은 시간이 소요될 수 있다.
이 외에도 테스트 시에 사용할 DiscountPolicy를 변경하기 쉽지 않은 등의 이유로 DI를 적용시키는 것이다.


DI 적용 방법

스프링 부트에서 DI를 적용시키는 방법으로는 주로 세 가지가 있다.


1. 필드 주입

1
2
3
4
5
@Service
public class Service {
    @Autowired
    private Repository repository;
}

필드에 직접 주입하는 방식이다.
외부에서 의존관계를 변경하기 힘들기 때문에 거의 사용하지 않지만, 외부에서 호출하지 않는 단위 테스트에서는 가끔 사용한다.


2. 세터 주입

1
2
3
4
5
6
7
8
9
@Service
public class Service {
    private Repository repository;
    
    @Autowired
    public void setRepository(Repository repository) {
        this.repository = repository;
    }
}

Setter 메소드를 통해 의존성을 주입하는 방식이다.
런타임 때 외부에서 언제든 의존관계를 바꿀 수 있기 때문에, 위험한 방식이다.


3. 생성자 주입

1
2
3
4
5
6
7
8
9
@Service
public class Service {
    private final Repository repository;
    
    @Autowired // 생략 가능
    public Service(Repository repository) {
        this.repository = repository;
    }
}

스프링에서 추천하는 방식이다.
클래스의 생성자가 하나밖에 없고, 클래스의 객체가 빈으로 등록되어 있으면 @Autowired를 생략할 수 있다.

DI 컨테이너
객체를 생성하고 관리하면서, 여러 의존관계를 연결해주는 것을 DI 컨테이너라고 한다.
DI 컨테이너, 어셈블러, 오브젝트 팩토리 등으로 불리기도 한다.
스프링에서는 스프링 컨테이너라고 하는 DI 컨테이너를 제공한다.

This post is licensed under CC BY 4.0 by the author.