icon

RESTful API 설계 원칙과 구현


 RESTful API는 확장성, 유연성, 독립성을 제공하는 웹 서비스 디자인 아키텍처입니다.

 NestJS는 이러한 RESTful 원칙을 쉽게 구현할 수 있는 강력한 프레임워크를 제공합니다.

REST 아키텍처 핵심 원칙

  1. 클라이언트-서버 분리
  2. 무상태성 (Statelessness)
  3. 캐시 가능성 (Cacheability)
  4. 계층화 시스템 (Layered System)
  5. 코드 온 디맨드 (선택사항)
  6. 통일된 인터페이스

 NestJS는 이러한 원칙들을 쉽게 구현할 수 있는 구조와 데코레이터를 제공합니다.

HTTP 메서드 구현

 NestJS 컨트롤러에서 HTTP 메서드 구현

import { Controller, Get, Post, Put, Patch, Delete, Param, Body } from '@nestjs/common';
 
@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return 'This action returns all users';
  }
 
  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} user`;
  }
 
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return 'This action adds a new user';
  }
 
  @Put(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }
 
  @Patch(':id')
  partialUpdate(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return `This action updates part of a #${id} user`;
  }
 
  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} user`;
  }
}

URI 설계 원칙

  1. 리소스를 명사로 사용
  2. 계층 관계를 /로 표현
  3. 소문자 사용
  4. 언더스코어 대신 하이픈 사용
  5. 파일 확장자 포함하지 않기

 NestJS에서 적용

@Controller('blog-posts')
export class BlogPostsController {
  @Get('recent')
  getRecentPosts() { ... }
 
  @Get(':id/comments')
  getPostComments(@Param('id') id: string) { ... }
}

리소스 표현과 HATEOAS

 HATEOAS 구현 예시

@Get(':id')
async findOne(@Param('id') id: string) {
  const user = await this.usersService.findOne(id);
  return {
    ...user,
    _links: {
      self: { href: `/users/${user.id}` },
      posts: { href: `/users/${user.id}/posts` },
    }
  };
}

상태 코드 사용

 NestJS에서 상태 코드 사용

import { HttpStatus } from '@nestjs/common';
 
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() createUserDto: CreateUserDto) {
  return this.usersService.create(createUserDto);
}
 
@Get(':id')
async findOne(@Param('id') id: string) {
  const user = await this.usersService.findOne(id);
  if (!user) {
    throw new NotFoundException(`User #${id} not found`);
  }
  return user;
}

쿼리 파라미터 처리

 페이지네이션, 필터링, 정렬 구현

@Get()
async findAll(
  @Query('page') page = 1,
  @Query('limit') limit = 10,
  @Query('sort') sort = 'createdAt',
  @Query('order') order: 'ASC' | 'DESC' = 'DESC',
  @Query('filter') filter: string
) {
  const options = {
    page: +page,
    limit: +limit,
    sort: { [sort]: order },
    filter: filter ? JSON.parse(filter) : {}
  };
  return this.usersService.findAll(options);
}

API 버저닝

 NestJS에서 API 버저닝 구현

  1. URI 버저닝
@Controller('v1/users')
export class UsersV1Controller { ... }
 
@Controller('v2/users')
export class UsersV2Controller { ... }
  1. 헤더 기반 버저닝
@Controller('users')
@Header('X-API-Version', '1')
export class UsersV1Controller { ... }
 
@Controller('users')
@Header('X-API-Version', '2')
export class UsersV2Controller { ... }

CORS 설정

 NestJS에서 CORS 설정

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    origin: 'http://example.com',
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    credentials: true,
  });
  await app.listen(3000);
}
bootstrap();

Best Practices 및 성능 최적화

  1. 일관된 명명 규칙 사용
  2. 버전 관리 전략 수립
  3. 적절한 HTTP 메서드 및 상태 코드 사용
  4. 에러 처리 표준화
  5. 페이지네이션 구현
  6. 캐싱 전략 수립
  7. 요청 유효성 검사
  8. 보안 고려 (인증, 인가, 데이터 암호화)
  9. API 문서화 (Swagger/OpenAPI 사용)
  10. 성능 모니터링 및 로깅

 성능 최적화 전략

  1. 데이터베이스 쿼리 최적화
@Get()
async findAll() {
  return this.usersRepository.createQueryBuilder('user')
    .leftJoinAndSelect('user.posts', 'post')
    .where('user.isActive = :isActive', { isActive: true })
    .take(10)
    .getMany();
}
  1. 캐싱 구현
import { CacheInterceptor } from '@nestjs/common';
 
@UseInterceptors(CacheInterceptor)
@Get(':id')
async findOne(@Param('id') id: string) {
  return this.usersService.findOne(id);
}
  1. 압축 사용
import compression from 'compression';
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(compression());
  await app.listen(3000);
}
  1. 비동기 작업의 병렬 처리
@Get('dashboard')
async getDashboard() {
  const [users, posts, comments] = await Promise.all([
    this.usersService.getActiveUsers(),
    this.postsService.getRecentPosts(),
    this.commentsService.getRecentComments()
  ]);
  return { users, posts, comments };
}

 NestJS를 사용한 RESTful API 개발은 프레임워크의 강력한 기능과 구조화된 접근 방식 덕분에 효율적이고 확장 가능한 애플리케이션을 구축할 수 있게 해줍니다.

 REST 아키텍처의 핵심 원칙을 따르면서, NestJS의 데코레이터와 모듈 시스템을 활용하여 깔끔하고 유지보수가 용이한 코드를 작성할 수 있습니다.

 HTTP 메서드의 적절한 사용, 명확한 URI 설계, HATEOAS 구현 등을 통해 클라이언트와 서버 간의 효과적인 통신을 구현할 수 있습니다.

 또한, 상태 코드의 올바른 사용은 API의 의미론적 명확성을 높이고 클라이언트 측의 에러 처리를 용이하게 합니다.