성능 최적화 가이드라인
1. 데이터베이스 최적화
1.1 쿼리 최적화
- 필요한 필드만 선택 (select)
- 적절한 인덱스 사용
- N+1 쿼리 문제 해결
- 불필요한 관계 조회 제거
예시:
// 나쁜 예시
const users = await prisma.user.findMany();
// 좋은 예시
const users = await prisma.user.findMany({
where: {
isActive: true
},
select: {
id: true,
email: true,
name: true
}
});
// N+1 문제 해결 예시
// 나쁜 예시
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({
where: { authorId: user.id }
});
}
// 좋은 예시
const users = await prisma.user.findMany({
include: {
posts: true
}
});
1.2 인덱스 관리
model User {
id Int @id @default(autoincrement())
email String @unique // 자동으로 인덱스 생성
firstName String
lastName String
posts Post[]
// 복합 인덱스
@@index([firstName, lastName])
}
model Post {
id Int @id @default(autoincrement())
title String
authorId Int
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
// 자주 조회되는 필드에 인덱스 추가
@@index([authorId])
@@index([createdAt])
}
1.3 커넥션 풀 관리
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
// 커넥션 풀 설정
poolTimeout = 20
pool = {
min = 5
max = 20
}
}
// 애플리케이션 코드
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
// 쿼리 성능 모니터링
middleware: [
async (params, next) => {
const before = Date.now();
const result = await next(params);
const after = Date.now();
console.log(`Query ${params.model}.${params.action} took ${after - before}ms`);
return result;
}
]
});
2. API 최적화
2.1 응답 캐싱
@Injectable()
export class CacheInterceptor implements NestInterceptor {
constructor(private cacheManager: Cache) {}
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
const cacheKey = this.generateCacheKey(request);
const cachedResponse = await this.cacheManager.get(cacheKey);
if (cachedResponse) {
return of(cachedResponse);
}
return next.handle().pipe(
tap(response => {
this.cacheManager.set(cacheKey, response, { ttl: 60 });
}),
);
}
}
2.2 페이지네이션
// 커서 기반 페이지네이션 (권장)
@Get()
async findAll(
@Query('cursor') cursor?: string,
@Query('take', new DefaultValuePipe(10), ParseIntPipe) take: number,
): Promise<PaginatedResponse<User>> {
const users = await prisma.user.findMany({
take: take + 1, // 다음 페이지 확인을 위해 1개 더 가져옴
cursor: cursor ? { id: cursor } : undefined,
orderBy: {
id: 'asc',
},
select: {
id: true,
email: true,
name: true,
},
});
const hasNextPage = users.length > take;
const items = hasNextPage ? users.slice(0, -1) : users;
const nextCursor = hasNextPage ? users[users.length - 2].id : null;
return {
items,
hasNextPage,
nextCursor,
};
}
// 오프셋 기반 페이지네이션
@Get()
async findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
@Query('perPage', new DefaultValuePipe(10), ParseIntPipe) perPage: number,
): Promise<PaginatedResponse<User>> {
const [items, total] = await Promise.all([
prisma.user.findMany({
skip: (page - 1) * perPage,
take: perPage,
select: {
id: true,
email: true,
name: true,
},
}),
prisma.user.count(),
]);
return {
items,
total,
page,
perPage,
totalPages: Math.ceil(total / perPage),
};
}
2.3 데이터 압축
import * as compression from 'compression';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(compression());
await app.listen(3000);
}
3. 메모리 관리
3.1 메모리 누수 방지
- 불필요한 객체 참조 제거
- 이벤트 리스너 정리
- 스트림 종료 처리
- 캐시 크기 제한
3.2 가비지 컬렉션 최적화
- 대용량 객체 처리 주의
- 메모리 사용량 모니터링
- 주기적인 메모리 프로파일링
- 메모리 단편화 방지
4. 비동기 처리 최적화
4.1 Promise 최적화
// 나쁜 예시
async function processUsers() {
const users = await getUsers();
for (const user of users) {
await processUser(user);
}
}
// 좋은 예시
async function processUsers() {
const users = await getUsers();
await Promise.all(users.map(user => processUser(user)));
}
4.2 이벤트 루프 블로킹 방지
// 나쁜 예시
function heavyComputation() {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return result;
}
// 좋은 예시
async function heavyComputation() {
return new Promise((resolve) => {
setImmediate(() => {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
resolve(result);
});
});
}
5. 프론트엔드 최적화
5.1 번들 크기 최적화
- 코드 스플리팅
- 트리 쉐이킹
- 레이지 로딩
- 번들 분석
5.2 렌더링 최적화
- 가상 스크롤링
- 컴포넌트 메모이제이션
- 불필요한 리렌더링 방지
- 이미지 최적화
6. 모니터링 및 프로파일링
6.1 성능 메트릭 수집
@Injectable()
export class PerformanceInterceptor implements NestInterceptor {
constructor(private metricsService: MetricsService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const start = Date.now();
const request = context.switchToHttp().getRequest();
return next.handle().pipe(
tap(() => {
const duration = Date.now() - start;
this.metricsService.recordLatency(request.path, duration);
}),
);
}
}
6.2 성능 모니터링
- 응답 시간 모니터링
- 리소스 사용량 추적
- 에러율 모니터링
- 병목 지점 식별
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.1.0 | 2025-03-25 | bok@weltcorp.com | 최초 작성 |