Skip to content

6 의사결정_Redis 도입 이유

Jina Ham edited this page Feb 18, 2026 · 1 revision

Redis 사용 이유 & 기능별 사용 전략 (코드 기반 정리)

0) Redis를 쓰는 공통 이유 (왜 DB가 아니라 Redis인가)

  • 초고빈도/짧은 TTL 데이터를 DB에 쌓으면 I/O + 인덱스 부하가 커짐 → Redis가 적합
  • 원자적 증가/감소(incr/decr), 키 만료(TTL), 정렬 컬렉션(ZSET) 같은 기능이 바로 제공됨
  • “조회/검증”을 DB로 보내기 전에 캐시/게이트 역할로 사용 → DB 트래픽 절감 + 응답 지연 감소
  • 만료 가능한 데이터(오늘/7일/30일/최근검색어) 특성상 자동 정리가 핵심 → TTL로 운영 단순화

1) 오늘 AI 호출 횟수 카운트 (AiCallCountService)

1-1. 목적

  • 사용자별/취미별 일일 AI 추천 호출 제한(DAILY_LIMIT=3) 을 DB 없이 빠르게 강제

1-2. 키 설계

  • Key: ai:activity:recommend:{userSocialId}:{hobbyId}:{yyyyMMdd}
  • 날짜를 키에 포함 → 매일 새 카운터로 자연스럽게 분리

1-3. 데이터 구조/연산

  • 구조: String(Value=Int) (Redis String)
  • 연산: INCR (opsForValue().increment)
    • Redis INCR는 원자적이라 동시 호출에도 카운트가 꼬이지 않음

1-4. TTL 전략

  • 최초 생성(count==1) 시에만 TTL 설정
  • TTL: secondsUntilMidnight()자정 기준 자동 초기화
  • 장점: “하루 제한” 정책을 코드/배치 없이 TTL로 해결

1-5. 실패/보정 전략

  • 초과(count > limit) 시 예외 발생으로 즉시 차단
  • 실패한 요청(예: FastAPI 호출 실패)에서 보정 필요 시 decrease()로 되돌림
    • 단, 감소는 보정용이므로 0 미만 방지 가드가 있음

2) 오늘 활동 기록 여부 체크 (TodayRecordRedisService)

2-1. 목적

  • “오늘 이 취미로 기록을 남겼는지”를 빠르게 판단해서
    • 홈 대시보드/뱃지/알림/미션 같은 UI를 즉시 구성
    • DB에서 매번 exists(select 1...) 치지 않도록 최적화

2-2. 키 설계

  • Key: record:{yyyy-MM-dd}:{userId}:{hobbyId}
  • 값은 의미 없으므로 "true" 같은 더미

2-3. 데이터 구조/연산

  • 구조: String(Boolean 의미만)
  • 연산:
    • 기록 생성 시: SET key "true" EX secondsUntilMidnight
    • 조회 시: EXISTS key
    • 삭제(기록 삭제/롤백) 시: DEL key

2-4. TTL 전략

  • TTL: secondsUntilMidnight() → 하루 단위 플래그 자동 소멸
  • 장점: “오늘 여부”는 하루가 지나면 의미가 사라지므로 TTL이 정답

3) 사용자 요약 (UserSummaryAIService)

3-1. 목적

  • FastAPI로 생성한 요약문을 7일간 재사용
  • 동일 요청이 반복될 때 매번 AI 호출/비용/지연이 발생하는 걸 방지

3-2. 키 설계

  • Key: ai:user:summary:text:{userSocialId}:{hobbyId}
  • 날짜를 키에 포함하지 않음 → 7일 동안 동일 키로 캐시 히트 유도

3-3. 데이터 구조/연산

  • 구조: String(Text) (Redis String)
  • 연산:
    • 존재 여부: EXISTS
    • 저장: SETEX (TTL=7 days)
    • 조회: GET

3-4. TTL 전략

  • TTL: 7일 고정
  • 장점:
    • “요약”은 매일 바뀌는 데이터가 아니고, 적당한 기간 단위로 갱신하면 충분
    • TTL로 자동 갱신 주기를 강제하여 운영이 단순

3-5. 실패 전략

  • FastAPI 실패 시 빈 문자열 반환(기본값)
  • 장애 시에도 서비스 전체가 막히지 않게 “캐시/AI는 부가 기능”로 취급

4) refreshToken 저장 (RefreshToken @RedisHash)

