Skip to content

[Fix] 가입된 동아리가 한개일때 바로 홈으로 이동되도록 수정#94

Merged
dalzzy merged 3 commits intodevelopfrom
fix/가입된-동아리-하나일때-오류수정
Apr 29, 2026

Hidden character warning

The head ref may contain hidden characters: "fix/\uac00\uc785\ub41c-\ub3d9\uc544\ub9ac-\ud558\ub098\uc77c\ub54c-\uc624\ub958\uc218\uc815"
Merged

[Fix] 가입된 동아리가 한개일때 바로 홈으로 이동되도록 수정#94
dalzzy merged 3 commits intodevelopfrom
fix/가입된-동아리-하나일때-오류수정

Conversation

@dalzzy
Copy link
Copy Markdown
Member

@dalzzy dalzzy commented Apr 29, 2026

✅ PR 유형

어떤 변경 사항이 있었나요?

  • 새로운 기능 추가
  • 버그 수정
  • 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

📌 관련 이슈번호

✅ Key Changes

  • 가입된 동아리가 하나일 때 club/select를 거치지 않고 바로 홈으로 이동되도록 수정
  • 홈 진입 가드를 보강해서 URL의 clubId를 우선 신뢰하도록 바꿨고, persist store의 비어 있는 값이나 hydration 타이밍 때문에 홈에서 다시 hub로 튕기는 문제 해결

📸 스크린샷 or 실행영상


🎸 기타 사항 or 추가 코멘트

Summary by CodeRabbit

  • New Features

    • 클럽 허브에서 사용자가 등록한 클럽이 하나뿐일 경우, 클럽 선택 페이지를 거치지 않고 직접 해당 클럽의 홈으로 이동합니다.
  • Bug Fixes

    • 클럽 ID가 URL 파라미터와 상태 저장소 간에 일치하지 않을 때 올바르게 동기화되도록 개선했습니다.
    • 앱 재시작 시 사용자 및 클럽 정보가 더 안정적으로 복원됩니다.

@dalzzy dalzzy requested review from JIN921, nabbang6 and woneeeee April 29, 2026 16:37
@dalzzy dalzzy self-assigned this Apr 29, 2026
@dalzzy dalzzy added the 🐞 BugFix Something isn't working label Apr 29, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Warning

Rate limit exceeded

@dalzzy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6cc61bc-8a5a-460a-be2b-05ef0b7caddb

📥 Commits

Reviewing files that changed from the base of the PR and between b01f049 and 48c4792.

📒 Files selected for processing (3)
  • src/hooks/home/useHomeGuard.ts
  • src/providers/user-hydrator.tsx
  • src/stores/useClubStore.ts
📝 Walkthrough

Walkthrough

허브 페이지에서 활성 클럽이 있을 때 단일 클럽 여부에 따라 네비게이션 대상을 동적으로 결정하도록 변경되었습니다. 또한 클럽 스토어 영속성 설정이 강화되고, 사용자 하이드레이터가 필드 기반 동기화로 전환되며, 라우트 가드가 매개변수 기반 상태 업데이트 로직을 추가했습니다.

Changes

Cohort / File(s) Summary
Club 스토어 상태 관리
src/stores/useClubStore.ts, src/providers/user-hydrator.tsx
useClubStore의 persist 미들웨어에 선택적 필드 필터링(partialize)과 병합 전략(merge) 추가. 사용자 하이드레이터는 일회성 플래그 기반에서 필드 차이 기반 동기화로 변경되어, 매번 실행 시 현재 상태와 저장된 값을 비교하여 업데이트합니다.
허브 네비게이션 로직
src/app/(private)/(auth)/hub/page.tsx, src/hooks/home/useHomeGuard.ts
허브 페이지가 클럽 개수에 따라 goHref 대상을 조건부로 결정하고 클럽 ID/이름을 전달. 라우트 가드는 라우트 매개변수에서 clubId를 읽어 즉시 리다이렉트 대신 스토어를 업데이트하는 로직 추가.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • JIN921
  • woneeeee
  • nabbang6

Poem

