""" Redis 캐싱 유틸리티 Dashboard API 성능 최적화를 위한 Redis 캐싱 기능을 제공합니다. YouTube Analytics API 호출 결과를 캐싱하여 중복 요청을 방지합니다. """ from typing import Optional from redis.asyncio import Redis from app.utils.logger import get_logger from config import db_settings logger = get_logger("redis_cache") # Dashboard 전용 Redis 클라이언트 (db=3 사용) _cache_client = Redis( host=db_settings.REDIS_HOST, port=db_settings.REDIS_PORT, db=3, decode_responses=True, ) async def get_cache(key: str) -> Optional[str]: """ Redis 캐시에서 값을 조회합니다. Args: key: 캐시 키 Returns: 캐시된 값 (문자열) 또는 None (캐시 미스) Example: >>> cached_data = await get_cache("dashboard:user123:2026-01-01:2026-12-31") >>> if cached_data: >>> return json.loads(cached_data) """ try: logger.debug(f"[GET_CACHE] 캐시 조회 시작 - key: {key}") value = await _cache_client.get(key) if value: logger.debug(f"[GET_CACHE] 캐시 HIT - key: {key}") else: logger.debug(f"[GET_CACHE] 캐시 MISS - key: {key}") return value except Exception as e: logger.error(f"[GET_CACHE] 캐시 조회 실패 - key: {key}, error: {e}") return None # 캐시 실패 시 None 반환 (원본 데이터 조회하도록 유도) async def set_cache(key: str, value: str, ttl: int = 43200) -> bool: """ Redis 캐시에 값을 저장합니다. Args: key: 캐시 키 value: 저장할 값 (문자열) ttl: 캐시 만료 시간 (초). 기본값: 43200초 (12시간) Returns: 성공 여부 Example: >>> import json >>> data = {"views": 1000, "likes": 50} >>> await set_cache("dashboard:user123:2026-01-01:2026-12-31", json.dumps(data), ttl=3600) """ try: logger.debug(f"[SET_CACHE] 캐시 저장 시작 - key: {key}, ttl: {ttl}s") await _cache_client.setex(key, ttl, value) logger.debug(f"[SET_CACHE] 캐시 저장 성공 - key: {key}") return True except Exception as e: logger.error(f"[SET_CACHE] 캐시 저장 실패 - key: {key}, error: {e}") return False async def delete_cache(key: str) -> bool: """ Redis 캐시에서 값을 삭제합니다. Args: key: 삭제할 캐시 키 Returns: 성공 여부 Example: >>> await delete_cache("dashboard:user123:2026-01-01:2026-12-31") """ try: logger.debug(f"[DELETE_CACHE] 캐시 삭제 시작 - key: {key}") deleted_count = await _cache_client.delete(key) logger.debug( f"[DELETE_CACHE] 캐시 삭제 완료 - key: {key}, deleted: {deleted_count}" ) return deleted_count > 0 except Exception as e: logger.error(f"[DELETE_CACHE] 캐시 삭제 실패 - key: {key}, error: {e}") return False async def delete_cache_pattern(pattern: str) -> int: """ 패턴에 매칭되는 모든 캐시 키를 삭제합니다. Args: pattern: 삭제할 키 패턴 (예: "dashboard:user123:*") Returns: 삭제된 키 개수 Example: >>> # 특정 사용자의 모든 대시보드 캐시 삭제 >>> deleted = await delete_cache_pattern("dashboard:user123:*") >>> print(f"{deleted}개의 캐시 삭제됨") Note: 대량의 키 삭제 시 성능에 영향을 줄 수 있으므로 주의해서 사용하세요. """ try: logger.debug(f"[DELETE_CACHE_PATTERN] 패턴 캐시 삭제 시작 - pattern: {pattern}") # 패턴에 매칭되는 모든 키 조회 keys = [] async for key in _cache_client.scan_iter(match=pattern): keys.append(key) if not keys: logger.debug(f"[DELETE_CACHE_PATTERN] 삭제할 키 없음 - pattern: {pattern}") return 0 # 모든 키 삭제 deleted_count = await _cache_client.delete(*keys) logger.debug( f"[DELETE_CACHE_PATTERN] 패턴 캐시 삭제 완료 - " f"pattern: {pattern}, deleted: {deleted_count}" ) return deleted_count except Exception as e: logger.error( f"[DELETE_CACHE_PATTERN] 패턴 캐시 삭제 실패 - pattern: {pattern}, error: {e}" ) return 0 async def close_cache_client(): """ Redis 클라이언트 연결을 종료합니다. 애플리케이션 종료 시 호출되어야 합니다. main.py의 shutdown 이벤트 핸들러에서 사용하세요. Example: >>> # main.py >>> @app.on_event("shutdown") >>> async def shutdown_event(): >>> await close_cache_client() """ try: logger.info("[CLOSE_CACHE_CLIENT] Redis 캐시 클라이언트 종료 중...") await _cache_client.close() logger.info("[CLOSE_CACHE_CLIENT] Redis 캐시 클라이언트 종료 완료") except Exception as e: logger.error( f"[CLOSE_CACHE_CLIENT] Redis 캐시 클라이언트 종료 실패 - error: {e}" )