Creat Message API

onyoo·2023년 1월 4일
0

NestJs

목록 보기
2/8
post-thumbnail

개요

Nest Js를 이용하여 메세지 API를 구현하고. Nest의 데코레이션 그리고 Nest 가 요청 데이터의 유효성을 검증하는 과정을 알아보자.

어떠한 것을 만들것인가 ?

우리는 message 라는 모듈을 만들 것이다. 그렇다면 필요한 것이 무엇인지 ? 어떤 기능을 만들것인지 생각해보아야한다.

앞서 말했듯이 Nest에서 API를 만들때 이 다섯가지의 기능 이용한다.만약, 해당 기능이 필요없으면 빼도 된다.

우리가 만들 기능은 세가지로 여기에서 어떤 기능이 필요한지,필요없는지 알아보자.

  1. 메세지의 리스트를 리턴하는 API이다. 해당 기능의 경우 Controller , Service , Repository가 필요하다.

    왜? 데이터를 라우팅해 줄 컨트롤러 , 로직을 처리 할 서비스 , 데이터베이스에 접근하는 리포지토리 세가지가 필요하다

  2. Body로 받은 데이터를 이용하여 새로운 메세지를 생성하는 API 이다. json으로 데이터를 받으니까 해당 데이터에 대한 유효성을 검사해야한다. 해당 API의 사용자가 누구인지에 대한 검사를 요구하는 부분은 없기 때문에 Guard에 대한 부분을 제외하고 나머지 부분이 필요하다.

  3. 해당 id를 가진 데이터에 대한 값을 리턴하는 API이다 1번과 마찬가지로 라우팅을 위한 컨트롤러 , 로직을 처리하는 서비스 , 데이터베이스에 접근하는 리포지토리 세가지가 필요하다.

데코레이터

우리가 이 기능을 구현하기 전 잠깐 알아야 할 내용이 있다. 바로 데코레이터다, 우리가 어떤 받은 데이터가 어떻게 들어오는지 설정하는 부분이 이 데코레이터들이다.

데코레이터에는 세가지가 존재한다.

클래스 데코레이터

속성 데코레이터

함수 데코레이터

위에서 언급한 첫번째 API의 경우와 달리 두번째,세번째 API의 경우 request 요청에서 데이터를 가져와야 한다. 이럴 경우 데코레이터를 이용해 데이터를 가져올 수 있다.

이러한 식으로 데코레이터를 이용하여 들어오는 데이터를 요모조모 뜯어볼 수 있다. 우리는 이것을 이용하여 Body에 들어오는 값과 Param 으로 들어오는 id 값을 확인 할 예정이다.

import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { createMessagesDto } from './dtos/create-message.dto';

@Controller('messages') // class decorator
export class MessagesController {
  @Get() //method decorator
  listMessages() {}

  @Post()
  createMessages(@Body() body: string /**argument decorator**/) {
    console.log(body);
  }
  //들어오는 데이터가 유효하지 않은지 확인하기 위해 pipe를 구현함
  // 유효성 검사 !

  @Get('/:id')
  getMessages(@Param('id') id: string) {
    console.log(id);
  }
}

함수 데코레이터를 이용하여 Body에 들어오는 값이 어떤 변수에 들어가는지, 그리고 어떤 타입을 가지는지 지정해주면 함수 내부에서 읽어들일 수 있다.

그러나 여기에서 주목해야할 점은 이것뿐만이 아니다. 만약 Body 값으로 들어오는 값이 string이지만 우리가 지정한 형태에 맞지 않는다면 어떻게 될까?

데이터 유효성 검사하기

우리는 이제 데이터의 유효성을 검사할 것이다. 본격적으로 시작하기 위해 pipe에 우리가 설정한 유효성 설정을 등록하는 방법에 대해서 알아보자

import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { MessagesModule } from './messages/messages.module';

async function bootstrap() {
  const app = await NestFactory.create(MessagesModule);

  app.useGlobalPipes(new ValidationPipe()); // 유효성 검사 파이프 추가
  //원하는 경우에는 하나의 단일 루트 핸들러에만 파이프를 적용할 수 있다
  //그러나 이 경우 들어오는 모든 요청에 대해서 파이프를 처리할 예정이다
  //Data transfer Object를 작성해주면 파이프에서 처리함 (DTO)

  await app.listen(3000);
}
bootstrap();

해당 코드를 확인하면 useGlobalPipe로서 전체에 해당 pipe를 적용한다는 뜻이다. 이런식으로 코드를 작성해주면 파이프라인이 작동한다. 그러나 파이프라인의 설정을 정해주지 않으면 없는거나 다름이 없으니 유효성을 검증하는 코드를 작성해보자.

들어온 데이터에 대한 유효성 검사 없이 무작위 값이 들어올 수 있다. 이럴때 우리가 해주어야 하는 처리는 매우매우 귀찮다. 들어온 값이 우리가 지정한 형태가 맞는지 하나하나 확인해주어야 한다.

이러한 귀찮은 과정을 대신해주는 객체가 존재하는데 그것을 우리는 DTO라고 부른다.

Data transfer Object 라고 불리우는 이것은 데이터를 전송할때 사용하는 오브젝트이다.

지금 우리와 같은 상황을 보면 데이터를 전송하는 오브젝트가 어떤형식으로 오는지 모른다. 그러면 우리는 이 오브젝트가 도착하기 전 미리 검증을 해서 API에 보내주면 API에서는 검사할 필요가 없다.

이것을 위해 사용하는 모듈이 바로 class-validator 이다. 이 모듈이 어떤 동작을 하는지 살짝 알아보도록하자.

import { IsString } from 'class-validator';
//유효성검사를 위한 라이브러리

export class createMessagesDto {
  @IsString()
  content: string;
}

작성한 코드를 살펴보자, IsString 이라는 데코레이터를 이용하여 content가 string 이냐는 것을 확인하고 있다.

즉, class-validator 의 IsString이라는 데코레이터를 이용하여 content의 데이터 타입이 string 임을 확인하는 작업을 자동화 한 코드다. 코드를 이렇게 작성하면 나중에 잘못된 요청이 오면 자동으로 string 타입이 아니기 때문에 거부한다고 pipe에서 요청을 거부한다.

추가적으로 해당 모듈에서는 class-transformer 도 사용이 되는데 이 모듈이 무엇이냐면,

우리가 작성한 Dto 코드를 json의 형태로 맞게 변환해준다고 생각하면 된다.

DTO의 경우 검증을 해야하기 때문에 저런 클래스 형태를 띄우지만 실제로 우리에게 들어오는 데이터의 형태는 json이기 때문에 json의 형태로 바꿔주는 class-transformer가 필요한 것이다.

그렇게 작성된 코드로 완성된 컨트롤러를 보면 다음과 같다.

import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { createMessagesDto } from './dtos/create-message.dto';

@Controller('messages') // class decorator
export class MessagesController {
  @Get() //method decorator
  listMessages() {}

  @Post()
  createMessages(@Body() body: createMessagesDto /**argument decorator**/) {
    console.log(body);
  }
  //들어오는 데이터가 유효하지 않은지 확인하기 위해 pipe를 구현함
  // 유효성 검사 !

  @Get('/:id')
  getMessages(@Param('id') id: string) {
    console.log(id);
  }
}

Post 부분을 확인하면 Body로 들어오는 타입이 createMessagesDto이다. 유효성이 검증된 데이터만 Body에 들어갈 수 있도록 말이다.

그런데 말입니다, 자바스크립트의 세계에서는 다음과 같은 코드는 있을 수 없는일이다. 타입스크립트의 경우 타입스크립트 자체를 읽어들이는 컴파일러가 있는 것이 아니고 자바스크립트로 변환된 다음 실행하는 것이기 때문이다.

우리가 쓴 데코레이터들은 자바스크립트상으로 가면 사라지기 때문에 이런 타입을 선언하는 것 자체가 이상한 것이다.. 어떻게 이게 파이프에서 인식을 하지 뭐, 이런거 말이다.

@Post()
  createMessages(@Body() **body: createMessagesDto** /**argument decorator**/) {
    console.log(body);
  }

이게 어떻게 동작하는지에 대해서 자세히 한번 살펴보자

tsconfig.json 부분에서 이 두가지 항목은 매우 중요하다.

"emitDecoratorMetadata": true,
"experimentalDecorators": true

어떠한 속성을 가지고 있는 것이길래 중요한 것 일까?

emitDecoratorMetadata 을 설정하게 되면 매우 작은양의 타입정보와 어노테이션이 저장되어 자바스크립트로 변환된다.그렇게 되면 우리가 작성한 타입 정보를 자바스크립트까지 살아서 잘 도착하게 된다.

/dist/messages/messages.controller.js

__decorate([
    (0, common_1.Post)(),
    __param(0, (0, common_1.Body)()),
    __metadata("design:type", Function),
    **__metadata("design:paramtypes", [create_message_dto_1.createMessagesDto]),**
    __metadata("design:returntype", void 0)
], MessagesController.prototype, "createMessages", null);

이러한 식으로 데코레이터가 코드로 남아있게된다.

즉, 타입스크립트를 자바스크립트로 변환하면 데코레이터가 실제로 적용되는 방식인 것 데코레이터 자체가 자바스크립트에 존재하는 것은 아니라고 볼 수 있다.

여기에서 메타데이터의 경우 전달되는 일부 유형 정보 혹은 상위 수준의 정보를 나타낸다.그리고 이것은 자바스크립트에 남아있거나 유지 관리가 된다.

따라서 유효성 검사 파이프로 요청이 들어올때마다 여기에 있는 메타데이터에 적힌 속성을 조회하는 것이다.

참조

해당 내용은 Udemy 강좌
https://www.udemy.com/course/nestjs-the-complete-developers-guide/
를 수강하며 작성한 내용입니다 :)

profile
반갑습니다 ! 백엔드 개발 공부를 하고있습니다.

0개의 댓글