๐Ÿ“‘ ๊ณต์‹ ๋ฌธ์„œ ๋ฒˆ์—ญ/Nest.js

[Nest.js] Overview - First Steps, Controllers

dev_writer 2025. 7. 5. 15:18

์ฒซ๊ฑธ์Œ

์ด ์ผ๋ จ์˜ ๊ธ€์—์„œ๋Š” Nest์˜ ํ•ต์‹ฌ ๊ธฐ์ดˆ ๊ฐœ๋…๋“ค์„ ํ•™์Šตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•„์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์— ์ต์ˆ™ํ•ด์ง€๊ธฐ ์œ„ํ•ด, ์šฐ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์ธ CRUD ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด๋ฉฐ ์ž…๋ฌธ ์ˆ˜์ค€์—์„œ ๋„“์€ ๋ฒ”์œ„๋ฅผ ๋‹ค๋ฃจ๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌํ˜„ํ•ด ๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์–ธ์–ด

์šฐ๋ฆฌ๋Š” TypeScript๋ฅผ ์‚ฌ๋ž‘ํ•˜์ง€๋งŒ, ๊ทธ ๋ฌด์—‡๋ณด๋‹ค๋„ Node.js๋ฅผ ์‚ฌ๋ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Nest๋Š” TypeScript์™€ ์ˆœ์ˆ˜ JavaScript ๋ชจ๋‘์™€ ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค. Nest๋Š” ์ตœ์‹  ์–ธ์–ด ๊ธฐ๋Šฅ๋“ค์„ ํ™œ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ˆœ์ˆ˜ JavaScript๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Babel ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์ œ์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„ TypeScript๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์–ธ์ œ๋“ ์ง€ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์˜ ์–ธ์–ด ์ „ํ™˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์ˆœ์ˆ˜ JavaScript ๋ฌธ๋ฒ•์œผ๋กœ ๋ฐ”๊ฟ”๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์ „ ์š”๊ตฌ์‚ฌํ•ญ

์šด์˜ ์ฒด์ œ์— Node.js (๋ฒ„์ „ 20 ์ด์ƒ)๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•˜์„ธ์š”.

์„ค์ •

Nest CLI๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ƒˆ ํ”„๋กœ์ ํŠธ ์„ค์ •์ด ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. npm์ด ์„ค์น˜๋˜์–ด ์žˆ๋‹ค๋ฉด, ์šด์˜ ์ฒด์ œ์˜ ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด Nest ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

$ npm i -g @nestjs/cli
$ nest new project-name
ํžŒํŠธ
๋ณด๋‹ค ์—„๊ฒฉํ•œ ๊ธฐ๋Šฅ ์„ธํŠธ๋กœ ์ƒˆ๋กœ์šด TypeScript ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด nest new ๋ช…๋ น์–ด์— --f ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

 

project-name ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , node_modules ๋ฐ ๋ช‡ ๊ฐ€์ง€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ํŒŒ์ผ๋“ค์ด ์„ค์น˜๋˜๋ฉฐ, src/ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์—ฌ๋Ÿฌ ํ•ต์‹ฌ ํŒŒ์ผ๋“ค๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.

src/
โ”œโ”€โ”€ app.controller.spec.ts
โ”œโ”€โ”€ app.controller.ts
โ”œโ”€โ”€ app.module.ts
โ”œโ”€โ”€ app.service.ts
โ””โ”€โ”€ main.ts

 

๋‹ค์Œ์€ ์ด๋Ÿฌํ•œ ํ•ต์‹ฌ ํŒŒ์ผ๋“ค์— ๋Œ€ํ•œ ๊ฐ„๋žตํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค:

app.controller.ts ๋‹จ์ผ ๋ผ์šฐํŠธ๋ฅผ ๊ฐ€์ง„ ๊ธฐ๋ณธ ์ปจํŠธ๋กค๋Ÿฌ์ž…๋‹ˆ๋‹ค.
app.controller.spec.ts ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.
app.module.ts ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฃจํŠธ ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.
app.service.ts ๋‹จ์ผ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ๊ธฐ๋ณธ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
main.ts Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ•ต์‹ฌ ํ•จ์ˆ˜์ธ NestFactory๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ง„์ž… ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

 

main.ts์—๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ถ€ํŠธ์ŠคํŠธ๋žฉํ•˜๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

 

Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด, NestFactory๋ผ๋Š” Nest์˜ ํ•ต์‹ฌ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. NestFactory๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ •์  ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ์ค‘ create() ๋ฉ”์„œ๋“œ๋Š” INestApplication ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” ์ดํ›„ ์žฅ๋“ค์—์„œ ์„ค๋ช…๋  ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ main.ts ์˜ˆ์ œ์—์„œ๋Š” ๊ฐ„๋‹จํžˆ HTTP ๋ฆฌ์Šค๋„ˆ๋ฅผ ์‹œ์ž‘ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋“ค์–ด์˜ค๋Š” HTTP ์š”์ฒญ์„ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

์ฐธ๊ณ ๋กœ, Nest CLI๋กœ ์Šค์บํด๋”ฉ ๋œ ํ”„๋กœ์ ํŠธ๋Š” ๊ฐ ๋ชจ๋“ˆ์„ ์ „์šฉ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ณด๊ด€ํ•˜๋Š” ๊ด€๋ก€๋ฅผ ๋”ฐ๋ฅด๋„๋ก ์ดˆ๊ธฐ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํžŒํŠธ
๊ธฐ๋ณธ์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ์•ฑ์€ ์ข…๋ฃŒ ์ฝ”๋“œ 1๊ณผ ํ•จ๊ป˜ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋Œ€์‹  ์˜ค๋ฅ˜๋ฅผ throw ํ•˜๋„๋ก ํ•˜๋ ค๋ฉด abortOnError ์˜ต์…˜์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค (์˜ˆ: NestFactory.create(AppModule, { abortOnError: false })).

 

ํ”Œ๋žซํผ

Nest๋Š” ํ”Œ๋žซํผ์— ๊ตฌ์• ๋ฐ›์ง€ ์•Š๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋žซํผ ๋…๋ฆฝ์„ฑ ๋•๋ถ„์—, ๊ฐœ๋ฐœ์ž๋Š” ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋…ผ๋ฆฌ ๊ตฌ์„ฑ ์š”์†Œ๋“ค์„ ๋งŒ๋“ค๊ณ  ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์ˆ ์ ์œผ๋กœ Nest๋Š” ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ œ๊ณต๋˜๋ฉด ์–ด๋–ค Node HTTP ํ”„๋ ˆ์ž„์›Œํฌ์™€๋„ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›๋˜๋Š” ๋‘ ๊ฐ€์ง€ HTTP ํ”Œ๋žซํผ์€ express์™€ fastify์ด๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ ์ ํ•ฉํ•œ ๊ฒƒ์„ ์„ ํƒํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

platform-express Express๋Š” Node๋ฅผ ์œ„ํ•œ ์ž˜ ์•Œ๋ ค์ง„ ๋ฏธ๋‹ˆ๋ฉ€๋ฆฌ์ŠคํŠธ ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์˜ํ•ด ๋‹ค์ˆ˜์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฉฐ, ์‹ค์ „์—์„œ ๊ฒ€์ฆ๋œ ํ”„๋กœ๋•์…˜ ์ค€๋น„ ์™„๋ฃŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” @nestjs/platform-express ํŒจํ‚ค์ง€๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์‚ฌ์šฉ์ž๋“ค์€ Express๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ๋งŒ์กฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ณ„๋„์˜ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
platform-fastify Fastify๋Š” ๋†’์€ ์„ฑ๋Šฅ๊ณผ ๋‚ฎ์€ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ง€๋‹Œ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ์ตœ๋Œ€ํ•œ์˜ ํšจ์œจ์„ฑ๊ณผ ์†๋„ ์ œ๊ณต์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค. Fastify ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์–ด๋–ค ํ”Œ๋žซํผ์„ ์‚ฌ์šฉํ•˜๋“ , ๊ฐ ํ”Œ๋žซํผ์€ ๊ณ ์œ ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ๊ฐ NestExpressApplication๊ณผ NestFastifyApplication์œผ๋กœ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

 

์•„๋ž˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ NestFactory.create() ๋ฉ”์„œ๋“œ์— ํƒ€์ž…์„ ์ „๋‹ฌํ•˜๋ฉด, ๋ฐ˜ํ™˜๋œ app ๊ฐ์ฒด๋Š” ํ•ด๋‹น ํ”Œ๋žซํผ์—๋งŒ ์กด์žฌํ•˜๋Š” ์ „์šฉ ๋ฉ”์„œ๋“œ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ์‹ค์ œ๋กœ ํ”Œ๋žซํผ์˜ ํ•˜์œ„ API์— ์ ‘๊ทผํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋ฉด ํƒ€์ž…์„ ๋ช…์‹œํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค.

const app = await NestFactory.create<NestExpressApplication>(AppModule);

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰

์„ค์น˜ ๊ณผ์ •์ด ์™„๋ฃŒ๋˜๋ฉด, ์šด์˜ ์ฒด์ œ์˜ ๋ช…๋ น ํ”„๋กฌํ”„ํŠธ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด HTTP ์š”์ฒญ์„ ์ˆ˜์‹ ํ•˜๋„๋ก ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

$ npm run start
ํžŒํŠธ
๊ฐœ๋ฐœ ์†๋„๋ฅผ ์•ฝ 20๋ฐฐ ๋น ๋ฅด๊ฒŒ ํ•˜๋ ค๋ฉด, -b swc ํ”Œ๋ž˜๊ทธ๋ฅผ start ์Šคํฌ๋ฆฝํŠธ์— ์ „๋‹ฌํ•˜์—ฌ SWC ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ:
npm run start -- -b swc

 

์ด ๋ช…๋ น์–ด๋Š” src/main.ts ํŒŒ์ผ์— ์ •์˜๋œ ํฌํŠธ์—์„œ HTTP ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์—ด๊ณ  http://localhost:3000/์œผ๋กœ ์ด๋™ํ•˜์„ธ์š”. “Hello World!” ๋ฉ”์‹œ์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

ํŒŒ์ผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•˜๋ฉด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

$ npm run start:dev

 

์ด ๋ช…๋ น์–ด๋Š” ํŒŒ์ผ์„ ๊ฐ์‹œํ•˜๋ฉด์„œ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•˜๊ณ  ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๋ฆฐํŠธ์™€ ํฌ๋งทํŒ…

CLI๋Š” ๋Œ€๊ทœ๋ชจ ๊ฐœ๋ฐœ์—์„œ๋„ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์Šค์บํด๋”ฉ ํ•˜๊ธฐ ์œ„ํ•ด ์ตœ์„ ์„ ๋‹คํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ƒ์„ฑ๋œ Nest ํ”„๋กœ์ ํŠธ์—๋Š” ์ฝ”๋“œ ๋ฆฐํ„ฐ์™€ ํฌ๋งคํ„ฐ๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์น˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค(๊ฐ๊ฐ eslint์™€ prettier).

ํžŒํŠธ
ํฌ๋งคํ„ฐ์™€ ๋ฆฐํ„ฐ์˜ ์ฐจ์ด์ ์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

 

์ตœ๋Œ€์˜ ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด, ์šฐ๋ฆฌ๋Š” ๊ธฐ๋ณธ eslint ๋ฐ prettier CLI ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ตฌ์„ฑ์€ ๊ณต์‹ ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์„ ํ†ตํ•œ ๊น”๋”ํ•œ IDE ํ†ตํ•ฉ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

 

IDE๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ํ—ค๋“œ๋ฆฌ์Šค ํ™˜๊ฒฝ(CI, Git hooks ๋“ฑ)์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Nest ํ”„๋กœ์ ํŠธ์—๋Š” ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ npm ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ•จ๊ป˜ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

# eslint๋กœ ๋ฆฐํŠธ ๋ฐ ์ž๋™ ์ˆ˜์ •
$ npm run lint

# prettier๋กœ ํฌ๋งทํŒ…
$ npm run format

์ปจํŠธ๋กค๋Ÿฌ

์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

 

์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ชฉ์ ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ผ์šฐํŒ… ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๊ฐ ์š”์ฒญ์„ ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ฒ˜๋ฆฌํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ํ•˜๋‚˜์˜ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ผ์šฐํŠธ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฐ ๋ผ์šฐํŠธ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

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

ํžŒํŠธ
๋‚ด์žฅ๋œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ๊ณผ ํ•จ๊ป˜ CRUD ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ƒ์„ฑํ•˜๋ ค๋ฉด CLI์˜ CRUD ์ƒ์„ฑ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: nest g resource [name]

๋ผ์šฐํŒ…

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ๊ธฐ๋ณธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์ˆ˜์ ์ธ @Controller() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์„ ํƒ์ ์œผ๋กœ cats๋ผ๋Š” ๊ฒฝ๋กœ ์ ‘๋‘์‚ฌ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. @Controller() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ๊ฒฝ๋กœ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ด€๋ จ๋œ ๋ผ์šฐํŠธ๋“ค์„ ๊ทธ๋ฃนํ™”ํ•˜๊ณ  ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ณ ์–‘์ด (cat) ์—”ํ‹ฐํ‹ฐ์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ผ์šฐํŠธ๋“ค์„ /cats ๊ฒฝ๋กœ ์•„๋ž˜๋กœ ๋ฌถ๊ณ  ์‹ถ๋‹ค๋ฉด, @Controller('cats')์™€ ๊ฐ™์ด ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ด๋‹น ํŒŒ์ผ ๋‚ด์˜ ๊ฐ ๋ผ์šฐํŠธ๋งˆ๋‹ค /cats๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

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

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}
ํžŒํŠธ
CLI๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด, ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค: nest g controller [main]

 

@Get() HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” findAll() ๋ฉ”์„œ๋“œ ์•ž์— ์œ„์น˜ํ•˜๋ฉฐ, Nest์—๊ฒŒ ํŠน์ • ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜๋ผ๊ณ  ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด ์—”๋“œํฌ์ธํŠธ๋Š” HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ(GET)์™€ ๋ผ์šฐํŠธ ๊ฒฝ๋กœ์— ์˜ํ•ด ์ •์˜๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋ผ์šฐํŠธ ๊ฒฝ๋กœ๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”? ํ•ธ๋“ค๋Ÿฌ์˜ ๋ผ์šฐํŠธ ๊ฒฝ๋กœ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์— ์„ ์–ธ๋œ (์„ ํƒ์ ์ธ) ์ ‘๋‘์‚ฌ์™€ ๋ฉ”์„œ๋“œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ง€์ •๋œ ๊ฒฝ๋กœ๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” cats ์ ‘๋‘์‚ฌ๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๊ณ , ๋ฉ”์„œ๋“œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์—๋Š” ๋ณ„๋„์˜ ๊ฒฝ๋กœ๊ฐ€ ์ง€์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ Nest๋Š” GET /cats ์š”์ฒญ์„ ์ด ํ•ธ๋“ค๋Ÿฌ์— ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.

 

์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, ๋ผ์šฐํŠธ ๊ฒฝ๋กœ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ๊ฒฝ๋กœ ์ ‘๋‘์‚ฌ์™€ ๋ฉ”์„œ๋“œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ง€์ •๋œ ๊ฒฝ๋กœ ๋ฌธ์ž์—ด์„ ํ•ฉ์ณ์„œ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ปจํŠธ๋กค๋Ÿฌ ์ ‘๋‘์‚ฌ๊ฐ€ cats์ด๊ณ , ๋ฉ”์„œ๋“œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ @Get('breed') ๋ผ๋ฉด, ์‹ค์ œ ๋ผ์šฐํŠธ๋Š” GET /cats/breed๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

์œ„ ์˜ˆ์‹œ์—์„œ GET ์š”์ฒญ์ด ํ•ด๋‹น ์—”๋“œํฌ์ธํŠธ๋กœ ๋“ค์–ด์˜ค๋ฉด, Nest๋Š” ์š”์ฒญ์„ ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ findAll() ๋ฉ”์„œ๋“œ๋กœ ๋ผ์šฐํŒ… ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€, ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ธ findAll์€ ์™„์ „ํžˆ ์ž„์˜๋กœ ์ •ํ•œ ๊ฒƒ์ด๋ฉฐ, ๋ผ์šฐํŠธ๋ฅผ ์—ฐ๊ฒฐํ•  ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•˜๊ธด ํ•˜์ง€๋งŒ Nest๋Š” ๊ทธ ์ด๋ฆ„ ์ž์ฒด์— ํŠน๋ณ„ํ•œ ์˜๋ฏธ๋ฅผ ๋ถ€์—ฌํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์ด ๋ฉ”์„œ๋“œ๋Š” 200 ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋˜๋ฉฐ, ์—ฌ๊ธฐ์„œ๋Š” ๋‹จ์ˆœํžˆ ๋ฌธ์ž์—ด ํ•˜๋‚˜๋ฅผ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. ์™œ ์ด๋Ÿฐ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚ ๊นŒ์š”? ์ด๋ฅผ ์„ค๋ช…ํ•˜๋ ค๋ฉด, Nest๊ฐ€ ์‘๋‹ต์„ ์กฐ์ž‘ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ์˜ต์…˜ ๊ฐœ๋…์„ ๋จผ์ € ์†Œ๊ฐœํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ‘œ์ค€ (๊ถŒ์žฅ) Nest์˜ ๊ธฐ๋ณธ ๋ฐฉ์‹์—์„œ๋Š” ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ JavaScript ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•  ๊ฒฝ์šฐ, ์ž๋™์œผ๋กœ JSON์œผ๋กœ ์ง๋ ฌํ™”๋˜์–ด ์‘๋‹ต๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, ๋ฌธ์ž์—ด, ์ˆซ์ž, ๋ถˆ๋ฆฌ์–ธ๊ณผ ๊ฐ™์€ JavaScript์˜ ์›์‹œ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•  ๊ฒฝ์šฐ์—๋Š” Nest๊ฐ€ ํ•ด๋‹น ๊ฐ’์„ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹ ๋•๋ถ„์— ์‘๋‹ต ์ฒ˜๋ฆฌ ๋กœ์ง์ด ๋งค์šฐ ๋‹จ์ˆœํ•ด์ง€๋ฉฐ, ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋‚˜๋จธ์ง€๋Š” Nest๊ฐ€ ์ฒ˜๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.

๋˜ํ•œ, ์‘๋‹ต์˜ ์ƒํƒœ ์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•ญ์ƒ 200์ด๋ฉฐ, POST ์š”์ฒญ์˜ ๊ฒฝ์šฐ์—๋Š” 201์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋™์ž‘์€ ํ•ธ๋“ค๋Ÿฌ ์ˆ˜์ค€์—์„œ @HttpCode(...) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์ž์„ธํ•œ ๋‚ด์šฉ์€ "Status codes" ์ฐธ๊ณ )
๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ๋ฐฉ์‹ Express์™€ ๊ฐ™์€ ํŠน์ • HTTP ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ํ•ธ๋“ค๋Ÿฌ์˜ ์ธ์ž๋กœ @Res() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค(์˜ˆ: findAll(@Res() response)). ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์‘๋‹ต ๋ฉ”์„œ๋“œ๋“ค์„ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Express์—์„œ๋Š” response.status(200).send() ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์‘๋‹ต์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ฃผ์˜
Nest๋Š” ํ•ธ๋“ค๋Ÿฌ์—์„œ @Res() ๋˜๋Š” @Next()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ๋ฐฉ์‹(์˜ˆ: Express)์„ ์„ ํƒํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํ•ด๋‹น ๋ผ์šฐํŠธ์— ๋Œ€ํ•ด์„œ๋Š” ํ‘œ์ค€ ๋ฐฉ์‹(Standard approach)์ด ์ž๋™์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋˜๋ฉฐ, ๋” ์ด์ƒ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๋งŒ์•ฝ ๋‘ ๋ฐฉ์‹์„ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด(์˜ˆ: ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ์ฟ ํ‚ค๋‚˜ ํ—ค๋”๋งŒ ์„ค์ •ํ•˜๊ณ , ๋‚˜๋จธ์ง€ ์‘๋‹ต ์ฒ˜๋ฆฌ๋Š” Nest์— ๋งก๊ธฐ๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ), @Res({ passthrough: true }) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ passthrough ์˜ต์…˜์„ true๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์š”์ฒญ ๊ฐ์ฒด

ํ•ธ๋“ค๋Ÿฌ๋Š” ์ข…์ข… ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ ์„ธ๋ถ€ ์ •๋ณด์— ์ ‘๊ทผํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Nest๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ”Œ๋žซํผ(๊ธฐ๋ณธ์€ Express)์˜ ์š”์ฒญ ๊ฐ์ฒด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ํ•ธ๋“ค๋Ÿฌ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— @Req() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Nest๊ฐ€ ์š”์ฒญ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค.

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

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Req() request: Request): string {
    return 'This action returns all cats';
  }
}
ํžŒํŠธ
์œ„ ์˜ˆ์ œ์ฒ˜๋Ÿผ request: Request์™€ ๊ฐ™์ด Express์˜ ํƒ€์ž… ์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜๋ ค๋ฉด, @types/express ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์š”์ฒญ ๊ฐ์ฒด๋Š” HTTP ์š”์ฒญ์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด, ํŒŒ๋ผ๋ฏธํ„ฐ, HTTP ํ—ค๋”, ๋ฐ”๋”” ๋“ฑ์˜ ์†์„ฑ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค (์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ ์ฐธ๊ณ ). ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ์†์„ฑ๋“ค์„ ์ˆ˜๋™์œผ๋กœ ์ ‘๊ทผํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ , Nest๋Š” @Body()๋‚˜ @Query()์™€ ๊ฐ™์€ ์ „์šฉ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋“ค์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜์—ฌ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” Nest์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋“ค๊ณผ ๊ฐ๊ฐ์ด ๋‚˜ํƒ€๋‚ด๋Š” ํ”Œ๋žซํผ๋ณ„ ๊ฐ์ฒด๋“ค์˜ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

@Request(), @Req() req
@Response(), @Res() res
@Next() next
@Session() req.session
@Param(key?: string) req.params / req.params[key]
@Body(key?: string) req.body / req.body[key]
@Query(key?: string) req.query / req.query[key]
@Headers(key?: string) req.headers / req.headers[name]
@Ip() req.ip
@HostParam() req.hosts

 

๊ธฐ๋ณธ HTTP ํ”Œ๋žซํผ (์˜ˆ: Express, Fastify) ๊ฐ„์˜ ํƒ€์ž… ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด, Nest๋Š” @Res()์™€ @Response() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. @Res()๋Š” ๋‹จ์ˆœํžˆ @Response()์˜ ๋ณ„์นญ์ด๋ฉฐ, ๋‘˜ ๋‹ค ๊ธฐ๋ณธ ํ”Œ๋žซํผ์˜ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ด๋‹น ํ”Œ๋žซํผ์˜ ํƒ€์ž… ์ •๋ณด (์˜ˆ: @types/express)๋ฅผ ํ•จ๊ป˜ ์„ค์น˜ํ•˜์—ฌ ํƒ€์ž… ์ง€์›์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. @Res() ๋˜๋Š” @Response()๋ฅผ ๋ฉ”์„œ๋“œ ํ•ธ๋“ค๋Ÿฌ์— ์ฃผ์ž…ํ•˜๋ฉด, ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ๋Š” Nest์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ๋ชจ๋“œ (Library-specific mode)๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์‘๋‹ต ์ฒ˜๋ฆฌ๋Š” ๊ฐœ๋ฐœ์ž์˜ ์ฑ…์ž„์ด๋ฉฐ, ๋ฐ˜๋“œ์‹œ res.json(...), res.send(...) ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์–ด๋–ค ํ˜•ํƒœ๋กœ๋“  ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด HTTP ์„œ๋ฒ„๋Š” ์‘๋‹ต ์—†์ด ๋Œ€๊ธฐ ์ƒํƒœ์— ๋น ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํžŒํŠธ
์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ํ•ด๋‹น ์ฑ•ํ„ฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌ์†Œ์Šค

