본문 바로가기
개발자 도전기/[STUDY] JS || TS

NestJS | docs | First steps, controllers

by 답수 2023. 2. 11.
728x90
SMALL

 

 

https://docs.nestjs.com/

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

NestJS는 Node.js의 아키텍처 측면에서 효과적인 설계를 할 수 있도록 도와주는 프레임워크이다. 물론 아키텍처 말고도 OOP, FP 등의 요소가 결합되어 사용할 수 있고, TS와도 친화적이라서 노드 진영에서는 꽤 혁신적인 프레임워크라고 생각한다. 반면에 노드 진영에서 혁신적인 프레임워크라는 것이 겨우 스프링의 MVC..? 라고 생각하시는 분들도 계시지만, node.js의 express만 사용해본 입장에서는 NestJS가 매우 매력적이었고, 학습하다보면 더 다양한 관점들을 얻을 수 있다고 생각하여 문서를 차근차근 정리해보려고 한다.

 

Nest CLI로 프로젝트를 새로 생성하면 디렉토리 구조는 다음과 같다.

npm이 설치된 상태에서 os터미널에 nest new project-name 멸령어를 입력하면 프로젝트 생성 가능

 

 

해당 파일들에 대한 설명은 다음과 같다.

  • 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(3000);
}
bootstrap();

네스트 애플리케이션 인스턴스를 생성하기 위해서는 NestFactory 클래스를 사용해야 한다. NestFactory는 앱 인스턴스를 생성시키는 정적 메서드들이 있다. create() 메서드는 애플리케이션 객체를 반환한다. 

 

 

Controllers

컨트롤러는 클라이언트에게 리퀘스트를 받거나 리스폰스를 응답하는 역할을 한다.

 

컨트롤러의 목적은 애플리케이션에 대한 특정 리퀘스트를 받는 것이다. 컨트롤러를 만들기 위해서는 클래스와 데코레이터를 사용한다. 데코레이터는 클래스를 필수 메타데이터와 연결하고 Nest가 라우팅 맵을 생성할 수 있도록 한다. 쉽게 말해서 함수나 클래스엥 기능을 첨가해서 재사용성을 극대화할 수 있다.

 

Routing

@Controller() 데코레이터를 이용하여 기본적인 컨트롤러를 정의하는 예시를 보자.

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

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
  
  @Post()
  async signUp(@Body() body: CatRequestDto) {
    return await this.catsService.signUp(body);
  }
}

@Controller() decorator에 경로 접두사를 사용하는 것은 더 연관된 경로들을 더 쉽게 그룹화하고 중복코드를 최소화할 수 있다. 그리고 데코레이터는 반드시 해당 함수 위에 붙여서 써야 한다. 

 

위의 코드로 봤을 때 findAll() 메서드 이전에 @Get() 은 네스트에게 HTTP 요청의 특정 엔드포인트에 대한 핸들러를 생성하도록 지시한다. 즉 BASE_URL이 http://localhost:3000/ 이라면 GET | http://localhost:3000/cats , POST | http://localhost:3000/cats 이렇게 라우트 경로가 지정된다.

 

Nest는 응답을 위해 두 가지 옵션을 제공한다.

표준적인 옵션은 자바스크립트 객체나 배열을 리턴할 때 자동으로 JSON으로 직렬화하지만, 만약 string, number, boolean 같은 원시 타입을 반환할 때에는 이 값이 JSON 말고 그대로 보내진다. 또한 상태 코드는 200이 디폴트이고(POST는 201) 이는 @HttpCode(...) 데코레이터에서 쉽게 바꿀 수 있다.

 

Request Object

우리는 @Req() 데코레이터를 이용하여 요청 객체에 접근할 수 있다.  

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';
  }
}

즉 HTTP 리퀘스트 객체에 포함되어 있는 query string, parameters, HTTP header, body 등을 따로 설정하지 않고 가져올 수 있다. 대신 위의 예시처럼 @Req() 같은 데코레이터를 사용하면 된다. 요청과 관련된 데코레이터는 아래와 같다.

 

Resources

Nest에서 제공하는 HTTP 메서드 데코레이터는 우리가 평소 사용하는 메서드를 위의 예시처럼 함수 위에 선언만 하면 된다. 

@Get() @Post() @Put() @Patch() @Delete() 등이 있고, @Options() @Head() @All() 도 있다고 한다(거의 안 쓸 것 같다).

 

Route wildcards

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

패턴 기반 경로도 지원이 된다고 한다. 즉 /abcd, /ab_cd, /abecd 등의 경로도 라우트로 지정이 된다.

 

Status code

상태 코드는 @HttpCode() 데코레이터를 사용하면 된다.

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

 

Headers

리스폰스의 헤더를 커스텀하기 위해서는 @Header() 데코레이터를 사용하면 된다.

@Post()
@Header('Cache-Control', 'none')
create() {
  return 'This action adds a new cat';
}

 

Route parameters

동적 라우트 경로를 설정하기 위한 파라미터 데코레이터는 다음과 같이 사용할 수 있다.

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

 

Request Payloads

@Body 데코레이터를 이용하여 클라이언트로부터 데이터를 받을 수 있는데, 그전에 DTO(Data Transfer Object) 스키마를 정의해야 한다. DTO는 데이터가 네트워크를 통해 전송되는 방법을 정의하는 객체이다. 그리고 타입스크립트로 개발하기 위해서는 클래스로 dto를 정의하는 것이 좋다고 한다. 그 이유로는 인터페이스는 컴파일 과정에서 삭제되기 떄문에 nest 런타임에서 dto를 참조할 수 없는 현상이 발생할 수 있기 떄문이고, 클래스로 정의된 엔티티는 컴파일 이후에도 js로 살아남기 때문이다.

// create-cat.dto.ts

export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}
// cats.controller.ts

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

DTO는 나중에 구체적으로 따로 정리해보자.

 

 

 

728x90
LIST

댓글