Skip to content

[25.01.16 / TASK-271] Hotfix - SVG Badge API 통계값 오류 해결 및 캐싱 적용#50

Merged
Jihyun3478 merged 7 commits intomainfrom
hotfix/total-stats-svg
Jan 18, 2026
Merged

[25.01.16 / TASK-271] Hotfix - SVG Badge API 통계값 오류 해결 및 캐싱 적용#50
Jihyun3478 merged 7 commits intomainfrom
hotfix/total-stats-svg

Conversation

@Jihyun3478
Copy link
Copy Markdown
Contributor

@Jihyun3478 Jihyun3478 commented Jan 16, 2026

🔥 변경 사항

  • 누적 조회수 중복 계산 오류 및 타 사용자 게시글이 섞여 조회되는 문제를 해결했습니다.
  • 서비스 계층에 레디스 캐싱 적용 및 IN 서브쿼리 → INNER JOIN으로 변경했습니다.

🏷 관련 이슈

  • 관련 이슈: #47

📸 스크린샷 (UI 변경 시 필수)

(변경 사항이 UI와 관련된 경우 스크린샷을 첨부해주세요.)

📌 체크리스트

  • 기능이 정상적으로 동작하는지 테스트 완료
  • 코드 스타일 가이드 준수 여부 확인
  • 관련 문서 업데이트 완료 (필요 시)

Summary by CodeRabbit

  • 성능

    • 배지 데이터 조회 결과를 10분간 캐시해 반복 요청 시 DB 부하 감소 및 응답 속도 개선
  • Refactor

    • 사용자 배지 통계 및 최근 게시글 조회 쿼리를 재구성해 복잡한 서브쿼리·시간 분기 제거 및 집계 성능과 일관성 개선

✏️ Tip: You can customize this high-level summary in your review settings.

배지 API 데이터 정합성 오류 수정

- 다른 사용자 게시글이 섞여 조회되는 문제 해결
- 누적 조회수 중복 계산 오류 수정
배지 API 캐싱 및 쿼리 최적화

- Cache-Aside 패턴 적용 (TTL 10분)
- IN 서브쿼리 → INNER JOIN 최적화
@Jihyun3478 Jihyun3478 added bug Something isn't working enhancement New feature or request labels Jan 16, 2026
@notion-workspace
Copy link
Copy Markdown

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 16, 2026

Walkthrough

저장소의 두 쿼리(getUserBadgeStats, getUserRecentPosts)를 다중 CTE 빌더로 재작성해 시간 파라미터를 pastDateKST로 통일했고, 서비스 레이어(getBadgeData)에 Redis 기반 캐시(BADGE_CACHE_TTL)를 추가해 캐시 조회·저장 흐름을 도입했습니다.

Changes

Cohort / File(s) Summary
저장소: 다중 CTE SQL 리라이트
src/repositories/totalStats.repository.ts
인라인 SQL 제거 후 user_posts, latest_stats, start_stats CTE와 빌더 함수(buildUserPostsCTE, buildLatestStatsCTE, buildStartStatsCTE, buildBadgeStatsQuery, buildRecentPostsQuery)로 쿼리 조립. nowDateKST 분기 제거, 쿼리는 pastDateKST 중심 파라미터 사용.
서비스: Redis 캐시 추가
src/services/totalStats.service.ts
export const BADGE_CACHE_TTL = 60 * 10 추가. getBadgeData에 캐시 키 생성, Redis GET/SET 로직 및 에러 로그 처리 도입; 캐시 히트 시 즉시 반환하도록 흐름 변경.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Service as totalStats.service
    participant Redis
    participant Repo as totalStats.repository
    participant DB

    Client->>Service: getBadgeData(username, type, limit?, pastDateKST)
    Service->>Redis: GET badge:{username}:{type}:{pastDateKST}
    alt cache hit
        Redis-->>Service: cached BadgeData
        Service-->>Client: return cached BadgeData
    else cache miss
        Redis-->>Service: nil
        Service->>Repo: getUserBadgeStats(username, pastDateKST)
        Service->>Repo: getUserRecentPosts(username, limit, pastDateKST)
        Repo->>DB: execute multi-CTE SQL queries
        DB-->>Repo: query results
        Repo-->>Service: aggregated stats + recent posts
        Service->>Redis: SET badge:{username}:{type}:{pastDateKST} = BadgeData EX BADGE_CACHE_TTL
        Redis-->>Service: OK
        Service-->>Client: return BadgeData
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • six-standard
  • ooheunda
  • Nuung

🐰 CTE로 줄줄이 엮어둔 길,
Redis 상자에 살짝 숨긴 빛,
캐시 톡 하면 답이 오고,
당겨봐, 검토는 폴짝폴짝! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경 사항을 명확하게 요약하고 있으며, SVG Badge API 통계값 오류 해결과 캐싱 적용이라는 핵심 내용을 정확히 반영하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hotfix/total-stats-svg


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c290171 and 44a9998.

📒 Files selected for processing (1)
  • src/services/totalStats.service.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/services/totalStats.service.ts (2)
src/configs/cache.config.ts (1)
  • cache (17-17)
src/types/dto/responses/totalStatsResponse.type.ts (1)
  • BadgeData (162-165)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build-and-test (20)
  • GitHub Check: build-and-test (21)
  • GitHub Check: build-and-test (23)
  • GitHub Check: build-and-test (22)
🔇 Additional comments (3)
src/services/totalStats.service.ts (3)

15-15: 상수 분리와 주석 처리 좋습니다.
캐시 TTL 의도가 명확해져 유지보수성이 올라갑니다.


51-63: 캐시 HIT/MISS 분기와 예외 처리 흐름이 명확합니다.
Redis 읽기 실패 시에도 정상 경로로 이어지는 점이 안전합니다.


75-100: 캐시 저장 실패를 서비스 응답과 분리한 처리 좋습니다.
저장 실패 시에도 결과를 반환해 장애 전파를 줄입니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/repositories/totalStats.repository.ts (1)

5-5: 파이프라인 실패: 사용되지 않는 import 제거 필요

getCurrentKSTDateString이 import되었지만 사용되지 않아 ESLint 에러가 발생합니다.

🔧 수정 제안
-import { getCurrentKSTDateString, getKSTDateStringWithOffset } from '@/utils/date.util';
+import { getKSTDateStringWithOffset } from '@/utils/date.util';
🧹 Nitpick comments (2)
src/services/totalStats.service.ts (2)

55-59: 캐시 실패 시 graceful degradation 고려 권장

현재 구현에서 Redis 장애 발생 시 전체 요청이 실패합니다. 배지 API의 가용성을 높이려면 캐시 실패 시에도 DB에서 데이터를 조회하도록 처리하는 것이 좋습니다.

♻️ 캐시 에러 핸들링 제안
-      const cached = await cache.get<BadgeData>(cacheKey);
-      if (cached) {
-        logger.info(`[Cache HIT] ${cacheKey}`)
-        return cached;
-      }
+      try {
+        const cached = await cache.get<BadgeData>(cacheKey);
+        if (cached) {
+          logger.info(`[Cache HIT] ${cacheKey}`)
+          return cached;
+        }
+      } catch (cacheError) {
+        logger.warn(`[Cache GET Error] ${cacheKey}:`, cacheError);
+        // Redis 장애 시 DB에서 직접 조회 진행
+      }

91-93: 캐시 저장 실패 시에도 결과 반환 권장

캐시 저장 실패가 전체 요청 실패로 이어지지 않도록 처리하면 안정성이 향상됩니다.

♻️ 캐시 저장 에러 핸들링 제안
-      await cache.set(cacheKey, result, BADGE_CACHE_TTL);
-      
-      return result;
+      try {
+        await cache.set(cacheKey, result, BADGE_CACHE_TTL);
+      } catch (cacheError) {
+        logger.warn(`[Cache SET Error] ${cacheKey}:`, cacheError);
+      }
+
+      return result;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 288e304 and 31a6024.

📒 Files selected for processing (2)
  • src/repositories/totalStats.repository.ts
  • src/services/totalStats.service.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-12-05T17:21:07.909Z
