""" 캐시 서비스 Redis 기반 캐싱 로직 관리하는 서비스 레이어 """ import json from typing import Optional, Dict, Any from extensions import redis_cli import settings from extensions import custom_logger logger = custom_logger(f"{settings.LOG_PREFIX}_auth_service") class CacheService: @staticmethod def _is_redis_available() -> bool: """Redis 연결 상태 확인""" return redis_cli.is_connected() @staticmethod def set(key: str, value: Any, ttl: int) -> bool: """Redis에 데이터 저장""" if not CacheService._is_redis_available(): logger.warning("레디스가 연결되어 있지 않습니다. 등록 로직을 ㄴ생략합니다.") return False try: if isinstance(value, dict): value = json.dumps(value, ensure_ascii=False) redis_cli.redis_client.setex(key, ttl, value) logger.debug(f"캐시 저장: {key} (TTL: {ttl}s)") return True except Exception as e: logger.error(f"캐시 저장 실패: {key} - {str(e)}") return False @staticmethod def get(key: str, parse_json: bool = True) -> Optional[Any]: """Redis에서 데이터 조회""" if not CacheService._is_redis_available(): logger.warning("레디스가 연결되어 있지 않습니다. 조회 로직을 생략합니다.") return None try: value = redis_cli.redis_client.get(key) if value: logger.debug(f"Cache hit: {key}") if parse_json: return json.loads(value) return value logger.debug(f"Cache miss: {key}") return None except Exception as e: logger.error(f"Failed to get cache {key}: {str(e)}") return None @staticmethod def delete(key: str) -> bool: """Redis에서 데이터 삭제 Args: key: Redis 키 Returns: bool: 성공 여부 """ if not CacheService._is_redis_available(): logger.warning("레디스가 연결되어 있지 않습니다. 삭제가 불가능 합니다.") return False try: redis_cli.redis_client.delete(key) logger.debug(f"Cache deleted: {key}") return True except Exception as e: logger.error(f"Failed to delete cache {key}: {str(e)}") return False @staticmethod def exists(key: str) -> bool: """Redis에 키가 존재하는지 확인""" if not CacheService._is_redis_available(): logger.warning("레디스가 연결되어 있지 않습니다. 조회가 불가능 합니다.") return False try: return redis_cli.redis_client.exists(key) > 0 except Exception as e: logger.error(f"Failed to check cache existence {key}: {str(e)}") return False class UserCacheService: """사용자 캐시 관리 서비스""" PREFIX = "user:cache:" TTL = settings.REDIS_USER_CACHE_TTL @classmethod def _get_key(cls, user_uuid: str) -> str: """캐시 키 생성""" return f"{cls.PREFIX}{user_uuid}" @classmethod def set(cls, user_uuid: str, user_data: Dict[str, Any], ttl: Optional[int] = None) -> bool: """사용자 정보를 Redis에 캐싱""" key = cls._get_key(user_uuid) ttl = ttl or cls.TTL return CacheService.set(key, user_data, ttl) @classmethod def get(cls, user_uuid: str) -> Optional[Dict[str, Any]]: """Redis에서 사용자 정보 조회""" key = cls._get_key(user_uuid) return CacheService.get(key, parse_json=True) @classmethod def delete(cls, user_uuid: str) -> bool: """캐시에서 사용자 정보 삭제""" key = cls._get_key(user_uuid) return CacheService.delete(key) @classmethod def refresh(cls, user_uuid: str, user_data: Dict[str, Any]) -> bool: """캐시 갱신 (삭제 후 재설정)""" cls.delete(user_uuid) return cls.set(user_uuid, user_data) class JWTBlacklistService: """JWT 블랙리스트 관리 서비스 (로그아웃된 토큰 추적)""" PREFIX = "jwt:blacklist:" TTL = settings.REDIS_JWT_BLACKLIST_TTL @classmethod def _get_key(cls, jti: str) -> str: """블랙리스트 키 생성""" return f"{cls.PREFIX}{jti}" @classmethod def add(cls, jti: str, ttl: Optional[int] = None) -> bool: """블랙리스트에 토큰 추가""" key = cls._get_key(jti) ttl = ttl or cls.TTL if not CacheService._is_redis_available(): logger.warning("레디스가 연결되어 있지 않습니다. 블랙리스트에 추가가 블가능합니다.") return False success = CacheService.set(key, "1", ttl) if success: logger.info(f"JWT 블랙리스트 추가완료: {jti}") return success @classmethod def is_blacklisted(cls, jti: str) -> bool: """토큰이 블랙리스트에 있는지 확인""" key = cls._get_key(jti) return CacheService.exists(key)