이번 편에서는 지난 편에 이어 Nest.js의 기본 개념을 활용하여 API의 개념과 API를 설계하는 방법에 대해 알아보도록 하겠습니다.
API(Application Programming Interface)는 소프트웨어 간의 통신을 돕는 매개체로, 시스템 간 상호작용을 가능하게 해주는 인터페이스입니다. API를 통해 애플리케이션들은 서로 데이터를 주고받으며 협업할 수 있습니다. 간단히 말해, API는 서로 다른 시스템이나 프로그램 간에 데이터를 주고받고, 기능을 공유할 수 있도록 해주는 일종의 계약입니다.
REST API와 GraphQL은 대표적인 API 설계 방식입니다.
Nest.js에서는 파일 유형에 따라 접미사를 구분하여 네이밍합니다.
*.module.ts
*.controller.ts
*.service.ts
*.repository.ts
*.middleware.ts
*.decorator.ts
*.guard.ts
*.exception.ts
*.pipe.ts
Nest.js에서는 대부분 케밥케이스(kebab-case)를 사용합니다.
user-profile.controller.ts
posts.controller.ts
: 게시판 컨트롤러posts.service.ts
: 게시판 서비스auth.guard.ts
: 인증 가드create-post.dto.ts
: 게시글 작성 DTOSwagger는 API를 설계, 구축, 문서화, 테스트하는 과정을 돕는 프레임워크입니다. Nest.js에서는 @nestjs/swagger
를 통해 설정할 수 있으며, 데코레이터를 통해 API 스펙을 정의합니다.
npm install --save @nestjs/swagger swagger-ui-express
main.ts
에 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 config = new DocumentBuilder()
.setTitle('게시판 API')
.setDescription('게시판 API 문서화 예제')
.setVersion('1.0')
.addTag('posts')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
컨트롤러에 Swagger 데코레이터 추가:
import { Controller, Get, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { CreatePostDto } from './dto/create-post.dto';
@ApiTags('posts')
@Controller('posts')
export class PostsController {
@ApiOperation({ summary: '게시글 작성' })
@ApiResponse({ status: 201, description: '게시글이 성공적으로 작성되었습니다.' })
@Post()
create(@Body() createPostDto: CreatePostDto): string {
return 'Post created!';
}
@ApiOperation({ summary: '게시글 목록 조회' })
@ApiResponse({ status: 200, description: '게시글 목록이 성공적으로 조회되었습니다.' })
@Get()
findAll(): string {
return 'Posts retrieved!';
}
}
DTO(Data Transfer Object)는 계층 간 데이터 전송을 위해 사용되는 객체입니다. Nest.js에서는 클래스로 선언하며, Typescript와 class-validator를 사용하여 강력한 데이터 유효성 검사를 적용할 수 있습니다.
DTO 예시:
import { IsString, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreatePostDto {
@ApiProperty({ description: '게시글 제목' })
@IsString()
@IsNotEmpty()
title: string;
@ApiProperty({ description: '게시글 내용' })
@IsString()
@IsNotEmpty()
content: string;
}
ValidationPipe를 통해 유효성 검사를 쉽게 적용할 수 있습니다.
main.ts
:
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
Nest.js의 데코레이터는 클래스, 메서드, 매개변수, 속성 등에 추가 메타데이터를 제공하거나 동작을 변경하는 데 사용됩니다.
클래스 데코레이터:
@Module()
: 모듈 클래스 정의@Controller()
: 컨트롤러 클래스@Injectable()
: 서비스 클래스메서드 데코레이터:
@Get
, @Post
, @Put
, @Delete
, @Patch
@UseGuards
, @UseInterceptors
매개변수 데코레이터:
@Req
, @Res
: 요청 및 응답 객체@Body
, @Query
, @Param
: 입력된 요청 데이터 추출import { Controller, Get, Post, Body, Param } from '@nestjs/common';
@Controller('items')
export class ItemsController {
@Get(':id')
findOne(@Param('id') id: string): string {
return `Item ${id} retrieved!`;
}
@Post()
create(@Body() createItemDto: CreateItemDto): string {
return 'Item created!';
}
}
Exception Filters는 프로그램 실행 중 예외가 발생하면 해당 예외를 처리하는 코드로 라우팅하는 기능을 제공합니다.
http-exception.filter.ts
:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
글로벌 예외 필터 적용:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
GraphQL은 Facebook에서 만든 데이터 쿼리 언어로, 필요한 데이터를 지정하여 서버로부터 효율적으로 데이터를 가져올 수 있습니다. Nest.js에서는 @nestjs/graphql
을 통해 GraphQL API를 쉽게 구현할 수 있습니다.
npm install --save @nestjs/graphql graphql-tools graphql apollo-server-express
app.module.ts
에 GraphQL 모듈 설정 추가:
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql',
}),
PostsModule,
],
})
export class AppModule {}
GraphQL에서는 컨트롤러 대신 Resolver를 사용하여 API 요청을 처리합니다.
posts.resolver.ts
:
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { PostsService } from './posts.service';
import { Post } from './entities/post.entity';
import { CreatePostInput } from './dto/create-post.input';
@Resolver(of => Post)
export class PostsResolver {
constructor(private readonly postsService: PostsService) {}
@Query(returns => [Post])
async posts(): Promise<Post[]> {
return this.postsService.findAll();
}
@Mutation(returns => Post)
async createPost(@Args('input') createPostInput: CreatePostInput): Promise<Post> {
return this.postsService.create(createPostInput);
}
}
create-post.input.ts
:
import { InputType, Field } from '@nestjs/graphql';
@InputType()
export class CreatePostInput {
@Field()
title: string;
@Field()
content: string;
}
post.entity.ts
:
import { ObjectType, Field, Int } from '@nestjs/graphql';
@ObjectType()
export class Post {
@Field(type => Int)
id: number;
@Field()
title: string;
@Field()
content: string;
}
이번 편에서는 Nest.js의 기본 개념을 활용하여 API를 설계하는 방법과 함께, 실습 프로젝트로 게시판을 만드는 방법을 알아보았습니다. 이러한 개념을 이해하고 활용함으로써, 효율적이고 안정적인 API를 구축할 수 있습니다.