본문으로 건너뛰기

성능 최적화 가이드라인

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.02025-03-25bok@weltcorp.com최초 작성