본문 바로가기

⭐ SpringBoot/𝄜 게시판 with SpringBoot

28. IOC 와 DI

(영상 참조하여 내용 정리 필요)

# 요약

돈가스를 요리할때 처음에는 쉐프가 재료 준비를 자기가 직접 new 를 통해 fork도 만들고 beef도 만들고 치킨도 만들고 그래야만 했었다. 그렇게 되면 요구사항이 바뀔때마다 요청이 달라질때마다 계속 내부적인 코드를 변경 해야만 했다. 즉 이렇게 비효율적인 방법이 아니라, DI 즉 외부에서 값을 삽입하는 방법으로 즉 DI하는 방식으로 코드를 개션하면 외부의 요구사항이 변하더라도 내부의 코드가 변경되지 않게 코드를 유연하게 작성할 수 있다. 라는것을 확인 하였고, 더 나아가 IoC 컨테이너에 @Component를 통해서 우리가 필요한 객체를 등록하고 이 IoC에 등록된 객체를 @Autowired를 통해서 DI 즉 땡겨올수 있다. 라는것을 코드로 확인 하였다.

 

# IoC와 DI 개념을 설명하고, 코드 예시를 작성

 

프로그램의 흐름이 개발자의 코드가 아닌 외부에 의해 제어되는 개념을 IoC라고 하고, 필요 객체를 외부에서 또다른 객체로 꽂아주는, 주입하는 방식을 DI라고 한다. 이러한 IoC와 DI는 객체간 상호 결합을 낮춰서 더욱 유연한 코드를 작성하도록 한다.

 

실제 코드 예시

기본 패키지 폴더에서 ioc 폴더를 생성한 후 Chef 라는 클래스를 생성한다.

package com.example.firstproject.ioc;

import org.springframework.stereotype.Component;

@Component
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;
    }
}

ChefTest 파일의 전체 코드

package com.example.firstproject.ioc;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class ChefTest {

    @Autowired
    IngredientFactory ingredientFactory;

    @Autowired
    Chef chef;

    @Test
    void 돈가스_요리하기() {
        // 준비
//        IngredientFactory ingredientFactory = new IngredientFactory();
//        Chef chef = new Chef(ingredientFactory);
        String menu = "돈가스";

        // 수행
        String food = chef.cook(menu);

        // 예상
        String expected = "한돈 등심으로 만든 돈가스";

        // 검증
        assertEquals(expected, food);
        System.out.println(food);
    }

    @Test
    void 스테이크_요리하기() {
        // 준비
//        IngredientFactory ingredientFactory = new IngredientFactory();
//        Chef chef = new Chef(ingredientFactory);
        String menu = "스테이크";

        // 수행
        String food = chef.cook(menu);

        // 예상
        String expected = "한우 꽃등심으로 만든 스테이크";

        // 검증
        assertEquals(expected, food);
        System.out.println(food);
    }

    @Test
    void 크리스피_치킨_요리하기() {
        // 준비
//        IngredientFactory ingredientFactory = new IngredientFactory();
//        Chef chef = new Chef(ingredientFactory);
        String menu = "크리스피 치킨";

        // 수행
        String food = chef.cook(menu);

        // 예상
        String expected = "국내산 10호 닭으로 만든 크리스피 치킨";

        // 검증
        assertEquals(expected, food);
        System.out.println(food);
    }
}

IngredientFactory 전체 코드

package com.example.firstproject.ioc;

import org.springframework.stereotype.Component;

@Component // 해당 클래스를 객체로 만들고, 이를 IoC 컨테이너에 등록!
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;
        }
    }
}

Ingredient

package com.example.firstproject.ioc;

public abstract class Ingredient {
    private String name;

    public Ingredient(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Beef

package com.example.firstproject.ioc;

public class Beef extends Ingredient {

    public Beef(String name) {
        super(name);
    }
}

Chicken

package com.example.firstproject.ioc;

public class Chicken extends Ingredient {
    public Chicken(String name) {
        super(name);
    }
}

Pork

package com.example.firstproject.ioc;

public class Pork extends Ingredient{

    public Pork(String name) {
        super(name);
    }
}