처음부터 차근차근

[NestJS] Controller 본문

FrameWork/NestJS

[NestJS] Controller

HangJu_95 2023. 11. 24. 14:30
728x90

Controller란?

Controllers are responsible for handling incoming requests and returning responses to the client.

-> 들어오는 요청에 반응하고, 적절한 응답을 보낸다.

  • 컨트롤러의 목적은 특정 요청에 응답을 반환하는 것
  • 여기서 routing mechanism이 도입되는데, 이것은 어떠한 요청에 어떤 컨트롤러가 반응할 지 결정한다.
  • 각각의 컨트롤러는 하나 이상의 route를 가지고, 각각의 route는 다른 행동을 수행할 수 있다.

Nest에서는 기본적으로 decorator를 통해 컨트롤러를 생성한다.

Decorators associate classes with required metadata and enable Nest to create a routing map.

➡️ 데코레이터는 class들을 연관지어주는데, 필요한 메타데이터와 Nest가 ruoting map을 그려주게 연관지어준다.

Routing

NestJS의 컨트롤러 사용법을 알아보기 전 Express에서 어떻게 동작시켰는지 알아보자.

// app.METHOD(Path, HANDLER)
app.get('/', (req, res) => {
    res.send('Hello World!');
});
  1. app.get method 작성한다.
  2. get method에 내부 인자로 Path와 Handler를 작성한다.
  3. 해당 Route를 동작시켰을때 동작 로직을 내부에 구현한다.

혹은 계층 아키텍쳐 패턴을 적용했을 때 Contoller와 Service, Router를 구분해서 작성도 가능하다.

그러나 NestJS는 Router를 작성할 필요가 없다. 예시를 통해 확인하자.

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

// Controller의 Path를 내부 인자로 지정할 수 있다.
@Controller('cats')
export class CatsController {
  // GET /cats
  // GET 데코레이터를 통해 endpoint를 지정할 수 있다.
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
  // GET /cats/breed
  @Get('breed')
  findAllBreed(): string {
    return 'This.action returns all Breed';
  }
}
  • @Get() 데코레이터를 통해 Nest가 자동으로 해당하는 Path의 HTTP endpoint 핸들러를 만들어낸다.
  • Path는 Controller에서도 지정할 수 있지만, Request method decorator(@Get(), @Post() 등)을 통해서도 지정할 수 있다. 

Controller 동작시키기

해당 예시를 통해 Controller를 작성해도, Nest 동작 시 Controller가 작동하지 않는다.

그 이유는 Module에 등록하지 않았기 때문이다.

해당 컨트롤러를 모듈에 등록시키는 방법은 단순하다.

아래 예시와 같이 해당 모듈에 Controller를 등록시키면 된다.

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

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

응답 처리 방식

Standard

  • Nest의 HTTP method 데코레이터는 기본적으로 method의 return을 통해 응답을 반환한다.
  • Built-in Method 데코레이터를 통해서 작성할 경우, 자동으로 응답이 JSON으로 직렬화된다.
  • 원시값의 경우 직렬화를 거치지 않고 반환한다.
  • StatusCode는 200으로 고정되어 있으며 Post의 경우 201로 동작한다.

Library-specific(예시 : Express)

  • 라이브러리를 통해 response object를 반환할 수 있다.
export class CatsController {
  @Post()
  create(@Res() res: Response) {
    res.status(HttpStatus.CREATED).send();
  }

  @Get()
  findAll(@Res() res: Response) {
     res.status(HttpStatus.OK).json([]);
  }
}
  • 해당 예시는 Response를 Express 라이브러리에서 주입받아 사용하고 있다.
  • @Res decorator를 사용해서 응답을 줄 수 있다.
  • @Res(), @Next()와 같은 라이브러리 종속적인 기능을 사용하면 NestJS는 자동으로 이걸 인식하고 해당 핸들러에 대해서만 Standard 처리 방식을 비활성화함 (둘 다 같이 사용하고 싶으면 @Res({ passthrough: true })와 같이 옵션을 설정해줘야 함)

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

그러나, 요청 객체를 전부 받아오는 것은 불필요하며, Nest에서는 @Body만 받아오거나, @Query, @Params만 받아올 수 있는 데코레이터를 지원하고 있다.

@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(name?: string) req.headers / req.headers[name]
@Ip() req.ip
@HostParam() req.hosts

 

@Res, @Response는 Library-specific Mode(예시 : Express)를 사용할 경우 권장한다.

Resource

