개발 slecs

SEO 크롤링 DB 부하를 인메모리 캐시로 줄인 과정

목차

SEO 메타데이터나 사이트맵 같은 정적에 가까운 데이터들을 매번 DB에서 조회하는 게 병목이 되고 있었다. 특히 검색 엔진 크롤러나 사용자 요청이 몰릴 때 DB 압력이 심해지더라. 그래서 SEO 관련 DB 모듈에 인메모리 캐시를 덧붙이기로 했다.

왜 인메모리 캐시를 선택했나

처음엔 Redis 같은 외부 캐시층을 고려했다. 확장성과 팀원들이 이미 알고 있는 기술이니까. 하지만 현 상황에선 오버엔지니어링이라고 봤다. SEO DB 조회는 읽기만 있고, 노드 인스턴스 수가 그리 많지 않으며, 캐시 미스 시 DB 락아웃까지는 아니거든. 인메모리 캐시로 먼저 시도해서 효과를 재고, 필요하면 나중에 외부 캐시로 마이그레이션하는 게 낫겠다고 판단했다.

인메모리의 장점도 여럿 있다. 배포 복잡도가 낮고, 추가 외부 의존성이 없으며, 응답 레이턴시가 극도로 짧다. 단점은 당연히 인스턴스 간 캐시 동기화가 없다는 점인데, SEO 데이터 정도의 업데이트 빈도면 큰 문제가 아니었다.

캐싱 방식 장점 단점 선택 이유
인메모리 저지연, 간단함, 의존성 없음 인스턴스간 불일치 현재 상황과 규모에 적합
Redis 분산 캐시, 동기화 가능 외부 의존성, 복잡도 미래 스케일 시점에 고려
DB 쿼리 정합성 완벽함 느림 기존 상태 (개선 대상)

TTL 5분을 설정한 의사결정

TTL을 정하는 게 핵심이었다. SEO 데이터는 얼마나 자주 바뀌나? 메타데이터 업데이트는 배포 단위로 이뤄지고, 동적 콘텐츠 기반 메타라도 보통 시간 단위. 그러니 분 단위 TTL은 합리적이다.

5분으로 잡은 건:
- 너무 짧으면 (1분 이하): 캐시 효과가 떨어진다. 요청 패턴상 5분 사이에 수십 번 같은 쿼리가 들어오곤 하는데, 1분이면 계속 미스가 난다.
- 너무 길면 (30분 이상): 배포 후 메타데이터가 바뀌었을 때 반영 지연이 심하다. SEO는 정확성이 중요하니까.
- 5분: 충분한 캐시 hit rate를 기대하면서도, 배포 후 합리적인 시간 내 최신 데이터로 업데이트되는 밸런스.

물론 이건 운영 중 메트릭을 보면서 조정할 수 있다. 캐시 hit rate가 너무 낮거나 stale data 민원이 들어오면 그때 TTL을 재논의하면 된다.

// 개념적인 구현 패턴
const seoCache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5분

function getCachedSeoData(key) {
  const cached = seoCache.get(key);
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }
  // TTL 만료되면 캐시 제거하고 DB 조회
  seoCache.delete(key);
  return fetchFromDB(key);
}

구현하며 마주친 고민들

캐시를 달면서 팀과 나눴던 코드리뷰 포인트들:

  • 캐시 무효화 타이밍: 관리자가 SEO 메타를 수정했을 때 어떻게 처리할까? Admin API에서 캐시를 강제 제거하는 로직을 넣기로 했다. 그렇지 않으면 변경 후 최대 5분까지 과거 데이터가 서빙될 수 있거든.
  • 메모리 누수: 무한정 캐시가 쌓이면 안 되니까, TTL 기반 자동 정리와 더불어 최대 크기 제한(LRU 같은)도 고려했다.
  • 모니터링: 캐시 hit/miss 레이트를 로깅해두기로 했다. 나중에 TTL 조정이나 성능 분석할 때 쓸 데이터.

마치며

사실 이 작업은 큰 기능 개발이 아니라 간단한 최적화였다. 하지만 이렇게 한 번에 완벽하게 풀려고 하기보다는, 작은 개선을 자주하고 관찰하면서 다음 단계를 정하는 게 좋다는 걸 다시 배웠다. 지금 인메모리 캐시가 필요면 충분하지만, 나중에 분산 환경에서 문제 생기면 그때 Redis로 업그레이드하면 된다. 과도 설계보다는 현재 맥락에 맞는 솔루션을 선택하고, 그 과정에서 팀과 의사결정을 공유하는 게 중요하다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.