Learnt from: HA0N1
Repo: Check-Data-Out/velog-dashboard-v2-api PR: 6
File: src/reposities/user.repository.ts:7-15
Timestamp: 2024-12-05T17:21:07.909Z
Learning: Function `findByUserEmail` in `src/reposities/user.repository.ts` is scheduled to be deleted and should be ignored in future code reviews.

Applied to files:

  • src/repositories/totalStats.repository.ts
🧬 Code graph analysis (1)
src/services/totalStats.service.ts (3)
src/configs/cache.config.ts (1)
  • cache (17-17)
src/types/dto/responses/totalStatsResponse.type.ts (1)
  • BadgeData (162-165)
src/types/index.ts (1)
  • BadgeData (41-41)
🪛 GitHub Actions: Test CI
src/repositories/totalStats.repository.ts

[error] 5-5: ESLint: 'getCurrentKSTDateString' is defined but never used. (no-unused-vars)

🔇 Additional comments (4)
src/services/totalStats.service.ts (2)

15-16: LGTM! 캐시 TTL 설정이 적절합니다.

10분(600초)의 TTL은 배지 데이터의 특성상 적절한 값입니다. Export하여 테스트에서도 활용할 수 있도록 한 점이 좋습니다.


72-89: LGTM! 결과 구조가 명확합니다.

BadgeData 인터페이스에 맞게 결과를 구성하고, safeNumber 유틸리티로 안전하게 숫자 변환을 처리하고 있습니다.

src/repositories/totalStats.repository.ts (2)

115-152: LGTM! CTE 기반 쿼리가 PR 목표를 잘 달성합니다.

  • user_posts CTE에서 JOIN users_user를 통해 특정 사용자의 게시글만 필터링하여 다른 사용자의 게시글 혼입 문제를 해결합니다.
  • DISTINCT ON (post_id) ORDER BY date DESC 패턴으로 각 게시글의 최신 통계만 정확히 가져옵니다.
  • COALESCE를 적절히 사용하여 NULL 값을 안전하게 처리합니다.

167-206: LGTM! 쿼리 최적화가 잘 적용되었습니다.

  • user_posts CTE에서 LIMIT을 먼저 적용하여 불필요한 JOIN을 줄였습니다.
  • INNER JOIN을 통해 해당 사용자의 게시글만 조회되도록 보장합니다.
  • Line 199의 COALESCE(ls.total_view - ss.start_view, ls.total_view, 0) 패턴이 start_view가 NULL인 경우를 올바르게 처리합니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Copy Markdown
Member

@six-standard six-standard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제 리뷰가 너무 늦어졌네요;;
코드 잘 읽었고, 큰 문제는 없는 것 같습니다..!

좋았던 점

  • 쿼리 코드를 하단으로 분리하면서 비교적 코드를 읽기 편리했습니다
  • 뱃지 캐시 TTL값 (매직넘버) 상수로 빼주신 점도 좋습니다!

Copy link
Copy Markdown
Member

@ooheunda ooheunda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드 잘 봤습니다! 현우님이 먼저 꼼꼼히 남겨주셨고 수정도 잘 된 것 같아서 더 드릴 말씀은 없는 것 같네요! 고생하셨습니다~! 🔥👍

좋았던 점

  • 복잡한 쿼리를 빌더 패턴으로 변경하시면서, 매개변수로 bool 값들을 받는 등 재사용에 대해 많이 고민하신 것 같아 좋았습니다!

Copy link
Copy Markdown
Member

@Nuung Nuung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋았던 점

  • 전체적으로 빌더 패턴과 외부 인프라 독립적인 상황까지 업데이트 되어서 너무 깔끔해지고 좋은 것 같아요! 고생많으셨습니다!!

아쉬운 점

  • 최종적은 PR 리뷰잉이라 여기까지 오면서 저희가 나눴던 코멘트들이 아쉬웠었지만 다 해결된 것 같아요!!

@Jihyun3478 Jihyun3478 merged commit d11bd42 into main Jan 18, 2026
5 checks passed
@Jihyun3478 Jihyun3478 deleted the hotfix/total-stats-svg branch January 18, 2026 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants