NestJS 학습 가이드 - 완전정복 시리즈
개요
이 문서는 NestJS 프레임워크의 기초부터 고급 기능까지 3단계로 나누어 설명하는 종합적인 학습 가이드입니다. 각 파트는 실제 애플리케이션 개발에 필요한 핵심 개념과 실무 예제를 포함하고 있습니다.
Part 1: NestJS 시작하기
학습 목표
- NestJS 개발 환경 구축
- 첫 번째 애플리케이션 생성
- Controller, Model, Service의 기본 구조 이해
- GET/POST 엔드포인트 구현
사전 요구사항
필수 소프트웨어:
- Node.js 버전 20 이상
- Nest CLI
설치 및 설정
Node.js 설치 확인
node -v
Nest CLI 전역 설치
npm i -g @nestjs/cli
프로젝트 생성
nest new api1
프로젝트 구조
생성된 프로젝트는 다음과 같은 기본 구조를 가집니다:
- App Controller - 라우팅 처리
- App Service - 비즈니스 로직
- App Module - 모듈 구성
- Main Controller - 애플리케이션 진입점
- 단위 테스트 파일들
컨트롤러 추가
CLI 명령어
nest generate controller book
생성되는 파일
book.controller.ts
- 컨트롤러 구현book.controller.spec.ts
- 단위 테스트
GET 엔드포인트 구현
@Controller('book')
export class BookController {
@Get()
findAll() {
return 'This action returns all books';
}
@Get(':id')
findOne(@Param('id') id: string) {
return `This action returns a #${id} book`;
}
}
모델 정의
인터페이스 기반 모델 생성:
export interface IBook {
id: number;
title: string;
author: string;
}
서비스 생성
CLI 명령어
nest generate service book
서비스 구현
@Injectable()
export class BookService {
private books: IBook[] = [
{ id: 1, title: '1984', author: 'George Orwell' },
{ id: 2, title: 'To Kill a Mockingbird', author: 'Harper Lee' },
{ id: 3, title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
];
findAll(): IBook[] {
return this.books;
}
findOne(id: number): IBook | undefined {
return this.books.find(book => book.id === id);
}
}
의존성 주입
컨트롤러에서 서비스 사용:
@Controller('book')
export class BookController {
constructor(private readonly bookService: BookService) {}
@Get()
findAll(): IBook[] {
return this.bookService.findAll();
}
}
POST 작업 구현
서비스 메서드
create(book: IBook): void {
this.books.push(book);
}
컨트롤러 엔드포인트
@Post()
create(@Body() book: IBook, @Res() res: Response): void {
try {
this.bookService.create(book);
res.status(HttpStatus.CREATED).send({ message: 'Resource created successfully' });
} catch (error) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ message: 'Internal Server Error' });
}
}
실용적인 팁
- 메모리 배열을 사용한 데이터 관리는 개발 단계에서만 사용
- Progress Telerik Fiddler Everywhere로 POST 요청 테스트 가능
- HTTP 상태 코드를 적절히 사용하여 API 응답 명확화
Part 2: 데이터베이스 연결
학습 목표
- SQL Azure 데이터베이스 연결
- TypeORM 설정 및 사용
- CRUD 작업 완전 구현
- Entity 생성 및 관리
데이터베이스 설정
연결 문자열 확인
Azure Portal에서 Connection Strings → ODBC 탭에서 확인
필수 의존성 설치
npm install @nestjs/typeorm
npm install typeorm mssql
TypeORM 구성
app.module.ts 설정
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mssql',
host: '<your-sql-azure-server>.database.windows.net',
port: 1433,
username: '<your-username>',
password: '<your-password>',
database: '<your-database>',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // ⚠️ 프로덕션에서는 false로 설정
options: {
encrypt: true,
},
}),
],
})
Entity 생성
book.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Book {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
author: string;
}
CRUD 작업 구현
1. 데이터 삽입 (Create)
서비스 메서드:
async create(bookData: Partial<Book>): Promise<Book> {
const book = this.bookRepository.create(bookData);
return this.bookRepository.save(book);
}
컨트롤러 엔드포인트:
@Post()
async create(@Body() bookData: Partial<Book>, @Res() res: Response) {
try {
const book = await this.bookService.create(bookData);
return res.status(HttpStatus.CREATED).json(book);
} catch (error) {
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Error creating book' });
}
}
2. 데이터 조회 (Read)
서비스 메서드:
async findAll(): Promise<Book[]> {
return this.bookRepository.find();
}
async findOne(id: number): Promise<Book | null> {
return this.bookRepository.findOneBy({ id });
}
3. 데이터 수정 (Update)
async update(id: number, updateData: Partial<Book>): Promise<Book | null> {
await this.bookRepository.update(id, updateData);
return this.bookRepository.findOneBy({ id });
}
4. 데이터 삭제 (Delete)
async remove(id: number): Promise<DeleteResult> {
return this.bookRepository.delete(id);
}
리포지토리 주입
@Injectable()
export class BookService {
constructor(
@InjectRepository(Book)
private readonly bookRepository: Repository<Book>,
) {}
}
주의사항
- synchronize: true는 개발 환경에서만 사용
- ODBC 드라이버 설치 필요
- 암호화 연결 옵션 활성화 권장
Part 3: 데이터 캐싱
학습 목표
- NestJS 캐싱 시스템 이해
- 인메모리 캐시와 Redis 캐시 구현
- 커스텀 키 사용법
- 성능 최적화 전략
필수 패키지 설치
npm install @nestjs/cache-manager cache-manager
기본 캐시 설정
AppModule 구성
import { CacheModule } from '@nestjs/cache-manager';
@Module({
imports: [
CacheModule.register({
ttl: 30000, // 30초
max: 100,
isGlobal: true,
store: 'memory',
}),
],
})
export class AppModule {}
라우트 레벨 캐싱
기본 캐시 인터셉터 사용
@Get()
@UseInterceptors(CacheInterceptor)
@CacheTTL(60) // TTL 오버라이드
async findAll() {
try {
return await this.bookService.findAll();
} catch (error) {
throw new Error('Error fetching books');
}
}
Azure Redis Cache 통합
Azure Portal 설정
- 리소스 그룹: nomadcoder
- 이름: nc
- 메모리: 0.5 GB
- 성능: Balanced vCPUs
Redis 캐시 구성
CacheModule.register({
store: 'redis',
host: 'your-host-name',
port: 10000,
password: 'primary-key-value',
tls: {},
ttl: 50000,
isGlobal: false,
})
커스텀 키 활용
서비스에서 캐시 매니저 주입
constructor(
@Inject(CACHE_MANAGER) private cacheManager: Cache
) {}
private readonly bookCacheKey = 'my_custom_books_key';
고급 캐싱 로직
async findAll(): Promise<Book[]> {
// 캐시에서 먼저 확인
let books = await this.cacheManager.get<Book[]>(this.bookCacheKey);
if (books) {
return books;
}
// 캐시에 없으면 DB에서 조회 후 캐싱
books = await this.bookRepository.find();
await this.cacheManager.set(this.bookCacheKey, books, 1000);
return books;
}
캐싱 구성 옵션
옵션 | 설명 | 기본값 |
---|---|---|
ttl | 캐시 생존 시간 (밀리초) | 5000 |
max | 최대 캐시 항목 수 | 100 |
isGlobal | 전역 캐시 사용 여부 | false |
store | 캐시 저장소 타입 | ‘memory’ |
캐싱 제한사항
- GET 엔드포인트만 캐시 가능
- @Res**() 객체** 사용 시 캐시 인터셉터 작동 불가
- 네이티브 응답 객체 주입 시 캐싱 비활성화
성능 최적화 팁
커스텀 키 사용의 장점
- 정밀한 제어 - 특정 데이터에 직접 접근
- 쉬운 무효화 - 키 이름으로 캐시 데이터 삭제
- 고성능 - Redis의 키 기반 룩업 최적화
- 개별 TTL - 키별로 다른 만료 시간 설정
실용적인 캐싱 전략
- 자주 조회되는 데이터에 우선적으로 캐싱 적용
- 변경이 적은 데이터는 긴 TTL 설정
- 실시간 데이터는 짧은 TTL 또는 캐싱 제외
추가 학습 리소스
공식 문서
- NestJS 공식 문서 [원문에 없었던 내용]
- TypeORM 공식 가이드 [원문에 없었던 내용]
권장 도구
- Progress Telerik Fiddler Everywhere - API 테스팅
- Azure Portal - 클라우드 리소스 관리
- VS Code - 개발 환경 [원문에 없었던 내용]
실습 프로젝트
- 기본 도서 관리 API (Part 1)
- 데이터베이스 연동 API (Part 2)
- 캐싱 적용 고성능 API (Part 3)
핵심 포인트 요약
Part 1 핵심
- MVC 패턴 기반의 NestJS 아키텍처
- 데코레이터 기반 라우팅 시스템
- 의존성 주입을 통한 서비스 연결
Part 2 핵심
- TypeORM을 통한 데이터베이스 추상화
- Entity 기반 데이터 모델링
- Repository 패턴으로 데이터 액세스
Part 3 핵심
- 다층 캐싱 전략 구현
- Redis 캐시 연동으로 확장성 확보
- 커스텀 키 관리로 정밀한 캐시 제어
다음 단계
(원문 미포함) 이 가이드를 완료한 후 고려해볼 고급 주제들:
- 인증/인가 시스템 구현
- API 문서화 (Swagger 연동)
- 테스팅 전략 수립
- 배포 및 DevOps 파이프라인
- 마이크로서비스 아키텍처 전환
이 문서는 NestJS 학습 시리즈를 바탕으로 작성되었으며, 실제 프로덕션 환경에서의 적용을 위해서는 추가적인 보안 및 성능 고려사항을 검토해야 합니다.