왜 NestJS인가?
👍 장점
- • 명확한 구조 (모듈/컨트롤러/서비스)
- • 의존성 주입 (DI) 지원
- • 채용 시장에서 인기
- • 대규모 팀에 적합
👎 단점
- • 학습 곡선이 가파름
- • 보일러플레이트 코드 많음
- • 작은 프로젝트에는 과함
- • 데코레이터 문법 익숙해야 함
프로젝트 구조
NestJS는 모듈 기반 아키텍처를 사용합니다. 각 기능은 모듈로 분리되고, 모듈은 컨트롤러와 서비스로 구성됩니다.
프로젝트 구조
src/
├── main.ts # 진입점
├── app.module.ts # 루트 모듈
├── health/
│ ├── health.module.ts # 헬스 모듈
│ └── health.controller.ts # 헬스 컨트롤러
└── projects/
├── projects.module.ts # 프로젝트 모듈
├── projects.controller.ts # 라우팅 담당
├── projects.service.ts # 비즈니스 로직
└── dto/
├── create-project.dto.ts
└── update-project.dto.ts핵심 개념
- Module: 관련 기능을 묶는 단위. imports/exports로 의존성 관리
- Controller: HTTP 요청 처리. 라우팅과 응답 담당
- Service: 비즈니스 로직. 컨트롤러에 주입됨
- DTO: 데이터 전송 객체. 입력 검증에 사용
코드 예제
1. 메인 진입점
src/main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// CORS 설정
app.enableCors({
origin: ['http://localhost:5173'],
credentials: true,
});
// 전역 검증 파이프
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
transform: true,
}));
await app.listen(8000);
console.log('🚀 NestJS running on http://localhost:8000');
}
bootstrap();2. 루트 모듈
src/app.module.ts
import { Module } from '@nestjs/common';
import { HealthModule } from './health/health.module';
import { ProjectsModule } from './projects/projects.module';
@Module({
imports: [HealthModule, ProjectsModule],
})
export class AppModule {}3. DTO (입력 검증)
src/projects/dto/create-project.dto.ts
import { IsString, IsOptional, MinLength, MaxLength } from 'class-validator';
export class CreateProjectDto {
@IsString()
@MinLength(1)
@MaxLength(100)
name: string;
@IsOptional()
@IsString()
@MaxLength(500)
description?: string;
}4. 컨트롤러
src/projects/projects.controller.ts
import { Controller, Get, Post, Body, Param, Delete, HttpCode } from '@nestjs/common';
import { ProjectsService } from './projects.service';
import { CreateProjectDto } from './dto/create-project.dto';
@Controller('api/v1/projects') // 라우트 프리픽스
export class ProjectsController {
// 의존성 주입
constructor(private readonly projectsService: ProjectsService) {}
@Post()
@HttpCode(201)
create(@Body() createProjectDto: CreateProjectDto) {
return this.projectsService.create(createProjectDto);
}
@Get()
findAll() {
return this.projectsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.projectsService.findOne(id);
}
@Delete(':id')
@HttpCode(204)
remove(@Param('id') id: string) {
return this.projectsService.remove(id);
}
}5. 서비스
src/projects/projects.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateProjectDto } from './dto/create-project.dto';
@Injectable() // DI 컨테이너에 등록
export class ProjectsService {
private projects = new Map();
create(dto: CreateProjectDto) {
const project = {
id: crypto.randomUUID(),
name: dto.name,
description: dto.description || null,
created_at: new Date().toISOString(),
};
this.projects.set(project.id, project);
return project;
}
findAll() {
return {
items: Array.from(this.projects.values()),
total: this.projects.size,
};
}
findOne(id: string) {
const project = this.projects.get(id);
if (!project) throw new NotFoundException('Project not found');
return project;
}
remove(id: string) {
if (!this.projects.delete(id)) {
throw new NotFoundException('Project not found');
}
}
}Hono vs NestJS 비교
| 항목 | Hono (Lv.1) | NestJS (Lv.4) |
|---|---|---|
| 파일 수 | 5개 | 12개 |
| 코드량 | ~150줄 | ~300줄 |
| 학습 곡선 | 낮음 | 높음 |
| 구조 | 자유로움 | 규칙적 |
| 팀 규모 | 1-3명 | 5명+ |
| 적합한 상황 | 빠른 프로토타입, 간단한 API | 복잡한 도메인, 장기 유지보수 |
전체 예제 코드
B2B Admin API의 NestJS 버전을 확인하세요: examples/b2b-admin/api-nest
다음 단계
백엔드 배포가 완료되었다면, Cycle 4: 데이터베이스에서 PostgreSQL을 연결하는 방법을 배웁니다.