Skip to content

Feat(client): job selection popup open condition change#291

Merged
constantly-dev merged 5 commits intodevelopfrom
feat/#290/job-selection-popup-open-condition-change
Feb 28, 2026
Merged

Feat(client): job selection popup open condition change#291
constantly-dev merged 5 commits intodevelopfrom
feat/#290/job-selection-popup-open-condition-change

Conversation

@constantly-dev
Copy link
Member

@constantly-dev constantly-dev commented Feb 27, 2026

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

📄 Tasks

  • job selection popup open condition change

⭐ PR Point (To Reviewer)

기존에 로그인 api에서 res로 오는 hasJob을 로컬스토리지에 넣고 이를 통해 기존 사용자 직무 선택 퍼널을 띄울지 말지 분기 처리를 했었어요.
다만 이렇게 하면 문제가

  1. 직접 URL을 입력해서 접근 시 문제
  2. 유저가 로컬 스토리지 변경 가능성

등이 있을 수 있다고 판단했어요. 따라서 서버팀원분께 hasJob을 주는 api를 분리해달라고 말씀을 드렸고, 이를 Layout에서 공통으로 요청을 하도록 설정했어요.

  const shouldShowJobSelectionFunnel =
    isLoggedIn &&
    !isAuthPage &&
    !isHasJobLoading &&
    hasJobData?.hasJob === false;

해당 조건 분기를 통해 funnel이 떠야하는 특정 조건을 분리해서 사용했어요. 추가로 캐싱도 사용해서 매번 요청을 보내지는 않아요!

📷 Screenshot

Summary by CodeRabbit

  • 새로운 기능

    • 로그인 사용자의 직무 보유 여부를 자동 확인하고, 조건에 따라 전체화면 직무 선택 모달을 표시합니다.
    • 직무 선택 완료 시 상태가 최신화되어 즉시 반영됩니다.
  • 버그 수정

    • 로그아웃 시 관련 캐시와 저장된 정보가 함께 정리되어 상태가 정확히 초기화됩니다.
  • 리팩토링

    • 직무 상태와 모달 표시 로직을 중앙에서 일관되게 관리하도록 개선했습니다.

@vercel
Copy link

vercel bot commented Feb 27, 2026

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

Project Deployment Actions Updated (UTC)
pinback-client-client Ready Ready Preview, Comment Feb 28, 2026 4:10am
pinback-client-landing Ready Ready Preview, Comment Feb 28, 2026 4:10am

@github-actions github-actions bot added the feat 기능 개발하라 개발 달려라 달려 label Feb 27, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4d5053 and 78f5e54.

📒 Files selected for processing (1)
  • apps/client/src/layout/Layout.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/client/src/layout/Layout.tsx

Walkthrough

Layout에서 인증·정책 페이지 여부와 로컬 토큰을 기준으로 서버의 hasJob 상태를 조회해 로그인한 사용자에게 JobSelectionFunnel 모달을 조건부로 전체화면 오버레이로 렌더링하고, 펀넬 완료 시 hasJob 쿼리를 무효화하도록 변경했습니다. Remind 페이지의 로컬 펀넬 로직은 제거되었습니다.

Changes

Cohort / File(s) Summary
Layout 및 페이지 변경
apps/client/src/layout/Layout.tsx, apps/client/src/pages/remind/Remind.tsx
JobSelectionFunnel 렌더링 로직을 Remind에서 Layout으로 이동: 인증 페이지 여부(isAuthPage), 정책 페이지(isPolicyPage) 기반 사이드바/오버레이 판단 추가. Remind에서 JobSelectionFunnel import·상태·JSX 제거.
HasJob API 및 쿼리
apps/client/src/shared/apis/axios.ts, apps/client/src/shared/apis/queries.ts, apps/client/src/shared/types/api.ts
getHasJob() API 추가, HasJobResponse 타입 추가, useGetHasJob(enabled?) 훅 추가. usePatchUserJob 성공 시 ['hasJob'] 쿼리 무효화 로직 추가.
프로필/로그아웃 쿼리 처리
apps/client/src/shared/components/profilePopup/ProfilePopup.tsx
로그아웃 시 useQueryClient()로 쿼리 클라이언트 취득 후 localStorage 항목 제거 및 queryClient.clear() 호출 추가(캐시 초기화).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Layout as Layout
    participant QueryHook as useGetHasJob
    participant API as GET /api/v3/users/job
    participant Modal as JobSelectionFunnel

    User->>Layout: 앱 접근
    rect rgba(120,160,200,0.5)
        Note over Layout: 토큰으로 isLoggedIn 결정\nisAuthPage/isPolicyPage 판단
        alt isLoggedIn && !isAuthPage
            Layout->>QueryHook: fetch hasJob (enabled=true)
            QueryHook->>API: GET /api/v3/users/job
            API-->>QueryHook: { hasJob: false }
            QueryHook-->>Layout: hasJob 데이터 반환
        end
    end

    rect rgba(160,120,200,0.5)
        alt shouldShowJobSelectionFunnel
            Layout->>Modal: show JobSelectionFunnel
            Modal->>User: 직무 선택 UI 표시
            User->>Modal: 선택 완료
            Modal-->>Layout: onComplete
        end
    end

    rect rgba(100,200,150,0.5)
        Layout->>QueryHook: queryClient.invalidateQueries(['hasJob'])
        QueryHook->>API: GET /api/v3/users/job (re-fetch)
        API-->>QueryHook: { hasJob: true }
        QueryHook-->>Layout: 업데이트
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • jllee000

Poem

🐰 레이아웃 숲에 모달이 폴짝,
토큰 킁킁 쿼리 훅 냄새 맡아,
직무 골라 팡! 쿼리 싹 무효화,
당근 한 입에 유저 여정 환하게 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR의 코드 변경사항은 주로 #290 (기존 사용자 직무 선택 팝업 open 조건 수정)을 충족합니다. 다만 #25 (Progress 컴포넌트 구현)는 이 PR의 범위와 무관합니다. 이 PR은 #290에만 관련되어 있습니다. #25는 별도의 디자인 시스템 PR이므로 linked_issues에서 제거하거나, 별도 PR으로 분리하는 것을 권장합니다.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 PR의 주요 변경사항을 명확하게 요약하고 있습니다. 기존 사용자의 직무 선택 팝업 open 조건 변경이 핵심 변화입니다.
Description check ✅ Passed PR 설명이 필수 섹션(관련 이슈, 작업 내용, 리뷰어 포인트)을 모두 포함하고 있으며, 변경 사항의 배경과 구현 방식을 상세히 설명하고 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #290의 범위 내에 있습니다: Layout에서 서버 API를 통해 hasJob을 조회하고, 조건부 렌더링을 구현했으며, 기존 로컬스토리지 기반 접근을 제거했습니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#290/job-selection-popup-open-condition-change

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

github-actions bot commented Feb 27, 2026

✅ Storybook chromatic 배포 확인:
🐿️ storybook

Copy link

@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: 2

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

Inline comments:
In `@apps/client/src/layout/Layout.tsx`:
- Around line 24-33: The funnel can open on policy/terms pages; update the
shouldShowJobSelectionFunnel condition to exclude legal pages by adding checks
like && !isPolicyPage && !isTermsPage (or use a single isLegalPage flag) so the
expression becomes: isLoggedIn && !isAuthPage && !isPolicyPage && !isTermsPage
&& !isHasJobLoading && hasJobData?.hasJob === false; ensure
isPolicyPage/isTermsPage (or derived isLegalPage) are defined/derived in the
same component before use.

In `@apps/client/src/shared/apis/queries.ts`:
- Around line 187-194: The hasJob query uses a global key ['hasJob'] which
causes cached results to leak across user sessions; update useGetHasJob to
include a per-user identifier in its queryKey (e.g., ['hasJob', userId] or
['hasJob', getCurrentAccountId()]) so the cache is scoped to the active user
(ensure getHasJob accepts/uses that id if needed), or alternatively ensure
logout logic calls queryClient.invalidateQueries({ queryKey: ['hasJob'] }) in
the ProfilePopup logout handler; modify useGetHasJob (and any callers) to pass
and use the user identifier and/or add the invalidate call to prevent
cross-account cache reuse.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ba5c25 and 18bc517.

📒 Files selected for processing (5)
  • apps/client/src/layout/Layout.tsx
  • apps/client/src/pages/remind/Remind.tsx
  • apps/client/src/shared/apis/axios.ts
  • apps/client/src/shared/apis/queries.ts
  • apps/client/src/shared/types/api.ts
💤 Files with no reviewable changes (1)
  • apps/client/src/pages/remind/Remind.tsx

Comment on lines 24 to 33
const { data: hasJobData, isLoading: isHasJobLoading } = useGetHasJob(
isLoggedIn && !isAuthPage
);

const shouldShowJobSelectionFunnel =
isLoggedIn &&
!isAuthPage &&
!isHasJobLoading &&
hasJobData?.hasJob === false;

Copy link

@coderabbitai coderabbitai bot Feb 27, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

정책/약관 페이지에서도 퍼널이 열릴 수 있어 법적 문서 접근을 막을 수 있습니다.

현재 조건은 Line [29]-[32]에서 !isPolicyPage를 제외하지 않아 /policy, /terms에서도 모달이 뜰 수 있습니다. 해당 경로는 퍼널에서 제외하는 게 안전합니다.

🔧 제안 수정안
   const { data: hasJobData, isLoading: isHasJobLoading } = useGetHasJob(
-    isLoggedIn && !isAuthPage
+    isLoggedIn && !isAuthPage && !isPolicyPage
   );

   const shouldShowJobSelectionFunnel =
     isLoggedIn &&
     !isAuthPage &&
+    !isPolicyPage &&
     !isHasJobLoading &&
     hasJobData?.hasJob === false;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/client/src/layout/Layout.tsx` around lines 24 - 33, The funnel can open
on policy/terms pages; update the shouldShowJobSelectionFunnel condition to
exclude legal pages by adding checks like && !isPolicyPage && !isTermsPage (or
use a single isLegalPage flag) so the expression becomes: isLoggedIn &&
!isAuthPage && !isPolicyPage && !isTermsPage && !isHasJobLoading &&
hasJobData?.hasJob === false; ensure isPolicyPage/isTermsPage (or derived
isLegalPage) are defined/derived in the same component before use.

Copy link
Collaborator

Choose a reason for hiding this comment

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

이부분도 확인해보셨나요 ?!?

Copy link

Choose a reason for hiding this comment

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

@jjangminii 네, 맞습니다! CodeRabbit이 지적한 부분이 타당한 이슈입니다.

현재 코드를 보면 isPolicyPage가 정의되어 있지만, 직무 선택 퍼널을 표시하는 조건에서는 제외되지 않고 있습니다. 이로 인해 로그인한 사용자가 이용약관이나 개인정보처리방침 페이지를 볼 때도 퍼널 모달이 나타날 수 있어서, 법적 문서 접근을 방해할 수 있는 문제가 있습니다.

다음과 같이 수정하는 것을 권장합니다:

const { data: hasJobData, isLoading: isHasJobLoading } = useGetHasJob(
  isLoggedIn && !isAuthPage && !isPolicyPage
);

const shouldShowJobSelectionFunnel =
  isLoggedIn &&
  !isAuthPage &&
  !isPolicyPage &&
  !isHasJobLoading &&
  hasJobData?.hasJob === false;

이렇게 하면:

  1. 정책/약관 페이지에서는 hasJob API 요청 자체를 하지 않음
  2. 해당 페이지에서는 퍼널이 절대 표시되지 않음
  3. 사용자가 법적 문서를 자유롭게 확인할 수 있음

Copy link
Member Author

Choose a reason for hiding this comment

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

엇 그렇네요! 해당 부분 조건 추가하겠습니다 👍

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link

@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.

♻️ Duplicate comments (1)
apps/client/src/shared/apis/queries.ts (1)

187-195: ⚠️ Potential issue | 🟠 Major

hasJob 쿼리 키를 사용자 스코프로 분리하는 편이 안전합니다.

Line 191의 queryKey: ['hasJob']는 전역 키라서, 인증 전환 경로 중 캐시 정리가 누락되면 이전 사용자 값 재사용 위험이 남습니다. ['hasJob', userId] 형태로 스코프를 분리하거나, 모든 토큰 전환 경로에서 ['hasJob'] 제거를 강제해 주세요.

#!/bin/bash
# 목적:
# 1) hasJob 쿼리 키가 사용자 식별자를 포함하는지 확인
# 2) 인증 상태 전환(로그인/로그아웃/토큰 교체) 경로에서 hasJob 캐시 정리가 보장되는지 확인

echo "== hasJob query key usage =="
rg -n --type ts --type tsx "queryKey:\s*\[\s*'hasJob'"

echo
echo "== hasJob cache invalidation/removal/clear =="
rg -n --type ts --type tsx "invalidateQueries\(|removeQueries\(|clear\(\)" apps/client/src -A3 -B2 | rg -n "hasJob|queryClient"

echo
echo "== auth/token transition points =="
rg -n --type ts --type tsx "setItem\('token'|removeItem\('token'|logout|signOut|login|postSignUp" apps/client/src -A4 -B2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/client/src/shared/apis/queries.ts` around lines 187 - 195, The hasJob
query is currently using a global cache key in useGetHasJob (queryKey:
['hasJob']), which can leak prior-user data across auth transitions; update
useGetHasJob to include a per-user scope (e.g., queryKey: ['hasJob', userId]
where userId is sourced from the same auth state used by getHasJob) or, if you
cannot add userId, ensure every auth/token transition path explicitly
invalidates/removes that key (calls to queryClient.invalidateQueries(['hasJob'])
/ removeQueries(['hasJob']) wherever login/logout/token swap occurs). Locate
useGetHasJob and the getHasJob caller, add the user identifier into the key or
add missing invalidate/remove calls at auth transition points to guarantee cache
is cleared.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/client/src/shared/apis/queries.ts`:
- Around line 187-195: The hasJob query is currently using a global cache key in
useGetHasJob (queryKey: ['hasJob']), which can leak prior-user data across auth
transitions; update useGetHasJob to include a per-user scope (e.g., queryKey:
['hasJob', userId] where userId is sourced from the same auth state used by
getHasJob) or, if you cannot add userId, ensure every auth/token transition path
explicitly invalidates/removes that key (calls to
queryClient.invalidateQueries(['hasJob']) / removeQueries(['hasJob']) wherever
login/logout/token swap occurs). Locate useGetHasJob and the getHasJob caller,
add the user identifier into the key or add missing invalidate/remove calls at
auth transition points to guarantee cache is cleared.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18bc517 and b4d5053.

📒 Files selected for processing (2)
  • apps/client/src/shared/apis/queries.ts
  • apps/client/src/shared/components/profilePopup/ProfilePopup.tsx

Copy link
Collaborator

@jjangminii jjangminii left a comment

Choose a reason for hiding this comment

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

기존에는 hasJob 값을 로컬스토리지에 저장해 조건 분기를 처리하면서 사용자가 개발자 도구 등을 통해 값을 직접 변경하는 경우는 어느 정도 사용자 책임의 영역이라고 생각해 로컬스토리지 방식 자체가 크게 문제되지는 않을 것이라고 판단했습니다.
다만 어제 회의에서 사용자가 URL로 직접 접근했을 때 발생할 수 있는 예외 상황에 대해 듣고 안일했다고 생각이들었습니다. 다양한 접근 방식과 예외 상황까지 더 폭넓게 고려해 봐야겠네요 😢

고생 많으셨습니다-!

Comment on lines 24 to 33
const { data: hasJobData, isLoading: isHasJobLoading } = useGetHasJob(
isLoggedIn && !isAuthPage
);

const shouldShowJobSelectionFunnel =
isLoggedIn &&
!isAuthPage &&
!isHasJobLoading &&
hasJobData?.hasJob === false;

Copy link
Collaborator

Choose a reason for hiding this comment

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

이부분도 확인해보셨나요 ?!?

localStorage.removeItem('token');
localStorage.removeItem('email');
localStorage.removeItem('userId');
queryClient.clear();
Copy link
Collaborator

Choose a reason for hiding this comment

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

로그아웃 시 단순히 토큰만 제거하면 된다고 생각했는데 React Query 캐시에 이전 사용자 데이터가 남아 있을 수 있다는 점을 놓쳤네요

토큰 삭제뿐 아니라 캐시 초기화까지 함께 처리하는 것이 사용자 전환 시 데이터 일관성과 보안 측면에서 더 안전한 방법이라고 이해했습니다. 하나 더 배워가요-!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발하라 개발 달려라 달려

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 기존 사용자 직무 선택 팝업 open 조건 수정

2 participants