본문 바로가기
서버/스프링

[Spring] Ioc(제어의 역전)와 DI(의존성 주입)의 개념과 그 차이

by 베어 그릴스 2022. 8. 5.
320x100

 

이번에는 스프링 공부를 하는 사람들에겐 필수적이자만 많이들 헷갈려하는 개념인 Ioc ( Inversion of Control ) 와 DI ( Dependency Injection )의 개념에 대하여 알아보겠습니다.

 

 

Ioc - Inversion of Control


우선, 우리 말로 직역하면 '제어의 역전'입니다.

 

이것이 도대체 무슨 말일까요?

 

객체지향 프로그래밍을 하는 사람도 이 말을 처음 듣는다면 난해하게 들릴 것입니다.

 

우리가 Spring이 아닌 메인 메소드를 통해서 작성해왔던 프로그래밍을 생각해봅시다.

 

객체를 우리는 직접 생성하고, 외부 라이브러리를 프로그래머가 직접 관리하고 하는 등

 

제어(코드의 흐름)는 결국 우리의 손에 달려있습니다.

 

그러나 스프링 프레임워크를 사용할 때를 생각해볼까요?

 

우리는 Controller Service등의 객체를 만들긴 하지만 우린 해당 객체들이 언제 호출 되는 지는 전혀 신경 쓰지 않죠.

 

그저 프레임워크가 정해준 방법대로 객체를 생성하면, 프레임워크가 코드를 가져다가 객체를 만들고, 메서드를 호출하고, 소멸시킵니다. 즉, 객체들의 제어는 프레임워크에 있습니다. 

 

제어권이 프레임워크로 넘어간 것이죠.

 

이것이 바로 제어가 역전된 것입니다.

 

위 설명에서 알 수 있듯 프레임워크와 라이브러리의 차이를 Ioc 개념으로 설명할 수 있습니다.

 

인터페이스나 추상 클래스를 사용하는 경우를 봅시다.

 

추상 클래스의 메서드들의 정의와 코드의 흐름은 추상 클래스에 있지만 실질적 구현은 실질적으로 하위 클래스에서 일어납니다.

 

즉, 하위 클래스에서의 구현이 상위 클래스에서 알아서 필요할 때 사용되게 됩니다.

 

바로 이처럼 코드 흐름이 제 3자에게 위임되는 것이 Ioc라고 할 수 있습니다.

 

우리는 구현을 하위 클래스에서 하지만 제어권(코드의 흐름)은 상위의 추상클래스에 있는 것도 제어의 역전의 한 예시라고 볼 수 있습니다.

 

제어의 역전의 목적은 다음과 같습니다.

  • 작업의 구현과 작업 흐름 자체를 분리한다.
  • 모듈을 제작할 때, 모듈과 외부 프로그램의 결합에 대해 고민할 필요 없이 모듈의 목적에 집중할 수 있다. ex) 하위클래스를 구현하는데에 집중하고, 이 흐름은 외부에 있으므로 신경쓰지 않는다.
  • 다른 시스템이 어떻게 동작할지에 대해 고민할 필요 없이, 미리 정해진 협약대로만 동작하게 하면 된다.
  • 모듈을 바꾸어도 다른 시스템에 부작용을 일으키지 않는다.

참고 : https://ko.wikipedia.org/wiki/%EC%A0%9C%EC%96%B4_%EB%B0%98%EC%A0%84

 

제어 반전 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전.

ko.wikipedia.org

 

DI (Dependency Injection)


우리말로 직역해보면 '의존성 주입'입니다.

 

Ioc는 위에서도 느꼈겠지만 광범위한 또, 일반적인 개념 그리고 용어입니다.

 

DI는 Ioc를 구현하기 위한 하나의 디자인 패턴이고, 이름 그대로 객체간의 의존 관계를 외부에 주입시키는 패턴을 말합니다.

 

식상한 예시지만 차 예시로 예를 들어보겠습니다.

 

public class Car {
    private final Engine B = new EngineB();
}
public interface Engine {
    String sample();
}
public class EngineA implements Engine{
    public String sample(){
        return "A";
    }
}
public class EngineB implements Engine{
    public String sample(){
        return "B";
    }
}

차라는 큰 틀에 다른 종류의 엔진 A와 B를 넣는다고 해보겠습니다.

 

DI 개념 없이 개발을 할 때는 사실 그냥 Car 객체에서 엔진 B가 필요하면 코드를 고쳐 새로 넣어주고, 엔진 A가 필요하면 코드를 고쳐 새로 넣어주고 할 것입니다.

 

엔진의 교체는 Car 객체는 사실 알 필요가 전혀 없는데 엔진의 교체에 영향을 받게 됩니다.

 

이런 경우를 'A가 Engine에 의존한다' 라고 합니다.

 

그러면 여기서 의존성 주입을 어떻게 해주느냐

 

지금은 객체의 인스턴스를 Car 객체가 직접 생성해주는데 이를 생성자 혹은 setter,인터페이스 등을 통해서 외부에서 생성하여 넣어주게 하는 것입니다. (외부에서 의존성을 주입)

 

그렇다면 실질적으로는 Car 객체는 어떤 엔진이 들어올지는 몰라도 코드 교체 없이 계속 자기의 할 일만 하면 됩니다.

 

코드를 봅시다.

public class Car {
    private final Engine B;

    public Car(Engine b) {
        B = b;
    }
}

 

이렇게 생성자를 통해서 Engine에 어떤 엔진이 올지 외부에서 의존성을 주입해주면 Engine이 교체되어도 Car 객체 내부의 코드를 고칠 일은 모두 사라지게 됩니다.

 

결국 중요한 점은, 의존 대상을 객체가 직접 결정하는 것이 아니라 외부로부터 주입을 받는 것이 중요합니다.

 

객체에서 제어를 하는 것이 아닌 외부에서 제어를 하게되므로, Ioc의 부분집합으로도 볼 수 있는 것이죠.

 

DI를 사용의 정점은 다음과 같습니다.

  • 코드의 변경에 유연하다.
  • 객체를 사용하고자 하는 클라이언트는 해당 객체의 구체적인 구현을 전혀 알 필요가 없어진다.
  • 코드의 가독성이 매우 높다.

 

DI와 IOC는 스프링에서도 매우 중요한 개념이지만, 객체지향개발에서도 동일하게 적용될 수 있는 개념입니다.

매우 매우 중요하니 꼭!!! 이해하고 넘어가도록 합시다😉

 

728x90