Skip to content

[feature] 동아리 클릭 게임 API 추가#1470

Merged
seongwon030 merged 2 commits intodevelop/befrom
feature/mouse-game
Apr 16, 2026
Merged

[feature] 동아리 클릭 게임 API 추가#1470
seongwon030 merged 2 commits intodevelop/befrom
feature/mouse-game

Conversation

@seongwon030
Copy link
Copy Markdown
Member

@seongwon030 seongwon030 commented Apr 16, 2026

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호

📝작업 내용

Summary

  • POST /api/game/click — clubName별 클릭 수를 Redis INCR으로 원자적 누적
  • GET /api/game/ranking — 클릭 수 기준 내림차순 랭킹 반환 (rank 필드, resetAt 포함)
  • 매일 자정(KST) ShedLock 기반 분산 스케줄러로 클릭 수 자동 초기화

작업사항

  • 실제 동아리 DB와 무관한 자유 텍스트 입력 (검증 없음)
  • 인증 불필요 (public API)
  • Redis key 패턴: club:click:{clubName}
  • StringRedisTemplate 사용으로 INCR 직렬화 충돌 방지
  • 자동완성: 입력 시 debounce(300ms) → GET /api/club/search/?keyword= 호출 → 매칭
    동아리명 드롭다운 표시
  • 유효성 검사: 시작 버튼 / Enter 시 정확한 이름 일치 여부 최종 검증, 없으면 에러 메시지 표시

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

새로운 기능

  • 클럽 클릭 추적: 사용자는 새로운 API 엔드포인트를 통해 클럽의 클릭을 기록하고 추적할 수 있습니다
  • 실시간 클럽 랭킹: 누적된 클릭 수를 기반으로 클럽의 실시간 순위를 확인할 수 있으며, 높은 순서부터 정렬됩니다
  • 자동 일일 초기화: 모든 클릭 카운터는 매일 자정(한국 표준시)에 자동으로 초기화되어 클럽 간 공정한 경쟁을 가능하게 합니다

Redis INCR을 활용한 원자적 클릭 수 누적, KST 자정 자동 초기화(ShedLock),
클릭 랭킹 조회 시 rank 필드 및 resetAt(다음 자정 ISO8601) 제공

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@seongwon030 seongwon030 self-assigned this Apr 16, 2026
@seongwon030 seongwon030 added the 💾 BE Backend label Apr 16, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview, Comment Apr 16, 2026 3:03pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

클럽 클릭 게임 기능을 구현하는 새로운 REST 컨트롤러, 서비스, 요청/응답 페이로드를 추가합니다. Redis에서 클릭 수를 추적하고, 순위를 계산하며, 매일 자정(KST)에 데이터를 초기화하는 스케줄된 작업을 포함합니다.

Changes

Cohort / File(s) Summary
Controller
backend/src/main/java/moadong/club/controller/ClubClickController.java
/api/game 경로 아래에 POST /click (클릭 기록) 및 GET /ranking (순위 조회) 엔드포인트를 포함하는 새로운 REST 컨트롤러 추가.
Payloads
backend/src/main/java/moadong/club/payload/request/ClubClickRequest.java, backend/src/main/java/moadong/club/payload/response/ClubClickResponse.java, backend/src/main/java/moadong/club/payload/response/ClubClickRankingResponse.java
클릭 요청 데이터 및 응답 데이터를 정의하는 불변 레코드 타입 추가. ClubClickRankingResponse는 순위 항목 리스트(clubs)와 다음 초기화 시간(resetAt)을 포함하고, 중첩된 ClubRankItem 레코드를 정의.
Service
backend/src/main/java/moadong/club/service/ClubClickService.java
Redis를 이용한 클릭 수 증가(recordClick), 순위 계산 및 조회(getRanking), 그리고 ShedLock을 통한 매일 자정(KST) 데이터 초기화(resetDailyClicks) 기능을 구현하는 새로운 서비스 클래스 추가.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as ClubClickController
    participant Service as ClubClickService
    participant Redis

    rect rgba(100, 150, 255, 0.5)
    Note over Client,Redis: 클릭 기록 흐름
    Client->>Controller: POST /api/game/click<br/>(clubName, ctAt)
    Controller->>Service: recordClick(clubName)
    Service->>Redis: INCR club:click:{clubName}
    Redis-->>Service: clickCount
    Service-->>Controller: ClubClickResponse
    Controller-->>Client: 200 OK
    end

    rect rgba(100, 255, 150, 0.5)
    Note over Client,Redis: 순위 조회 흐름
    Client->>Controller: GET /api/game/ranking
    Controller->>Service: getRanking()
    Service->>Redis: KEYS club:click:*
    Redis-->>Service: key list
    Service->>Redis: MGET (각 key에 대한 값)
    Redis-->>Service: counts
    Service->>Service: 정렬 및 순위 계산
    Service-->>Controller: ClubClickRankingResponse
    Controller-->>Client: 200 OK
    end

    rect rgba(255, 150, 100, 0.5)
    Note over Service,Redis: 매일 자정 초기화 (ShedLock)
    Service->>Service: resetDailyClicks() 자동 실행<br/>(매일 00:00 KST)
    Service->>Redis: DEL club:click:*
    Redis-->>Service: 삭제 완료
    Service->>Service: 로그 기록
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 요약하고 있습니다. 동아리 클릭 게임 API 추가는 이 PR의 핵심 변경사항이며, 제목은 간결하고 구체적입니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mouse-game

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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 16, 2026