์•ž์„œ ์šฐ๋ฆฌ๋Š” ๊ณ ์–‘์ด (cats) ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” (GET ๋ผ์šฐํŠธ) ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ •์˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๋„ ํ•จ๊ป˜ ์ œ๊ณตํ•˜๊ณ  ์‹ถ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด POST ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

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

@Controller('cats')
export class CatsController {
  @Post()
  create(): string {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

 

์ด์ฒ˜๋Ÿผ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. Nest๋Š” ํ‘œ์ค€ HTTP ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋“ค์„ ๋ชจ๋‘ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค: @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), @Head(). ์ถ”๊ฐ€๋กœ, @All()์€ ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค.

๋ผ์šฐํŠธ ์™€์ผ๋“œ์นด๋“œ

NestJS๋Š” ํŒจํ„ด ๊ธฐ๋ฐ˜ ๋ผ์šฐํŠธ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฒฝ๋กœ์˜ ๋์—์„œ ๋ชจ๋“  ๋ฌธ์ž ์กฐํ•ฉ๊ณผ ์ผ์น˜์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๋ณ„ํ‘œ(*)๋ฅผ ์™€์ผ๋“œ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ์—์„œ findAll() ๋ฉ”์„œ๋“œ๋Š” abcd/๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋ผ์šฐํŠธ์— ๋Œ€ํ•ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๊ทธ ๋’ค์— ์–ด๋–ค ๋ฌธ์ž๊ฐ€ ์˜ค๋“  ์ƒ๊ด€์—†์Šต๋‹ˆ๋‹ค.

@Get('abcd/*')
findAll() {
  return 'This route uses a wildcard';
}

 

abcd/* ๊ฒฝ๋กœ๋Š” abcd/, abcd/123, abcd/abc ๋“ฑ๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ดํ”ˆ(-)๊ณผ ์ (.)์€ ๋ฌธ์ž์—ด ๊ธฐ๋ฐ˜ ๊ฒฝ๋กœ์—์„œ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค.

 

์ด ๋ฐฉ์‹์€ Express์™€ Fastify ๋ชจ๋‘์—์„œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ตœ์‹  ๋ฒ„์ „์˜ Express(v5)์—์„œ๋Š” ๋ผ์šฐํŒ… ์‹œ์Šคํ…œ์ด ๋” ์—„๊ฒฉํ•ด์กŒ์Šต๋‹ˆ๋‹ค. ์ˆœ์ˆ˜ Express์—์„œ๋Š” ๊ฒฝ๋กœ๋ฅผ ๋™์ž‘์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๋ช…๋ช…๋œ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด abcd/*splat๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ splat์€ ๋‹จ์ˆœํžˆ ์™€์ผ๋“œ์นด๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ด๋ฆ„์ผ ๋ฟ์ด๋ฉฐ ํŠน๋ณ„ํ•œ ์˜๋ฏธ๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ์ด๋ฆ„์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ, Nest๋Š” Express๋ฅผ ์œ„ํ•œ ํ˜ธํ™˜์„ฑ ๋ ˆ์ด์–ด๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ์—ฌ์ „ํžˆ ๋ณ„ํ‘œ (*)๋ฅผ ์™€์ผ๋“œ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ฒฝ๋กœ ์ค‘๊ฐ„์—์„œ ๋ณ„ํ‘œ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, Express๋Š” ๋ช…๋ช…๋œ ์™€์ผ๋“œ์นด๋“œ(e.g., ab{*aplat}cd)๋ฅผ ์š”๊ตฌํ•˜๋ฉฐ, Fastify๋Š” ์ด๋ฅผ ์ „ํ˜€ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ƒํƒœ ์ฝ”๋“œ

์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, ์‘๋‹ต์˜ ๊ธฐ๋ณธ ์ƒํƒœ ์ฝ”๋“œ๋Š” ํ•ญ์ƒ 200์ด๋ฉฐ, POST ์š”์ฒญ์˜ ๊ฒฝ์šฐ์—๋Š” ๊ธฐ๋ณธ๊ฐ’์ด 201์ž…๋‹ˆ๋‹ค. ์ด ๋™์ž‘์€ ํ•ธ๋“ค๋Ÿฌ ์ˆ˜์ค€์—์„œ @HttpCode(...) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Post()
@HttpCode(204)
create() {
  return 'This action adds a new cat';
}
ํžŒํŠธ
HttpCode๋Š” @nestjs/common ํŒจํ‚ค์ง€์—์„œ ์ž„ํฌํŠธ ํ•˜์„ธ์š”.

 

์ƒํƒœ ์ฝ”๋“œ๊ฐ€ ๊ณ ์ •์ ์ด์ง€ ์•Š๊ณ  ์—ฌ๋Ÿฌ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ์‘๋‹ต ๊ฐ์ฒด(@Res()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์ž…)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์—๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์‘๋‹ต ํ—ค๋”

์ปค์Šคํ…€ ์‘๋‹ต ํ—ค๋”๋ฅผ ์ง€์ •ํ•˜๋ ค๋ฉด @Header() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด res.header()๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Post()
@Header('Cache-Control', 'no-store')
create() {
  return 'This action adds a new cat';
}
ํžŒํŠธ
Header๋Š” @nestjs/common ํŒจํ‚ค์ง€์—์„œ ์ž„ํฌํŠธ ํ•˜์„ธ์š”.

๋ฆฌ๋‹ค์ด๋ ‰์…˜

์‘๋‹ต์„ ํŠน์ • URL๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ•˜๋ ค๋ฉด @Redirect() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ res.redirect()๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

@Redirect()๋Š” url๊ณผ statusCode ๋‘ ๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›์œผ๋ฉฐ, ๋‘˜ ๋‹ค ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. statusCode๋ฅผ ์ƒ๋žตํ•˜๋ฉด ๊ธฐ๋ณธ๊ฐ’์€ 302 (Found)์ž…๋‹ˆ๋‹ค.

@Get()
@Redirect('https://nestjs.com', 301)
ํžŒํŠธ
๊ฒฝ์šฐ์— ๋”ฐ๋ผ HTTP ์ƒํƒœ ์ฝ”๋“œ๋‚˜ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ URL์„ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿด ๋• @nestjs/common์˜ HttpRedirectResponse ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋”ฐ๋ฅด๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

๋ฐ˜ํ™˜๋œ ๊ฐ’์€ @Redirect() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ „๋‹ฌ๋œ ๋ชจ๋“  ์ธ์ž๋ฅผ ๋ฎ์–ด์”๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
  if (version && version === '5') {
    return { url: 'https://docs.nestjs.com/v5/' };
  }
}

๋ผ์šฐํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ

์ •์ ์ธ ๊ฒฝ๋กœ๋กœ๋Š” ์š”์ฒญ์˜ ์ผ๋ถ€๋กœ ๋™์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์•ผ ํ•  ๊ฒฝ์šฐ (์˜ˆ: GET /cats/1์—์„œ id๊ฐ€ 1์ธ ๊ณ ์–‘์ด๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ) ์›ํ•˜๋Š” ๋™์ž‘์„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ๊ฒฝ๋กœ์— ๋ผ์šฐํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ ํ† ํฐ์„ ์ถ”๊ฐ€ํ•˜์—ฌ URL์—์„œ ๋™์  ๊ฐ’์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ @Get() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์˜ˆ์ œ์—์„œ์ฒ˜๋Ÿผ, ๋ผ์šฐํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ •์˜๋œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์€ ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— @Param() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํžŒํŠธ
ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํฌํ•จํ•œ ๊ฒฝ๋กœ๋Š” ์ •์  ๊ฒฝ๋กœ ๋’ค์— ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์•ผ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฒฝ๋กœ๊ฐ€ ์ •์  ๊ฒฝ๋กœ๋กœ ํ–ฅํ•˜๋Š” ํŠธ๋ž˜ํ”ฝ์„ ๊ฐ€๋กœ์ฑ„์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
@Get(':id')
findOne(@Param() params: any): string {
  console.log(params.id);
  return `This action returns a #${params.id} cat`;
}

 

@Param() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ (์œ„ ์˜ˆ์ œ์—์„œ๋Š” params)๋ฅผ ๋ฐ์ฝ”๋ ˆ์ด์…˜ ํ•˜์—ฌ, ๋ผ์šฐํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ํ•ด๋‹น ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์†์„ฑ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค. ์ฝ”๋“œ์—์„œ ๋ณด์ด๋“ฏ์ด, param.id๋ฅผ ์ฐธ์กฐํ•˜์—ฌ id ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜๋Š”, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ํŠน์ • ํŒŒ๋ผ๋ฏธํ„ฐ ํ† ํฐ์„ ์ „๋‹ฌํ•จ์œผ๋กœ์จ, ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ ํ•ด๋‹น ๋ผ์šฐํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ด๋ฆ„์œผ๋กœ ์ง์ ‘ ์ฐธ์กฐํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ํžŒํŠธ
Param์€ @nestjs/common ํŒจํ‚ค์ง€์—์„œ ์ž„ํฌํŠธ ํ•˜์„ธ์š”.
@Get(':id')
findOne(@Param('id') id: string): string {
  return `This action returns a #${id} cat`;
}

์„œ๋ธŒ๋„๋ฉ”์ธ ๋ผ์šฐํŒ…

@Controller ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” host ์˜ต์…˜์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๋“ค์–ด์˜ค๋Š” HTTP ์š”์ฒญ์˜ ํ˜ธ์ŠคํŠธ๊ฐ€ ํŠน์ • ๊ฐ’๊ณผ ์ผ์น˜ํ•ด์•ผ๋งŒ ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Controller({ host: 'admin.example.com' })
export class AdminController {
  @Get()
  index(): string {
    return 'Admin page';
  }
}
๊ฒฝ๊ณ 
Fastify๋Š” ์ค‘์ฒฉ ๋ผ์šฐํ„ฐ (nested routers)๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋ธŒ ๋„๋ฉ”์ธ ๋ผ์šฐํŒ…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ Express ์–ด๋Œ‘ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

 

๋ผ์šฐํŠธ ๊ฒฝ๋กœ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, host ์˜ต์…˜์—์„œ๋„ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜ธ์ŠคํŠธ๋ช… ๋‚ด ํŠน์ • ์œ„์น˜์˜ ๋™์  ๊ฐ’์„ ์บก์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ @Controller() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์˜ˆ์ œ์— ์žˆ๋Š” ํ˜ธ์ŠคํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ ํ† ํฐ์ด ์ด๋Ÿฌํ•œ ์‚ฌ์šฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์„ ์–ธ๋œ ํ˜ธ์ŠคํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— @HostParam() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Controller({ host: ':account.example.com' })
export class AccountController {
  @Get()
  getInfo(@HostParam('account') account: string) {
    return account;
  }
}

์ƒํƒœ ๊ณต์œ 

๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์˜จ ๊ฐœ๋ฐœ์ž์—๊ฒŒ๋Š” Nest์—์„œ ๊ฑฐ์˜ ๋ชจ๋“  ๊ฒƒ์ด ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ ๊ฐ„์— ๊ณต์œ ๋œ๋‹ค๋Š” ์ ์ด ๋‹ค์†Œ ๋†€๋ผ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ํ’€, ์ „์—ญ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ์‹ฑ๊ธ€ํ†ค ์„œ๋น„์Šค ๋“ฑ๊ณผ ๊ฐ™์€ ๋ฆฌ์†Œ์Šค๋“ค์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” Node.js๊ฐ€ ๊ฐ ์š”์ฒญ์„ ๊ฐœ๋ณ„ ์Šค๋ ˆ๋“œ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ๋ฌด์ƒํƒœ ๋ชจ๋ธ (request/response Multi-Threaded Stateless Model)์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ, Nest์—์„œ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•ด ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด๋„, ์ผ๋ถ€ ์˜ˆ์™ธ์ ์ธ ๊ฒฝ์šฐ์—๋Š” ์ปจํŠธ๋กค๋Ÿฌ์— ์š”์ฒญ ๊ธฐ๋ฐ˜ ์ˆ˜๋ช… (request-based lifetime)์„ ์„ค์ •ํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด GraphQL ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ์˜ ์š”์ฒญ ๋‹จ์œ„ ์บ์‹ฑ, ์š”์ฒญ ์ถ”์ , ๋ฉ€ํ‹ฐ ํƒœ๋„Œ์‹œ ๊ตฌํ˜„ ๋“ฑ์ด ์ด์— ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ ์ฃผ์ž… ๋ฒ”์œ„ (scope)๋ฅผ ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ์—ฌ๊ธฐ์—์„œ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

์šฐ๋ฆฌ๋Š” ํ˜„๋Œ€์ ์ธ JavaScript, ํŠนํžˆ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์— ์ฃผ๋ชฉํ•ฉ๋‹ˆ๋‹ค. Nest๊ฐ€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์™„๋ฒฝํžˆ ์ง€์›ํ•˜๋Š” ์ด์œ ๋„ ๋ฐ”๋กœ ์ด๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  async ํ•จ์ˆ˜๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด Nest๋Š” ๋‚˜์ค‘์— ๋ฐ˜ํ™˜๋  ๊ฐ’์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ๊ทธ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

@Get()
async findAll(): Promise<any[]> {
  return [];
}

 

์ด ์ฝ”๋“œ๋Š” ์™„์ „ํžˆ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Nest๋Š” ํ•œ ๋‹จ๊ณ„ ๋” ๋‚˜์•„๊ฐ€ ๋ผ์šฐํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ RxJS์˜ Observable ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ๋„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. Nest๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ํ•ด๋‹น ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด ๊ตฌ๋…์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ŠคํŠธ๋ฆผ์ด ์™„๋ฃŒ๋˜๋ฉด ์ตœ์ข…์ ์œผ๋กœ ๋ฐฉ์ถœ๋œ ๊ฐ’์„ ์‘๋‹ต์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

@Get()
findAll(): Observable<any[]> {
  return of([]);
}

 

๋‘ ๋ฐฉ์‹ ๋ชจ๋‘ ์œ ํšจํ•˜๋ฉฐ, ์ƒํ™ฉ์— ๋”ฐ๋ผ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋ฐฉ์‹์„ ์„ ํƒํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์š”์ฒญ ๋ณธ๋ฌธ

์ด์ „ ์˜ˆ์ œ์—์„œ๋Š” POST ๋ผ์šฐํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋œ ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ๋ฐ›์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ด์ œ @Body() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ด๋ฅผ ๊ฐœ์„ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ง„ํ–‰์— ์•ž์„œ(TypeScript๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ), DTO(Data Transfer Object) ์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. DTO๋Š” ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฐฉ์‹์„ ์ •์˜ํ•˜๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์ด DTO ์Šคํ‚ค๋งˆ๋Š” TypeScript์˜ ์ธํ„ฐํŽ˜์ด์Šค๋‚˜ ๋‹จ์ˆœ ํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” ํด๋ž˜์Šค ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์™œ์ผ๊นŒ์š”? ํด๋ž˜์Šค๋Š” JavaScript ES6 ํ‘œ์ค€์˜ ์ผ๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์— ์ปดํŒŒ์ผ๋œ JavaScript์—์„œ๋„ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํŠธ๋žœ์ŠคํŒŒ์ผ ์‹œ ์ œ๊ฑฐ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฐํƒ€์ž„์—์„œ๋Š” Nest๊ฐ€ ์ด๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Š” Pipe ๊ธฐ๋Šฅ๋“ค์ด ๋Ÿฐํƒ€์ž„์—์„œ ๋ณ€์ˆ˜์˜ ๋ฉ”ํƒ€ํƒ€์ž…์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋งค์šฐ ์ค‘์š”ํ•˜๋ฉฐ, ํด๋ž˜์Šค์ผ ๋•Œ๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

์ด์ œ CreateCatDto ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค:

export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}

 

์ด ํด๋ž˜์Šค๋Š” ์„ธ ๊ฐ€์ง€ ๊ธฐ๋ณธ ์†์„ฑ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„์—๋Š” ์ด ์ƒˆ๋กœ ๋งŒ๋“  DTO๋ฅผ CatsController์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  return 'This action adds a new cat';
}
ํžŒํŠธ
Nest์˜ ValidationPipe๋Š” ๋ฉ”์„œ๋“œ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋ฐ›์•„์„œ๋Š” ์•ˆ ๋˜๋Š” ์†์„ฑ๋“ค์„ ํ•„ํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํ—ˆ์šฉํ•  ์†์„ฑ๋“ค์„ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ๋กœ ์ง€์ •ํ•˜๋ฉด, ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์— ํฌํ•จ๋˜์ง€ ์•Š์€ ์†์„ฑ์€ ๊ฒฐ๊ณผ ๊ฐ์ฒด์—์„œ ์ž๋™์œผ๋กœ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. CreateCatDto ์˜ˆ์ œ์—์„œ๋Š” name, age, breed ์†์„ฑ์ด ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ

๋ผ์šฐํŠธ์—์„œ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” @Query() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์—์„œ ํ•ด๋‹น ๊ฐ’์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, age์™€ breed ๊ฐ™์€ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณ ์–‘์ด ๋ชฉ๋ก์„ ํ•„ํ„ฐ๋งํ•˜๊ณ ์ž ํ•˜๋Š” ๋ผ์šฐํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค. ๋จผ์ €, CatsController์—์„œ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค:

@Get()
async findAll(@Query('age') age: number, @Query('breed') breed: string) {
  return `This action returns all cats filtered by age: ${age} and breed: ${breed}`;
}

 

์ด ์˜ˆ์ œ์—์„œ @Query() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์—์„œ age์™€ breed ๊ฐ’์„ ์ถ”์ถœํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค๋ฉด:

GET /cats?age=2&breed=Persian

 

age๋Š” 2, breed๋Š” Persian์œผ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:

?filter[where][name]=John&filter[where][age]=30
?item[]=1&item[]=2

 

์‚ฌ์šฉ ์ค‘์ธ HTTP ์–ด๋Œ‘ํ„ฐ (Express ๋˜๋Š” Fastify)์— ์ ์ ˆํ•œ ์ฟผ๋ฆฌ ํŒŒ์„œ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. Express์—์„œ๋Š” ํ™•์žฅ ํŒŒ์„œ (extended parser)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ’๋ถ€ํ•œ ํ˜•ํƒœ์˜ ์ฟผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.set('query parser', 'extended');

 

Fastify์—์„œ๋Š”, querystringParser ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter({
    querystringParser: (str) => qs.parse(str),
  }),
);
ํžŒํŠธ
qs๋Š” ์ค‘์ฒฉ ๊ตฌ์กฐ์™€ ๋ฐฐ์—ด์„ ์ง€์›ํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด ํŒŒ์„œ์ž…๋‹ˆ๋‹ค. npm install qs ๋ช…๋ น์–ด๋กœ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์™ธ ์ฒ˜๋ฆฌ

์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ• (์ฆ‰, ์˜ˆ์™ธ ์ฒ˜๋ฆฌ)์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ๋ณ„๋„์˜ ์žฅ์—์„œ ๋‹ค๋ฃจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ „์ฒด ๋ฆฌ์†Œ์Šค ์˜ˆ์ œ

์•„๋ž˜๋Š” ์—ฌ๋Ÿฌ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ์ด ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ณ  ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}
ํžŒํŠธ
Nest CLI๋Š” ๋ชจ๋“  ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ์ œ๋„ˆ๋ ˆ์ดํ„ฐ(์Šคํ‚ค๋งคํ‹ฑ) ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ˆ˜๋™ ์ž‘์—…์„ ์ค„์ด๊ณ  ์ „๋ฐ˜์ ์ธ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ํ–ฅ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ (Getting up and running)

CatsController๋ฅผ ์™„์ „ํžˆ ์ •์˜ํ•˜๋”๋ผ๋„, Nest๋Š” ์ด๋ฅผ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜๊ฑฐ๋‚˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋ฐ˜๋“œ์‹œ ์–ด๋–ค ๋ชจ๋“ˆ์˜ ์ผ๋ถ€๋กœ ํฌํ•จ๋˜์–ด์•ผ ํ•˜๋ฉฐ, ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— @Module() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์•ˆ์˜ controllers ๋ฐฐ์—ด์— ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ๋ฃจํŠธ ๋ชจ๋“ˆ์ธ AppModule ์™ธ์— ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ์ •์˜ํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ, CatsController๋ฅผ AppModule์— ๋“ฑ๋กํ•˜์—ฌ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';

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

 

Module() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋“ˆ ํด๋ž˜์Šค์— ์—ฐ๊ฒฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ด์ œ Nest๋Š” ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๋“ค์„ ๋งˆ์šดํŠธํ•ด์•ผ ํ•˜๋Š”์ง€ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ๋ฐฉ์‹

์ง€๊ธˆ๊นŒ์ง€๋Š” ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” Nest์˜ ํ‘œ์ค€ ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ๋ฐฉ์‹์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํŠน์ • ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๋ ค๋ฉด @Res() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐจ์ด์ ์„ ๊ฐ•์กฐํ•˜๊ธฐ ์œ„ํ•ด CatsController๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

import { Controller, Get, Post, Res, HttpStatus } from '@nestjs/common';
import { Response } from 'express';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Res() res: Response) {
    res.status(HttpStatus.CREATED).send();
  }

  @Get()
  findAll(@Res() res: Response) {
     res.status(HttpStatus.OK).json([]);
  }
}

 

์ด ๋ฐฉ์‹์€ ํ—ค๋” ์กฐ์ž‘์ด๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณ ์œ  ๊ธฐ๋Šฅ ์ ‘๊ทผ ๋“ฑ ์‘๋‹ต ๊ฐ์ฒด์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์ œ์–ด๊ถŒ์„ ์ œ๊ณตํ•˜์—ฌ ๋” ๋†’์€ ์œ ์—ฐ์„ฑ์„ ๊ฐ€์ง€์ง€๋งŒ, ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์ด ๋ฐฉ๋ฒ•์€ ๋ช…ํ™•์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ๋ช‡ ๊ฐ€์ง€ ๋‹จ์ ์„ ๋™๋ฐ˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํฐ ๋‹จ์ ์€ ์ฝ”๋“œ๊ฐ€ ํ”Œ๋žซํผ ์ข…์†์ ์ด ๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜๋Š” ํ•˜์œ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋”ฐ๋ผ ์‘๋‹ต ๊ฐ์ฒด์˜ API๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ์‘๋‹ต ๊ฐ์ฒด ๋“ฑ์„ ๋ชจํ‚นํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ๋” ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ฒŒ๋‹ค๊ฐ€ ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, @HttpCode(), @Header() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋‚˜ Interceptor ๋“ฑ Nest์˜ ํ‘œ์ค€ ์‘๋‹ต ์ฒ˜๋ฆฌ ๋ฐฉ์‹์— ์˜์กดํ•˜๋Š” ๊ธฐ๋Šฅ๋“ค๊ณผ์˜ ํ˜ธํ™˜์„ฑ์ด ๋–จ์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด passthrough ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

@Get()
findAll(@Res({ passthrough: true }) res: Response) {
  res.status(HttpStatus.OK);
  return [];
}

 

์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด, ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ฟ ํ‚ค๋‚˜ ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๋Š” ๋“ฑ ๋„ค์ดํ‹ฐ๋ธŒ ์‘๋‹ต ๊ฐ์ฒด์™€ ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉด์„œ๋„, ๋‚˜๋จธ์ง€ ์‘๋‹ต ์ฒ˜๋ฆฌ๋Š” Nest ํ”„๋ ˆ์ž„์›Œํฌ์— ๋งก๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Reference