본문 바로가기

공부 자료/Spring

[Spring Core] DI (Dependency Injection)

DI(Dependency Injection)란?

: IoC의 개념을 조금 더 구체화 한 것으로 의존성 주입을 의미함

즉, IoC 개념을 구체화시킨 것으로 객체간의 관계를 느슨하게 해줌 (느슨한 결합) / 클래스들 간의 강한 결합을 느슨한 결합으로 만듦

 

1. What(의존성 주입이란?)

: 사용하고자 하는 클래스의 객체를 생성하여 참조하면 의존 관계가 성립(클래스 내부에서 다른 클래스의 객체를 생성할 경우)되며, 클래스 내부에서 참조할 클래스의 객체를 생성하지 않고, 생성자를 통해 외부에서 다른 클래스의 객체를 전달받는 것을 의존성 주입이라고 함 (의존 관계 성립 이후 주입까지 이루어져야 의존성 주입이 일어남)

 

2. Why(의존성 주입 필요 이유)

: 의존성 주입 사용 시 new 키워드를 쓸지 말지 여부 결정이 필요함 (애플리케이션 코드 내부에서 직접적으로 new 키워드를 사용할 경우 객체지향 설계의 관점에서 중요한 문제가 발생할 수 있기 때문)

>> new 키워드를 사용해 객체를 생성할 경우 강하게 결합되어 있어 참조 클래스가 바뀌면 모든 클래스를 수정해야 해야 하기 때문에 의존성 주입 혜택을 위해 클래스간의 강한 결합을 피할 필요가 존재할 경우도 있음

 

3. How(느슨한 의존성 주입)

: interface(인터페이스)를 사용하는 것이 가장 대표적인 방법

: 클래스를 직접적으로 의존하는 것이 아닌 이름이 동일한 인터페이스를 의존하도록 할 경우 느슨하게 결합되어 있다고 함

>> 여전히 new를 사용하고 있는데 new 키워드를 제거하고 의존 관계를 느슨하게 만드는 것은 Spring이 대신 해줌

 

4. Who(Spring 기반 애플리케이션에서 의존성 주입)

: Config 클래스에 정의한 객체를 Spring의 도움을 받아 클래스에 제공함으로 new 키워드 삭제

: Config 클래스의 경우 Spring Framework 영역에 해당하여 실제 애플리케이션 핵심 로직에 관여하지 않는 온전한 Spring Framework의 영역

>> Spring에서는 애플리케이션 코드에서 이루어지는 의존성 주입을 Spring에서 대신 해줌

 

public class CafeClient{
    public static void main(String[] args) {
        MenuService menuService = new MenuServiceStub() { //(1) MenuService 클래스 객체 생성 후 getMenuList() 메서드 호출 >> 의존 관계 성립
            @Override
            public List<Menu> getMenuList() {
                return null;
            }
        };
        MenuController controller = new MenuController(menuService); 
        //(3) MenuServiceStub 클래스의 객체를 생성해 MenuService 인터페이스에 할당
        //인터페이스 타입의 변수에 그 인터페이스의 구현 객체를 할당하는 것을 업캐스팅이라고 함
        
        //(4) 위에서 new 키워드를 사용하는 것을 삭제하고 의존 관계를 느슨하게 하기 위해서 위의 것을 아래와같이 변경이 가능함
        /*
        GenericApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        MenuController controller = context.getBean(MenuController.class);
        Config클래스 생성도 필요함
        */
        
        List<Menu> menuList = controller.getMenus();
    }
}

interface MenuService {
    List<Menu> getMenuList();
}

class MenuController{
    private MenuService menuService;

    public MenuController(MenuService menuService){ //(2) 의존성 주입을 위해 MenuController 생성자로 MenuService 객체를 전달받음 (직접 생성하지 않음)
        this.menuService = menuService;
    }
    public List<Menu> getMenus(){
        return menuService.getMenuList();
        //(3)업캐스팅을 통한 의존성 주입으로 MenuController와 MenuService는 느슨한 결합 관계를 유지
    }
}

class MenuServiceStub implements MenuService {
    @Override
    public List<Menu> getMenuList() {
        return List.of(
                new Menu(1, "아메리카노", 2500),
                new Menu(2, "카라멜 마끼야또", 4500),
                new Menu(3, "바닐라 라떼", 4500)
        );
    }
}