Skip to content

[URECA-72] Fix: refresh시 userId 세팅, summary 및 bookmark 수정 - #70

Merged
40food merged 7 commits into
developfrom
URECA-72/Fix/fixstoreandcall
Jan 28, 2026
Merged

40food merged 7 commits into
developfrom
URECA-72/Fix/fixstoreandcall

Conversation

@40food

@40food 40food commented Jan 27, 2026 •

Copy link
Copy Markdown
Contributor

Key Changes

  • store, summary, bookmark 부분 살짝씩 수정

작업 내역

📸 스크린샷

💬 공유사항 to 리뷰어

  • summary 로딩 끝났을 때 바로 뜨게 pulling 고려
  • 북마크 바로 반영 안 됨(tanstack 때문에) 수정 필요
  • 북마크에서 새고하니 api 호출 아무것도 안 해서 refresh도 동작을 안 함=user id도 못 받아옴

비고

PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • 커밋 메시지 컨벤션에 맞게 작성했습니다.
  • Eslint 및 Prettier 규칙을 준수했습니다.
  • origin/develop 브랜치에서의 최신 커밋을 확인하고 반영했습니다.
  • 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트)

Summary by CodeRabbit

  • 개선 사항
    • 북마크된 요약과 요약 목록이 현재 로그인한 사용자 정보에 따라 정확히 로드됩니다.
    • 날짜 표시가 한국어(ko-KR) 형식으로 개선되어 가독성이 향상되었습니다.
    • 북마크 토글 동작이 더 안정적이고 일관되게 작동합니다.
    • 토큰 갱신 시 사용자 식별 정보가 동기화되어 인증 상태가 정확해졌습니다

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

@40food 40food self-assigned this Jan 27, 2026
@vercel

vercel Bot commented Jan 27, 2026 •

Copy link
Copy Markdown

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

Project Deployment Review Updated (UTC)
unity Ready Ready Preview, Comment Jan 28, 2026 2:45am
unity-client Ready Ready Preview, Comment Jan 28, 2026 2:45am

@github-actions github-actions Bot changed the title Ureca 72/fix/fixstoreandcall [URECA-72] Fix: Ureca 72/fix/fixstoreandcall Jan 27, 2026
@coderabbitai

coderabbitai Bot commented Jan 27, 2026 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

여러 컴포넌트와 훅에서 하드코딩된 사용자 ID(1)를 인증 상태의 동적 userId로 대체했습니다. useSummaryList와 useBookmarkedSummaries 훅 시그니처가 userId: number | null을 받도록 변경되고, userId가 없을 때 쿼리를 건너뛰도록 enabled/skip 처리가 추가되었습니다. 일부 컴포넌트는 createdAt 출력을 공통 유틸 convertDate로 포맷하도록 변경했고, API 인터셉터의 토큰 리프레시 응답에서 userId를 추출해 auth 스토어에 저장하도록 로직이 추가되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

요약

변경은 주로 인증 기반 데이터 분리와 UI의 날짜 포맷 통일에 초점이 있습니다. 훅 시그니처 변경으로 쿼리키에 userId가 포함되며, userId가 null일 경우 쿼리를 건너뛰는 보호 로직이 들어갔습니다. 인터셉터는 리프레시 응답으로부터 userId를 받아 auth 상태를 갱신합니다. 날짜 포맷터 유틸이 추가되어 여러 컴포넌트가 이를 사용하도록 수정되었습니다.

예상 코드 리뷰 난이도

🎯 3 (보통) | ⏱️ ~25분

