๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
JavaScript/js ๋ฌธ๋ฒ•

NestJs๋ž€

by GREEN๋‚˜๋ฌด 2025. 2. 13.
728x90

๐Ÿš€ NestJS๋ž€?

NestJS๋Š” TypeScript๋กœ ์ž‘์„ฑ๋œ Node.js ๊ธฐ๋ฐ˜ ๋ฐฑ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.
Express๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋ฉฐ, ๋ชจ๋“ˆํ™”(Modular), ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection), ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”น NestJS์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

๐Ÿ“Œ 1. ๋ชจ๋“ˆ(Module) ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ

NestJS๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ชจ๋“ˆ ๋‹จ์œ„๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

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

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

๐Ÿ“Œ 2. ์ปจํŠธ๋กค๋Ÿฌ(Controller) โ€“ ์š”์ฒญ ์ฒ˜๋ฆฌ

์ปจํŠธ๋กค๋Ÿฌ๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

import { Controller, Get } from '@nestjs/common';

@Controller('users')  // '/users' ๊ฒฝ๋กœ๋ฅผ ์ฒ˜๋ฆฌ
export class UserController {
  @Get()
  findAll() {
    return "๋ชจ๋“  ์œ ์ € ์กฐํšŒ";
  }
}

๐Ÿ“Œ 3. ์„œ๋น„์Šค(Service) โ€“ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‹ด๋‹น

์„œ๋น„์Šค๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ˜ธ์ถœ๋˜๋ฉฐ, ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

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

@Injectable()
export class UserService {
  getUsers() {
    return ['User1', 'User2', 'User3'];
  }
}

๐Ÿ“Œ 4. ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection, DI)

NestJS๋Š” ์˜์กด์„ฑ ์ฃผ์ž…(DI)์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜์—ฌ, ๊ฐ์ฒด ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

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

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

๐Ÿ—๏ธ NestJS์˜ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด๊ณผ ์ฃผ์˜ํ•  ์ 

๐ŸŽฏ ์‹ฑ๊ธ€ํ†ค(Singleton) ํŒจํ„ด์ด๋ž€?

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์ƒ์„ฑํ•˜๋„๋ก ์ œํ•œํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
์ฃผ๋กœ ์ „์—ญ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ฑฐ๋‚˜ ๊ณตํ†ต ๋ฆฌ์†Œ์Šค๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

โœ… ์‹ฑ๊ธ€ํ†ค ํŠน์ง•

  • 1ํด๋ž˜์Šค 1๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๊ฐ์ฒด๊ฐ€ ํ•œ ๋ฒˆ ์ƒ์„ฑ๋˜๋ฉด ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณ„์† ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ธ์Šคํ„ด์Šค๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๋‹จ ํ•˜๋‚˜๋งŒ ์œ ์ง€ํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.
  • NestJS์—์„œ๋Š” @Injectable({ providedIn: 'root' }) ๋˜๋Š” module ๋‹จ์œ„๋กœ providers์— ๋“ฑ๋กํ•˜์—ฌ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

โœ… NestJS์˜ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค ๊ด€๋ฆฌ

NestJS์—์„œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ชจ๋“ˆ์„ ํ†ตํ•ด ์ž๋™์œผ๋กœ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
์ฆ‰, NestJS์—์„œ provider๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํŠน์ • ์กฐ๊ฑด์—์„œ๋Š” ์‹ฑ๊ธ€ํ†ค์ด ์•„๋‹Œ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!


โ— ์‹ค์ˆ˜ ์‚ฌ๋ก€: WebSocket ์‘๋‹ต์ด ๋‘ ๋ฒˆ ์˜ค๋Š” ๋ฌธ์ œ

๐Ÿ”น ๋ฌธ์ œ ์ƒํ™ฉ

NestJS์—์„œ WebSocket์„ ์ด์šฉํ•œ ๋ฉ”์‹œ์ง€ ์ „์†ก ์‹œ, ์‘๋‹ต์ด ๋‘ ๋ฒˆ ์˜ค๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋„ ๋กœ์ง์ƒ ์ด๋ฒคํŠธ๋ฅผ ์ค‘๋ณต์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ด์œ ๊ฐ€ ์—†์—ˆ๋Š”๋ฐ, ์„œ๋ฒ„์—์„œ ๋™์ผํ•œ ์‘๋‹ต์ด ๋‘ ๋ฒˆ ์ „๋‹ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”น ์›์ธ ๋ถ„์„

@Module({
  imports: [ChatModule],
  providers: [ChatGateway],
})
export class AppModule {}

์œ„์™€ ๊ฐ™์ด AppModule์—์„œ ChatModule์„ importํ•˜๊ณ , ๋™์‹œ์— providers์— ChatGateway๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ค‘๋ณต ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿšจ ๊ฒฐ๊ณผ์ ์œผ๋กœ WebSocket ์‘๋‹ต๋„ ๋‘ ๋ฒˆ ์ „๋‹ฌ๋จ ๐Ÿšจ

๋””๋ฒ„๊น… ๊ฒฐ๊ณผ:

constructor() {
  console.log('ChatGateway ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ!');
}

โ†’ console.log๊ฐ€ ๋‘ ๋ฒˆ ์ฐํ˜”์Œ โ†’ ์ฆ‰, ์ธ์Šคํ„ด์Šค๊ฐ€ ๋‘ ๊ฐœ ์ƒ์„ฑ๋จ!

๐Ÿ”น ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

โœ”๏ธ ๋™์ผํ•œ provider๋ฅผ ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์—์„œ ์ค‘๋ณต ์ฃผ์ž…ํ•˜์ง€ ์•Š๊ธฐ

@Module({
  imports: [],
  controllers: [],
  providers: [ChatGateway],
  exports: [ChatGateway], // ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก exports ์„ค์ •
})
export class ChatModule {}

@Module({
  imports: [ChatModule], // ChatGateway๋Š” ChatModule์—์„œ ์ œ๊ณต
})
export class AppModule {}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ChatGateway ์ธ์Šคํ„ด์Šค๊ฐ€ ๋‹จ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑ๋˜๋ฉฐ, ์ค‘๋ณต ์‘๋‹ต ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“Œ NestJS์˜ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์›๋ฆฌ

โœ”๏ธ ๊ธฐ๋ณธ์ ์œผ๋กœ provider๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋™์ž‘ โœ”๏ธ provider๋ฅผ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์ค‘๋ณต ์ •์˜ํ•˜๋ฉด ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋จ โœ”๏ธ ๋ชจ๋“ˆ์—์„œ provider๋ฅผ exports ํ•˜๊ณ  imports๋ฅผ ํ†ตํ•ด ์ฐธ์กฐํ•˜๋ฉด ์•ˆ์ „ โœ”๏ธ ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์€ provider๋Š” Global ๋ชจ๋“ˆ๋กœ ์„ ์–ธ ๊ฐ€๋Šฅ

๐Ÿ“– ๊ณต์‹ ๋ฌธ์„œ ์ฐธ๊ณ : NestJS Injection Scopes


๐ŸŽฏ ๊ฒฐ๋ก : ์˜ฌ๋ฐ”๋ฅธ ์ฃผ์ž… ๋ฐฉ๋ฒ•

1๏ธโƒฃ ๊ฐ€๋Šฅํ•˜๋ฉด ๋ชจ๋“ˆ ๋‹จ์œ„๋กœ imports ํ•˜์—ฌ provider๋ฅผ ๊ณต์œ  2๏ธโƒฃ ๊ธ€๋กœ๋ฒŒํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๊ฒฝ์šฐ @Global() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉ 3๏ธโƒฃ ๋ฐ˜๋“œ์‹œ providers์— ์ง์ ‘ ์ฃผ์ž…ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์‹ ์ค‘ํ•˜๊ฒŒ ํŒ๋‹จ

NestJS๋Š” ๊ฐ•๋ ฅํ•œ DI ์‹œ์Šคํ…œ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ฌด์กฐ๊ฑด ๋ชจ๋“  provider๊ฐ€ ์‹ฑ๊ธ€ํ†ค์ด ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค! ๋ผ๋Š” ์ ์„ ๊ธฐ์–ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ‘‰ ์˜ฌ๋ฐ”๋ฅธ provider ์ฃผ์ž… ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜์—ฌ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฒ„๊ทธ๋ฅผ ๋ฐฉ์ง€ํ•˜์„ธ์š”! ๐Ÿš€


 

๐Ÿ”น ์‹ฑ๊ธ€ํ†ค์„ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ

์‹ฑ๊ธ€ํ†ค์ด ์ ์ ˆํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋„ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

1๏ธโƒฃ ํŒฉํ† ๋ฆฌ ํŒจํ„ด(Factorial Pattern)์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ

  • ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
  • ์˜ˆ: ์‚ฌ์šฉ์ž ์š”์ฒญ๋งˆ๋‹ค ๋‹ค๋ฅธ ์„ค์ •์„ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•  ๋•Œ
  • ์‹ฑ๊ธ€ํ†ค์€ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜์ง€๋งŒ, ํŒฉํ† ๋ฆฌ ํŒจํ„ด์€ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • NestJS์—์„œ๋Š” useFactory๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋™์ ์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ จ ๊ณต์‹ ๋ฌธ์„œ: NestJS Factory Providers

2๏ธโƒฃ ์˜์กด์„ฑ ์ฃผ์ž…(DI)์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ

  • ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋Š” ์ฃผ์ž…๋ฐ›์€ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฐ์ฒด(์˜ˆ: ์š”์ฒญ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ๊ฐ์ฒด)๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“ค๋ฉด ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ์ฆ‰, ์˜์กด์„ฑ์„ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์‹ฑ๊ธ€ํ†ค์„ ์‚ฌ์šฉํ•˜๋ฉด ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

๐Ÿ”„ ํŒฉํ† ๋ฆฌ ํŒจํ„ด (Factory Pattern)

ํŒฉํ† ๋ฆฌ ํŒจํ„ด์€ ๊ฐ์ฒด์˜ ์ƒ์„ฑ์„ ๋‹ด๋‹นํ•˜๋Š” ๋ณ„๋„์˜ ํด๋ž˜์Šค๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋‘์–ด ๊ฐ์ฒด๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ์ƒ์„ฑํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
NestJS์—์„œ๋Š” useFactory๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โœ… ํŒฉํ† ๋ฆฌ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

  • ๊ฐ ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
    • ์˜ˆ: ์‚ฌ์šฉ์ž๋ณ„ ์ปค์Šคํ…€ ์„ค์ •์ด ํ•„์š”ํ•œ ์„œ๋น„์Šค
  • ๊ฐ์ฒด ์ƒ์„ฑ ๊ณผ์ •์ด ๋ณต์žกํ•  ๋•Œ
    • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์˜์กด์„ฑ์„ ์กฐํ•ฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
  • ๋™์ ์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ์„ ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

๐Ÿšซ ํŒฉํ† ๋ฆฌ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ

  • ์ „์—ญ์ ์œผ๋กœ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋งŒ ์œ ์ง€ํ•˜๋ฉด ๋˜๋Š” ๊ฒฝ์šฐ
    • ์˜ˆ: ๋กœ๊ทธ ๊ด€๋ฆฌ, ์บ์‹ฑ ์‹œ์Šคํ…œ, ์„ค์ •๊ฐ’ ๊ด€๋ฆฌ
  • ๊ฐ์ฒด ์ƒ์„ฑ ๋น„์šฉ์ด ํฌ์ง€ ์•Š๊ณ , ์žฌ์‚ฌ์šฉ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
    • ๋นˆ๋ฒˆํ•œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ์ด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Œ

๐Ÿ”น NestJS์—์„œ ์‹ฑ๊ธ€ํ†ค๊ณผ ํŒฉํ† ๋ฆฌ ํŒจํ„ด ํ™œ์šฉ ์˜ˆ์‹œ

โœ… ์‹ฑ๊ธ€ํ†ค ์„œ๋น„์Šค (Repository ํŒจํ„ด ํ™œ์šฉ)

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

@Injectable()
export class SingletonService {
  private data: string = '์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ';

  getData() {
    return this.data;
  }

  setData(newData: string) {
    this.data = newData;
  }
}

ํ•ด๋‹น ์„œ๋น„์Šค๋Š” @Injectable()๋กœ ๋“ฑ๋ก๋˜๋ฉฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

โœ… ํŒฉํ† ๋ฆฌ ํŒจํ„ด ํ™œ์šฉ (๋™์  ๊ฐ์ฒด ์ƒ์„ฑ)

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

