NestJS에서의 DI 작동 원리

SEUNGJUN·2024년 6월 22일
0

NestJS

목록 보기
3/8

NestJS에서의 DI (Dependency Injection)는 애플리케이션 개발에서 중요한 개념이며, 코드의 재사용성, 테스트 용이성, 모듈화 등을 촉진하는 핵심적인 매커니즘이다.

NestJS에서의 DI 작동 원리

1. 의존성 주입 컨테이너

  • NestJS의 DI 시스템은 주로 의존성 주입 컨테이너에 의해 관리된다. 이 컨테이너는 NestJS가 애플리케이션의 모든 구성 요소를 인식하고 관리하는 중앙 집중식 컨테이너 역할을 한다.

2. @Injectable() 데코레이터

  • @Injectable() 데코레이터는 NestJS에서 클래스가 주입 가능한 서비스임을 나타내는 데 사용된다. 이 데코레이터가 달린 클래스는 DI 컨테이너에 등록되어, 필요한 곳에서 DI를 통해 인스턴스가 주입된다.

3. 서비스 및 컴포넌트 등록

  • NestJS 모듈에서는 providers 배열을 통해 서비스를 등록하고, 이를 다른 부분에서 사용할 수 있도록 한다. 이 배열에 등록된 서비스는 모듈 내에서만 사용할 수도 있고, exports 배열을 통해 다른 모듈에서 공유할 수 있다.

4. 의존성 주입

  • NestJS 컨트롤러, 서비스, 미들웨어 등의 클래스는 생성자를 통해 필요한 서비스나 다른 구성 요소를 주입받는다. 이를 통해 각 클래스는 직접 의존성을 해결하지 않고도 필요한 기능을 사용할 수 있다.

@Injectable() 데코레이터의 주요 기능

1. 의존성 주입 관리

  • NestJS는 @Injectable()로 주석이 달린 클래스를 의존성 주입 컨테이너에 등록한다.

  • 이 클래스의 인스턴스는 필요한 곳에서 DI를 통해 주입될 수 있다.

2. 모듈화와 재사용성

  • @Injectable()을 사용하여 클래스를 정의하면 해당 클래스는 NestJS 모듈의 범위 내에서 재사용 할수 있다.

  • 다른 모듈에서 같은 서비스를 다시 정의하지 않고도 주입할 수 있다.

3. 컴포넌트 기반 아키텍처

  • NestJS는 Angular와 같은 컴포넌트 기반 아키텍처에서 영감을 받아 만들어 졌으며, @Injectable()은 이 아키텍처의 핵심 원칙 중 하나인 서비스 분리를 가능하게 한다.

4. 의존성 주입의 투명성

  • @Injectable()을 사용하면 코드가 의존성 주입 메커니즘을 명확히 보여주므로 코드의 의도를 이해하기 쉽다.
import { Injectable } from '@nestjs/common';

@Injectable()
export class MyService {
  // 서비스 로직 구현
}

위의 예시에서 MyService 클래스는 @Injectable() 데코레이터로 주석이 달려있다. 이는 NestJS에게 이 클래스가 서비스로 사용될 수 있음을 알리는 것이다.

주의할점

  • @Injectable()을 사용하는 클래스는 NestJS에서 관리되어야 하며, 그 외의 클래스에서는 일반 클래스로 간주된다.

  • NestJS 모듈의 providers 배열이나 imports 배열 내에서 @Injectable() 클래스를 등록해야 DI가 올바르게 작동한다.

예제

// user.service.ts

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

@Injectable()
export class UserService {
  private users = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' },
  ];

  findAll(): any[] {
    return this.users;
  }

  findById(id: number): any {
    return this.users.find(user => user.id === id);
  }

  createUser(name: string): void {
    const newUser = { id: this.users.length + 1, name };
    this.users.push(newUser);
  }
}

위의 코드에서 UserService는 @Injectable() 데코레이터로 주석이 달린 서비스 클래스로 이 클래스는 사용자 데이터를 관리하는 간단한 메서드들을 포함하고 있다. (findAll, findById, createUser).

// user.module.ts

import { Module } from '@nestjs/common';
import { UserService } from './user.service';

@Module({
  providers: [UserService], // UserService를 providers 배열에 등록
  exports: [UserService], // UserService를 다른 모듈에서 사용할 수 있도록 export
})
export class UserModule {}

위의 코드는 UserModule에서 UserService를 NestJS 모듈의 providers 배열에 등록하고, 다른 모듈에서도 이 서비스를 사용할 수 있도록 exports 배열에 추가한다.

// user.controller.ts

import { Controller, Get, Param, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  findAll() {
    return this.userService.findAll();
  }

  @Get(':id')
  findById(@Param('id') id: string) {
    return this.userService.findById(parseInt(id, 10));
  }

  @Post()
  createUser(@Body('name') name: string) {
    this.userService.createUser(name);
    return 'User created successfully';
  }
}

위의 코드에서 UserController는 UserService를 생성자 주입을 통해 사용한다. 각각의 메서드는 HTTP 요청에 따라 UserService의 메서드를 호출하여 작업을 수행한다.

DI의 장점

  • 모듈화와 재사용성: 서비스 클래스를 별도로 관리하고 재사용할 수 있다.

  • 테스트 용이성: 의존성을 주입하여 모의 객체(mock)를 사용하여 테스트하기 쉽다.

  • 의존성 분리: 클래스 간의 결합도를 낮추어 유연성을 높이고 유지보수를 쉽게 한다.

profile
RECORD DEVELOPER

0개의 댓글