Nest CLI 를 통해 모듈, 컨트롤러, 서비스 프로젝트를 간편하게 생성할 수 있습니다.
$ nest generate module name
$ nest g mo name
$ nest generate controller name
$ next g co name
$ nest generate servies name
$ nest g s name
DTO 는 클라이언트와 서버간에 데이터를 주고받을 때, 데이터의 구조와 타입을 정의하는 객체입니다.
// back/src/users/users.controller.ts
@Post()
postUsers(@Body() data: JoinRequestDto) {
this.usersService.postUsers(data.email, data.nickname, data.password);
}
// back/src/users/dto/join.request.dto.ts
export class JoinRequestDto {
public email: string;
public nickname: string;
public password: string;
}
이와 같이 interface 가 아닌 class 로 제작하게 되면 빌드 후 JS 에서 사라지지 않아 타입 안정성 뿐만 아니라 런타임 검증( Express validator, joi 라이브러리 사용 )까지 가능합니다.
controller 제작 시 NestJS 는 req res 객체 직접 접근은 최소화하고, @Body() @Query() @Param() 같은 데코레이터로 필요한 데이터만 받는 걸 권장합니다.
// back/src/users/users.controller.ts
import { Body, Controller, Get, Post, Req, Res } from '@nestjs/common';
import { JoinRequestDto } from './dto/join.request.dto';
import { UsersService } from './users.service';
@Controller('api/users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Get()
getUsers(@Req() req) {
return req.users;
}
@Post('logout')
logOut(@Req() req, @Res() res) {
req.logOut();
res.clearCookie('connect.sid', { httpOnly: true });
res.send('ok');
}
}
// back/src/dms/dms.controller.ts
import { Controller, Get, Param, Post, Query } from '@nestjs/common';
@Controller('api/workspaces/:url/dms')
export class DmsController {
@Get(':id/chats')
getchat(@Query() query, @Param() param) {
console.log(query.perPage, query.page, param.id, param.url);
}
// 분리해서 사용도 가능
@Get(':id/chats')
getchat(@Query('perPage') perPage, @Query('page') page, @Param('id') id, @Param('url') url) {
console.log(perPage, page, id, url);
}
}
Swagger 는 애플리케이션의 RESTful API 문서를 자동으로 구성하는 특수 도구입니다.
NestJS 는 이러한 Swagger 제작을 위한 모듈을 제공합니다.
npm install --save @nestjs/swagger
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = process.env.PORT || 3000;
const config = new DocumentBuilder()
.setTitle('Sleact API')
.setDescription('Sleact 개발을 위한 API 문서입니다.')
.setVersion('1.0')
.addCookieAuth('connect.sid')
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, documentFactory);
await app.listen(port);
console.log(`listening on port ${port}`);
}
bootstrap();
NestJS 에 Swagger 을 설정한 후 서버를 실행하면 /api 경로에 Swagger UI를 만들어줍니다.
// User Controller
@ApiTags('USER') // 컨트롤러 간의 구분
@Controller('api/users')
export class UsersController {
@ApiResponse({ status: 200, description: '성공', type: UserDto }) // 응답 설명
@ApiOperation({ summary: '내 정보 조회' }) // api 설명
@Get()
getUsers(@Req() req) {
return req.users;
}
}
// DM Controller
@ApiTags('DM') // 컨트롤러 간의 구분
@Controller('api/workspaces/:url/dms')
export class DmsController {
@ApiParam({ name: 'url', description: '워크스페이스 url', required: true }) // Param 설명
@ApiParam({ name: 'id', description: '시용자 id', required: true }) // Param 설명
@ApiQuery({ name: 'perPage', description: '한 번에 가져오는 개수', required: true }) // Query 설명
@ApiQuery({ name: 'page', description: '불러올 페이지', required: true }) // Query 설명
@Get(':id/chats')
getchat(@Query() query, @Param() param) {}
}
// Dto
export class JoinRequestDto {
@ApiProperty({ example: 'kangkyeongseo@gmail.com', description: '이메일', required: true }) // Property 설명
public email: string;
@ApiProperty({ example: '덮밥', description: '닉네임', required: true }) // Property 설명
public nickname: string;
@ApiProperty({ example: 'password', description: '비밀번호', required: true }) // Property 설명
public password: string;
}
위와 같이 @nestjs/swagger 가 제공하는 데코레이터를 이용하여 Swagger 문서를 구성할 수 있습니다.
// back/src/common/decorator/user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
});
// back/src/users/users.controller.ts
@Get() // 커스텀 데코레이터사용
getUsers(@User() user) {
return user;
}
ExecutionContext 를 통해 Wep Socket RPC HTTP 를 하나의 컨텍스트로 관리하고 있습니다.
커스텀 데코레이터를 구성하여 사용하면 코드의 중복을 감소, 코드 수정의 편의 및 컨트롤러의 의존성을 줄여 테스트의 편의를 증진시킬 수 있습니다.
인터셉터는 미들웨어와 달리 컨트롤러의 작동 전 뿐만이 아니라 컨트롤로 작동 후에도 작업이 가능합니다.
// back/src/common/interceptors/undefinedToNull.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { map, Observable } from 'rxjs';
@Injectable()
export class undefinedToNullInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> | Promise<Observable<any>> {
// 컨트롤로 작동 전 부분
return next.handle().pipe(map((data) => (data === undefined ? null : data))); // 컨트롤로 작동 후 부분
}
}
// back/src/users/users.controller.ts
@UseInterceptors(UndefinedToNullInterceptor) // 컨트롤러 전체에 인터셉터 사용
@Controller('api/users')
export class UsersController {
@UseInterceptors(UndefinedToNullInterceptor) // 특정 컨트롤러에 인터셉터 사용
@Get()
getUser() {}
}
RxJS(Reactive Extensions for JavaScript)는 비동기 프로그래밍을 위한 라이브러리로, Observable 을 사용하여 데이터 스트림을 처리하고 조작할 수 있도록 해줍니다.
Observable : 시간이 지남에 따라 발생하는 데이터(이벤트, Ajax 응답 등)를 감싸는 객체.
NestJS가 추구하는 구조적이고 명확한 구분을 하는 개발 방식을 볼 수 있었습니다.