NestJS#
Nest.js는 효율적이고 확장 가능한 Node.js 서버 측 애플리케이션을 구축하기 위한 프레임워크.
Express.js를 기반으로 하며, TypeScript를 완벽하게 지원한다.
Angular의 아키텍처에서 영감을 받아 설계되었으며, 모듈식 아키텍처를 통해 애플리케이션을 체계적으로 구성할 수 있게 해주며, 의존성 주입(Dependency Injection)과 데코레이터 패턴을 적극적으로 활용한다.
주요 특징#
모듈화된 구조
- 애플리케이션을 기능별로 모듈화하여 관리
- 각 모듈은 독립적으로 동작하면서도 서로 연결 가능
의존성 주입
- 컴포넌트 간의 결합도를 낮추고 테스트 용이성 향상
- 서비스와 컨트롤러 간의 관계를 명확하게 정의
미들웨어 지원
- Express 미들웨어 완벽 지원
- 커스텀 미들웨어 작성 가능
예외 필터
- 중앙집중식 예외 처리 메커니즘
- 커스텀 예외 필터 구현 가능
파이프
- 입력 데이터 검증과 변환
- 커스텀 파이프 구현 가능
- 체계적인 아키텍처 제공으로 대규모 애플리케이션 개발에 적합
- Express.js와의 완벽한 호환성
- 강력한 CLI 도구 제공
- 풍부한 문서화와 활발한 커뮤니티
- 테스트 용이성이 높음
단점 및 한계#
- 학습 곡선이 상대적으로 가파름
- 작은 프로젝트에는 과도한 구조일 수 있음
- JavaScript로 사용 시 데코레이터 지원을 위한 추가 설정 필요
- TypeScript에 비해 JavaScript 사용 시 일부 기능 제한
사용 방법#
먼저 NestJS CLI를 설치하고 새 프로젝트를 생성합니다:
1
2
| npm i -g @nestjs/cli
nest new project-name
|
JavaScript로 개발할 때 필요한 기본 설정:
1
2
3
4
5
6
7
8
9
10
| // package.json
{
"type": "module",
"dependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"reflect-metadata": "^0.1.13"
}
}
|
주요 명령어#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 새 모듈 생성
nest generate module users
# 새 컨트롤러 생성
nest generate controller users
# 새 서비스 생성
nest generate service users
# 애플리케이션 실행
npm run start
# 개발 모드로 실행 (자동 재시작)
npm run start:dev
|
예시 코드#
기본적인 CRUD 기능을 갖춘 사용자 관리 애플리케이션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
| // main.js
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module.js';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
// app.module.js
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module.js';
@Module({
imports: [UsersModule],
})
export class AppModule {}
// users/users.module.js
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller.js';
import { UsersService } from './users.service.js';
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
// users/users.controller.js
import { Controller, Get, Post, Body, Param, Delete, Put } from '@nestjs/common';
import { UsersService } from './users.service.js';
@Controller('users')
export class UsersController {
constructor(usersService) {
this.usersService = usersService;
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id) {
return this.usersService.findOne(id);
}
@Post()
create(@Body() userData) {
return this.usersService.create(userData);
}
@Put(':id')
update(@Param('id') id, @Body() userData) {
return this.usersService.update(id, userData);
}
@Delete(':id')
remove(@Param('id') id) {
return this.usersService.remove(id);
}
}
// users/users.service.js
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
constructor() {
this.users = [];
}
findAll() {
return this.users;
}
findOne(id) {
return this.users.find(user => user.id === parseInt(id));
}
create(userData) {
const newUser = {
id: this.users.length + 1,
…userData,
};
this.users.push(newUser);
return newUser;
}
update(id, userData) {
const user = this.findOne(id);
if (!user) return null;
Object.assign(user, userData);
return user;
}
remove(id) {
const index = this.users.findIndex(user => user.id === parseInt(id));
if (index === -1) return null;
return this.users.splice(index, 1)[0];
}
}
|
미들웨어 사용 예시#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // middleware/logger.middleware.js
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware {
use(req, res, next) {
console.log(`Request… ${req.method} ${req.url}`);
next();
}
}
// app.module.js에 미들웨어 적용
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*');
}
|
예외 필터 사용 예시#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // filters/http-exception.filter.js
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter {
catch(exception, host) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}
|
파이프 사용 예시#
1
2
3
4
5
6
7
8
9
10
11
12
| // pipes/validation.pipe.js
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ValidationPipe {
transform(value, metadata) {
if (!value) {
throw new BadRequestException('No data submitted');
}
return value;
}
}
|
참고 및 출처#
NestJS - A progressive Node.js framework