4-1. 목적

  • refreshToken은 보안/폐기/갱신이 빈번하고 TTL이 명확함
  • DB 저장 시:
    • 테이블 증가 + 정리 배치 필요 + 로그인/재발급 트래픽 증가
  • Redis에 두면:
    • TTL로 자동 만료
    • 토큰 교체/폐기가 빠름

4-2. 키 설계

  • @RedisHash(value="refreshToken") + @Id(username=socialId)
  • 결과적으로 “사용자별 refreshToken 1개” 모델에 적합
    • 재발급 시 update()로 덮어쓰기

4-3. TTL 전략

  • timeToLive = 7일
  • 장점: 만료 정책을 Redis가 자동 수행 → 별도 정리 로직 불필요

5) 최근 검색어 (RecentRedisService, ZSET)

5-1. 목적

  • 사용자별 최근 검색어 “최신순” 유지 + 중복 제거 + 최대 N개 유지
  • DB로 만들면:
    • INSERT/DELETE 빈번 + 정렬/페이징 비용 + 청소 배치 필요
  • Redis ZSET이 딱 맞음:
    • score로 시간 정렬
    • 멤버 중복 시 score 갱신으로 “자동 최신화”

5-2. 키 설계

  • Key: recent:{userId}

5-3. 데이터 구조/연산

  • 구조: ZSET(member=keyword, score=timestamp)
  • 연산:
    • 저장/최신화: ZADD key keyword score(now)
      • 동일 keyword면 score만 갱신 → “중복 제거 + 최신화” 동시 달성
    • 조회(최신 5개): ZREVRANGE WITHSCORES 0..4
    • 용량 제한(20개 유지): size 초과분 ZREMRANGEBYRANK
    • 만료: key TTL 30일

5-4. TTL & 크기 제한 전략

  • TTL: 30일 → 사용 안 하는 유저의 데이터 자동 정리
  • MAX_SIZE: 20 → 메모리 사용 예측 가능
  • VIEW_SIZE: 5 → 응답 payload 최소화

6) 최근 30일 반응(리액션) 랭킹 (RedisReactionService + ReactionRankingBatchService)

6-1. 목적

  • “핫한 기록(Hot Feed)”을 DB에서 매번 집계하면 비용이 큼
    • reaction 테이블은 증가 속도가 빠르고, 집계는 join/group by가 무거움
  • Redis ZSET으로 점수(반응 수)를 유지하면:
    • 쓰기(리액션 발생) 시 O(logN) 업데이트
    • 읽기(랭킹 조회) 시 상위 N개를 빠르게 제공

6-2. 키 설계

  • Key: reaction:ranking:30days
  • member: recordId
  • score: “최근 30일 반응 점수”

6-3. 실시간 업데이트 전략 (RedisReactionService)

  • 리액션 추가: ZINCRBY +1
  • 리액션 취소: 점수 조회 후 ZINCRBY -1 (0 미만 방지)
  • 핫피드 조회:
    • ZREVRANGEBYSCORE로 score 기준 상위 추출
    • 동점(score 동일) 정렬/커서 정밀화는 DB에서 처리하도록 설계(혼합 전략)

6-4. 30일 윈도우 유지 전략 (ReactionRankingBatchService)

  • 매일 00:05 배치:
    • 31일 전 하루치 반응 집계값을 DB에서 가져옴
    • 해당 recordId에 대해 Redis score를 -count로 차감
  • score <= 0 인 항목 제거: removeRangeByScore(-inf, 0)
  • 결과: Redis에는 항상 “최근 30일” 점수만 남도록 유지

6-5. 이 구조의 장점 (핵심)

  • 실시간(증가/감소) + 배치(만료분 차감)를 조합해
    • “정확한 30일 슬라이딩 윈도우”를 비교적 단순하게 구현
  • 읽기 트래픽이 커질수록 DB를 보호하는 효과가 커짐

7) 기능별 요약: 자료구조/TTL/키 패턴 한눈에

  • 일일 제한 카운터: String(Int) + 자정 TTL + 날짜 포함 키
  • 오늘 여부 플래그: String(Boolean) + 자정 TTL + 날짜 포함 키
  • AI 요약 캐시: String(Text) + 7일 TTL + 날짜 미포함 키
  • refreshToken: RedisHash + 7일 TTL + 사용자 1:1 키
  • 최근 검색어: ZSET(keyword, timestamp) + 30일 TTL + size cap(20)
  • 30일 리액션 랭킹: ZSET(recordId, score) + (실시간 incr + 배치 차감) + score<=0 제거

Clone this wiki locally