컨트롤러는 사용자로부터 오는 요청을 받고 다시 응답을 돌려주는 것에 대한 책임이 있다.

컨트롤러는 사용자로부터 온 특정한 요청을 어디로 보낼 것인지에 대한 목적으로 만들어졌다. 라우팅 메커니즘은 어떤 컨트롤러가 각각의 요청을 받을 것인지를 결정한다. 종종, 컨트롤러는 여러개의 라우트를 가지며, 그 각각의 라우트는 다른 동작을 수행한다.
컨트롤러를 만들기 위해서, 우리는 클래스와 데코레이터를 사용한다. 데코레이터는 해당 클래스의 메타 정보를 통해, Nest가 일치하는 응답을 라우팅 해 줄 수 있도록 도와준다.
- Routing
예시에서 우리는 기본적인 컨트롤러를 만들기 위해 필요한 @Controller() 데코레이터를 사용할 것이다. cats라는 접두사를 가진 경로를 명시하며, @Controller에 접두사를 작성하는 것은 이후에 추가적으로 해당 경로를 더 명시할 필요없이 그룹화 할 수 있도록 도와준다. 예를 들어 우리가 Cats와 상호작용하는 라우트를 만들고 싶다면, 해당 경로의 접두사는 /cats가 될 것이며, @Controller('cats')로 작성하면 이 경로들을 그룹화 해주는 것이다. 그렇기에 컨트롤러에 추가할 라우트들에 'cats를 반복해서 작성 할 필요가 없다.
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
@Get()이라는 Http 요청 데코레이터는 findAll()이라는 메서드 이름 앞에 위치하며, Nest가 특정 경로에 대한 라우트를 생성하도록 한다. 이제 이 route path는 controller에 이미 명시해둔 경로와 메서드에 작성한 데코레이터의 경로를 조합하여 만들어진다. 여기에서는 @Get()에 경로 명시가 없기 때문에 /cats로 들어오는 @Get() 요청에 대해 동작하는 것이다.
위의 예시에서 해당 경로로 Get 요청이 들어온다면, Nest는 라우트하여 findAll() 메서드를 호출한다. 여기서 메서드의 이름은 임시이며, Nest는 이 컨트롤러의 메서드 이름에 의미를 부여하지 않는다.
해당 메서드는 아마 200 status code를 리턴할 것이다, 저기 작성해둔 문자열과 함께 말이다. 이것에 대해 설명하기 위해서는 2가지의 응답을 바꾸는 방법에 대해 알아야 한다.
| standard (recommended) |
해당 내장 메서드를 이용하면, Javascript가 객체 혹은 배열을 리턴 할 때, Json으로 자동 변환되어 돌려주게 된다. 하지만 만약 Javascript의 원시타입(string, number..)만 응답한다면, 직렬화를 시도하지 않는다. |
| Library-specific | 만약 @Res()로 사용하는 response object와 관련된 라이브러리를 사용할 수 있다. 이것을 사용하면, 개발자는 response.status(200).send()과 같이 작성해서 객체를 응답 할 수 있다. |
- Request object
핸들러는 종종 클라이언트에서 보낸 요청의 세부 정보를 알아와야 할 때가 있다. Nest는 이런 요청 객체에 대한 접근을 허용한다. 개발자는 @Req() 데코레이터를 사용하여 사용자가 보낸 요청의 객체에 접근이 가능하다.
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
console.log(request);
return 'This action returns all cats';
}
}
위와 같은 코드로 request를 출력해보니,

이런식으로 긴데이터가 들어왔던 것을 볼 수 있었다.
이렇게 많은 데이터에서 원하는 속성을 찾아서 가져오기는 힘들 것 같고, @Body()와 @Query()같은 전용 데코레이터가 있기에 이것들을 사용해서 객체에 접근하게 될 것이다.
아래는 각각의 데코레이터들이 어떤것과 대응되는지를 보여주는 표이다.
| @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()를 사용하면, Nest에서 해당 핸들러가 응답까지 알아서 처리한다고 생각하고 끝까지 처리해주지 않는다.
그렇기에 해당 데코레이터를 사용하면 응답까지 개발자가 완성해서 리턴해줘야 한다.
- Resources
이전에 cats의 GET API를 만들어보았다. 이번에는 POST를 통해 새로운 핸들러를 만들어보도록 하자.
import { Controller, Get, Post } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
@Post()
create(): string {
return 'This action adds a new cat';
}
}
굉장히 간단하며, Nest는 표준의 Http 메서드를 데코레이터를 통해 모두 지원한다.
만약 모든 메서드를 지원하는 엔드포인트를 만들고 싶다면 @All() 데코레이터를 사용하면 된다.
- Route wildcards
패턴을 통해서 라우트하는 기능도 Nest는 지원한다. *을 사용하면 와일드카드로 해당 위치에는 어떤 조합의 경로도 라우트해준다.
아래의 예시처럼 사용하면 seungkyu/ 뒤의 경로로 어떤 문자열이 오더라도 해당 핸들러로 라우팅해준다.
@Get('abcd/*')
findAll(){
return 'This route uses a wildcard';
}
- Status code
기본적인 응답 코드는 항상 200이고, POST 메서드의 응답코드는 항상 201이다.
이거를 @HttpCode()를 통해 핸들러 레벨에서 쉽게 바꿀 수 있다.
@Post()
@HttpCode(204)
create(){
return 'This action adds a new cat';
}
물론 중간에 에러가 발생하면 바뀌기도 하며, @Res() 혹은 @Response()를 사용해서 직접 넘길 때도 바뀌기도 한다.
- Response headers
response의 header를 직접 명시하기 위해, @Header() 데코레이터를 사용할 수 있다.
물론 res.header()를 통해서 직접 접근도 가능은 하다.
@Post()
@Header('Cache-Control', 'no-store')
create(){
return 'This action adds a new cat';
}
- Redirection
특정 주소로 리다이렉트 하고 싶다면, @Redirect() 데코레이터에 url과 statusCode를 명시해주면 된다.
만약 statusCode를 명시하지 않는다면, statusCode의 기본값은 302이다.
@Get()
@Redirect('https://nestjs.com', 301)
- Route parameters
정적인 경로만으로 요청하면, 원하는 동적 데이터들을 받을 수 없다.
만약 id와 같은 값을 동적으로 받아오고 싶다면, 파라미터 토큰을 넣어서 가져올 수 있다.
아래 @Get() 데코레이터 예제의 라우트 매개변수 토큰이 바로 그 방식이다.
라우트에 정의한 파리미터는 메서드에 @Param() 데코레이터를 통해 접근 할 수 있다.
@Get(':id')
findOne(@Param() params: any): string{
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
@Param() 데코레이터는 메서드의 파라미터 데코레이터며, 경로 파라미터에 정의된 값을 메서드가 사용 할 수 있도록 해준다.
위에서는 id에 접근하기 위해 params.id를 사용했지만, 라우트 파라미터의 이름에 직접 접근하여 바로 값을 가져올 수도 있다.
@Get(':id')
findOne(@Param('id') id: string): string{
return `This action returns a #${id} cat`;
}
- Sub-domain routing
@Controller() 데코레이터는 host 옵션을 받을 수 있다.
host 옵션은 해당 주소에서 온 HTTP 요청만 처리하겠다는 의미이다.
@Controller({host: 'admin.example.com'})
export class AdminController{
@Get()
index(): string {
return 'Admin page';
}
}
여기서도 주소에서 동적인 값을 찾아서 올 수 있다.
host에서 선언된 매개변수는 @HostParam() 데코레이터를 추가해서 접근 가능합니다.
@Controller({host: ':account.example.com'})
export class AccountController{
@Get()
getInfo(@HostParam('account') account: string) {
return account;
}
}
- Asynchronicity
자바스크립트의 기능을 최대한 활용하기 위해, 비동기 데이터 핸들링을 사용한다.
모든 async 함수들은 해당 데이터를 다루며 자동으로 값을 반환하는 Promise 타입을 반환한다.
@Get()
async findAll(): Promise<any[]>{
return [];
}
해당 코드도 충분히 멋지지만, Nest는 RxJs를 통한 observable stream도 지원한다.
Nest가 해당 데이터를 구독하며, 내부적으로 알아서 값을 반환하게 된다.
@Get()
findAll(): Observable<any[]>{
return of([]);
}
- Request payloads
이전에서 했던 POST는 사용자가 보내는 파라미터들을 받지 않았었다.
그것을 @Body()로 해결해보자.
우선 사용하기 전에 DTO를 정의해보자.
DTO는 네트워크를 통해 데이터를 전송할 때, 어떻게 보내야할지 명시하는 객체이다.
Nest에서는 인터페이스 혹은 클래스를 사용해 DTO 스키마를 정의한다.
하지만 이 중에서도 class로 정의하는 것을 추천한다.
클래스는 ES6 자바스크립트 문법이기에 javascript 자체로 컴파일이 가능하다.
그에 비해, 인터페이스는 변환 과정에서 삭제되기 때문에 Nest는 런타임 중에 해당 인터페이스를 참조 할 수 없다.
이것은 파이프같은 기능들이 런타임 중 변수의 타입에 접근해야 하기에 중요하다.
export class CreateCatDto{
name: string;
age: number;
breed: string;
}
이제 CatsController에 넣어보자.
import { Body, Controller, Get, Post } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
@Post()
create(@Body() createCatDto: CreateCatDto): string {
console.log(createCatDto);
return 'This action adds a new cat';
}
}
이렇게 @Body() 데코레이터를 통해, POST에서 사용자가 보낸 데이터를 사용할 수 있다.
- Query parameters
@Query()를 사용해 요청에서 들어온 쿼리 파라미터를 가져올 수 있다.
@Get()
findAll(@Query() age: number, @Query() breed: string): string {
return `This action returns all cats filtered by age: ${age} and breed: ${breed}`;
}
이런 핸들러에 다음과 같은 요청을 보내면
http://localhost:3000/cats?age=2&breed=thief

이렇게 응답이 오는 것을 볼 수 있다.
- Getting up and running
CatsController를 완벽하게 다 작성했어도, 동작이 바로 되는 것은 아니다.
컨트롤러는 모듈의 일부이며, 우리는 @Module 데코레이터에 이 컨트롤러를 넣어줘야 한다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule{}'Node > Nest 공식문서' 카테고리의 다른 글
| Modules (0) | 2025.08.23 |
|---|---|
| Providers (0) | 2025.08.21 |
| First steps (2) | 2025.08.18 |
| Introduction (4) | 2025.08.18 |