[NestJS] Controllers
정의
NestJS Controller 는 @Controller() 데코레이터가 붙은 클래스로, HTTP 요청을 라우팅하고 응답을 반환합니다. Method 별로 @Get, @Post, @Put, @Patch, @Delete 데코레이터를 붙여 URL + method 조합을 처리합니다.
기본
import { Controller, Get, Post, Body, Param, Query, HttpCode } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll(@Query('page') page: number = 1) {
return this.usersService.findAll(page);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(id);
}
@Post()
@HttpCode(201)
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
@Patch(':id')
update(@Param('id') id: string, @Body() dto: UpdateUserDto) {
return this.usersService.update(id, dto);
}
@Delete(':id')
@HttpCode(204)
remove(@Param('id') id: string) {
return this.usersService.remove(id);
}
}
URL prefix /users, method 별 하위 경로.
Path Parameter
@Get(':id')
find(@Param('id') id: string) { ... }
@Get(':id/posts/:postId')
findPost(
@Param('id') id: string,
@Param('postId') postId: string,
) { ... }
// 전체 params 객체
@Get(':id/posts/:postId')
findPost(@Param() params: {id: string; postId: string}) { ... }
Query Parameter
@Get()
list(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
) { ... }
// 전체 query 객체
@Get()
list(@Query() query: {page?: string; limit?: string}) { ... }
// DTO 로 (검증 포함)
@Get()
list(@Query() query: ListUsersQuery) { ... }
DTO + ValidationPipe 로 자동 변환/검증:
import { IsInt, Min, Max, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';
export class ListUsersQuery {
@IsInt()
@Min(1)
@Type(() => Number)
@IsOptional()
page: number = 1;
@IsInt()
@Min(1)
@Max(100)
@Type(() => Number)
@IsOptional()
limit: number = 10;
}
Request Body
@Post()
create(@Body() dto: CreateUserDto) { ... }
// 특정 필드만
@Post()
create(@Body('email') email: string, @Body('name') name: string) { ... }
Headers, Cookies, IP
@Get()
findAll(
@Headers('authorization') auth: string,
@Headers() allHeaders: Record<string, string>,
@Ip() ip: string,
) { ... }
// Cookies (cookie-parser 필요)
@Get()
withCookie(@Req() req: Request) {
return req.cookies;
}
Custom Decorator
자주 쓰는 파라미터를 decorator 로:
// common/decorators/user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
// 사용
@Get('me')
me(@User() user: UserEntity) { ... }
@Get('me/email')
myEmail(@User('email') email: string) { ... }
Response
기본 (자동 직렬화)
Method return 값이 자동 JSON 직렬화 + 200 응답.
@Get()
findAll() {
return {users: [...]}; // -> 200 {"users": [...]}
}
// async 도 OK
@Get(':id')
async find(@Param('id') id: string): Promise<User> {
return await this.usersService.findOne(id);
}
// Observable 도 OK (RxJS)
@Get()
stream(): Observable<Data> {
return this.dataService.stream();
}
Status Code
@Post()
@HttpCode(201) // 명시적 code
create(@Body() dto: CreateUserDto) { ... }
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT) // 204
remove(@Param('id') id: string) { ... }
Headers
@Get()
@Header('Cache-Control', 'none')
@Header('X-Custom', 'value')
findAll() { ... }
Redirect
@Get('legacy')
@Redirect('/new-path', 301)
legacy() { ... }
@Get('dynamic')
dynamic() {
return {url: '/new-path', statusCode: 301}; // Runtime redirect
}
Raw Response
세밀한 제어 필요 시 @Res():
import { Res } from '@nestjs/common';
import { Response } from 'express';
@Get('download')
download(@Res() res: Response) {
res.setHeader('Content-Type', 'application/pdf');
res.sendFile('/path/to/file.pdf');
}
주의: @Res() 를 쓰면 자동 직렬화 못 함. Interceptor 도 우회. Passthrough option 으로 절충:
@Get()
findAll(@Res({passthrough: true}) res: Response) {
res.setHeader('X-Custom', 'value');
return {users: [...]}; // 여전히 자동 직렬화
}
Wildcard / Regex
@Get('a*b') // /aXXb, /axb, ... 모두 매칭
handle() { ... }
@Get(':version(v[0-9]+)/*') // /v1/anything
versioned(@Param('version') v: string) { ... }
Sub-domain routing
@Controller({ host: 'admin.example.com' })
export class AdminController { ... }
@Controller({ host: ':account.example.com' })
export class TenantController {
@Get()
index(@HostParam('account') account: string) { ... }
}
Async, Promise, Observable
세 스타일 모두 지원:
@Get()
sync() {
return {ok: true};
}
@Get()
async asyncReturn() {
return await this.service.get();
}
@Get()
observable(): Observable<Data> {
return this.service.stream();
}
Streaming Response
import { StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';
@Get('file')
file(): StreamableFile {
const file = createReadStream('/path/to/file.txt');
return new StreamableFile(file);
}
또는 @Sse() (Server-Sent Events):
import { Sse } from '@nestjs/common';
import { Observable, interval, map } from 'rxjs';
@Sse('events')
events(): Observable<MessageEvent> {
return interval(1000).pipe(
map(_ => ({data: {time: new Date()}}))
);
}
Versioning
// main.ts
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: '1',
});
@Controller({path: 'users', version: '1'})
export class UsersV1Controller { ... }
@Controller({path: 'users', version: '2'})
export class UsersV2Controller { ... }
URL: /v1/users, /v2/users. Header, Media Type 방식도 있음.
Route Prefix
// main.ts
app.setGlobalPrefix('api');
모든 route 앞에 /api.
@Controller('users')
class UsersController {
@Get() // -> GET /api/users
}
특정 controller 는 예외:
app.setGlobalPrefix('api', {
exclude: [{path: 'health', method: RequestMethod.GET}]
});
Guard / Interceptor / Pipe / Filter 부착
@Controller('users')
@UseGuards(AuthGuard) // controller 전체
export class UsersController {
@Get()
@UseInterceptors(CacheInterceptor) // method 만
@UsePipes(new ValidationPipe())
@UseFilters(new HttpExceptionFilter())
findAll() { ... }
}
Global 도 가능 (main.ts 참조).
함정
[!WARNING>
@Res()사용 시 자동 응답/interceptor 우회.passthrough: true사용.
CAUTION
Path 경로 순서. /users/:id 가 /users/me 보다 먼저 정의되면 /users/me 가 id=“me” 로 매치. 구체적 route 를 먼저.
WARNING
class-validator metadata 필요. main.ts 에 import 'reflect-metadata' (또는 tsconfig 에 emitDecoratorMetadata: true).
IMPORTANT
DTO 안의 Optional field. ? 만으로는 부족. @IsOptional() 데코레이터 함께.
CAUTION
@Body() type 은 runtime 검증 X. TypeScript 는 컴파일 후 사라짐. Pipe 로 실제 검증 필수.
관련 위키
- NestJS - 상위 개요
- Modules - Controller 등록
- Providers & DI - Service 주입
- Pipes - Body/Query 검증
- Guards -
@UseGuards() - Interceptors -
@UseInterceptors() - Exception Filters -
@UseFilters() - Middleware
- TypeScript Decorators
- FastAPI Routing - 대비
- Koa.js - 대비
이 글의 용어 (11개)
- [FastAPI] Routing (Path Operations)fastapi
- 정의 FastAPI Routing 은 HTTP method + URL 을 Python 함수 (path operation) 에 매핑하는 시스템입니다. Starlette 의 라우팅 …
- [Framework] Koa.jsframeworks
- 정의 Koa.js 는 Express 창시자 TJ Holowaychuk 이 2013년 발표한 Node.js 웹 프레임워크 입니다. Express 의 후계자 성격이며, 미들웨어를 a…
- [Framework] NestJSframeworks
- 정의 NestJS 는 Kamil Myśliwiec 이 2017년 발표한 Node.js 서버측 프레임워크 입니다. TypeScript first, Angular 에서 영감받은 모듈…
- [NestJS] Exception Filtersframeworks
- 정의 NestJS Exception Filter 는 애플리케이션에서 발생한 예외를 catch 하여 클라이언트에게 응답 형식을 통일합니다. 데코레이터로 어떤 예외를 처리할지 지정합…
- [NestJS] Guardsframeworks
- 정의 NestJS Guard 는 특정 요청이 controller method 에 도달하기 전에 실행되어 접근 허용 여부 를 결정합니다. Middleware 이후, Intercep…
- [NestJS] Interceptorsframeworks
- 정의 NestJS Interceptor 는 요청 처리 전/후에 로직을 삽입하는 클래스입니다. AOP (Aspect-Oriented Programming) 패턴의 구현체로, 로깅,…
- [NestJS] Middlewareframeworks
- 정의 NestJS Middleware 는 route handler 실행 이전, Guard 도 실행되기 전에 실행되는 함수/클래스입니다. Express/Fastify 표준 midd…
- [NestJS] Modulesframeworks
- 정의 NestJS Module 은 데코레이터가 붙은 클래스로, 관련된 controller, provider, import/export 를 하나로 묶는 조직 단위입니다. Angul…
- [NestJS] Pipes (Validation, Transformation)frameworks
- 정의 NestJS Pipe 는 controller method 로 들어가는 인자를 변환 (transformation) 하거나 검증 (validation) 하는 클래스입니다. Gu…
- [NestJS] Providers & Dependency Injectionframeworks
- 정의 NestJS Provider 는 데코레이터가 붙은 클래스 (또는 값/factory) 로, DI container 에 등록되어 다른 클래스에 주입됩니다. Service, Re…
- [TypeScript] Decoratorstypescript
- 정의 Decorator 는 class, method, accessor, property, parameter 에 부착되는 함수로 그 대상을 검사/수정할 수 있습니다. TypeScr…
이 개념을 다룬 위키 페이지 (12)
- wiki[Koa] Context (ctx)
- wiki[Koa] Router (@koa/router)
- wiki[Framework] NestJS
- wiki[NestJS] Exception Filters
- wiki[NestJS] Guards
- wiki[NestJS] Interceptors
- wiki[NestJS] Microservices (Kafka, NATS, RabbitMQ, gRPC, TCP)
- wiki[NestJS] Middleware
- wiki[NestJS] Modules
- wiki[NestJS] Pipes (Validation, Transformation)
- wiki[NestJS] Providers & Dependency Injection
- wiki[NestJS] Testing (Jest, Supertest, e2e)
💬 댓글