[Spring] 의존성 주입(DI)과 제어의 역전(IoC)

2024. 2. 25. 16:03BE/Spring

728x90

본 포스팅은 의존성 주입(DI)과 제어의 역전(IoC)에 대해 다룹니다.

의존성(Denpendency)

'A가 B에 의존성을 가진다'라는 뜻은 B가 변할 때마다 A도 함께 변해야하는 것을 말합니다. 아래의 예시를 보겠습니다.

아래의 예시에서 comsumer가 eat 메소드를 호출하면, chicken 객체의 eat 메소드가 호출됩니다. 코드 동작상으로는 문제가 없지만, 다른 음식을 먹고 싶으면 eat 메소드를 수정해야하는 번거로움이 있습니다. 

위와 같은 경우 Consumer와 Chicken은 강한 의존성을 가진다라고 말할 수 있습니다.

# Code Snippet 1
public class Consumer {

    void eat() {
        Chicken chicken = new Chicken();
        chicken.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.eat();
    }
}

class Chicken {
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

 

느슨하게 만들기

그렇다면 어떻게 의존성 및 결합을 느슨하게 할 수 있을까요 ? Java에서는 Interface의 다형성의 원리를 이용하여 의존성 및 결합을 느슨하게 만들 수 있습니다. 

아래의 코드에서는 Food를 구현하는 Chicken, Pizza 클래스를 선언하여 eat 메소드의 인자로 넣어주고 있습니다. 이렇게 코드를 작성하면, consumer는 음식들과의 의존성이 낮아지게 됩니다.

public class Consumer {

    void eat(Food food) {
        food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.eat(new Chicken());
        consumer.eat(new Pizza());
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

 

의존성 주입(Dependency Injection)

위의 예시에서처럼 Interface를 이용하여 각 음식 객체의 클래스를 인자로 주게되는데, 이 행위를 의존성 주입이라고합니다. 조금 더 정돈된 코드는 아래와 같습니다. 

# Code Snippet 2
public class Consumer {

    Food food;

    void eat() {
        this.food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.food = new Chicken();
        consumer.eat();

        consumer.food = new Pizza();
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

 

제어의 역전(Inversion of Control)

Code Snippet1에서는 제어의 흐름이 Consumer -> Food 입니다. 즉, Consumer 클래스를 먼저 정의하고, Food 클래스를 정의해야했습니다. 이때는 두 클래스의 의존성이 너무 큰 문제가 있었습니다.

Code Snippet2에서는 제어의 흐름이 Food -> Consumer 입니다. 즉, Consumer에게 줄 Food 클래스를 먼저 정의하고 Consumer 클래스를 정의하게됩니다.

위의 설명과 같이 의존성 주입을 통해 주입되는 클래스 객체를 먼저 제어하게 되는 경우를 제어의 역전이라고 부릅니다.