🐰 한 개의 클럽이면 쏜살같이 가고,
많으면 선택 화면으로 살짝 우회하네!
스토어는 똑똑하게 기억하고,
상태는 부드럽게 동기화되니,
네비게이션의 마법 완성! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 주요 목적을 명확하게 설명하고 있습니다. 가입된 동아리가 하나일 때 바로 홈으로 이동되도록 수정한다는 내용이 code changes와 일치합니다.
Description check ✅ Passed PR 설명이 템플릿을 따르고 있으나, 관련 이슈번호 섹션이 비워져 있고 스크린샷/실행영상이 첨부되지 않았습니다. 그러나 PR 유형(버그 수정)이 선택되어 있고 Key Changes에서 변경 사항을 설명하고 있어 주요 내용은 충분합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/가입된-동아리-하나일때-오류수정

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
Review rate limit: 0/1 reviews remaining, refill in 43 minutes.

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

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/(private)/(auth)/hub/page.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/hooks/home/useHomeGuard.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/providers/user-hydrator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/stores/useClubStore.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-o8lc30yr3-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 실패
Build: 통과

⚠️ 일부 검증에 실패했습니다. 확인 후 수정해주세요.

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/(private)/(auth)/hub/page.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/hooks/home/useHomeGuard.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/providers/user-hydrator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/stores/useClubStore.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/hooks/home/useHomeGuard.ts`:
- Around line 25-28: The current branch resets clubName and clubProfileImageUrl
to null when calling setClubId from useHomeGuard (when clubIdParam differs),
causing UI flicker; instead, update the call so it does not wipe existing club
data: either extend setClubId to accept a partial payload or call the store
action with the existing clubName and clubProfileImageUrl values (read from
useClubStore) when updating clubId; ensure you reference useClubStore,
setClubId, clubIdParam and clubId and preserve or rehydrate clubName and
clubProfileImageUrl rather than setting them to null.

In `@src/providers/user-hydrator.tsx`:
- Line 29: The hydration call useClubStore.setState(clubInfo, false, 'setClub')
is passing a ClubIdentifier that lacks the clubProfileImageUrl field required by
the store's setClub action; update the code so clubInfo includes
clubProfileImageUrl (or map/merge it in before calling setState) or extend the
ClubIdentifier type to include clubProfileImageUrl, ensuring the setClub action
receives the profile URL when invoking useClubStore.setState (refer to
identifiers ClubIdentifier, clubInfo, clubProfileImageUrl, useClubStore.setState
and setClub).

In `@src/stores/useClubStore.ts`:
- Around line 36-45: The merge implementation currently favors persistedState
via the nullish coalescing operator (persisted.clubId ?? currentState.clubId),
which overwrites intentional nulls from currentState; change the logic in the
merge function to prefer currentState values when they are present (including
explicit null) and only fall back to persistedState when currentState property
is undefined—e.g., for each field like clubId, clubName, clubProfileImageUrl use
a conditional that checks currentState.<prop> !== undefined ?
currentState.<prop> : persisted.<prop> so reset() or a null clubInfo from
user-hydrator.tsx is preserved.
🪄 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: 144fcc25-6c93-441c-a198-92c58af44aa5

📥 Commits

Reviewing files that changed from the base of the PR and between 570f0a6 and b01f049.

📒 Files selected for processing (4)
  • src/app/(private)/(auth)/hub/page.tsx
  • src/hooks/home/useHomeGuard.ts
  • src/providers/user-hydrator.tsx
  • src/stores/useClubStore.ts

Comment thread src/hooks/home/useHomeGuard.ts
currentClub.clubId !== clubInfo.clubId ||
currentClub.clubName !== clubInfo.clubName
) {
useClubStore.setState(clubInfo, false, 'setClub');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check ClubIdentifier type definition

rg -n "type ClubIdentifier|interface ClubIdentifier" --type=ts -A10

Repository: Team-Weeth/weeth-client

Length of output: 225


🏁 Script executed:

#!/bin/bash
# View the code context around line 29 in user-hydrator.tsx
cat -n src/providers/user-hydrator.tsx | sed -n '20,40p'

Repository: Team-Weeth/weeth-client

Length of output: 630


🏁 Script executed:

#!/bin/bash
# Find the club store definition and setClub action
rg -n "setClub|export.*createStore" --type=ts -B2 -A8 | head -50

Repository: Team-Weeth/weeth-client

Length of output: 2883


🏁 Script executed:

#!/bin/bash
# Check what type clubInfo has in user-hydrator.tsx
rg -n "clubInfo" src/providers/user-hydrator.tsx -B3 -A3

Repository: Team-Weeth/weeth-client

Length of output: 686


ClubIdentifier 타입에서 clubProfileImageUrl 필드가 누락되어 불완전한 hydration 발생

useClubStore.setState(clubInfo, false, 'setClub')ClubIdentifier 타입의 clubInfo를 직접 전달하는데, 이 타입에는 clubProfileImageUrl이 포함되지 않습니다. store의 setClub action은 clubProfileImageUrl을 지원하도록 설계되었으나, 현재 코드는 이 필드를 제공하지 않아 동기화되지 않습니다.

ClubIdentifier에 clubProfileImageUrl 필드를 추가하거나, 해당 데이터를 별도로 관리해야 합니다.

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

In `@src/providers/user-hydrator.tsx` at line 29, The hydration call
useClubStore.setState(clubInfo, false, 'setClub') is passing a ClubIdentifier
that lacks the clubProfileImageUrl field required by the store's setClub action;
update the code so clubInfo includes clubProfileImageUrl (or map/merge it in
before calling setState) or extend the ClubIdentifier type to include
clubProfileImageUrl, ensuring the setClub action receives the profile URL when
invoking useClubStore.setState (refer to identifiers ClubIdentifier, clubInfo,
clubProfileImageUrl, useClubStore.setState and setClub).

Comment on lines +36 to +45
merge: (persistedState, currentState) => {
const persisted = (persistedState ?? {}) as Partial<ClubState>;

return {
...currentState,
clubId: persisted.clubId ?? currentState.clubId,
clubName: persisted.clubName ?? currentState.clubName,
clubProfileImageUrl:
persisted.clubProfileImageUrl ?? currentState.clubProfileImageUrl,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

merge 로직이 의도적인 null 업데이트를 덮어쓸 수 있습니다.

?? 연산자를 사용하면 persisted 값이 존재할 경우 항상 currentState보다 우선됩니다. 예를 들어:

  • reset() 호출 시 clubIdnull로 설정해도, 기존에 저장된 값이 있으면 다음 hydration 사이클에서 복원됩니다.
  • user-hydrator.tsx에서 clubInfonull 값을 포함할 경우, persisted storage의 이전 값으로 덮어쓰게 됩니다.

의도적인 null 할당을 존중하려면 currentState를 우선하거나, persisted 값을 fallback으로 사용하는 것이 적절합니다.

🔧 currentState 우선 방식으로 변경 제안
         merge: (persistedState, currentState) => {
           const persisted = (persistedState ?? {}) as Partial<ClubState>;

           return {
             ...currentState,
-            clubId: persisted.clubId ?? currentState.clubId,
-            clubName: persisted.clubName ?? currentState.clubName,
-            clubProfileImageUrl:
-              persisted.clubProfileImageUrl ?? currentState.clubProfileImageUrl,
+            clubId: currentState.clubId ?? persisted.clubId,
+            clubName: currentState.clubName ?? persisted.clubName,
+            clubProfileImageUrl:
+              currentState.clubProfileImageUrl ?? persisted.clubProfileImageUrl,
           };
         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/stores/useClubStore.ts` around lines 36 - 45, The merge implementation
currently favors persistedState via the nullish coalescing operator
(persisted.clubId ?? currentState.clubId), which overwrites intentional nulls
from currentState; change the logic in the merge function to prefer currentState
values when they are present (including explicit null) and only fall back to
persistedState when currentState property is undefined—e.g., for each field like
clubId, clubName, clubProfileImageUrl use a conditional that checks
currentState.<prop> !== undefined ? currentState.<prop> : persisted.<prop> so
reset() or a null clubInfo from user-hydrator.tsx is preserved.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-721c0on9y-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/(private)/(auth)/hub/page.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/hooks/home/useHomeGuard.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/providers/user-hydrator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/stores/useClubStore.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-q7r66omeo-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@dalzzy dalzzy merged commit 660bf27 into develop Apr 29, 2026
5 checks passed
@dalzzy dalzzy deleted the fix/가입된-동아리-하나일때-오류수정 branch April 30, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 BugFix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant