![[nest] Api](/_next/image?url=%2Fimages%2Fempty.png&w=2048&q=75)
1
목차
nest Api 설계하기
express 에서 사용했던 api를 옮겨보자
nest cli
`nest`의 `convention`
query string 사용법
API 문서 작성
각 api description 작성
`Property` 작성
`query` 작성
`tag` 작성
`dto` 관리
`dto extend` (에러 코드마다 별개로 작성해 줘야함)
`custom decorator` 만들기
`interceptor` 사용하기
nest Api 설계하기
2023년 11월 26일express 에서 사용했던 api를 옮겨보자
📍 참고
https://github.com/ZeroCho/sleact/blob/master/API.md
nest cli
- nest는 컨트롤러 모듈 서비스를 생성하는 명령어를 제공함
📍 참고
https://docs.nestjs.com/cli/usages#nest-generate
# 모듈 nest g mo users nest g mo workspaces nest g mo channels nest g mo dms
- 이렇게 하게 되면 각각 폴더내 모듈 생성후
app.module.ts에 의존성 주입이 된다.
// app.module.ts ... @Module({ imports: [ ConfigModule.forRoot(), UsersModule, WorkspacesModule, ChannelsModule, DmsModule, ], ... }) ...
- 이제 서비스와 컨트롤러도 같은 맥락으로 만들어 준다.
# 서비스 nest g s users nest g s dms nest g s channels nest g s workspaces # 컨트롤러 nest g co users nest g co dms nest g co channels nest g co workspaces
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.user; } @Post() postUsers(@Body() data: JoinRequestDto) { this.usersService.postUsers(data.email, data.nickname, data.password); } @Post("login") logIn() {} @Post("logout") logout(@Req() req, @Res() res) { req.logOut(); res.clearCookie("connect.sid", { httpOnly: true }); res.send("ok"); } }
- 위에 있는
JoinRequestDto타입을 만들어 보도록 하자
DTO (Data Transfer Object)란 ?
- 데이터를 전송하거나 서로 다른 계층 간에 데이터를 전달하기 위한 객체이다. 주로 비즈니스 계층과 퍼시스턴스 계층 간의 데이터 전송에 사용되며, 데이터베이스나 외부 시스템과의 통신 등에서 주로 활용
nest의 convention
hypen이나camelcase잘안쓰고 점으로 namingexport default를 잘 안씀interface잘안쓰고class를 많이 씀(컴파일 이후에도 남아있어서 js단에서도 타입검증 , 벨리데이션 같은 것을 수행할 수 있다.)
nest는 di 까지는 해줘도 타입까지는 정확하게 못잡아주기 때문에 타입을 붙여줘야한다.
// user/dto/join.request.dto.ts export class JoinRequestDto { public email: string; public nickname: string; public password: string; }
query string 사용법
- 아래 부분을
nest에서 구현해 보자면..
https://github.com/ZeroCho/sleact/blob/master/API.md#get-workspacesworkspacedmsidchats
- 다음과 같이 작성
// user/dms/dms.controller.ts import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common"; @Controller("api/workspaces/:url/dms") export class DmsController { @Get(":id/chats") getChet(@Query() query, @Param() param) { console.log(query.perPage, query.page); console.log(param.id, param.url); } @Post(":id/chats") postChat(@Body() body) {} }
// user/channels/channels.controller.ts import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common'; @Controller('channels') export class ChannelsController { @Get() getAllChannels() {} @Post() createChannels() {} @Get(':name') getSpecificChannel() {} @Get(':name/chats') getChet(@Query() query, @Param() param) { console.log(query.perPage, query.page); console.log(param.id, param.url); } @Post(':name/chats') postChat(@Body() body) {} @Get(':name') getAllMembers() {} @Post(':name/chats') inviteMemvbers() {} } Chat(@Body() body) {}
API 문서 작성
express는 자유도가 너무 높아서 api 문서를 만들어 주는데 한계가... 다음패키지 설치
yarn add @nestjs/swagger
- 다음과 같이 api 문서가 세팅된다 ~

각 api description 작성
- 다음과 같이 각
api위에@ApiOperation를 달아주면
// users.controller.ts ... export class UsersController { constructor(private usersService: UsersService) {} @ApiOperation({ summary: '내 정보 조회' }) @Get() getUsers(@Req() req) { return req.user; } ... } ...
-
다음과 같이 볼수 있다.

Property 작성
dto에서도@ApiProperty를 달면
// user/dto/join.request.dto.ts export class JoinRequestDto { @ApiProperty({ example: "uiop01900@gmail.com", description: "이메일", required: true, }) public email: string; ... }
- 다음과 같이 확인할 수 있다.


query 작성
- 또한
dms에서 다음과 같이@ApiQuery를 작성해주면
// dms.controller.ts ... export class DmsController { @ApiQuery({ name: 'perPage', required: true, description: '한 번에 가져오는 개수', }) @Get(':id/chats') getChet(@Query() query, @Param() param) { console.log(query.perPage, query.page); console.log(param.id, param.url); } }
- 이렇게 확인 가능하다

tag 작성
@ApiTags공통으로 묶어서
// dms.controller.ts ... @ApiTags("DM") @Controller("api/workspaces/:url/dms") export class DmsController {}
- 다음과 같이 구분
tag를 가질수 있음

dto 관리
- 자주 사용하는
dto는 따로 빼서 관리를 해주는게 편함(나중에validation하는 라이브러리 붙여서bodyparser동작하게 하면서 동시에validation까지 같이 할 수 있음)
// common/dto/user.dto.ts import { ApiProperty } from "@nestjs/swagger"; import { JoinRequestDto } from "src/users/dto/join.request.dto"; export class UserDto extends JoinRequestDto { @ApiProperty({ required: true, example: 1, description: "아이디", }) id: number; @ApiProperty({ required: true, example: "uiop01900@gmail.com", description: "이메일", }) id: number; }
dto extend (에러 코드마다 별개로 작성해 줘야함)
- 위에 작성한
dto를@ApiResponse로 가져다 사용하면 된다.
// users.controller.ts ... export class UsersController { constructor(private usersService: UsersService) {} @ApiResponse({ status: 200, description: '성공', type: UserDto, }) @ApiOperation({ summary: '내 정보 조회' }) @Get() getUsers(@Req() req) { return req.user; } ... } ...
- 그럼 다음과 같이 보여짐

custom decorator 만들기
-
contorller안에 req, res 를 사용하는것은 왠만하면 지양해야한다고 했었음(의존성 관련해서, 나중에 festify 나 Koa로 쉽게 건너갈수 있음/ 중복제거) =>custom decorator사용해서 해결할 수있음 -
다음과 같이 작성이후
users.controller.ts의@Req() req=>@User() user로req=>user로 바꿔준다.
// common/decorator/user.decorator.ts import { createParamDecorator, ExecutionContext } from "@nestjs/common"; export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => { const req = ctx.switchToHttp().getRequest(); return req.user; });
// common/decorator/token.decorator.ts import { createParamDecorator, ExecutionContext } from "@nestjs/common"; export const Token = createParamDecorator((data: unknown, ctx: ExecutionContext) => { const res = ctx.switchToHttp().getResponse(); // 이런식으로 실행 컨텍스트에 접근이 가능하면 이 함수 안에서 동시에 http, rpc 서버 정보 등을 가져올수 있고 웹소켓이랑 http 통신을 쉽게 해준다. return res.locals.jwt; }); // 이것도 다음과 같이 사용가능함 // @Token() token
interceptor 사용하기
- aop 관점으로 개발하게 된다.
AOP
- AOP는 관점 지향 프로그래밍을 의미한다. 이는 모듈성을 높이고 소프트웨어 개발에서 교차 관심을 촉진하는 것을 목표로 하는 프로그래밍 패러다임이다.
기존 객체 지향 프로그래밍(OOP)에서는 로깅, 보안 또는 트랜잭션 관리와 같은 문제가 코드베이스 전체에 분산되어 관리 및 유지 관리가 더 어려워지는 경우가 많았다.

-
middleware/logger.middleware.ts을 보게 되면controller 전과controller 후둘을 동시에 담당하고 있음 => 이런것들을 nest 의interceptor로 그대로 구현 할 수 있음 -
controller에서return 한 값도 가공을 해줄수 있음
// 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, code: 'SUCCESS' }))); return next.handle().pipe(map((data) => (data === undefined ? null : data))); } }
- 적용은 다음과 같이 가능하다.
// users.controller.ts ... @UseInterceptors(UndefinedToNullInterceptor) // 전체 적용하려면 여기 위치시킴 @ApiTags('USER') @Controller('api/users') export class UsersController { ... } ...
... export class UsersController { constructor(private usersService: UsersService) {} @UseInterceptors(UndefinedToNullInterceptor) // 개별 적용 @ApiResponse({ status: 200, description: '성공', type: UserDto, }) ... } ...
undefinedToNull.interceptor.ts에서 사용한rxjs는 다음을 참고하자