IoC와 DI

MinSeong Kang·2022년 1월 9일
0

spring

목록 보기
7/18
post-thumbnail

IoC (제어의 역전)?

프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것

개발자 입장에서는 구현 객체가 프로그램의 제어 흐름을 스스로 제어하는 것이 자연스러운 흐름이다.

public class ServiceImpl implements Service {

    private final Repository repository = new A_Repository();
    private final Policy policy = new A_Policy();

    @Override
    ...
}
  • 위 코드를 보면, Service 인테페이스를 상속한 ServiceImpl 클래스가 직접 필요한 인터페이스를 호출하고, 어떤 구현 객체를 사용할지도 선택한다. → 자연스러운 흐름
  • 하지만 이는 DIP 의존관계 역전 원칙을 지키지 않는 코드이다.
    DIP 원칙에 의하면 '추상화에 의존해야지, 구체화에 의존하면 안된다.'를 만족해야하지만 ServiceImpl 클래스는 추상화 인터페이스(Repository, Policy)에 의존하고, 구체화 구현 클래스(A_Respositor, A_Policy)에도 의존하고 있다. → DIP 의존관계 위배

위 코드가 DIP 원칙을 지키기 위해서, 아래와 같이 위 코드를 수정하고 설정 클래스(AppConfig)를 추가해야 한다.

public class AppConfig {
    
    public Service service() {
        return new ServiceImpl(new A_Repository(), new A_Policy());
    }
}
public class ServiceImpl implements Service {

    private final Repository repository;
    private final Policy policy;

    public ServiceImpl(Repository repository, Policy policy) {
        this.repository = repository;
        this.policy = policy;
    }

    @Override
    ...
}
  • 기존 ServiceImpl 클래스를 추상화 인터페이스(Repository, Policy)에만 의존하고 구현 클래스는 의존하지 않도록 코드를 변경하였다.
    → 따라서 DIP 원칙은 지켰지만, ServiceImpl 클래스는 구현 객체를 모른채 인터페이스만 가지고는 아무것도 실행할 수 없다.
    → 여기서 필요한 개념이 DI (의존관계 주입) 이다.

IoC 설명을 마무리 하기위해 DI에 대해서 간단히 설명하면, DI는 실행 시점에 필요한 객체를 직접 생성하는 것이 아니라 외부로 부터 필요한 객체를 받아서 사용하는 것이다.

  • 따라서 ServiceImpl 클래스가 필요한 구현 객체를 AppConfig 클래스가 생성하여 실행 시점에 생성자를 통해서 전달받아 사용할 수 있다.
  • ServiceImpl 클래스는 자신이 어떤 구현 객체를 사용할지도 모른채 자신의 로직을 실행하는 것에 집중할 수 있다.
  • ServiceImpl의 제어흐름에 대한 권한을 AppConfig 클래스가 가져갔다고 할 수 있으며, 이와 같이 자신의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)라고 한다.


DI (의존관계 주입)?

실행 시점에 외부에서 실제 구현 객체를 생성하고 이를 필요로 할 클래스에게 전달하여 두 클래스의 실제 의존관계가 연결되는 것

  • 먼저, 의존 관계는 정적인 클래스 의존관계동적인 객체 의존 관계로 분리된다.
  1. 정적인 클래스 의존관계
    클래스가 사용하는 Import 코드만 보고 쉽게 정적인 클래스 의존관계를 파악할 수 있으며, 어플리케이션을 실행하지 않아도 알 수 있다.
public class ServiceImpl implements Service {

    private final Repository repository;
    private final Policy policy;

    public ServiceImpl(Repository repository, Policy policy) {
        this.repository = repository;
        this.policy = policy;
    }

    @Override
    ...
}

위 코드를 보면, ServiceImpl 클래스는 Repository와 Policy를 의존한다고 할 수 있다. 하지만 실제 어떤 구현 객체가 ServiceImpl 클래스에 주입될지는 알 수 없다.

  1. 동적인 객체 의존 관계
    어플리케이션 실행 시점에 파악 가능하다.
public class AppConfig {
    
    public Service service() {
        return new ServiceImpl(new A_Repository(), new A_Policy());
    }
}

ServiceImp 클래스에 Repository와 Policy으로 A_Repository와 A_Policy를 주입한다는 것은 어플리케이션 실행 시점에 알 수 있다.

  • 의존관계 주입을 사용하면, 정적인 클래스 의존관계를 변경하지 않고, 동적 객체 의존 관계를 쉽게 변경 가능하다.
  • 또한 의존관계가 있는 대상이 바뀌거나 변경되더라도 자신은 영향을 받지 않으며, 변경을 통한 다양한 확장이 자유롭다.

참고 자료

https://www.inflearn.com/course/스프링-핵심-원리-기본편#

0개의 댓글