@Injectable()
export class DynamicService {
  constructor(private readonly config: any) {}

  getConfig() {
    return this.config;
  }
}

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

@Module({
  providers: [
    {
      provide: 'DYNAMIC_SERVICE',
      useFactory: () => {
        return new DynamicService({ key: 'value' });
      },
    },
  ],
  exports: ['DYNAMIC_SERVICE'],
})
export class DynamicModule {}

useFactory๋ฅผ ์ด์šฉํ•˜๋ฉด ๋งค๋ฒˆ ๋‹ค๋ฅธ ์„ค์ •๊ฐ’์„ ๊ฐ€์ง„ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ”น 3๋ ˆ์ด์–ด ๊ตฌ์กฐ + DB ๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ ์šฉ

3๋ ˆ์ด์–ด ์•„ํ‚คํ…์ฒ˜(NestJS ์Šคํƒ€์ผ)์—์„œ ์‹ฑ๊ธ€ํ†ค๊ณผ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๐Ÿ“Œ ๊ตฌ์กฐ
1๏ธโƒฃ Controller - ์š”์ฒญ์„ ๋ฐ›๊ณ  ์‘๋‹ต์„ ์ฒ˜๋ฆฌ
2๏ธโƒฃ Service - ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ
3๏ธโƒฃ Repository - ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ จ ๋กœ์ง ์ฒ˜๋ฆฌ

โœ… ๋ ˆํฌ์ง€ํ† ๋ฆฌ (Repository)

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

@Injectable()
export class UserRepository {
  private users = [];

  findAll() {
    return this.users;
  }

  save(user) {
    this.users.push(user);
  }
}

UserRepository๋Š” ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ ์—ญํ• ์„ ํ•˜๋ฉฐ, ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

โœ… ์„œ๋น„์Šค (Service)

import { Injectable } from '@nestjs/common';
import { UserRepository } from './user.repository';

@Injectable()
export class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  createUser(user) {
    this.userRepository.save(user);
  }

  getAllUsers() {
    return this.userRepository.findAll();
  }
}

UserService๋Š” UserRepository๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

โœ… ์ปจํŠธ๋กค๋Ÿฌ (Controller)

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

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

  @Post()
  createUser(@Body() user) {
    this.userService.createUser(user);
    return { message: 'User created' };
  }

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

UserController๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›์•„ UserService๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”น ์ •๋ฆฌ

โœ… ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

  • ์ „์—ญ์ ์œผ๋กœ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์ ํ•ฉ
  • ๋กœ๊ทธ, ์„ค์ •๊ฐ’, ๊ณตํ†ต ์บ์‹œ ๊ด€๋ฆฌ ๋“ฑ์— ์œ ๋ฆฌ

โœ… ํŒฉํ† ๋ฆฌ ํŒจํ„ด

  • ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ ํ•ฉ
  • ์˜์กด์„ฑ์„ ๋‹ค๋ฅด๊ฒŒ ์ฃผ์ž…ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์œ ์šฉ

โœ… NestJS์—์„œ ์‹ฑ๊ธ€ํ†ค๊ณผ ํŒฉํ† ๋ฆฌ ์ ์šฉ

  • Repository๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์‚ฌ์šฉ
  • ์œ ๋™์ ์ธ ์„ค์ •์ด ํ•„์š”ํ•œ ๊ฐ์ฒด๋Š” ํŒฉํ† ๋ฆฌ ํŒจํ„ด์„ ํ™œ์šฉ

๐Ÿ“Œ NestJS์—์„œ ์„œ๋น„์Šค(Service)์™€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ(Repository) ์˜ˆ์ œ

NestJS์—์„œ 3๋ ˆ์ด์–ด ๊ตฌ์กฐ(Controller โ†’ Service โ†’ Repository) ๋ฅผ ์ ์šฉํ•˜์—ฌ ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.
์ด ์˜ˆ์ œ์—์„œ๋Š” ์‚ฌ์šฉ์ž(User) ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.


โœ… 1. Repository ํŒŒ์ผ (UserRepository)

Repository๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ง์ ‘์ ์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
NestJS์—์„œ๋Š” @Injectable()์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ user.repository.ts

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

interface User {
  id: number;
  name: string;
  email: string;
}

