마지막 시리즈가 될 것 같다.
이번 포스트에서는 Redis를 사용한 캐싱을 정리해볼 것이다.
전체 코드
@Injectable()
export class NewsCacheService {
private readonly logger = new Logger(NewsCacheService.name);
constructor(
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
private readonly newsCrawlingService: NewsCrawlingService,
) {}
private getCacheKey(category: Category): string {
return `${CACHE_KEY_PREFIX}:${category}`;
}
async getByCategory(category: Category): Promise<NewsResponseDto[]> {
const key = this.getCacheKey(category);
const cached = await this.cacheManager.get<NewsResponseDto[]>(key);
if (cached) {
return cached;
}
await this.fetchAndCache(category);
return (await this.cacheManager.get<NewsResponseDto[]>(key)) ?? [];
}
async fetchAndCache(category: Category): Promise<void> {
const key = this.getCacheKey(category);
const newsItems = await this.newsCrawlingService.fetchByCategory(category);
const dtos = newsItems.map((item) => NewsResponseDto.fromNaverItem(item));
await this.cacheManager.set(key, dtos, CACHE_TTL);
}
}
크롤링 결과를 Redis 캐시에 저장/조회하는 기능을 구현하였다. 크롤링이 오래 걸리니 캐시 먼저 확인 후 없으면 크롤링 후 저장하는 구조로 구현했다.
const CACHE_TTL = 30 * 60 * 1000; // 30분 (ms)
const CACHE_KEY_PREFIX = 'news';
상수로 ttl와 key prefix를 분리해 관리하였다. v5+ 부터 ttl은 밀리세컨드를 사용한다.
constructor(
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
private readonly newsCrawlingService: NewsCrawlingService,
) {}
@Inject(CACHE_MANAGER) 는 NestJS가 캐시 모듈을 주입할 때 쓰는 토큰이다. 일반 @Injectable()과 달리 토큰 기반 주입이라 @Inject가 필요하다.
private getCacheKey(category: Category): string {
return `${CACHE_KEY_PREFIX}:${category}`;
}
카테고리별로 독립된 캐시를 가진다. 예를 들어 category가 'politics' 면, 키: "news:politics" 이다.
async getByCategory(category: Category): Promise<NewsResponseDto[]> {
const key = this.getCacheKey(category);
const cached = await this.cacheManager.get<NewsResponseDto[]>(key);
if (cached) {
return cached; // 캐시 히트 → 즉시 반환
}
// 캐시 미스 → 크롤링 후 캐시 저장
await this.fetchAndCache(category);
return (await this.cacheManager.get<NewsResponseDto[]>(key)) ?? [];
}
캐시를 확인하고 있으면 데이터를 반환한다. 없다면 fetchAndCache를 실행한 후 다시 조회하여 반환한다.
async fetchAndCache(category: Category): Promise<void> {
const key = this.getCacheKey(category);
const newsItems = await this.newsCrawlingService.fetchByCategory(category);
const dtos = newsItems.map((item) => NewsResponseDto.fromNaverItem(item));
await this.cacheManager.set(key, dtos, CACHE_TTL);
}
fetchByCategory()를 호출하여 크롤링한 네이버 뉴스 데이터를 수집하고, Redis에 30분 TTL로 저장한다.
잔여 한계
TTL 만료 후 첫 요청은 크롤링 시간만큼 지연이 발생한다. 트래픽 증가 시 TTL 만료 전 백그라운드 갱신 스케줄러 도입을 검토할 수 있으나, 현재 규모에서는 API 비용과 서버 부하를 고려해 현행 방식을 유지하려고 한다.!

감사합니다 (^^ゞ
'Development > Back-end' 카테고리의 다른 글
| [NestJS] 네이버 뉴스 카테고리 지정하기 (2) (0) | 2026.03.28 |
|---|---|
| [NestJS] 네이버 뉴스 카테고리 지정하기 (1) (0) | 2026.03.28 |
| [NestJS] Pipe 사용하기 (1) | 2026.02.26 |
| [Spring Boot] 어댑터 패턴 구현하기(with. JPA, Redis) (0) | 2025.12.26 |
| [Spring Boot] @Data 알고 쓰기 (0) | 2025.11.26 |