주요 검토 포인트

  1. 타입/쿼리 일관성
  • 훅들에 전달되는 userId가 number | null로 바뀌었으니 모든 호출부에서 null 가능성을 고려했는지 확인하세요. 쿼리 건너뛰기는 통일된 패턴으로 (enabled: !!userId 또는 skipToken) 적용할 것. 쿼리 함수 내부에서 의도적으로 throw 하는 패턴은 불필요한 에러 로그를 유발하니 제거 권장.
  1. 인터셉터의 안전성
  • 리프레시 응답에 userId가 항상 존재하지 않을 수 있으므로 존재 검사 후 디스패치하세요. 타입 가드와 실패 시 로깅을 추가해 예기치 않은 구조 변경에 대비하세요.
  1. 날짜 포맷터 중앙화 및 중복 제거
  • 새 유틸(convertDate)로 중복 정의들을 통합했는지 확인하세요. 접근성 개선을 위해 렌더링 시 semantic <time dateTime={createdAt}> 패턴 적용을 권장합니다.
  1. 컴포넌트의 auth-로딩 경계
  • 컴포넌트가 마운트 시점에 userId에 의존하므로 auth 초기화 지연을 고려한 UI(로딩/빈 상태/로그인 유도)를 명확히 구분하세요. 쿼리의 enabled로 제어하거나 상위에서 ready 플래그를 전달하세요.
  1. 변경 영향 범위(레거시 호출 스캔)
  • 훅 시그니처 변경이 다른 모듈에 미치는 영향을 코드베이스 전체에서 검색해 수정하세요. 단위 테스트 및 통합 테스트(특히 인터셉터→auth 스토어 흐름)를 추가해 회귀를 방지하세요.
  1. 예외 처리 및 사용성
  • userId가 없는 상태와 네트워크 에러를 구분한 에러 UI/로그 정책을 마련하세요. 날짜 포맷 실패 등 변환 예외는 안전하게 기본 문자열을 출력하도록 처리하세요.
  1. 클린 아키텍처 권고
  • 인터셉터가 직접 스토어를 수정하는 현재 구조는 테스트와 분리 측면에서 취약합니다. 인증 동기화 책임은 auth 서비스 레이어로 추상화해 사이드 이펙트를 중앙에서 관리하도록 리팩터링 고려하세요.

필요하면 변경된 파일별 구체적 라인 리뷰와 제안 패치를 제공하겠습니다.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항(userId 설정 및 summary/bookmark 수정)을 명확하게 반영하고 있습니다.
Linked Issues check ✅ Passed PR이 #66의 체크리스트 항목들을 충족합니다: refresh 요청시 userId 반환, summary/bookmark userId 세팅이 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #66 이슈의 요구사항과 직접 관련이 있으며, 범위를 벗어난 변경은 없습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (2)
src/services/api/interceptors.ts (1)

113-143: refresh 응답의 userId 검증 강화 필요

현재 타입 가드가 accessToken만 검증하므로, 서버에서 userId를 누락하거나 유효하지 않은 값(NaN, undefined, 음수)을 반환해도 통과합니다. 이후 store.dispatch(authActions.setUserid(userId))로 잘못된 사용자 ID가 저장되어 요청 실패나 보안 문제가 발생할 수 있습니다.

isValidRefreshResponse에서 userId 필드와 타입을 검증하세요. 사용자 ID는 양의 정수여야 하므로 Number.isInteger() 또는 Number.isFinite()와 함께 범위 체크를 권장합니다.

✅ 제안 수정
 function isValidRefreshResponse(data: unknown): data is RefreshResponse {
   return (
     typeof data === 'object' &&
     data !== null &&
     'accessToken' in data &&
     typeof (data as RefreshResponse).accessToken === 'string' &&
+    'userId' in data &&
+    Number.isInteger((data as RefreshResponse).userId) &&
+    (data as RefreshResponse).userId > 0
   );
 }

또는 Number.isFinite() 사용 시:

+    'userId' in data &&
+    Number.isFinite((data as RefreshResponse).userId)
src/app/(private)/summary/_components/SummarySuccessPage.tsx (1)

29-36: 북마크 토글에 GET 메서드 사용은 HTTP 스펙 위반입니다

상태를 변경하는 요청에는 PATCH/POST가 표준입니다. GET은 멱등성(idempotency)과 캐싱 안전성 때문에 부작용 없는 조회 전용이어야 하는데, 현재 북마크 상태를 변경하고 있습니다. 이는 프록시/브라우저 캐싱 무시, 서버 스펙 불일치로 실패할 가능성이 높습니다.

더 나아가 기존의 toggleSummaryBookmark 함수는 이미 PATCH를 올바르게 사용하고 있고, useToggleBookmark hook은 React Query의 useMutation으로 캐시 동기화도 자동 처리합니다. 이미 구현된 hook을 재사용하는 것이 권장됩니다.

추가로 convertDate 함수가 SummarySuccessPage.tsx와 SummaryNavigateCard.tsx에 중복 정의되어 있으니, 공용 유틸리티로 분리하여 관리하세요.

✅ 제안 수정
-import { apiClient } from '@/services/api';
+import { useToggleBookmark } from '@/hooks/summary/useToggleBookmark';

-  const handleToggleBookmark = async () => {
-    if (loading) return;
-
-    try {
-      setLoading(true);
-      await apiClient.get(`/api/summaries/${summaryId}/bookmark`);
-      setBookmarked((prev) => !prev);
-    } finally {
-      setLoading(false);
-    }
-  };
+  const { mutate: toggleBookmark, isPending } = useToggleBookmark(summaryId, userId);
+
+  const handleToggleBookmark = () => {
+    toggleBookmark(undefined, {
+      onSuccess: () => setBookmarked((prev) => !prev),
+    });
+  };
🤖 Fix all issues with AI agents
In `@src/app/`(private)/bookmark/page.tsx:
- Around line 12-13: Remove the debugging console.log and change the selector to
return only the userId to avoid unnecessary re-renders: stop selecting the
entire auth object and instead select userId directly via useAppSelector (i.e.,
ensure the selector passed to useAppSelector extracts only s.auth.userId),
keeping references to the existing symbol names useAppSelector and userId in
page.tsx.
- Line 15: The hook call useBookmarkedSummaries(userId) can trigger unnecessary
errors when userId is null; update the hook to guard the query with React
Query's enabled option (mirror useSummaryDetail's pattern) so the queryFn only
runs when userId is a valid number, i.e., set enabled: userId !== null (or
Number.isFinite(userId)) inside useBookmarkedSummaries while still using
getBookmarkedSummaries(userId) in the queryFn, or alternatively prevent calling
useBookmarkedSummaries from the component by early-returning (e.g., show auth
UI) when userId is falsy before invoking useBookmarkedSummaries.

In `@src/hooks/summary/useBookmarkedSummaries.ts`:
- Around line 6-12: The hook useBookmarkedSummaries currently throws inside
queryFn when userId is null, causing error states during auth load; instead add
TanStack Query's enabled option to the useQuery call (using the existing
queryKey ['summaries','bookmarks', userId]) so the query only runs when userId
!== null, remove the manual throw in queryFn, and keep queryFn to call
getBookmarkedSummaries(userId) and return res.data; this ensures the query is
disabled while userId is null and re-enables automatically when userId changes.

In `@src/hooks/summary/useSummaryList.ts`:
- Around line 16-22: useSummaryList currently uses a static queryKey
['summaries'] and will call the API even when userId is null, causing cache
pollution and wrong results; update useSummaryList so the queryKey includes
userId (e.g. ['summaries', userId]) and prevent queries when userId is null by
either adding enabled: !!userId and guarding the queryFn inside useSummaryList
(option 1) or by switching to TanStack Query's skipToken pattern for the
queryKey to avoid querying when userId is null (option 2); locate and change the
useQuery call in useSummaryList to implement one of these fixes.
🧹 Nitpick comments (2)
src/app/(private)/summary/_components/SummarySuccessPage.tsx (1)

41-51: 날짜 포맷 헬퍼 중복 가능성 → 공용 유틸로 분리 권장

다른 컴포넌트에서도 동일한 로직이 추가되고 있어, utils/date 같은 공용 유틸로 분리하면 유지보수/일관성이 좋아집니다.

src/app/(private)/bookmark/page.tsx (1)

47-64: badges 파싱 로직을 유틸리티 함수로 분리하는 것을 권장합니다.

현재 IIFE 패턴으로 작성된 keywords → badges 변환 로직이 복잡하고, 다른 summary 관련 컴포넌트에서도 동일한 처리가 필요할 수 있습니다.

분리의 장점:

  • 테스트 용이성: 순수 함수로 분리하면 단위 테스트 작성이 쉬워집니다.
  • 재사용성: 다른 컴포넌트에서 동일 로직 사용 가능
  • 가독성: 컴포넌트 내 렌더링 로직이 간결해집니다.
♻️ 유틸리티 함수 분리 예시
// src/utils/parseKeywordsToBadges.ts
export function parseKeywordsToBadges(
  keywords: string | string[] | undefined | null
): string[] | undefined {
  if (!keywords) return undefined;

  if (Array.isArray(keywords)) {
    return keywords;
  }

  if (typeof keywords === 'string') {
    try {
      const parsed = JSON.parse(keywords);
      return Array.isArray(parsed) ? parsed : [keywords];
    } catch {
      return [keywords];
    }
  }

  return undefined;
}

컴포넌트에서 사용:

+import { parseKeywordsToBadges } from '@/utils/parseKeywordsToBadges';

 {data?.map((summary) => {
-  const badges = (() => {
-    // ... 기존 로직
-  })();
+  const badges = parseKeywordsToBadges(summary.keywords);

   return (
     // ...
   );
 })}

Comment thread src/app/(private)/bookmark/page.tsx Resolved
Comment thread src/app/(private)/bookmark/page.tsx Resolved
Comment thread src/hooks/summary/useSummaryList.ts Resolved
Chaejy
Chaejy previously approved these changes Jan 28, 2026
@40food 40food changed the title [URECA-72] Fix: Ureca 72/fix/fixstoreandcall [URECA-72] refresh시 userId 세팅, summary 및 bookmark 수정 Jan 28, 2026
@github-actions github-actions Bot changed the title [URECA-72] refresh시 userId 세팅, summary 및 bookmark 수정 [URECA-72] Fix: refresh시 userId 세팅, summary 및 bookmark 수정 Jan 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/hooks/summary/useSummaryList.ts`:
- Around line 32-35: The current inline JSON.parse in useSummaryList.ts can
throw on malformed strings; create a defensive helper (e.g., safeParseJSON) in
src/utils/parse that accepts the string and a fallback (default []), tries
JSON.parse inside try/catch and returns fallback on error, then replace the
ternary in useSummaryList (the typeof item.keywords === 'string' ?
JSON.parse(item.keywords) : (item.keywords ?? [])) to call
safeParseJSON(item.keywords, []) so malformed JSON becomes a controlled fallback
and prevents runtime exceptions.
🧹 Nitpick comments (2)
src/hooks/summary/useSummaryList.ts (1)

22-26: API 응답 타입 명시로 타입 안정성 개선 권장

현재 res.data 또는 res.data.data 분기 처리는 동작하지만, API 응답 타입을 명시하면 타입 안정성과 유지보수성이 향상됩니다.

📝 API 응답 타입 정의 예시
// src/types/summary/summary.ts에 추가
interface ApiSummaryResponse {
  data?: ApiSummaryItem[];
}

// 훅에서 사용
const res = await apiClient.get<ApiSummaryItem[] | ApiSummaryResponse>('/api/summaries', {
  params: { userId },
});

const list: ApiSummaryItem[] = Array.isArray(res.data) 
  ? res.data 
  : (res.data?.data ?? []);

백엔드 API 스펙이 확정되면 응답 구조를 단일화하는 것도 좋은 방향입니다.

src/app/(private)/bookmark/page.tsx (1)

12-42: userId가 null일 때 빈 상태로 보이는 UX 확인 필요

userId가 아직 없거나 인증이 완료되지 않은 순간에는 쿼리가 비활성화되더라도 화면이 “북마크한 요약이 없습니다”로 보일 수 있습니다. 이 페이지가 미들웨어로 보호되는지 확인하고, 아니라면 별도의 로그인/로딩 분기를 추가하는 편이 안전합니다.

✅ 예시 분기
+  if (userId == null) {
+    return (
+      <>
+        <Header />
+        <div className="py-12 text-center text-sm text-gray-400">로그인이 필요합니다</div>
+        <BottomNav />
+      </>
+    );
+  }

Comment thread src/hooks/summary/useSummaryList.ts Resolved

@Chaejy Chaejy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

짱

@40food
40food merged commit 8d1250b into develop Jan 28, 2026
2 of 5 checks passed
@40food
40food deleted the URECA-72/Fix/fixstoreandcall branch January 28, 2026 02:43

This branch was successfully deployed

2 active deployments
Preview – unity — e70fcdde Deployed Jan 28, 2026 by vercel[bot]
Preview – unity-client — e70fcdde Deployed Jan 28, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[URECA-72] Fix: store user id, 전화 로직 수정

3 participants