[NestJS] Controllers

곽태민·2023년 6월 20일
0

NestJS

목록 보기
2/8

Controllers

Controller는 들어오는 request를 처리하고, 클라이언트에 response를 반환하는 역할을 한다. Controller의 목적은 어플리케이션에 대한 특정 request를 수신하는 것이다.

Routing을 통해서 어떤 Controller가 어떤 요청을 받는지 제어한다. 종종 각 Controller에는 둘 이상의 경로가 있고, 다른 경로는 다른 작업을 수행할 수 있다.

NestJS Docs

기본 Controller를 만들기 위해 classdecorator를 사용하는데, decoratorclass를 필수 meta data와 연결하며 Nest가 Routing 앱을 생성하게 도와준다.

(request를 해당 Controller에 연결)

Routing

아래 예제에서는 기본 Controller를 정의하는데 필요한 @Controller() 데코레이터를 사용한다. @Controller 데코레이터에서 선택적 경로 접두사를 지정하여 관련 경로 집합을 쉽게 그룹화하고, 반복코드를 최소화 한다.

예를들어 /cats 경로 아래 cats entity와의 상호 작용을 관리하는 일련의 경로를 그룹화하도록 선택을 할 수 있다.

이 경우 @Controller() 데코레이터에 경로 접두사 cats를 지정해서 파일의 각 경로에 대한 경로 부분을 반복할 필요가 없도록 할 수 있다.

cats.controller.ts

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

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

findAll() 메서드 위에 @Get() HTTP 요청 메서드 데코레이터는 Nest에게 HTTP 요청에 대한 특정 엔드포인트에 대한 핸들러를 생성하도록 지시한다.

(엔드포인트는 HTTP 요청 메서드 및 Route 경로에 해당된다.)

Request object

Handler는 종종 클라이언트 reqeuest 세부 정보에 엑세스해야 한다. Nest는 기본 플랫폼의 request object에 대한 엑세스를 제공한다. (기본적으로 Express)

Handler Signature에 @Req() 데코레이터를 추가해서 Nest에 주입하도록 지시해서 request object에 액세스할 수 있다.

cats.controller.ts

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

@Controller('cats')
export class CatsController {
	@Get()
  	findAll(@Req() req: Request): string {
    	return 'This action returns all cats';
    }
}

request object는 HTTP 요청을 나타내며 request query string, parameters, HTTP Header, body를 포함한다.

하지만 query stringbody 경우에는 @Query() 또는 @Body()와 같은 전용 데코레이터를 사용할 수 있다.

request object가 있으면 response object도 있다. response object@Res() 데코레이터를 사용하면 되는데, @Response()의 별칭으로 둘 다 response object 인터페이를 직접 노출한다.

@Res() 데코레이터로 주입하여 Express response object에 접근을 하고 싶을 때는 Express를 사용할 때와 같이 res.send() 또는 res.json()을 사용하면 된다.

Resource

위 예제에서 @Get()을 사용하여 엔트포인트를 정의했듯이 Nest에서도 HTTP 메서드를 모두 제공해준다. @Get, @Post, @Put(), @Patch(), @Put(), @Delete(), @Oprions()@Head()와 같은 데코레이터를 제공한다.

(또한 @All()은 이들을 모두 처리하는 엔드포인트를 정의한다.)

Status code

Response Status Code는 기본적으로 200이다. POST 요청은 201이다. Handler level에서 @HttpCode(...) 데코레이터를 추가해서 이 동작들을 쉽게 변경할 수 있다.

@Post()
@HttpCode(204)
create() {
	return 'This action add a new cat';
}

(@HttpCode() 데코레이터는 pakcage @nestjs/common에서 가져온다.)

Route parameters

request의 일부로 dynamic data를 수락해야하는 경우 (e.g., GET /cats/1 ID가 1인 cat을 가져올 경우) 정적 경로가 있는 Route는 작동하지 않는다.

Nest에서는 @Param() 데코레이터를 사용해서 엑세스를 할 수 있다.

cats.conroller.ts

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

@Param()은 메서드 parameter를 decorated하는데 사용되고, route parameter를 메서드 body에 decorated된 메서드 parameter 속성으로 사용할 수 있도록 한다.

Scopes

Node.js는 모든 요청이 별도의 스레드에 의해서 처리되는 요청/응답 다중 스레드 상태 비저장 모델을 따르지 않는다. 따라서, 싱글톤 인스턴스를 사용하는 것이 어플리케이션에 안전하다.

싱글톤 인스턴스
class에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근(엑세스) 지점을 제공하는 생성 디자인 패턴이다.

Request payloads

POST HTTP 요청을 통해서 @Body()를 이용해서 데이터를 추가할 수 있다. 데이터를 추가하기 위해서는 DTO(Data Transfer Object) 스키마를 정의해야한다.

DTO는 데이터가 네트워크를 통해 전송되는 방법을 정의하는 객체로, Typescript interface나 간단한 class를 이용해서 DTO 스키마를 정의할 수 있다.

하지만 Typescript에서는 class를 사용하는 것이 좋다. 그 이유는 class는 Javascript ES6 표준의 일부이므로 Javascript에서 실존 Entity로 보존된다.

반면에 interface는 트랜스파일링을 하면서 제거가 되기 때문에 Nest (Typescript도) 는 interface를 참조할 수 없다.

DTO는 파이프 (입력받는 파라미터를 검증하거나 변환할 때 사용) 와 같은 기능이 런타입 시 변수의 metatype을 엑세스할 때 추가 가능성을 가능하게 하기 때문에 중요하다.

create-cat.dto.ts

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

위 예제 DTO에는 세 가지의 property만 있다. DTO를 생성한 후 CatsController 내에서 새로 생성된 DTO를 사용할 수 있다.

cats.controller.ts

@Post()
async create(@Body() createCatDto: CreateCatDto) {
	return 'This action add a new cat';
}

🤔 Entity와 DTO는 왜 분류해야 할까?

분리해야하는 가장 큰 이유는 DB LayerView Layer 사이의 역할을 분리하기 위해서다.

Entity Class실제 DB와 매핑되어서 변경하게 된다면 여러 다른 Class에 영향을 끼치고, DTO Class는 View와 통신하기 때문에 자주 변경되어 분리를 해줘야한다.

DTO는 Domain Model 객체를 그대로 두고, 복사해서 다양한 Presentaion Logic 즉, 보여주기 위한 로직을 추가한 정도로 사용한다. (Domain Model 객체는 persistant만 위해서 사용한다.)

profile
Node.js 백엔드 개발자입니다!

0개의 댓글