Back to the Basics

[NestJS] IoC(Inversion of Control) 제어의 역전과 DI(Dependcency) Injection)에 대해 알아보자 본문

Programming Languages/Typescript & NestJS

[NestJS] IoC(Inversion of Control) 제어의 역전과 DI(Dependcency) Injection)에 대해 알아보자

9Jaeng 2023. 10. 15. 21:15
728x90

Nest.js의 레이어는 기본적으로 [ Controller - Service - Repository ] 레이어로 구성이 된다. Controller Class에서는 Service를 호출하고 Service Class에서는 Repository를 호출한다. 이렇듯 각 Class는 다른 Class에 대한 의존성이 존재한다.

Nest.js는 이런 의존성을 관리하기 위해 "제어의 역전" 이라는 개념과 "의존성 주입"이라는 개념을 사용한다.(사실 이 두 개념은 객체지향과 관련된 주제이기도 하다) Nest.js의 동작은 모두 "의존성 주입"이라는 개념으로부터 시작한다고 한다.

프로그램을 짜다보면 나와있는 매뉴얼대로 빠르게 적용을 하는 것도 중요하지만 기본적인 원리를 알아두면 응용하기에 도움이 많이 되고 어떤 애러가 발생했을 때에도 원인 파악에 도움이 많이 될 때가 종종 있었다. Nest.js 프레임워크를 잘 사용하기 위해 기본 개념이 되는 의존성 주입이 무엇이고 왜 사용하는지 알아보자

이번 포스팅에서는 제어의 역전과 의존성 주입이라는 개념에 대해 간단하게 알아보고 다음 포스팅에서 Nest.js 프레임워크에서 이를 어떻게 관리하는지 차근차근 알아보자.

IoC(Inversion of Control) 제어 연전

일반적으로 Instance의 생성 또는 삭제는 프로그래머가 관리하는 영역이다. 하지만 이를 프로그래머에서 프레임워크가 관리하도록 제어권이 역전되는 것을 Inversion of Control이라고 한다.

제어의 역전에서 중요한 원칙은 클래스는 자체 종속성을 만들지 말아야 한다는 원칙이 있다.
Inversion of Control Principle

  • Classes should not create instances of its dependencies on its own

Class가 자체적인 종속성을 만들지 않게 하기 위해 의존성 주입 이라는 개념이 등장한다

DI(Depencency Injection) 의존성 주입

만약 A Class에서 B Class의 기능을 사용하고 있다면 A는 B에 의존하고 있다라고 할 수 있다.

Nest.js의 레이어는 기본적으로 [ Controller - Service - Repository ] 레이어로 구성이 된다.
Service는 Repository에 의존한다. Repository가 없으면 Service는 제대로 작동하지 않을 것이다.
마찬가지로 Controller는 Service에 의존한다. Service가 없으면 Controller는 제대로 작동하지 않을 것이다.
이 세 개의 Class 사이에는 분명한 종속성이 존재한다.

의존성 주입이란 이렇게 종속 관계에 있는 Class들이 직접 종속성을 생성하는 것이 아닌 외부에서 주입받는 것을 의미한다.
A Class에서 직접 B를 생성하는 것이 아닌 외부에서 B를 주입받는 것이다. 여기서 외부는 프레임워크 즉, Nest.js이다.

위의 그림에서 보듯이 Nest.js의 Conrainer 라는 곳에서 종송석을 주입(Injection)하게 된다.

자체 종속성을 만드는 경우

만약 자체적으로 종속성을 만든다면 어떨까? 아래의 코드를 확인해 보자

export class MessageController {
    messagesService: MessagesService;

    constructor() {
        this.messagesService = new MessagesService();
    }
}

위의 MessageController MessagesService 의존한다(종속성이 있다) 그리고 constructor에서 직접 new MessagesService()를 호출하여 Service Instance를 생성하고 있다.
위의 코드를 사용한다면 MessageController 만들 때마다 새로운 MessagesService Instance를 생성해야 하고 메모리 관리까지 신경 써야 하는 문제가 있다.
그리고 MessagesService Class가 바뀌면 MessageController에 생성된 MessageService Instance도 같이 바꿔주어야 한다. 이런 경우를 "강한 결합"이라고 한다.

생성자를 통해 외부에서 주입받는다.

위의 코드를 리팩터링 해보자

export class MessageController {
    constructor(private messagesService:MessagesService) {}
}

위의 코드는 MessageController를 생성할 때마다 MessageService를 자체적으로 생성하지 않고 이미 생성되어 있는 MessageService Instance를 생성자에서 인수로 받고있다. 즉, 생성자의 인자로서 외부에서 종속성을 주입받도록 되어있다.

그렇다면 이미 생성되어있는 MessageService Instance는 어디서 생성이 되고 어디서 넣어주는 것일까? 이런 역할을 하는 것이 바로 Nest.js 프레인워크이다. Nest.js 프레임워크는 싱글톤 패턴으로 하나의 Instance만 생성하고 관리하는 역할을 한다.

위의 내용을 정리해 보면 Nest.js 프레임워크에서 Instance를 생성하고 필요한 곳에 주입해 준다. 즉, 의존성 주입(DI)을 통해 제어의 역전(IoC)을 구현(?) 한 것이라고 할 수 있을 것 같다.

그렇다면 Nest.js에서는 어떻게 의존성 주입이 작동될까? 원래 이버 포스팅에서 작성하려고 하였지만 조금 길어질 것 같아 다음 포스팅에서 이어서 작성해 보도록 하겠다.

728x90
Comments