캐싱은 자주 사용되지만 잘 변하지 않는 데이터나, 계산 비용이 비싼 작업의 결과를 메모리와 같은 빠른 저장소에 임시로 저장해두고, 다음 요청 시에는 실제 작업을 다시 수행하는 대신 저장된 결과를 즉시 반환하는 기술입니다.
핵심 목표: 데이터베이스나 외부 API에 대한 반복적인 호출을 줄여, 애플리케이션의 응답 속도를 향상시키고 시스템 부하를 감소시킵니다.
@nestjs/cache-manager)NestJS는 @nestjs/cache-manager 모듈을 통해, 기반 기술(인메모리, Redis 등)에 상관없이 일관된 방식으로 캐싱을 적용할 수 있도록 캐시 추상화를 제공합니다.
사용법:
패키지 설치: npm install @nestjs/cache-manager (Redis를 사용하려면 cache-manager-redis-store 등 추가 설치)
루트 모듈(app.module.ts)에 CacheModule 등록:
import { CacheModule } from '@nestjs/cache-manager';
@Module({
imports: [CacheModule.register({ isGlobal: true })], // isGlobal: true로 전역 설정
})
export class AppModule {}
CacheInterceptor 또는 @UseInterceptors 사용:
CacheInterceptor를 적용하면, 해당 엔드포인트의 GET 요청에 대한 응답이 자동으로 캐싱됩니다.@CacheKey(): 캐시를 구분할 키를 지정.@CacheTTL(): 캐시의 유효 시간(Time-To-Live)을 밀리초 단위로 지정.// products.controller.ts
@Controller('products')
@UseInterceptors(CacheInterceptor) // 이 컨트롤러의 모든 GET 요청에 캐싱 적용
export class ProductsController {
@Get(':id')
@CacheTTL(30 * 1000) // 이 엔드포인트의 캐시는 30초 동안 유효
findProduct(@Param('id') id: string) {
// 이 로직은 30초에 한 번만 실행됨
return this.productsService.findOne(id);
}
}
프로그래매틱 캐싱: 인터셉터 방식 외에도, Cache 객체를 직접 주입(@Inject(CACHE_MANAGER))받아, 서비스 로직 내에서 cacheManager.get()이나 cacheManager.set()을 사용하여 수동으로 캐시를 제어할 수도 있습니다.
문제점: 이메일 발송, 이미지 리사이징, 동영상 인코딩 등 시간이 오래 걸리거나 즉각적인 결과가 필요 없는 작업을 동기(Synchronous) 방식으로 처리하면, 사용자는 작업이 끝날 때까지 기다려야 하므로 사용자 경험(UX)이 저하됩니다.
큐 (Queue): 이러한 작업을 처리하기 위한 "작업 대기열"을 제공하는 시스템입니다. 요청을 받은 즉시 "작업을 등록했습니다"라고 빠르게 응답하고, 실제 오래 걸리는 작업은 백그라운드에서 별도의 워커(Worker) 프로세스가 처리하도록 위임합니다.
Bull: Node.js 생태계에서 널리 사용되는, Redis를 기반으로 동작하는 강력하고 안정적인 큐 라이브러리입니다.@nestjs/bull: Bull을 NestJS에서 쉽게 사용할 수 있도록 통합해주는 모듈입니다.Producer (생산자):
@InjectQueue() 데코레이터를 사용하여 특정 큐를 주입받고, queue.add('jobName', data) 메서드를 호출하여 작업을 등록합니다.Consumer (소비자/워커):
@Processor() 데코레이터가 붙은 클래스로, 실제 작업 로직을 담고 있습니다.@Process('jobName') 데코레이터가 붙은 메서드에서, 해당 이름의 작업을 어떻게 처리할지를 구현합니다.사용법:
npm install @nestjs/bull bullBullModule.forRoot()로 Redis 연결 정보를, BullModule.registerQueue()로 사용할 큐의 이름을 등록합니다.// image.service.ts (Producer)
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
@Injectable()
export class ImageService {
constructor(@InjectQueue('image-processing') private imageQueue: Queue) {}
async processImage(file) {
// 작업을 큐에 등록하고 즉시 반환
await this.imageQueue.add('resize', {
file: file.path,
});
return { message: '이미지 처리 작업이 등록되었습니다.' };
}
}
// image.processor.ts (Consumer)
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
@Processor('image-processing') // 'image-processing' 큐를 처리
export class ImageProcessor {
@Process('resize') // 'resize' 이름의 작업을 처리
async handleResize(job: Job) {
console.log('이미지 리사이징 시작:', job.data.file);
// ... 시간이 오래 걸리는 실제 이미지 처리 로직 ...
console.log('이미지 리사이징 완료');
}
}
@nestjs/cache-manager)은 반복적인 데이터 조회나 계산 결과를 임시 저장하여, 애플리케이션의 응답 속도를 향상시키고 DB 부하를 줄이는 핵심적인 성능 최적화 기법입니다.@nestjs/bull)는 시간이 오래 걸리는 작업을 백그라운드에서 비동기적으로 처리하기 위한 시스템입니다.