Redis 완벽 마스터
Redis의 핵심 개념과 실전 활용법
학습 항목
이미지 로딩 중...
Caching 기초부터 심화까지 완벽 가이드
캐싱의 기본 개념부터 실전 활용까지 단계별로 학습합니다. 메모리 캐싱, Redis 활용, 캐시 무효화 전략 등 실무에서 바로 적용 가능한 캐싱 기법을 다룹니다.
들어가며
이 글에서는 Caching 기초부터 심화까지 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- 기본_메모리_캐싱
- Memoization_패턴
- LRU_캐시_구현
- TTL_기반_캐시
- Redis_기본_캐싱
- Cache_Aside_패턴
- Write_Through_캐싱
- 캐시_무효화_전략
- 다중_레벨_캐싱
- Cache_Stampede_방지
- 조건부_캐싱
- Pub_Sub_캐시_동기화
1. 기본_메모리_캐싱
개요
간단한 객체를 사용하여 함수 결과를 메모리에 캐싱하는 기본 패턴입니다.
코드 예제
const cache = {};
function expensiveCalculation(n) {
if (cache[n]) return cache[n];
const result = n * n; // 실제로는 복잡한 연산
cache[n] = result;
return result;
}
console.log(expensiveCalculation(5)); // 25
설명
계산 결과를 객체에 저장하여 동일한 입력에 대해 재계산을 방지합니다. 캐시 히트 시 즉시 결과를 반환합니다.
2. Memoization_패턴
개요
고차 함수를 사용하여 어떤 함수든 메모이제이션을 적용할 수 있는 범용 패턴입니다.
코드 예제
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
const result = fn(...args);
cache[key] = result;
return result;
};
}
const fibonacci = memoize((n) =>
n <= 1 ? n : fibonacci(n-1) + fibonacci(n-2)
);
설명
함수를 감싸서 자동으로 결과를 캐싱합니다. 인자를 문자열로 변환하여 캐시 키로 사용합니다.
3. LRU_캐시_구현
개요
최근 사용 빈도 기반으로 캐시를 관리하는 LRU(Least Recently Used) 알고리즘 구현입니다.
코드 예제
class LRUCache {
constructor(limit) {
this.cache = new Map();
this.limit = limit;
}
get(key) {
if (!this.cache.has(key)) return null;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value); // 최신으로 이동
return value;
}
set(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
else if (this.cache.size >= this.limit) {
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
}
}
설명
Map을 사용하여 삽입 순서를 유지하고, 용량 초과 시 가장 오래된 항목을 자동으로 제거합니다.
4. TTL_기반_캐시
개요
시간 제한(Time To Live)을 적용하여 일정 시간 후 캐시가 자동으로 만료되도록 합니다.
코드 예제
class TTLCache {
constructor(ttl = 60000) {
this.cache = new Map();
this.ttl = ttl;
}
set(key, value) {
const expiry = Date.now() + this.ttl;
this.cache.set(key, { value, expiry });
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expiry) {
this.cache.delete(key);
return null;
}
return item.value;
}
}
설명
각 캐시 항목에 만료 시간을 저장하고, 조회 시 만료 여부를 확인하여 자동으로 삭제합니다.
5. Redis_기본_캐싱
개요
Redis를 사용한 분산 캐싱으로 여러 서버 간 캐시를 공유합니다.
코드 예제
const redis = require('redis');
const client = redis.createClient();
async function getCachedData(key) {
const cached = await client.get(key);
if (cached) return JSON.parse(cached);
const data = await fetchFromDB(key);
await client.setEx(key, 3600, JSON.stringify(data));
return data;
}
async function fetchFromDB(key) {
return { id: key, name: 'example' };
}
설명
Redis에서 데이터를 조회하고, 없으면 DB에서 가져온 후 1시간 TTL로 캐싱합니다.
6. Cache_Aside_패턴
개요
애플리케이션이 캐시를 직접 관리하는 가장 일반적인 캐싱 전략입니다.
코드 예제
async function getData(userId) {
// 1. 캐시 확인
let user = await cache.get(`user:${userId}`);
if (user) return user;
// 2. DB에서 조회
user = await db.users.findById(userId);
// 3. 캐시에 저장
await cache.set(`user:${userId}`, user, 3600);
return user;
}
설명
캐시 미스 시 DB에서 조회 후 캐시에 저장합니다. 가장 유연하지만 캐시와 DB 동기화를 신경 써야 합니다.
7. Write_Through_캐싱
개요
데이터 쓰기 시 캐시와 DB에 동시에 저장하여 항상 동기화 상태를 유지합니다.
코드 예제
async function updateUser(userId, userData) {
// 1. DB 업데이트
const updatedUser = await db.users.update(
userId,
userData
);
// 2. 캐시 업데이트
await cache.set(
`user:${userId}`,
updatedUser,
3600
);
return updatedUser;
}
설명
쓰기 작업 시 캐시와 DB를 동시에 업데이트하여 데이터 일관성을 보장합니다.
8. 캐시_무효화_전략
개요
데이터 변경 시 관련된 캐시를 삭제하여 오래된 데이터 제공을 방지합니다.
코드 예제
async function deleteUser(userId) {
// 1. DB에서 삭제
await db.users.delete(userId);
// 2. 관련 캐시 무효화
await cache.del(`user:${userId}`);
await cache.del(`user:${userId}:posts`);
await cache.del(`user:${userId}:profile`);
return { success: true };
}
설명
데이터 삭제 또는 수정 시 관련된 모든 캐시 키를 명시적으로 삭제하여 정합성을 유지합니다.
9. 다중_레벨_캐싱
개요
메모리 캐시와 Redis를 조합하여 2단계 캐싱 전략을 구현합니다.
코드 예제
const localCache = new Map();
async function getWithMultiLevel(key) {
// L1: 로컬 메모리 캐시
if (localCache.has(key)) return localCache.get(key);
// L2: Redis 캐시
const redisData = await redis.get(key);
if (redisData) {
localCache.set(key, redisData);
return redisData;
}
// L3: DB
const data = await db.get(key);
await redis.setEx(key, 3600, data);
localCache.set(key, data);
return data;
}
설명
로컬 메모리(빠름) → Redis(중간) → DB(느림) 순서로 조회하여 최적의 성능을 제공합니다.
10. Cache_Stampede_방지
개요
동시에 많은 요청이 캐시 미스를 일으킬 때 DB 과부하를 방지하는 패턴입니다.
코드 예제
const loading = new Map();
async function getDataSafe(key) {
const cached = await cache.get(key);
if (cached) return cached;
if (loading.has(key)) {
return await loading.get(key);
}
const promise = fetchAndCache(key);
loading.set(key, promise);
const result = await promise;
loading.delete(key);
return result;
}
async function fetchAndCache(key) {
const data = await db.get(key);
await cache.set(key, data, 3600);
return data;
}
설명
동일한 키에 대한 중복 요청을 Promise로 그룹화하여 DB에는 한 번만 요청합니다.
11. 조건부_캐싱
개요
특정 조건을 만족하는 데이터만 선택적으로 캐싱하여 메모리를 효율적으로 사용합니다.
코드 예제
async function smartCache(userId) {
const user = await db.users.findById(userId);
// 활성 사용자만 캐싱
if (user.isActive && user.lastLogin > Date.now() - 86400000) {
await cache.set(
`user:${userId}`,
user,
7200
);
}
return user;
}
설명
자주 조회되는 활성 사용자만 캐싱하여 캐시 메모리를 효율적으로 활용합니다.
12. Pub_Sub_캐시_동기화
개요
Redis Pub/Sub을 사용하여 여러 서버 간 캐시 무효화 이벤트를 동기화합니다.
코드 예제
const subscriber = redis.createClient();
const publisher = redis.createClient();
subscriber.subscribe('cache:invalidate');
subscriber.on('message', (channel, key) => {
localCache.delete(key);
});
async function invalidateCache(key) {
await cache.del(key);
await publisher.publish('cache:invalidate', key);
localCache.delete(key);
}
설명
한 서버에서 캐시를 무효화하면 모든 서버의 로컬 캐시도 함께 삭제되어 일관성을 유지합니다.
마치며
이번 글에서는 Caching 기초부터 심화까지 완벽 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#JavaScript #Caching #Redis #Memoization #Performance