Redis 캐싱으로 Node.js API 속도 개선
Node.js와 Redis를 이용한 API 응답 캐싱 원리와 구현 예제, 세션 캐시 연동, TTL 관리 및 성능 측정 방법을 종합한 설명
목차
개요
API 응답 속도는 사용자 경험과 서버 비용에 직접적인 영향을 준다. Redis 캐싱은 빈번한 읽기 작업을 메모리 기반 저장소로 옮겨 응답 지연을 줄이고 데이터베이스 부하를 경감하는 실무적 솔루션이다. 이 글에서는 Node.js Redis 캐시 구현 원리와 실제 예제를 통해 이해를 돕는다.
캐싱의 기본 개념
캐시는 자주 읽히는 데이터를 임시로 저장하는 계층이다. 캐시 적용 시 얻는 장점은 다음과 같다.
- 응답 시간 감소
- 데이터베이스 동시 접속 수 감소
- 비용 절감 및 확장성 향상
단점으로는 최신성 보장 문제와 캐시 무효화 설계의 복잡성이 있다. 따라서 TTL(유효기간)과 키 전략이 중요하다.
Redis와 Node.js 연동 준비
필수 패키지 설치
npm install express redis express-session connect-redis
공식 Redis 클라이언트는 현대적인 비동기 API를 제공한다. 이후 예제는 CommonJS 스타일로 작성한다.
간단한 Express 캐시 미들웨어
아래 예제는 GET 요청에 대해 Redis에서 캐시를 조회하고, 캐시 미스일 때만 데이터를 생성해 저장하는 흐름을 보여준다. 키는 엔드포인트와 쿼리 문자열로 구성한다.
const express = require('express');
const { createClient } = require('redis');
const app = express();
const redisClient = createClient();
(async () => { await redisClient.connect(); })();
const cacheMiddleware = (ttlSeconds) => async (req, res, next) => {
try {
const key = `cache:${req.originalUrl}`;
const cached = await redisClient.get(key);
if (cached) {
res.setHeader('X-Cache', 'HIT');
return res.json(JSON.parse(cached));
}
res.sendResponse = res.json;
res.json = async (body) => {
await redisClient.setEx(key, ttlSeconds, JSON.stringify(body));
res.setHeader('X-Cache', 'MISS');
res.sendResponse(body);
};
next();
} catch (err) {
next(err);
}
};
app.get('/api/data', cacheMiddleware(60), async (req, res) => {
const data = { time: Date.now(), value: 'db-result' };
res.json(data);
});
app.listen(3000);
이 코드는 요청을 캐시에서 먼저 확인한다. 캐시가 없을 때는 실제 처리 후 Redis에 저장한다. 헤더로 HIT/MISS를 표시해 테스트 시 캐싱 동작을 확인하기 쉽다.
캐시 키 설계와 TTL 전략
효율적인 캐싱을 위해 키 명명 규칙과 TTL 정책이 필요하다. 권장 방식은 다음과 같다.
- 키 네임스페이스 사용: service:resource:id 형태
- 쿼리나 사용자별 데이터는 키에 명시적으로 포함
- 짧은 TTL과 긴 TTL의 혼합 사용으로 신선도와 효율 균형
예를 들어 목록 조회는 짧은 TTL(수십 초), 통계나 빈번히 변하지 않는 데이터는 긴 TTL(수분~수시간)을 적용한다. 변경이 발생하면 관련 키를 삭제하는 무효화 전략을 병행한다.
세션과 Redis 연동: express Redis 세션 캐시
세션 저장소로 Redis를 사용하면 세션 읽기/쓰기가 빠르고 여러 인스턴스 간 세션 공유가 가능하다. 아래는 express-session과 connect-redis를 사용하는 기본 설정 예제다.
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 1000 * 60 * 60 }
}));
이 설정은 세션 데이터를 Redis에 저장한다. 세션 관련 부하를 Redis로 분산시켜 API 서버의 메모리 사용을 줄일 수 있다. express Redis 세션 캐시는 로그인 기반 애플리케이션에서 특히 유용하다.
성능 측정과 모니터링
적용 전후의 성능을 비교해야 실제 이익을 확인 가능하다. 주요 지표는 다음과 같다.
- 응답 시간 평균 및 퍼센타일(95th, 99th)
- 데이터베이스 쿼리 수
- Redis 히트율과 메모리 사용량
Redis는 INFO 명령과 모니터링 도구로 상태를 점검할 수 있다. 히트율이 낮다면 키 설계나 TTL 조정이 필요하다.
주의사항
- 캐시 일관성: 중요한 업데이트는 캐시 무효화로 처리해야 한다.
- 보안: 민감한 데이터를 캐시에 보관할 때 암호화와 접근 제어 고려.
- 메모리 관리: Redis 메모리 정책과 eviction 설정 확인.
결론
Node.js Redis 캐시 구현은 API 응답 속도와 서버 비용 측면에서 실질적인 개선을 제공한다. Redis 캐싱 예제 Node.js를 통해 기본 패턴을 익히면 세션 캐시와 복잡한 무효화 전략까지 확장할 수 있다. 단계별 적용과 모니터링을 병행하면 안정적이고 효율적인 캐시 계층을 운영할 수 있다.