2 분 소요

Nest.js

지난 번에 Nest.js의 구조에 대해 알아보았다. 이번엔 controllers, providers, module에 대해 구체적으로 알아보겠다.

controllers

컨트롤러는 MVC 패턴에서 말하는 컨트롤러를 말한다. 컨트롤러는 들어오는 요청(Request)를 받고 처리된 결과를 응답(Response)으로 돌려주는 인터페이스 역할을 한다.

위의 그림과 같이 프론트 단에서 요청을 보내면 그에 맞는 엔드포인트 라우팅에 따라 컨트롤러로 보내지게 되는 엔드포인트 라우팅 메커니즘을 사용한다. 먼저 app.controller.ts의 예시는 이렇게 구성 되어 있다.

import { Controller, Get, Req, Request } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get("/hello")
  getHello(@Req() req: Request): string {
    console.log(req);

    return this.appService.getHello();
  }
}

코드를 봐보면, AppController 클래스 위에 @Controller 데코레이터가 보인다. 이는 AppController 클래스가 컨르롤러로 사용된다 라는 것을 말해준다. 그렇다면 각 라우팅의 엔드포인트는 어떻게 지정을 해주고, Http 메소드는 어떻게 사용을 할까?

Http 메소드 사용하기

우리는 엔드포인트를 지정을 해주기 전, 이 엔드포인트에서 어떤 Http 메소드를 사용할 것인가 지정을 해주게 된다. 이때, 우리는 데코레이터를 사용하게 된다.

@Get @Post @Delete @Put

이와 같이 클라이언트의 요청을 받아 처리를 해주는 함수 위에 데코레이터를 지정을 해주어, Http 메소드를 지정을 해준다.

엔드포인트 지정 해주기

각 함수의 Http 메소드를 지정을 해주었다면, 엔드포인트를 지정을 해주어야 한다. 이는 데코레이터 인자를 통해 설정을 해줄 수 있다.

export declare const Get: (path?: string | string[]) => MethodDecorator;

Get 데코레이터를 보면, 인자로 path를 받는 것을 볼 수 있다. 이 인자를 통해 엔드포인트를 지정을 해줄수 있다.

또한 앞서 컨트롤러 데코레이터도 인자를 받을 수 있는데, 여기에 path를 설정을 해주면, 엔드포인트의 prefix를 설정 해줄수 있다.

status 코드 바꿔주기

Nest에서는 CRU에 대해 성공 응답으로 POST는 201, 그 외는 200을 가진다. 만약 이 상태코드를 바꿔 주려면 어떻게 해야 할까?

@HttpCode() // 인자로 상태 코드 넣어주기

이때도 HttpCode 데코레이터를 사용하여 바꿔줄 수 있다. 인자로 statusCode를 넣어 줌으로써 바꿔줄 수 있다.

param 받아주기

그렇다면 param를 받아줄 땐 어떻게 해야할까? 이전에 썼던 방식과 똑같이 엔드포인트에 ‘:param’ 이런식으로 받아주면 된다. 이를 사용할 때에는 해당 함수에 @Param 데코레이터를 사용하여 이용해준다. 예를 들어

@Get(':id')
findOne(@Param('id') id : string) {}

이런식으로 인자로 받아 id의 값을 이용하면 param 값을 사용할 수 있다. param을 여러개를 받는 경우가 종종 있는데 그럴 때는 타입을 object로 받아 사용을 해도 된다.

@Delete(':userId/memo/:memoId')
deleteUserMemo(@Param() params : { [key: string] : string}) {
  return `userId : ${params.userId}, memoId : ${params.memoId}`;
}

이렇게 해줘도 되고, 여러개를 각각 인자로 받아줘도 된다.

하위 도메인 라우팅

만약 회사가 사용하고 있는 도메인은 example.com 이고, API 요청은 api.example.com으로 받기로 했다고 하자. ‘http://example.com’, ‘http://api.example.com’로 들어온 요청을 다르게 처리하고 싶다. 또한 하위 도메인에서 처리하지 못하는 요청은 원래의 도메인에서 처리되도록 하고 싶다면, 하위 도메인 라우팅 기법을 사용할 수 있다.

app.module.ts를 보면 @module 데코레이터 객체에 컨트롤러가 있는 것을 볼 수 있다. 앞에 있는 컨트롤러 부터 처리가 되기 때문에, 먼저 처리하고 싶은 것을 순서를 앞에 두면 된다.

@Module({
  controllers: [ApiController, AppController],
    ...
})
export class AppModule { }

이제 하위 도메인을 어떻게 처리할 것이냐 인데, @controller 데코레이터의 인자로 host를 설정을 해줄 수 있다. 이것을 이용하면 된다.

@Controller({ host: "api.example.com" }) // 하위 도메인 요청 처리 설정
export class ApiController {
  @Get() // 같은 루트 경로
  index(): string {
    return "Hello, API"; // 다른 응답
  }
}

이렇게 하면 각각 GET 요청의 응답이 다르게 나타나는 것을 볼 수 있다.

페이로드 처리하기

POST, PUT, PATCH 요청은 보통 처리에 필요한 데이터를 함께 실어 보내게 된다. 이 페이로드를 body 라고 부르는데, DTO(Data Transfer Object)를 정의하여 쉽게 다룰 수 있다.

// userDto
export class CreateUserDto {
  name: string;
  email: string;
}

//app.controller.ts
@Post()
create(@Body() createUserDto: CreateUserDto) {
  const { name, email } = createUserDto;

  return `유저를 생성했습니다. 이름: ${name}, 이메일: ${email}`;
}

이런식으로 @Body 데코레이터에 userDto 클래스를 받아 객체로 사용하면 된다.

댓글남기기