Node.js 멀티 프로세스 환경에서 메모리 캐시 동기화 문제

문제 상황

트래픽 증가로 단일 프로세스 Node.js 앱을 PM2 클러스터 모드로 전환했다. 기존에 사용하던 인메모리 캐시가 프로세스별로 독립적으로 동작하면서 데이터 불일치 문제가 발생했다.

// 기존 코드
const cache = new Map();

function getUserData(userId) {
  if (cache.has(userId)) {
    return cache.get(userId);
  }
  const data = db.query(userId);
  cache.set(userId, data);
  return data;
}

프로세스 A에서 캐시를 업데이트해도 프로세스 B는 구 데이터를 반환하는 상황이 반복됐다.

해결 방법

Redis를 캐시 레이어로 도입했다. ioredis 라이브러리를 사용했고, 기존 Map 인터페이스와 유사하게 래퍼를 만들어 마이그레이션 비용을 줄였다.

const Redis = require('ioredis');
const redis = new Redis();

async function getUserData(userId) {
  const cached = await redis.get(`user:${userId}`);
  if (cached) {
    return JSON.parse(cached);
  }
  const data = await db.query(userId);
  await redis.setex(`user:${userId}`, 3600, JSON.stringify(data));
  return data;
}

고려사항

  • 직렬화 비용: JSON.stringify/parse 오버헤드가 있지만, 네트워크 I/O에 비하면 무시할 수준이었다.
  • TTL 설정: 기존 인메모리 캐시는 TTL이 없었는데, Redis에서는 명시적으로 설정해야 했다. 데이터 특성에 따라 1시간~24시간으로 차등 적용했다.
  • 에러 핸들링: Redis 장애 시 fallback 로직을 추가했다. Redis 연결 실패 시 DB로 직접 쿼리하도록 처리했다.

성능 테스트 결과 응답 시간이 평균 5ms 증가했지만, 데이터 일관성 확보가 더 중요했다. 프로덕션 배포 후 캐시 관련 버그 리포트가 사라졌다.