Nest.js API 설계하기

shooting star·2024년 5월 9일
0
post-thumbnail

들어가며

이번 편에서는 지난 편에 이어 Nest.js의 기본 개념을 활용하여 API의 개념과 API를 설계하는 방법에 대해 알아보도록 하겠습니다.

1. API란 무엇인가?

API(Application Programming Interface)는 소프트웨어 간의 통신을 돕는 매개체로, 시스템 간 상호작용을 가능하게 해주는 인터페이스입니다. API를 통해 애플리케이션들은 서로 데이터를 주고받으며 협업할 수 있습니다. 간단히 말해, API는 서로 다른 시스템이나 프로그램 간에 데이터를 주고받고, 기능을 공유할 수 있도록 해주는 일종의 계약입니다.

API의 주요 역할

  1. 데이터 교환: 서로 다른 시스템 간에 데이터 교환을 가능하게 함.
  2. 기능 노출: 특정 애플리케이션의 기능을 다른 프로그램에서 활용할 수 있도록 기능을 노출함.
  3. 서비스 통합: 다양한 서비스 및 기능을 통합하여 복합적인 애플리케이션을 개발할 수 있음.

REST APIGraphQL은 대표적인 API 설계 방식입니다.

파일 네이밍 규칙

Nest.js에서는 파일 유형에 따라 접미사를 구분하여 네이밍합니다.

  • 모듈: *.module.ts
  • 컨트롤러: *.controller.ts
  • 서비스: *.service.ts
  • 리포지토리: *.repository.ts
  • 미들웨어: *.middleware.ts
  • 데코레이터: *.decorator.ts
  • 가드: *.guard.ts
  • 예외 처리: *.exception.ts
  • 파이프: *.pipe.ts

케밥케이스 vs 카멜케이스

Nest.js에서는 대부분 케밥케이스(kebab-case)를 사용합니다.

  • 예) user-profile.controller.ts

예시 파일 네이밍

  • posts.controller.ts: 게시판 컨트롤러
  • posts.service.ts: 게시판 서비스
  • auth.guard.ts: 인증 가드
  • create-post.dto.ts: 게시글 작성 DTO

3. API 문서화: Swagger 사용하기

Swagger는 API를 설계, 구축, 문서화, 테스트하는 과정을 돕는 프레임워크입니다. Nest.js에서는 @nestjs/swagger를 통해 설정할 수 있으며, 데코레이터를 통해 API 스펙을 정의합니다.

Swagger 설치 및 설정

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!';
  }
}

4. DTO 및 Validation

DTO(Data Transfer Object)는 계층 간 데이터 전송을 위해 사용되는 객체입니다. Nest.js에서는 클래스로 선언하며, Typescriptclass-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;
}

Validation 적용

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();

5. 데코레이터 활용

Nest.js의 데코레이터는 클래스, 메서드, 매개변수, 속성 등에 추가 메타데이터를 제공하거나 동작을 변경하는 데 사용됩니다.

주요 데코레이터

  • 클래스 데코레이터:

    • @Module(): 모듈 클래스 정의
    • @Controller(): 컨트롤러 클래스
    • @Injectable(): 서비스 클래스
  • 메서드 데코레이터:

    • HTTP 요청 데코레이터: @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!';
  }
}

6. Exception Filters를 이용한 예외 처리

Exception Filters는 프로그램 실행 중 예외가 발생하면 해당 예외를 처리하는 코드로 라우팅하는 기능을 제공합니다.

내장 Exception 필터

  • BadRequestException: 잘못된 요청 예외
  • UnauthorizedException: 인증 실패 예외
  • NotFoundException: 리소스가 존재하지 않는 예외
  • ForbiddenException: 접근 권한 실패 예외
  • InternalServerErrorException: 서버 오류 예외

사용자 정의 Exception 필터

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();

7. GraphQL을 활용한 API 구현

GraphQL은 Facebook에서 만든 데이터 쿼리 언어로, 필요한 데이터를 지정하여 서버로부터 효율적으로 데이터를 가져올 수 있습니다. Nest.js에서는 @nestjs/graphql을 통해 GraphQL API를 쉽게 구현할 수 있습니다.

GraphQL 환경 설정

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 구현

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);
  }
}

GraphQL Input 타입

create-post.input.ts:

import { InputType, Field } from '@nestjs/graphql';

@InputType()
export class CreatePostInput {
  @Field()
  title: string;

  @Field()
  content: string;
}

GraphQL Entity 타입

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;
}

8. 결론

이번 편에서는 Nest.js의 기본 개념을 활용하여 API를 설계하는 방법과 함께, 실습 프로젝트로 게시판을 만드는 방법을 알아보았습니다. 이러한 개념을 이해하고 활용함으로써, 효율적이고 안정적인 API를 구축할 수 있습니다.

0개의 댓글