@Injectable()
export class UserRepository {
  private users: User[] = []; // ์ž„์‹œ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ (DB ๋Œ€์‹  ์‚ฌ์šฉ)

  // ๋ชจ๋“  ์‚ฌ์šฉ์ž ์กฐํšŒ
  findAll(): User[] {
    return this.users;
  }

  // ํŠน์ • ์‚ฌ์šฉ์ž ์กฐํšŒ
  findById(id: number): User | undefined {
    return this.users.find(user => user.id === id);
  }

  // ์‚ฌ์šฉ์ž ์ถ”๊ฐ€
  save(user: User): User {
    this.users.push(user);
    return user;
  }

  // ์‚ฌ์šฉ์ž ์‚ญ์ œ
  delete(id: number): boolean {
    const index = this.users.findIndex(user => user.id === id);
    if (index !== -1) {
      this.users.splice(index, 1);
      return true;
    }
    return false;
  }
}

๐Ÿ›  ์„ค๋ช…:

  • findAll(): ๋ชจ๋“  ์‚ฌ์šฉ์ž ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
  • findById(id): ํŠน์ • ID์˜ ์‚ฌ์šฉ์ž ์ฐพ๊ธฐ
  • save(user): ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ถ”๊ฐ€
  • delete(id): ํŠน์ • ์‚ฌ์šฉ์ž๋ฅผ ์‚ญ์ œ

โœ… 2. ์„œ๋น„์Šค ํŒŒ์ผ (UserService)

Service๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณณ์œผ๋กœ, Repository๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ user.service.ts

import { Injectable } from '@nestjs/common';
import { UserRepository } from './user.repository';

interface User {
  id: number;
  name: string;
  email: string;
}

@Injectable()
export class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  // ๋ชจ๋“  ์‚ฌ์šฉ์ž ์กฐํšŒ
  getAllUsers(): User[] {
    return this.userRepository.findAll();
  }

  // ํŠน์ • ์‚ฌ์šฉ์ž ์กฐํšŒ
  getUserById(id: number): User | undefined {
    return this.userRepository.findById(id);
  }

  // ์‚ฌ์šฉ์ž ์ƒ์„ฑ
  createUser(name: string, email: string): User {
    const newUser: User = {
      id: Date.now(), // ๊ฐ„๋‹จํ•œ ID ์ƒ์„ฑ
      name,
      email,
    };
    return this.userRepository.save(newUser);
  }

  // ์‚ฌ์šฉ์ž ์‚ญ์ œ
  deleteUser(id: number): boolean {
    return this.userRepository.delete(id);
  }
}

๐Ÿ›  ์„ค๋ช…:

  • getAllUsers(): ๋ชจ๋“  ์‚ฌ์šฉ์ž ์กฐํšŒ
  • getUserById(id): ํŠน์ • ์‚ฌ์šฉ์ž ์กฐํšŒ
  • createUser(name, email): ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ถ”๊ฐ€
  • deleteUser(id): ์‚ฌ์šฉ์ž ์‚ญ์ œ

โœ… 3. ๋ชจ๋“ˆ ํŒŒ์ผ (UserModule)

Repository์™€ Service๋ฅผ UserModule์— ๋“ฑ๋กํ•˜์—ฌ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ user.module.ts

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

@Module({
  providers: [UserService, UserRepository],
  exports: [UserService], // ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‚ด๋ณด๋‚ด๊ธฐ
})
export class UserModule {}

๐Ÿ›  ์„ค๋ช…:

  • providers: UserService์™€ UserRepository๋ฅผ NestJS์˜ DI ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก
  • exports: UserService๋ฅผ ๋‚ด๋ณด๋‚ด์„œ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

โœ… 4. ์ปจํŠธ๋กค๋Ÿฌ ํŒŒ์ผ (UserController)

ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ๋ฐ›์•„ Service๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ user.controller.ts

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

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

  // ๋ชจ๋“  ์‚ฌ์šฉ์ž ์กฐํšŒ
  @Get()
  getAllUsers() {
    return this.userService.getAllUsers();
  }

  // ํŠน์ • ์‚ฌ์šฉ์ž ์กฐํšŒ
  @Get(':id')
  getUserById(@Param('id') id: string) {
    return this.userService.getUserById(Number(id));
  }

  // ์‚ฌ์šฉ์ž ์ƒ์„ฑ
  @Post()
  createUser(@Body() body: { name: string; email: string }) {
    return this.userService.createUser(body.name, body.email);
  }

  // ์‚ฌ์šฉ์ž ์‚ญ์ œ
  @Delete(':id')
  deleteUser(@Param('id') id: string) {
    return this.userService.deleteUser(Number(id));
  }
}