Nest는 decorator를 통해 다양한 메서드를 지원한다.

  • @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), @Head()
  • 추가적으로, @All() 메서드를 통해 특정 엔드포인트의 모든 메서드를 한번에 처리할 수 있다.

Route wildcards

다음 예시를 보자.

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

@Get 메서드 안에 'ab*cd' path route로 등록되어 있다. 이는 abcd, ab_cd, abecd 등 다양한 path를 모두 받을 수 있다.

메서드 안에 ?, +, * 과 같은 정규식 표현이 사용 가능하다.

Status Code

StatusCode는 200으로 고정되어 있으며, 예외적으로 Post는 201이다. 하지만 @HttpCode()데코레이터를 통해 상태코드를 변경할 수 있다.

@Post()
// HttpCode 데코레이터를 통해 해당 StatusCode를 반환
@HttpCode(204)
create() {
  return 'This action adds a new cat';
}

만약 다양한 응답이 있을 경우, @Res() 를 사용하여 응답하는 것이 좋다.

(혹은 Exception 을 통해 처리하는 방법도 있다.)

Headers

@Header() 데코레이터를 통해 응답 헤더를 추가할 수 있다.

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

Redirection

@Redirect() 데코레이터를 통해 redirection을 할 수 있다. 인자는 url과 statusCode가 들어간다.

@Get()
@Redirect('https://nestjs.com', 301)

혹은 Return value를 받아서 override를 할 수 있다.

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

Route parameters

Http method decorator 안에 받을 parameter를 입력하여 동적으로 parameter 데이터를 받을 수 있다.

예시로 GET /cats/1 을 받는다고 가정하자.

// parameter key 입력
@Get(':id')
// @Param() 데코레이터를 통해 route 매개변수를 받는다.
findOne(@Param() params: any): string {
  console.log(params.id);
  return `This action returns a #${params.id} cat`;
}
@Get(':id')
// 혹은 데코레이터 안에 받을 route 매개변수를 입력하여 해당 매개변수만 받을 수 있다.
findOne(@Param('id') id: string): string {
  return `This action returns a #${id} cat`;
}

두 가지 방법을 통해 URL의 매개변수를 받았다.

이처럼 @Param 데코레이터를 통해 URL 매개변수를 받을 수 있다.

Sub-Domain Routing

// host를 설정해서 서브 도메인을 받을 수 있다.
@Controller({ host: 'admin.example.com' })
export class AdminController {
  @Get()
  index(): string {
    return 'Admin page';
  }
}

@Controller에 host option을 추가하여 서브 도메인을 추가할 수 있다. 이를 통해 요청 호스트에 따라 다른 요청을 받을 수 있다.

또한 매개변수값을 통해 동적으로 받을 수 있다.

@Controller({ host: ':account.example.com' })
export class AccountController {
  @Get()
  // @HostParam 데코레이터를 통해 매개변수로 받는다.
  getInfo(@HostParam('account') account: string) {
    return account;
  }
}

Scope

  • NestJS는 거의 대부분의 자원이 모든 요청들과 공유됨 (DB 커넥션풀, singleton service)
  • Node.js 특성상 각 요청마다 별도의 스레드로 처리하는 멀티 스레드 방식이 아니기 때문에 singleton 객체를 공유하는게 안전
  • 하지만, 요청 단위의 생명주기가 필요한 엣지 케이스들이 있는데 이럴땐 scope를 다르게 설정해주면 됨

Asynchronicity

  • 핸들러에 async를 적용하여 비동기 처리가 가능함(Promise 반환)
@Get()
async findAll(): Promise<any[]> {
  return [];
}
  • RxJS의 observable 반환도 가능.
@Get()
findAll(): Observable<any[]> {
  return of([]);
}

Request payload

Post Method는 데이터를 Param으로 받지 않고 Body를 통해 받는다.

이때 DTO(Data Transfer Object) schema를 통해 받아야하는 데이터 타입을 명시해줄 수 있다.

NestJS에서는 Class로 DTO로 사용할 것을 권장한다. (interface는 트랜스파일 과정에서 사라지므로 NestJS가 런타임에서 refer할 수 없음 - Pipes 같은 기능에서 해당 특징이 중요함)

export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
  return 'This action adds a new cat';
}

 

'FrameWork > NestJS' 카테고리의 다른 글

[NestJS] Middleware  (0) 2023.11.25
[NestJS] Request Lifecycle  (0) 2023.11.25
[NestJS] Module  (1) 2023.11.24
[NestJS] Providers  (0) 2023.11.24
[NestJS] NestJS는 왜 만들어졌을까??  (1) 2023.11.13