티스토리 뷰

IoC (제어의 역전)란 무엇인가?

Inversion of Control is a design pattern in which custom-written portions of a computer program receive the flow of control from a generic framework.
-> 제어의 역전이란 하나의 디자인 패턴으로, 사용자가 작성한 부분이 어느 프레임워크에 의해 제어를 받게 되는 것을 의미한다.
여기서 flow of control 제어 흐름이란, 객체의 생성/수정/삭제, 메서드 호출 등 프로그램의 전체 흐름을 말하는 것이다.
- 위키피디아

 

간단하게 말하면 프레임워크가 사용자의 코드를 호출하는 것이다.

 

일반적인 프로그래밍에서는 어플리케이션 코드 (개발자가 작성한 코드)에서 프로그램의 흐름 (flow)를 제어한다.
처음 C언어를 배울 때 많이 연습했던 main 함수 내에 코드를 작성하고, 직접 만들었거나, 만들어진 라이브러리의 함수들을 호출하도록 하는 코드가 바로 이런 케이스다.

IoC는 이것과 정 반대의 개념으로, 프레임워크에서 어플리케이션 코드가 실행되도록 맡기는 것을 의미한다.

 

여기까지가 대부분의 개발 관련 글에서 볼 수 있는 제어의 역전에 대한 내용이고, 이후엔 보통 의존성 주입 (DI)로 넘어간다.

하지만 여러 글을 읽을수록 IoC와 DI의 경계가 흐려지고, 각 개념이 정확히 뭔지에 대해 이해하기 어려웠다.

심지어, IoC를 이렇게 정의하는 일부 글도 있었다.
"IoC는 IoC 컨테이너에서 어플리케이션 코드가 실행되도록 하는 것을 의미한다."

 

영어로 작성된 여러 글들을 보면서 내린 결론은 일반적인 IoC 개념과 스프링이라는 범위에서 말하는 IoC가 어느정도 다르다는 것이다.

실제 위키피디아의 Inversion Of control 문서를 읽어보면 이런 맥락의 설명이 있다.

 

Inversion of control has been widely used by application development frameworks since the rise of GUI environments and continues to be used both in GUI environments and in web server application frameworks. Inversion of control makes the framework extensible by the methods defined by the application programmer.

Event-driven programming is often implemented using IoC so that the custom code need only be concerned with the handling of events, while the event loop and dispatch of events/messages is handled by the framework or the runtime environment. In web server application frameworks, dispatch is usually called routing, and handlers may be called endpoints.

The phrase "inversion of control" has separately also come to be used in the community of Java programmers to refer specifically to the patterns of injecting objects' dependencies that occur with "IoC containers" in Java frameworks such as the Spring framework. In this different sense, "inversion of control" refers to granting the framework control over the implementations of dependencies that are used by application objects rather than to the original meaning of granting the framework control flow (control over the time of execution of application code e.g. callbacks).

 

요약하자면 "제어의 역전" 이라는 표현은 먼저 GUI가 발전하면서 사용되기 시작했고, 이후에 스프링 프레임워크에서 의존성 주입을 하는 패턴을 특정해서 지칭하는 단어로 쓰였다는 것이다.

특히, 스프링에서 말하는 IoC는 프레임워크에게 어떤 구현체 사용할 것인지에 대한 제어를 맡기는 것을 의미한다.

이게 무슨 뜻인지 다음 코드에서 살펴보자.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Application {
    private final NotificationService notificationService;

    @Autowired
    public Application(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public void processNotification() {
        notificationService.notify("application process!");
    }
}

@Component
public interface NotificationService {
    void notify(String message);
}

@Component
public class EmailNotificationService implements NotificationService {
    public void notify(String message) {
        System.out.println("Email Notification: " + message);
    }
}

@Component
public class SmsNotificationService implements NotificationService {
    public void notify(String message) {
        System.out.println("SMS Notification: " + message);
    }
}

 

이 코드를 보면 어떤 NotificationService를 사용할 것인지 코드 레벨에서 명시하지 않는다.

이에 대한 설정은 Spring application context에서 이루어지는데, 바로 런타임에 이 둘 중 어느 구현체를 사용할 것인지 선택한다.

 

이런 배경에서 보면 "Inversion of Control" 의 정의가 어디서 차이가 발생하는지 알 수 있다.

바로 "Control"의 대상이 무엇이냐를 서로 다르게 보고 있던 것이다.

일반적인 프로그래밍 원칙에서 Control은 바로 Control Flow, 코드의 실행 순서인 흐름의 제어를 의미했던 것이다.

반면 스프링에서 말한 Control은 어떤 구현체 사용할 것인지 (객체에 대한 관리)에 대한 권한을 의미한다.

 

그렇기 때문에 스프링 공화국인 한국에서는 보통 IoC라고 하면 스프링에서 말하는 개념인 DI와 거의 동일한 개념으로 통용되던 것이다.

그렇다면 의존성 주입(DI)에 대해 자세하게 알아보자.

 

의존성 주입(DI)란 무엇인가?

위에서 말했듯이, 의존성 주입은 제어의 역전을 구현하는 방법 중 하나이다.

 

의존성이란 어떤 한 객체가 제 기능을 하기 위해서 다른 객체를 필요하는 것을 의미한다.

public class Store {
    private Item item;
 
    public Store() {
        item = new ItemImpl1();    
    }
}

이 예시 코드에선 Store 클래스는 Item을 멤버 변수로 가지고 있으며, 초기화 시 Item의 구체적인 타입까지 명시하며 직접 생성하고 있다.

즉, Store는 Item에 대한 의존성을 가지고 있는 것이다.

 

위 방식과 달리 객체가 스스로 의존성을 명시하지 않고, 외부에서 의존 관계를 받는 행위를 "주입(injection)"이라고 한다.

public class Store {
    private Item item;
    public Store(Item item) {
        this.item = item;
    }
}

위 코드에서는 Store가 어느 Item 구현체를 가지게 될지 명시하지 않았다.

외부에서 Store 인스턴스를 생성할 때 어느 특정 구현체를 가지게 될지 설정하게 되며, Store 입장에서는 "의존성을 주입 받았다" 라고 할 수 있는 것이다.

 

또한 이전에 살펴본 IoC 개념으로 위 예시를 보면, Store 입장에선 스스로 Item의 구현체를 결정하던 권한이 외부로 넘어갔기 때문에 객체 생성 제어가 역전된 것이다. 즉 IoC를 DI라는 구체적인 수단으로 이룬게 된 것이다.

출처: https://www.baeldung.com/cs/ioc

 

스프링에서 의존성 주입은 여러 가지 방식으로 이루어지는데, 이는 다음 글에서 살펴보겠다.

 

참고

1) https://www.baeldung.com/cs/ioc

2) https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring

3) https://en.wikipedia.org/wiki/Inversion_of_control

 

'Java > Spring' 카테고리의 다른 글

스프링이란 무엇인가  (0) 2022.02.08