๐Ÿ›  ์„ค๋ช…:

  • @Get() โ†’ ๋ชจ๋“  ์‚ฌ์šฉ์ž ์กฐํšŒ
  • @Get(':id') โ†’ ํŠน์ • ์‚ฌ์šฉ์ž ์กฐํšŒ
  • @Post() โ†’ ์‚ฌ์šฉ์ž ์ถ”๊ฐ€
  • @Delete(':id') โ†’ ์‚ฌ์šฉ์ž ์‚ญ์ œ

โœ… 5. ๋ฉ”์ธ ๋ชจ๋“ˆ ํŒŒ์ผ (app.module.ts)

๋ชจ๋“  ๋ชจ๋“ˆ์„ AppModule์—์„œ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ app.module.ts

import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { UserController } from './user/user.controller';

@Module({
  imports: [UserModule],
  controllers: [UserController],
})
export class AppModule {}

UserModule์„ AppModule์— ๋“ฑ๋กํ•˜์—ฌ NestJS๊ฐ€ ํ•ด๋‹น ๋ชจ๋“ˆ์„ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.


โœ… 6. ์‹คํ–‰ ๋ฐฉ๋ฒ•

1๏ธโƒฃ NestJS ํ”„๋กœ์ ํŠธ์—์„œ ์‹คํ–‰

npm run start

2๏ธโƒฃ API ํ…Œ์ŠคํŠธ (Postman ๋˜๋Š” curl ์‚ฌ์šฉ)

  • ๋ชจ๋“  ์‚ฌ์šฉ์ž ์กฐํšŒ
    curl -X GET http://localhost:3000/users
    
  • ํŠน์ • ์‚ฌ์šฉ์ž ์กฐํšŒ (ID: 1)
    curl -X GET http://localhost:3000/users/1
    
  • ์ƒˆ ์‚ฌ์šฉ์ž ์ถ”๊ฐ€
    curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name": "John Doe", "email": "john@example.com"}'
    
  • ์‚ฌ์šฉ์ž ์‚ญ์ œ (ID: 1)
    curl -X DELETE http://localhost:3000/users/1
    

โœ… ์ •๋ฆฌ

  • Repository (user.repository.ts) โ†’ DB์™€ ์ง์ ‘ ์—ฐ๊ฒฐ (๋ฐ์ดํ„ฐ ์ €์žฅ/์กฐํšŒ/์‚ญ์ œ)
  • Service (user.service.ts) โ†’ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‹ด๋‹น (๋ฐ์ดํ„ฐ ๊ฐ€๊ณต ๋ฐ ์ฒ˜๋ฆฌ)
  • Controller (user.controller.ts) โ†’ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  Service ํ˜ธ์ถœ
  • Module (user.module.ts) โ†’ ์„œ๋น„์Šค์™€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ NestJS์— ๋“ฑ๋ก

NestJS์˜ ๊ธฐ๋ณธ์ ์ธ 3๋ ˆ์ด์–ด ๊ตฌ์กฐ๋ฅผ ์ ์šฉํ•œ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ๐Ÿš€
์ด์ œ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, DB์™€ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


 

์ฐธ๊ณ 

https://velog.io/@seongkyun/%EC%B0%8D%EC%96%B4%EB%A8%B9%EB%8A%94-NestJS-%EA%B8%B0%EB%B3%B8%EA%B0%9C%EB%85%90

https://jun-choi-4928.medium.com/nest-js-behind-the-curtain-712b39abd49c

 

https://jay-ji.tistory.com/106

 

https://velog.io/@kdhn712/NestJs-%EB%B0%9C%ED%91%9C

 

https://velog.io/@pear/Query-String-%EC%BF%BC%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A7%81%EC%9D%B4%EB%9E%80

 

ํŒฉํ† ๋ฆฌ์–ผํŒจํ„ด

https://docs.nestjs.com/fundamentals/custom-providers#factory-providers-usefactory