/** * Redis 클라이언트 (로그인 시도 제한, 토큰 블랙리스트) * * Redis 미연결 시 메모리 폴백으로 동작 */ const { createClient } = require('redis'); const REDIS_HOST = process.env.REDIS_HOST || 'redis'; const REDIS_PORT = process.env.REDIS_PORT || 6379; let client = null; let connected = false; // 메모리 폴백 (Redis 미연결 시) const memoryStore = new Map(); async function initRedis() { try { client = createClient({ url: `redis://${REDIS_HOST}:${REDIS_PORT}` }); client.on('error', () => { connected = false; }); client.on('connect', () => { connected = true; }); await client.connect(); console.log('Redis 연결 성공'); } catch { console.warn('Redis 연결 실패 - 메모리 폴백 사용'); connected = false; } } async function get(key) { if (connected) { return await client.get(key); } const entry = memoryStore.get(key); if (!entry) return null; if (entry.expiry && entry.expiry < Date.now()) { memoryStore.delete(key); return null; } return entry.value; } async function set(key, value, ttlSeconds) { if (connected) { await client.set(key, value, { EX: ttlSeconds }); } else { memoryStore.set(key, { value, expiry: ttlSeconds ? Date.now() + ttlSeconds * 1000 : null, }); } } async function del(key) { if (connected) { await client.del(key); } else { memoryStore.delete(key); } } async function incr(key) { if (connected) { return await client.incr(key); } const entry = memoryStore.get(key); const current = entry ? parseInt(entry.value) || 0 : 0; const next = current + 1; memoryStore.set(key, { value: String(next), expiry: entry?.expiry || null }); return next; } async function expire(key, ttlSeconds) { if (connected) { await client.expire(key, ttlSeconds); } else { const entry = memoryStore.get(key); if (entry) { entry.expiry = Date.now() + ttlSeconds * 1000; } } } module.exports = { initRedis, get, set, del, incr, expire };