Node.js · 2026-05-07

서버 측 렌더링 캐시: Cache-Aside와 CDN 적용

서버 측 렌더링에서 Cache-Aside 패턴과 CDN/Edge 캐싱 적용 방법을 개념, 구현 예제, 무효화 전략과 장애 대응 중심으로 정리한 실무 자료

작성일 : 2026-05-07 ㆍ 작성자 : 관리자
post
목차

개요

서버 측 렌더링(SSR) 환경에서 캐시는 성능과 비용에 직접적인 영향을 준다. 여기서는 SSR 캐시 전략 Node.js 관점에서 Cache-Aside 패턴과 CDN(또는 Edge) 캐싱을 어떻게 결합할지 설명한다. 초보자도 이해하기 쉽도록 개념, 흐름, 코드 예제, 유의사항 순으로 정리한다.

기본 개념

Cache-Aside 패턴이란?

Cache-Aside는 애플리케이션이 먼저 캐시를 조회하고, 캐시에 없으면 원본(데이터베이스나 렌더러)에서 데이터를 읽어 캐시에 저장한 뒤 응답하는 방식이다. 읽기 중심 워크로드에서 일관성과 응답 속도 사이에 균형을 맞추기 용이하다.

CDN / Edge 캐싱 역할

CDN은 엣지 노드에 콘텐츠를 저장해 사용자에 가까운 위치에서 응답을 제공한다. SSR에서는 완전 HTML을 엣지에 저장하거나, 페이지 조각(fragment)을 캐시해서 네트워크 왕복을 줄인다. Edge 캐싱 Node 앱 설계는 적절한 캐시 제어 헤더와 무효화 전략이 핵심이다.

Cache-Aside 패턴 적용 흐름

  • 클라이언트 요청 수신
  • 서버(또는 미들웨어)가 먼저 내부 캐시(예: Redis)를 조회
  • 캐시 미스 시 렌더러나 DB 호출로 콘텐츠 생성
  • 생성된 콘텐츠를 캐시에 저장(유효기간 포함)
  • 응답과 함께 CDN에 캐시될 수 있도록 적절한 헤더 설정

Node.js 구현 예제

설계 포인트

Cache-Aside 패턴 Node에서 구현할 때 고려할 항목:

  • 캐시 키 설계: URL, 쿼리, 인증 상태 등으로 구분
  • TTL: 페이지 특성에 맞는 만료시간 설정
  • 동시성 제어: Cache stampede 방지(락 또는 요청 코alescing)
  • 무효화 정책: 콘텐츠 변경 시 캐시를 즉시 비우는 방법

Express + Redis 예제

const express = require('express')
const redis = require('redis')
const fetchPage = require('./render') // 렌더링 함수

const app = express()
const client = redis.createClient({ url: process.env.REDIS_URL })
client.connect()

app.get('*', async (req, res) => {
  const key = `page:${req.originalUrl}`
  try {
    const cached = await client.get(key)
    if (cached) {
      res.set('X-Cache', 'HIT')
      res.set('Cache-Control', 'public, max-age=60')
      return res.send(cached)
    }

    // 캐시 미스: 렌더링 수행
    const html = await fetchPage(req)

    // 캐시에 저장 (비동기 처리 고려)
    await client.setEx(key, 60, html)

    res.set('X-Cache', 'MISS')
    res.set('Cache-Control', 'public, max-age=60')
    return res.send(html)
  } catch (err) {
    // 장애 시도: 원본으로 응답
    console.error(err)
    const html = await fetchPage(req)
    res.set('Cache-Control', 'no-store')
    return res.send(html)
  }
})

app.listen(3000)

위 예제는 기본적인 Cache-Aside 패턴 Node 방식이다. 동시성 문제나 대규모 트래픽에는 추가적인 보호 장치가 필요하다.

CDN / Edge 캐싱 설정

헤더 전략

CDN에 의존하려면 응답 헤더로 의도를 명확히 전달해야 한다. 일반적인 설정 예시는 다음과 같다.

// 서버에서 설정할 헤더 예시
res.set('Cache-Control', 'public, max-age=60, s-maxage=300')
res.set('Surrogate-Control', 'max-age=300')
// Vary, ETag 등을 통해 캐시 버전 관리 가능

여기서 s-maxage는 리버스 프록시나 CDN 전용 TTL로, Edge 캐싱 Node 앱 설계에서 자주 사용된다. Surrogate-Control는 일부 CDN에서 우선적으로 참조되는 헤더다.

무효화와 재검증

  • 퍼즈(Purge): 콘텐츠가 변경되면 CDN API로 즉시 제거
  • 캐시 키 버전: URL에 버전(query 또는 path) 첨가로 블루그린 방식 적용
  • 조건부 요청: ETag/Last-Modified로 재검증

운영에서 주의할 점

일관성 vs 성능

짧은 TTL은 최신성 확보에 유리하지만 빈번한 원본 호출을 유발한다. 반면 긴 TTL은 성능을 높이지만 콘텐츠 지연 문제를 동반한다. 중요한 데이터는 캐시 제외나 짧은 TTL로 처리하고, 정적 콘텐츠는 긴 TTL을 사용한다.

장애 대응

  • 캐시 장애 시 원본 폴백 경로 확보
  • 캐시 스탬피드 방지: 요청 coalescing 또는 분산 락 적용
  • 모니터링: 캐시 히트율, 레이턴시, 오류율 관찰

요약과 권장 사항

SSR 캐시 전략 Node.js 환경에서는 Cache-Aside 패턴 Node 적용으로 서버 내부 캐시를 운용하고, CDN/Edge 캐싱을 병행해 사용자 경험을 개선하는 것이 효과적이다. 핵심은 캐시 키 설계, TTL 정책, 무효화 전략, 그리고 장애 시 폴백 경로다. 실무에서는 작은 사례부터 시작해 모니터링을 통해 정책을 조율하는 방식이 안전하다.

SSR 캐시 전략 Node.js Cache-Aside 패턴 Node Edge 캐싱 Node 앱 CDN 캐싱 Redis 캐시 서버 렌더링 캐시 Cache-Control 캐시 무효화