Next.js์—์„œ class-validator๋กœ ํƒ€์ž… ์•ˆ์ „์„ฑ ๋†’์ด๊ธฐ

Next.js์—์„œ class-validator๋กœ ํƒ€์ž… ์•ˆ์ „์„ฑ ๋†’์ด๊ธฐ

D
dongAuthor
7 min read

Next.js๋กœ ๊ฐœ๋ฐœํ•  ๋•Œ API ๋ผ์šฐํŠธ๋‚˜ ํผ์—์„œ ๋„˜์–ด์˜ค๋Š” ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ์–ด๋–ป๊ฒŒ ๊ฒ€์ฆํ•˜๊ณ  ๊ณ„์‹ ๊ฐ€์š”? Express๋‚˜ NestJS์™€ ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋‹ฌ๋ฆฌ, Next.js์—๋Š” ๋‚ด์žฅ๋œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ณ„์ธต์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋ฐ์ดํ„ฐ์˜ ์ •ํ•ฉ์„ฑ์„ ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜์ฃ .

์ด ๊ธ€์—์„œ๋Š” NestJS ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์ต์ˆ™ํ•œ class-validator ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ Next.js ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋ฐ˜์˜ ๊น”๋”ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋กœ์ง์„ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ๋” ์•ˆ์ •์ ์ด๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์ข‹์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ๊ฐœ์ธ์ ์ธ ๊ฒฝํ—˜ ์ƒ class-validator ๋„์ž… ํ›„ ์ œํ’ˆ ์•ˆ์ •์„ฑ๊ณผ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ์ด ํ›จ์”ฌ ์ฆ๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. Nextjs๋Š” API ์„œ๋ฒ„๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ์— ๋ถˆํŽธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ ์•„ํ‚คํ…์ณ๋ฅผ ์ž˜ ์„ค๊ณ„ํ•˜๋ฉด Next.js์—์„œ๋„ ๊ฝค ์“ธ๋งŒํ•œ API ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ์•„ํ‹ฐํด์€ Next.js 15๋ฒ„์ „์„ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”. ๐Ÿ˜‰

์™œ Next.js์—์„œ class-validator๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

Next.js์—์„œ API ์š”์ฒญ ๋ณธ๋ฌธ(body)์„ ์ฒ˜๋ฆฌํ•  ๋•Œ, ๊ฐ ํ•„๋“œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์ธ์ง€, ํ•„์ˆ˜๊ฐ’์ด ๋ˆ„๋ฝ๋˜์ง€๋Š” ์•Š์•˜๋Š”์ง€ ์ผ์ผ์ด ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ๋ฒˆ๊ฑฐ๋กœ์šด ์ผ์ž…๋‹ˆ๋‹ค.

// ์ผ๋ฐ˜์ ์ธ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋กœ์ฝ”์ง
if (!body.email || typeof body.email !== 'string') {
  // ์—๋Ÿฌ ์ฒ˜๋ฆฌ...
}
if (!body.password || body.password.length < 6) {
  // ์—๋Ÿฌ ์ฒ˜๋ฆฌ...
}

์ด๋Ÿฐ ๋ฐฉ์‹์€ ์ฝ”๋“œ๋ฅผ ์ง€์ €๋ถ„ํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , ์ƒˆ๋กœ์šด ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ๊ฒ€์ฆ ๋กœ์ง์„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๋ถˆํŽธํ•จ์ด ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

class-validator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DTO(Data Transfer Object) ํด๋ž˜์Šค์— ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ด์šฉํ•ด ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ทœ์น™์„ ์„ ์–ธ์ ์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์–ด, ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•˜๊ณ  ์ง๊ด€์ ์œผ๋กœ ๋ณ€ํ•ฉ๋‹ˆ๋‹ค.

class-validator ์„ค์ •ํ•˜๊ธฐ

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Next.js ํ”„๋กœ์ ํŠธ์— class-validator๋ฅผ ์„ค์ •ํ•ด ๋ด…์‹œ๋‹ค.

1. ํ•„์š” ํŒจํ‚ค์ง€ ์„ค์น˜ํ•˜๊ธฐ

๋จผ์ €, ์œ ํšจ์„ฑ ๊ฒ€์ฆ์— ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

npm install class-validator class-transformer reflect-metadata
  • class-validator: ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • class-transformer: ์ˆœ์ˆ˜(plain) ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜ํ•ด์ค๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ œ๋Œ€๋กœ ์ฝ์œผ๋ ค๋ฉด ์ด ๋ณ€ํ™˜ ๊ณผ์ •์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • reflect-metadata: ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ๋Ÿฐํƒ€์ž„์— ํƒ€์ž… ์ •๋ณด๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฌธ๋ฒ•์„ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Babel ๊ด€๋ จ ํŒจํ‚ค์ง€๋„ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

npm install --save-dev @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator

2. tsconfig.json ์„ค์ •ํ•˜๊ธฐ

TypeScript๊ฐ€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฌธ๋ฒ•์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์„ํ•˜๊ณ , ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก tsconfig.json ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. compilerOptions์— ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.

// tsconfig.json
{
  "compilerOptions": {
    // ... ๊ธฐ์กด ์„ค์ •
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
  • experimentalDecorators: TypeScript์—์„œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • emitDecoratorMetadata: reflect-metadata๊ฐ€ ๋Ÿฐํƒ€์ž„์— ํƒ€์ž… ์ •๋ณด๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

3. .babelrc ์„ค์ •ํ•˜๊ธฐ

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— .babelrc ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ด€๋ จ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ Babel์ด TypeScript ์ฝ”๋“œ๋ฅผ ๋ณ€ํ™˜ํ•  ๋•Œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๋„๋ก ๋•์Šต๋‹ˆ๋‹ค.

// .babelrc
{
  "presets": ["next/babel"],
  "plugins": [
    "babel-plugin-transform-typescript-metadata",
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    "babel-plugin-parameter-decorator",
    [
      "@babel/plugin-transform-runtime",
      {
        "regenerator": true
      }
    ]
  ]
}

4. reflect-metadata ์ „์—ญ์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

reflect-metadata๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹œ์ž‘์ ์—์„œ ๋‹จ ํ•œ ๋ฒˆ๋งŒ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. API ๋ผ์šฐํŠธ์˜ ์ง„์ž…์ ์ด๋‚˜ ๊ณตํ†ต ์œ ํ‹ธ๋ฆฌํ‹ฐ ํŒŒ์ผ ์ƒ๋‹จ์— import ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด src/lib/validation.ts์™€ ๊ฐ™์€ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// src/lib/validation.ts ๋˜๋Š” app/api/.../route.ts ์ƒ๋‹จ
import "reflect-metadata";

// ... ์ดํ›„ ์ฝ”๋“œ

DTO(Data Transfer Object) ์ •์˜ํ•˜๊ธฐ

์ด์ œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ทœ์น™์„ ๋‹ด์„ DTO ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. src/dto/create-user.dto.ts ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

// src/dto/create-user.dto.ts
import { IsEmail, IsNotEmpty, IsOptional, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty({ message: '์ด๋ฆ„์€ ํ•„์ˆ˜ ํ•ญ๋ชฉ์ž…๋‹ˆ๋‹ค.' })
  name: string;

  @IsEmail({}, { message: '์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.' })
  email: string;

  @MinLength(6, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ตœ์†Œ 6์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.' })
  password: string;

  @IsNotEmpty({ message: 'ํ•™๊ต ์ •๋ณด๋Š” ํ•„์ˆ˜ ํ•ญ๋ชฉ์ž…๋‹ˆ๋‹ค.' })
  school: string;

  @IsNotEmpty({ message: '์ „ํ™”๋ฒˆํ˜ธ๋Š” ํ•„์ˆ˜ ํ•ญ๋ชฉ์ž…๋‹ˆ๋‹ค.' })
  phoneNumber: string;

  @IsOptional()
  introduce?: string;
}

๊ฐ ์†์„ฑ ์œ„์— @IsEmail(), @MinLength(6)์™€ ๊ฐ™์€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ทœ์น™์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ปค์Šคํ…€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด, ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ๋” ์นœ์ ˆํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด์š”.

์œ ํšจ์„ฑ ๊ฒ€์ฆ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋งŒ๋“ค๊ธฐ

NestJS์˜ ValidationPipe์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์ด Next.js์—๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ์—, DTO๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. src/lib/validation.ts ํŒŒ์ผ์— ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค.

// src/lib/validation.ts
import 'reflect-metadata';
import { validate, ValidationError } from 'class-validator';
import { plainToInstance } from 'class-transformer';

export async function validateDto<T extends object>(
  dtoClass: new () => T,
  payload: unknown,
): Promise<{ instance: T; errors: string[] }> {
  const instance = plainToInstance(dtoClass, payload);
  const errors: ValidationError[] = await validate(instance);

  if (errors.length > 0) {
    const errorMessages = errors
      .map((err) => Object.values(err.constraints || {}))
      .flat();
    return { instance, errors: errorMessages };
  }

  return { instance, errors: [] };
}

์ด ํ•จ์ˆ˜๋Š” ๋‘ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

  1. plainToInstance(dtoClass, payload): ์ผ๋ฐ˜ JavaScript ๊ฐ์ฒด(payload)๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ DTO ํด๋ž˜์Šค(dtoClass)์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ •์˜๋œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.
  2. validate(instance): ๋ณ€ํ™˜๋œ ์ธ์Šคํ„ด์Šค์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ , ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋‹ค๋ฉด ValidationError ๊ฐ์ฒด์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

API Route์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ

์ด์ œ ๋งŒ๋“  DTO์™€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋ฅผ API ๋ผ์šฐํŠธ์—์„œ ์‚ฌ์šฉํ•ด๋ณผ ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. app/api/users/route.ts ํŒŒ์ผ์„ ์˜ˆ์‹œ๋กœ ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// app/api/users/route.ts
import { NextResponse } from 'next/server';
import { CreateUserDto } from '@/dto/create-user.dto';
import { validateDto } from '@/lib/validation';

export async function POST(req: Request) {
  try {
    const body = await req.json();
    const { instance, errors } = await validateDto(CreateUserDto, body);

    if (errors.length > 0) {
      // ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์‹คํŒจ
      return NextResponse.json(
        { message: 'Validation failed', errors },
        { status: 400 },
      );
    }

    // ์œ ํšจ์„ฑ ๊ฒ€์ฆ ํ†ต๊ณผ โ†’ DB ์ €์žฅ ๋˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํ˜ธ์ถœ
    // ...

    return NextResponse.json({
      message: 'โœ… Validation passed',
      data: instance,
    });
  } catch (err: any) {
    // req.json() ํŒŒ์‹ฑ ์—๋Ÿฌ ๋“ฑ ๊ธฐํƒ€ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
    return NextResponse.json(
      { message: 'An unexpected error occurred' },
      { status: 500 },
    );
  }
}

POST ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด req.json()์œผ๋กœ ๋ฐ›์€ ์š”์ฒญ ๋ณธ๋ฌธ์„ validateDto ํ•จ์ˆ˜์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ errors ๋ฐฐ์—ด์— ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด, 400 ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ฆ์„ ํ†ต๊ณผํ–ˆ๋‹ค๋ฉด, instance ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค์Œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. instance๋Š” CreateUserDto ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์ด๋ฏ€๋กœ ํƒ€์ž…์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

Controller ์ถ”๊ฐ€ํ•˜๊ธฐ

๋ผ์šฐํŠธ์™€ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๊ฒ€์ฆ์„ ํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

class UserController {
  async createUser(payload: CreateUserDto) {
    await guardAdmin();
    await validateDto(CreateUserDto, payload);

    return userService.createUserByAdmin(payload);
  }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋”์šฑ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Service์™€ Repository๋„ ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๊ฒ ์ฃ ?

๋” ๋‚˜์€ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์œ„ํ•˜์—ฌ

Next.js์—์„œ class-validator๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋‹จ์ˆœํžˆ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๊ฒƒ ์ด์ƒ์˜ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

  • ๊ฐ€๋…์„ฑ ๋ฐ ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ: ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ทœ์น™์ด DTO์— ๋ช…ํ™•ํ•˜๊ฒŒ ์„ ์–ธ๋˜์–ด ์žˆ์–ด, ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ˆ˜์ •ํ•˜๊ธฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.
  • ํƒ€์ž… ์•ˆ์ „์„ฑ ํ™•๋ณด: plainToInstance๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜๋œ ๊ฐ์ฒด๋Š” ํด๋ž˜์Šค ํƒ€์ž…์ด ๋ณด์žฅ๋˜๋ฏ€๋กœ, ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ํƒ€์ž… ๊ด€๋ จ ์‹ค์ˆ˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ: โ€œ์–ด๋–ป๊ฒŒโ€ ๊ฒ€์ฆํ• ์ง€๊ฐ€ ์•„๋‹Œ, โ€œ๋ฌด์—‡์„โ€ ๊ฒ€์ฆํ• ์ง€์— ์ง‘์ค‘ํ•˜๊ฒŒ ๋˜์–ด ๋” ๊น”๋”ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์กฐ๊ธˆ์˜ ์ดˆ๊ธฐ ์„ค์ •์ด ํ•„์š”ํ•˜์ง€๋งŒ, class-validator๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์•ˆ์ •์„ฑ๊ณผ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ์„ ๊ณ ๋ คํ•˜๋ฉด ์ถฉ๋ถ„ํžˆ ํˆฌ์žํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์˜ Next.js ํ”„๋กœ์ ํŠธ์—๋„ ๋„์ž…ํ•˜์—ฌ ๋” ๊ฒฌ๊ณ ํ•˜๊ณ  ํƒ€์ž…-์•ˆ์ „ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”.

References

Next.js์—์„œ class-validator๋กœ ํƒ€์ž… ์•ˆ์ „์„ฑ ๋†’์ด๊ธฐ