[Nest.JS] Dependency Injection 과 Inversion of Control

궁금하면 500원·2024년 8월 8일
0

NestJS의 Dependency Injection (DI) 및 Inversion of Control (IoC) 개념은 애플리케이션의 구조를 유연하고 관리하기 쉽게 만들어 주는 핵심 기능입니다.

이 두 개념을 이해하면 코드의 재사용성, 유지보수성, 테스트 용이성을 크게 향상시킬 수 있습니다.

1. Dependency Injection (DI)와 Inversion of Control (IoC)

Dependency Injection (DI)

DI는 객체 간의 의존성을 외부에서 주입하는 디자인 패턴입니다.
즉, 객체가 자신의 의존성을 직접 생성하지 않고, 외부에서 주입받습니다.
이는 객체 간의 결합도를 낮추고, 코드의 재사용성과 테스트 용이성을 높입니다.

Inversion of Control (IoC)

IoC는 객체의 생성 및 의존성 주입을 프레임워크가 대신 처리하도록 하는 개념입니다.
이는 객체가 자신의 의존성을 직접 관리하지 않고, 제어의 역전이 일어나는 구조를 의미합니다.
NestJS에서 IoC는 주로 DI 컨테이너를 통해 구현됩니다.

2. NestJS의 DI와 IoC

NestJS는 DI와 IoC를 다음과 같은 방식으로 구현합니다

  • 모듈 시스템: NestJS는 애플리케이션을 모듈 단위로 구성하며, 모듈은 의존성을 관리합니다.

  • 프로바이더: 서비스, 레포지토리, 유틸리티 클래스 등 다양한 프로바이더를 정의하고, 이들 프로바이더는 DI 컨테이너를 통해 주입됩니다.

  • DI 컨테이너: NestJS의 DI 컨테이너는 애플리케이션의 시작 시 모든 프로바이더를 생성하고 관리합니다. 이를 통해 필요한 인스턴스를 주입받을 수 있습니다

3. NestJS에서 DI와 IoC 예제

다음은 NestJS에서 DI와 IoC를 사용하는 간단한 예제입니다.

3.1. 프로바이더 생성

먼저, 서비스를 정의합니다. 이 서비스는 다른 클래스에서 주입받아 사용할 수 있습니다.

app.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

여기서 @Injectable() 데코레이터는 이 클래스가 NestJS의 DI 컨테이너에 의해 주입될 수 있음을 나타냅니다.

3.2. 컨트롤러 생성

컨트롤러는 클라이언트의 요청을 처리하고, 서비스의 메서드를 호출합니다.

app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

AppControllerAppService를 생성자에서 주입받아 사용합니다.
AppService는 DI 컨테이너에 의해 자동으로 주입됩니다.

3.3. 모듈 정의

모듈은 애플리케이션의 구조를 정의하고, 프로바이더와 컨트롤러를 구성합니다.

app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

AppModule은 AppController와 AppService를 선언합니다.
AppService는 providers 배열에 포함되어 DI 컨테이너에 등록됩니다.

3.4. 애플리케이션 부트스트랩

NestJS 애플리케이션을 시작하는 코드입니다.

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

애플리케이션을 부트스트랩 할 때,

NestFactory.create(AppModule)를 통해 AppModule이 초기화되고, DI 컨테이너가 생성됩니다.

이 때 AppService와 AppController가 DI 컨테이너에 등록되고 주입됩니다.

4. DI와 IoC의 장점

  • 유연성: 클래스의 의존성을 외부에서 주입받기 때문에, 코드의 결합도가 낮아지고 유연성이 증가합니다.

  • 테스트 용이성: DI를 통해 의존성을 주입받기 때문에, 테스트 시 모의 객체(mock objects)를 쉽게 주입할 수 있습니다.

  • 재사용성: 코드의 재사용성이 높아지며, 기능을 모듈화하여 관리할 수 있습니다.

5. Advanced Example: Custom Providers

커스텀 프로바이더를 정의하여 의존성 주입을 좀 더 세밀하게 제어할 수 있습니다.

custom.provider.ts

import { Injectable, InjectionToken } from '@nestjs/common';

export const CUSTOM_SERVICE_TOKEN = new InjectionToken<string>('CUSTOM_SERVICE_TOKEN');

@Injectable()
export class CustomService {
  getMessage(): string {
    return 'Custom Service Message';
  }
}

export const CustomServiceProvider = {
  provide: CUSTOM_SERVICE_TOKEN,
  useClass: CustomService,
};

app.module.ts에서 커스텀 프로바이더를 사용하는 방법

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { CustomServiceProvider } from './custom.provider';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [CustomServiceProvider],
})
export class AppModule {}

app.controller.ts에서 커스텀 프로바이더 사용하기

import { Controller, Get, Inject } from '@nestjs/common';
import { CUSTOM_SERVICE_TOKEN } from './custom.provider';

@Controller()
export class AppController {
  constructor(@Inject(CUSTOM_SERVICE_TOKEN) private readonly customService: any) {}

  @Get()
  getMessage(): string {
    return this.customService.getMessage();
  }
}

이 코드에서 CUSTOM_SERVICE_TOKEN을 사용하여 CustomService를 주입받고 있습니다.
이를 통해 DI 컨테이너를 보다 세밀하게 제어할 수 있습니다.

NestJS의 DI와 IoC는 애플리케이션의 구조를 깨끗하게 유지하고, 유연한 코드 작성을 가능하게 합니다.
이를 통해 복잡한 애플리케이션도 관리하기 쉬워지고, 테스트와 유지보수도 간편해집니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글