Test Results

123 tests   123 ✅  17s ⏱️
 29 suites    0 💤
 29 files      0 ❌

Results for commit 93e9fdb.

♻️ This comment has been updated with latest results.

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: 3

🧹 Nitpick comments (1)
backend/src/main/java/moadong/club/payload/request/ClubClickRequest.java (1)

3-3: 사용되지 않는 ctAt 필드는 요청 계약에서 제거하는 편이 좋겠습니다.

현재 backend/src/main/java/moadong/club/controller/ClubClickController.java의 Line 29와 backend/src/main/java/moadong/club/service/ClubClickService.java의 Line 35-38에서는 clubName만 사용하고 있어서, ctAt는 무엇을 보내도 무시됩니다. 공개 API에 의미 없는 입력 필드를 남겨두면 클라이언트 혼란과 향후 호환성 부담만 늘어납니다. 시간이 필요하면 클라이언트 입력 대신 서버에서 기록하는 쪽이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/java/moadong/club/payload/request/ClubClickRequest.java` at
line 3, The ClubClickRequest record includes an unused ctAt field which should
be removed; update the request contract by deleting the ctAt component from the
ClubClickRequest declaration so it only exposes clubName, then update any
usages/constructors or deserialization expectations accordingly (inspect
ClubClickController and ClubClickService which currently only use clubName) and
ensure server-side timestamping/logging is performed where needed instead of
relying on a client-supplied ctAt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/main/java/moadong/club/controller/ClubClickController.java`:
- Around line 26-29: The controller currently passes unvalidated
ClubClickRequest.clubName() into Redis via clubClickService.recordClick,
allowing arbitrary/empty/oversized keys; add validation annotations (e.g.,
`@NotBlank` and `@Size`(max=...)) to the ClubClickRequest fields (the clubName
property) and change the controller method recordClick to accept a validated
request by adding `@Valid` to the parameter (and annotate the controller class
with `@Validated` if your setup requires it) so Spring will reject blank/too-long
inputs before calling clubClickService.recordClick.

In `@backend/src/main/java/moadong/club/service/ClubClickService.java`:
- Around line 41-42: getRanking and resetDailyClicks currently call
stringRedisTemplate.keys(CLICK_KEY_PATTERN) which will block Redis as the club
count grows; refactor to use a Sorted Set for click counts (use ZINCRBY when
incrementing clicks instead of per-key INCR) and implement getRanking to call
ZREVRANGE (with scores) to return top-N in constant time; for resetDailyClicks
replace KEYS+bulk DEL with a SCAN-based iterator (scan with MATCH club:click:*
and process in small batches, using pipeline/unlink or batched DEL) to avoid
long blocking operations; update references to CLICK_KEY_PATTERN, getRanking,
resetDailyClicks and stringRedisTemplate interactions accordingly.
- Around line 23-27: ClubClickService is currently annotated with
`@ConditionalOnProperty` which prevents the bean from being created when
scheduling.enabled=false and breaks injection into ClubClickController; make
ClubClickService unconditional (remove `@ConditionalOnProperty` from the
ClubClickService class) so recordClick() and getRanking() stay available, then
extract the scheduled logic (resetDailyClicks()) into a new scheduler component
class (e.g., ClubClickScheduler) annotated with `@Component` and
`@ConditionalOnProperty`(name="scheduling.enabled", havingValue="true",
matchIfMissing=true) and move the `@Scheduled` method resetDailyClicks() there,
wiring the new scheduler with ClubClickService via constructor injection.

---

Nitpick comments:
In `@backend/src/main/java/moadong/club/payload/request/ClubClickRequest.java`:
- Line 3: The ClubClickRequest record includes an unused ctAt field which should
be removed; update the request contract by deleting the ctAt component from the
ClubClickRequest declaration so it only exposes clubName, then update any
usages/constructors or deserialization expectations accordingly (inspect
ClubClickController and ClubClickService which currently only use clubName) and
ensure server-side timestamping/logging is performed where needed instead of
relying on a client-supplied ctAt.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d744ab84-69f5-4690-a6fa-97d41e1830af

📥 Commits

Reviewing files that changed from the base of the PR and between 6f14800 and 1504113.

📒 Files selected for processing (5)
  • backend/src/main/java/moadong/club/controller/ClubClickController.java
  • backend/src/main/java/moadong/club/payload/request/ClubClickRequest.java
  • backend/src/main/java/moadong/club/payload/response/ClubClickRankingResponse.java
  • backend/src/main/java/moadong/club/payload/response/ClubClickResponse.java
  • backend/src/main/java/moadong/club/service/ClubClickService.java

Comment thread backend/src/main/java/moadong/club/service/ClubClickService.java
Comment thread backend/src/main/java/moadong/club/service/ClubClickService.java
@seongwon030 seongwon030 added the ✨ Feature 기능 개발 label Apr 16, 2026
scheduling.enabled=false 시 서비스 빈 자체가 생성되지 않아 API 엔드포인트가 동작하지 않는 문제 수정.
@ConditionalOnProperty를 ClubClickService에서 제거하고, resetDailyClicks 스케줄 작업만 ClubClickScheduler로 분리.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@seongwon030 seongwon030 merged commit 39b69ba into develop/be Apr 16, 2026
5 checks passed
@seongwon030 seongwon030 deleted the feature/mouse-game branch April 16, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💾 BE Backend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants