[스프링 부트 입문 29] IoC와 DI(객체를 주입한다고?)

Kevin·2022년 6월 15일
0

이 글은 유튜브에서 홍팍님의 "[스프링 부트 입문 29] IoC와 DI(객체를 주입한다고?)"을 수강 후 개인적으로 공부한 내용을 정리한 글입니다.

Mission IoC와 DI 개념을 설명하고, 코드 예시를 이야기하시오.
IoC
Injection of Container (제어의 역전)
개발자가 직접 오브젝트를 생성하고 의존 관계를 맺는 수고로움을 덜 수 있다.

IoC의 장점
1. 모듈간의 의존성이 낮아진다.
2. 더욱 객체지향 적인 코드를 작성할 수 있다.
3. 프로그램의 수행과 구체적인 구현을 분리시킬 수 있다.


DI
Dependency Injection(의존성 주입)
간단하게 말하면 DI란 유저의 코드에 외부 컨테이너에 있는 코드가 주입되는 것이다.

DI의 장점
1. 테스트하기 좋은 코드가 된다.
2. 의존성이 줄어든다.
3. 가독성이 높아진다.
4. 재사용성이 높은 코드가 된다.


public class Chef {
    public String cook(String menu) {
        // 요리 재료 준비
        Pork pork = new Pork("한돈 등심");
        Beef beef = new Beef("한우 꽃등심");
        // 요리 반환
        return pork.getName() + "으로 만든 " + menu;
     }
 }

다음과 같은 코드는 Chef가 직접 menu를 조달하고 있기 때문에 의존성이 강한 코드.
menu가 늘어남에 따라 Testcase를 작성할 때 직접 추가 해야므로 매우 번거롭다.

public class IngredientFactory {
    public Ingredient get(String menu) {
        switch (menu){
            case "돈가스":
                return new Pork("한돈 등심");
            case "스테이크":
                return new Beef("한우 꽃등심");
            default:
                return null;
        }
    }
public class Ingredient {
    private String name;
    public Ingredient(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
public class Beef extends Ingredient{
    public Beef(String name) {
        super(name);
    }
}
public class Chef {
    // 셰프는 식재료 공장을 알고 있음
    private IngredientFactory ingredientFactory;
    // 셰프가 식재료 공장과 협업하기 위한 DI
    public Chef(IngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }
    public String cook(String menu) {
        // 요리 재료 준비
        Ingredient ingredient = ingredientFactory.get(menu);
        // 요리 반환
        return ingredient.getName() + "으로 만든 " + menu;
     }
}

DI를 통한 코드 개선 효과
위와 같이 Chef와 Cook사이에 IngredientFactory 라는 조달 공장을 두어 Chef는 '요리' 라는 행동에 집중할 수 있게 되어 의존성이 줄어들고 코드 변경 없이 모두 요리가 가능해진다.

요구사항에 의한 유연한 확장
다음과 같이 추가로 주문이 들어 올 때도, 유연하게 확장이 가능하다.

public class IngredientFactory {
    public Ingredient get(String menu) {
        switch (menu) {
            case "돈가스":
                return new Pork("한돈 등심");
            case "스테이크":
                return new Beef("한우 꽃등심");
            case "크리스피 치킨":
                return new Chicken("국내산 10호 닭");
            default:
                return null;
        }
    }
}
public class Chicken extends Ingredient {
    public Chicken(String name) {
        super(name);
    }
}

@Component
해당 클래스를 객체로 만들고, 이를 IoC 컨테이너에 등록하는 어노테이션

@Autowired
IoC 컨테이너에 등록된 컴포넌트를 자동으로 DI되게 만들어 준다.

@Component
public class Chef {
    // 셰프는 식재료 공장을 알고 있음
    private IngredientFactory ingredientFactory;
    // 셰프가 식재료 공장과 협업하기 위한 DI
    public Chef(IngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }
    
// Chef chef = new chef(); // 기존 방식
@Autowired // IoC 방식
Chef chef;

이처럼 요구사항이 늘어나도 변경사항에 유연하며 코드가 간편해짐을 알 수 있다.

profile
성장해나가는 개발자입니다.

0개의 댓글