Skip to content

feat: 홈 화면 고민 카테고리 TOP4 통계 컴포넌트 구현 - #48

Closed
wjdalss21 wants to merge 9 commits into
devfrom
feature/homepage-statistics-jm
Closed

wjdalss21 wants to merge 9 commits into
devfrom
feature/homepage-statistics-jm

Conversation

@wjdalss21

@wjdalss21 wjdalss21 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • StatsCategorySection 컴포넌트 구현: 판결 완료 사건 기반 카테고리 비율을 단일 스택 바 + 2×2 레이블 그리드로 표시
  • QueryProvider 추가: TanStack Query의 QueryClientProvider를 Server Component인 layout.tsx에서 사용하기 위한 Client Component 래퍼
  • layout.tsxQueryProvider 적용

DB 연동 구조

dispute 테이블 (status = 'JUDGED', deletedAt = null, anonymizedAt = null)
    ↓  Prisma groupBy(['categoryGroup'])
GET /api/statistics/categories  (ISR revalidate: 86400s)
    ↓  fetch + TanStack Query (staleTime: 24h)
useStatistics() 훅  →  count → percentage 변환
    ↓
StatsCategorySection 컴포넌트

통계 바가 비어 보이는 이유

현재 dev 환경에서 통계 바가 표시되지 않는 것은 정상 동작입니다.

상태 원인 동작
미로그인 API 401 반환 컴포넌트 null 반환 (에러 없음)
로그인 + 판결 완료 사건 없음 total = 0 → 모든 percentage = 0% 바 세그먼트 width: 0% (빈 바)
로그인 + 판결 완료 사건 있음 정상 집계 비율대로 바 채워짐

판결 완료(status = 'JUDGED') 사건이 생기면 자동으로 통계가 표시됩니다.

카테고리 색상 (CSS 변수)

연인관계(ROMANCE): --category-love-bg / --category-love-text
직장관계(WORK):   --category-work-bg / --category-work-text
친구관계(FRIEND): --category-friend-bg / --category-friend-text
가족관계(FAMILY): --category-family-bg / --category-family-text

Test Plan

  • 미로그인 상태: API 401 → 컴포넌트 null 반환, JS 에러 없음 ✅
  • 로딩 상태: 타이틀 + 빈 바 스켈레톤 정상 표시 ✅
  • 홈 페이지 전체 레이아웃 (로고 헤더, 캐릭터 이미지, 일기 박스) 정상 렌더링 ✅
  • 로그인 후 판결 완료 사건 생성 시 통계 바 정상 표시 (DB 데이터 필요)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • 홈 페이지 추가: 사용자 맞춤 인사말과 감정일기 작성 링크 제공
    • 기록 페이지 추가
    • 감정 카테고리 TOP 4 통계 시각화 섹션 추가
    • 헤더에 로고 표시 옵션 추가
  • Refactor

    • Query 상태 관리 시스템 통합
  • Style

    • 홈 페이지 레이아웃 및 스타일 정의
    • 카테고리 통계 섹션 UI 스타일 적용
    • 헤더 로고 영역 스타일 추가

wjdalss21 and others added 8 commits June 17, 2026 17:52
- 헤더: 세션 유저명 + character-welcome.png 부엉이 이미지
- 일기 박스: 365x88, border black-700 2px, bg primary-100
- TODO: GangwonEduPower 폰트 추가 후 헤더 로고 텍스트 적용 필요

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Header.tsx에 variant prop 추가 (logo | title)
  - logo: 좌측 상단 말해부엉 로고 표시 (66x19, h:56, padding: 16px 20px)
  - title: 기존 뒤로가기 + 페이지 제목 형태 유지
- Header.module.scss에 __logo 스타일 추가
- home/page.tsx에 Header variant='logo' 적용
  - 헤더를 컨테이너 padding 밖으로 분리하여 상단 여백 제거
- home/page.module.scss diaryBox에 align-self: center 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- width: 365px → width: 100% + max-width: 365px
- 작은 화면에서 좌우 패딩과 결합 시 오버플로우 방지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 일기 박스 전체를 Link로 변경 (텍스트 + 버튼 모두 클릭 가능)
- diaryBox에 text-decoration: none, cursor: pointer 추가
- /diary/new 경로는 임시 지정 (담당자 확인 후 수정 예정)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- character-welcome.png → character-home.png 교체 (169x138)
- 캐릭터 절대 위치 적용 (top: 41px, right: -20px)
- 캐릭터가 일기 박스 뒤에 위치하도록 z-index 조정 (character: 0, container: 1)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StatsCategorySection 컴포넌트 추가
- 단일 스택 바 (w:346, h:12, border-radius:0) 4개 카테고리 비율 표시
- 바 컬러: category-*-bg 변수 사용
- 레이블 2x2 그리드 (가운데 정렬, 세로 간격 8px)
- 카테고리명 전체 볼드, 퍼센트 regular
- 가상 데이터 적용 (API 연동 추후 예정)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StatsCategorySection: 판결 완료 사건 기반 카테고리별 비율을 단일 스택 바 + 2x2 레이블 그리드로 표시
- QueryProvider: TanStack Query 사용을 위한 Client Component 래퍼 추가
- layout.tsx: QueryProvider로 앱 전체 래핑

[DB 연동]
- GET /api/statistics/categories: dispute.status = JUDGED 사건만 groupBy categoryGroup 집계
- 판결 완료 사건 없으면 total=0 → 모든 percentage=0% → 바가 비어 보임 (정상 동작)
- 미로그인 상태에서 API 401 반환 시 컴포넌트는 null 반환으로 안전 처리

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
talky-owl Ready Ready Preview, Comment Jun 18, 2026 1:13am

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

홈 페이지(HomePage 서버 컴포넌트)와 기록 페이지(RecordsPage) 스텁을 신규 추가하고, Header 컴포넌트를 logo/title variant 유니온으로 확장하며, TanStack Query 지원을 위한 QueryProvider를 루트 레이아웃에 연결하고, StatsCategorySection 컴포넌트를 구현한다.

Changes

홈/기록 페이지 및 공유 인프라 구축

Layer / File(s) Summary
QueryProvider 생성 및 루트 레이아웃 연결
src/components/providers/QueryProvider.tsx, src/app/layout.tsx
use client 기반 QueryProvider를 신규 생성하고, RootLayoutmainQueryProvider로 감싸도록 layout.tsx를 수정한다. QueryClient 인스턴스는 useState로 고정 생성한다.
Header 컴포넌트 variant 확장
src/components/layout/Header.tsx, src/components/layout/Header.module.scss
HeaderPropslogo/title 유니온 타입으로 재구성하고, variant === 'logo'일 때 next/image 로고만 렌더링하는 분기 로직을 추가한다. .header__logo 스타일 블록도 함께 추가된다.
StatsCategorySection 컴포넌트 및 스타일
src/components/home/StatsCategorySection.tsx, src/components/home/StatsCategorySection.module.scss
useStatistics()로 카테고리 데이터를 조회하며, 로딩·에러·공백 상태에서 플레이스홀더 바를 표시하고, 데이터가 있으면 percentage 기반 세그먼트 바와 2×2 라벨 그리드를 렌더링한다.
HomePage 서버 컴포넌트 및 스타일
src/app/(page)/home/page.tsx, src/app/(page)/home/page.module.scss
getServerSession으로 사용자 이름을 결정하고, Header·캐릭터 이미지·/diary/new 링크·StatsCategorySection·구분선을 포함하는 비동기 서버 컴포넌트를 구현한다. 12개 CSS 클래스를 포함하는 전용 SCSS 모듈도 추가된다.
RecordsPage 스텁 추가
src/app/(page)/records/page.tsx
Header(variant="logo")와 빈 <main> 요소만 포함하는 RecordsPage 기본 내보내기 컴포넌트 스텁을 추가한다.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant HomePage
  participant getServerSession
  participant StatsCategorySection
  participant useStatistics

  Browser->>HomePage: 페이지 요청
  HomePage->>getServerSession: authOptions 전달해 세션 조회
  getServerSession-->>HomePage: session (user.name 포함)
  HomePage->>StatsCategorySection: 렌더링 요청
  StatsCategorySection->>useStatistics: 카테고리 통계 조회
  useStatistics-->>StatsCategorySection: data / isLoading / isError
  StatsCategorySection-->>HomePage: 바 세그먼트 + 라벨 그리드 JSX 반환
  HomePage-->>Browser: 완성된 홈 페이지 HTML 반환
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • I5-Project/TALKY-OWL#44: StatsCategorySection이 의존하는 useStatistics() 훅과 /api/statistics/categories 엔드포인트를 구현한 PR로, 직접적인 데이터 소스 연결 관계가 있다.

Poem

🐇 토끼가 홈 화면을 뚝딱 지었네,
캐릭터 이미지에 인사말까지 달고,
QueryProvider로 데이터를 감싸고,
Header엔 로고 variant를 새로 달았지.
카테고리 바가 반짝반짝 빛나네! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명이 요구되는 템플릿 섹션 대부분을 누락했습니다. 작업 내용, 담당 영역, 관련 이슈, 변경사항, 테스트 결과, 작업 범위 확인 등의 필수 섹션이 채워지지 않았습니다. 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 제목이 StatsCategorySection 컴포넌트 구현과 홈 화면 고민 카테고리 TOP4 통계 기능을 명확하게 설명하며, 변경사항의 주요 목적을 잘 반영합니다.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/homepage-statistics-jm

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

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

- 데이터 없음/로딩/에러 시 회색 25% 균등 플레이스홀더 바 표시
- 퍼센트 레이블 0% 대신 '-' 표시
- 통계 섹션 하단 구분선 추가 (h:8px, color: black-100, gap: 42px)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
src/components/layout/Header.tsx (1)

27-33: ⚡ Quick win

로고 이미지 스타일은 SCSS 모듈로 이동하는 편이 좋습니다.

objectFit 인라인 스타일을 모듈 클래스로 옮기면 스타일 기준이 한 곳으로 모여 유지보수가 쉬워집니다.

♻️ 제안 diff
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
@@
           <Image
             src="/images/common/logo.png"
             alt="말해부엉"
             width={66}
             height={19}
-            style={{ objectFit: 'contain' }}
+            className={styles.header__logoImage}
           />
diff --git a/src/components/layout/Header.module.scss b/src/components/layout/Header.module.scss
@@
   &__logo {
     display: flex;
     align-items: center;
     height: fn.r(56);
     padding: fn.r(16) fn.r(20);
   }
+
+  &__logoImage {
+    object-fit: contain;
+  }

As per coding guidelines, "Use SCSS and SCSS Modules for styling, maintaining consistent style basis across the application without MUI ThemeProvider".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Header.tsx` around lines 27 - 33, Remove the inline
style prop with objectFit property from the Image component in the Header
section, and instead move this styling to an SCSS module. Create a new SCSS
class (or use an existing one) that applies object-fit: contain styling, then
apply this class to the Image component using the className prop. This ensures
all styling is centralized in SCSS modules rather than scattered as inline
styles, making maintenance easier and keeping your style basis consistent across
the application.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/home/StatsCategorySection.tsx`:
- Around line 24-26: The current logic in StatsCategorySection.tsx treats all
errors the same by including isError in the showPlaceholder condition, but the
requirement is to return null specifically for 401 authentication errors.
Separate the error handling logic by first checking if the error is a 401
authentication failure and return null directly from the component in that case,
then only treat other errors as part of the showPlaceholder condition. This
ensures authentication failures do not render the section at all, while other
errors can display a placeholder.
- Line 12: The CATEGORY_CONFIG constant uses React.ElementType without properly
importing the React namespace, which can cause TypeScript resolution errors. Add
an explicit import of ElementType from the react package at the top of the file,
and then replace React.ElementType with just ElementType in the type annotation
for the icon property within the CATEGORY_CONFIG Record definition.

In `@src/components/providers/QueryProvider.tsx`:
- Line 10: The QueryClient initialization in the useState hook needs a custom
retry policy to prevent unnecessary retries on 401 unauthorized errors. Modify
the QueryClient constructor to include a defaultOptions parameter with a queries
retry function that checks the error status and returns false if the error
status is 401, while allowing other errors to be retried up to 3 times. This
prevents TanStack Query v5's default behavior of retrying all errors including
401 responses, which causes unnecessary API calls when users are not logged in.

---

Nitpick comments:
In `@src/components/layout/Header.tsx`:
- Around line 27-33: Remove the inline style prop with objectFit property from
the Image component in the Header section, and instead move this styling to an
SCSS module. Create a new SCSS class (or use an existing one) that applies
object-fit: contain styling, then apply this class to the Image component using
the className prop. This ensures all styling is centralized in SCSS modules
rather than scattered as inline styles, making maintenance easier and keeping
your style basis consistent across the application.
🪄 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: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 52ab498e-ab5a-4dc1-b601-8dab8dc53a15

📥 Commits

Reviewing files that changed from the base of the PR and between d7f47d4 and cda4aa9.

📒 Files selected for processing (9)
  • src/app/(page)/home/page.module.scss
  • src/app/(page)/home/page.tsx
  • src/app/(page)/records/page.tsx
  • src/app/layout.tsx
  • src/components/home/StatsCategorySection.module.scss
  • src/components/home/StatsCategorySection.tsx
  • src/components/layout/Header.module.scss
  • src/components/layout/Header.tsx
  • src/components/providers/QueryProvider.tsx


type CategoryKey = 'ROMANCE' | 'WORK' | 'FRIEND' | 'FAMILY';

const CATEGORY_CONFIG: Record<CategoryKey, { label: string; icon: React.ElementType; styleKey: string }> = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 목적: React 네임스페이스 타입 사용 패턴과 TS 설정을 확인해 타입 호환성 검증
fd -a tsconfig.json
echo "---- tsconfig excerpt ----"
sed -n '1,220p' tsconfig.json
echo "---- React namespace type usage ----"
rg -n "React\\.ElementType|React\\.ReactNode|React\\." src
echo "---- explicit ElementType imports ----"
rg -n "import type \\{[^}]*ElementType[^}]*\\} from 'react'" src

Repository: I5-Project/TALKY-OWL

Length of output: 2197


🏁 Script executed:

cat -n src/components/home/StatsCategorySection.tsx | head -30

Repository: I5-Project/TALKY-OWL

Length of output: 1548


🏁 Script executed:

cat -n src/components/ui/CategoryFilter.tsx | head -30

Repository: I5-Project/TALKY-OWL

Length of output: 1413


🏁 Script executed:

# Check if React is imported in the StatsCategorySection file
rg -n "import.*React" src/components/home/StatsCategorySection.tsx

Repository: I5-Project/TALKY-OWL

Length of output: 46


🏁 Script executed:

# Check for ambient React declarations or global type definitions
rg -n "declare.*React|React.*=" src --type ts --type tsx | head -20

Repository: I5-Project/TALKY-OWL

Length of output: 92


🏁 Script executed:

# Check next-env.d.ts which might provide ambient React types
cat -n next-env.d.ts

Repository: I5-Project/TALKY-OWL

Length of output: 368


🏁 Script executed:

# Check React version and setup
cat -n package.json | grep -A 5 '"react"'

Repository: I5-Project/TALKY-OWL

Length of output: 263


React.ElementType 타입을 명시적 import로 변경하세요.

파일에서 React 네임스페이스를 import하지 않으면서 React.ElementType을 사용하고 있습니다. 현재 TypeScript 설정에서는 타입 해석 오류가 발생할 수 있습니다. 다음과 같이 수정하세요:

수정 예시
+import type { ElementType } from 'react';
 import FavoriteIcon from '`@mui/icons-material/Favorite`';
 import BusinessCenterIcon from '`@mui/icons-material/BusinessCenter`';
 import Diversity3Icon from '`@mui/icons-material/Diversity3`';
 import FamilyRestroomIcon from '`@mui/icons-material/FamilyRestroom`';
 import { useStatistics } from '`@/hooks/useStatistics`';
 import styles from './StatsCategorySection.module.scss';

 type CategoryKey = 'ROMANCE' | 'WORK' | 'FRIEND' | 'FAMILY';

-const CATEGORY_CONFIG: Record<CategoryKey, { label: string; icon: React.ElementType; styleKey: string }> = {
+const CATEGORY_CONFIG: Record<CategoryKey, { label: string; icon: ElementType; styleKey: string }> = {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const CATEGORY_CONFIG: Record<CategoryKey, { label: string; icon: React.ElementType; styleKey: string }> = {
import type { ElementType } from 'react';
import FavoriteIcon from '`@mui/icons-material/Favorite`';
import BusinessCenterIcon from '`@mui/icons-material/BusinessCenter`';
import Diversity3Icon from '`@mui/icons-material/Diversity3`';
import FamilyRestroomIcon from '`@mui/icons-material/FamilyRestroom`';
import { useStatistics } from '`@/hooks/useStatistics`';
import styles from './StatsCategorySection.module.scss';
type CategoryKey = 'ROMANCE' | 'WORK' | 'FRIEND' | 'FAMILY';
const CATEGORY_CONFIG: Record<CategoryKey, { label: string; icon: ElementType; styleKey: string }> = {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/home/StatsCategorySection.tsx` at line 12, The CATEGORY_CONFIG
constant uses React.ElementType without properly importing the React namespace,
which can cause TypeScript resolution errors. Add an explicit import of
ElementType from the react package at the top of the file, and then replace
React.ElementType with just ElementType in the type annotation for the icon
property within the CATEGORY_CONFIG Record definition.

Comment on lines +24 to +26
const isEmpty = !data || data.total === 0;
const showPlaceholder = isLoading || isError || isEmpty;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

401 에러 시 null 렌더링 요구사항과 현재 분기가 충돌합니다.

현재 isError를 플레이스홀더 렌더링으로 처리해서, 인증 실패(401)에서도 섹션이 표시됩니다. PR 목표대로라면 인증 실패 시 null 반환 분기를 분리해야 합니다.

수정 예시
 export default function StatsCategorySection() {
   const { data, isLoading, isError } = useStatistics();

   const isEmpty = !data || data.total === 0;
-  const showPlaceholder = isLoading || isError || isEmpty;
+  if (isError) return null;
+  const showPlaceholder = isLoading || isEmpty;

Also applies to: 27-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/home/StatsCategorySection.tsx` around lines 24 - 26, The
current logic in StatsCategorySection.tsx treats all errors the same by
including isError in the showPlaceholder condition, but the requirement is to
return null specifically for 401 authentication errors. Separate the error
handling logic by first checking if the error is a 401 authentication failure
and return null directly from the component in that case, then only treat other
errors as part of the showPlaceholder condition. This ensures authentication
failures do not render the section at all, while other errors can display a
placeholder.

import { useState } from 'react';

export default function QueryProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

TanStack Query v5에서 브라우저 환경 useQuery의 기본 retry 동작이 HTTP 401에도 적용되는지, 401에 대해서만 retry를 비활성화하는 권장 설정 예시를 알려줘.

💡 Result:

TanStack Query v5에서 브라우저 환경의 useQuery는 기본적으로 모든 에러에 대해 3회 재시도(retry)를 수행하도록 설정되어 있습니다[1][2]. 이 동작은 HTTP 401(Unauthorized) 에러에도 동일하게 적용됩니다[3]. 즉, 별도의 설정이 없으면 401 에러가 발생해도 라이브러리가 자동으로 재시도를 시도하게 되어 불필요한 네트워크 요청이 발생할 수 있습니다[3]. HTTP 401 에러에 대해 재시도를 비활성화하는 권장 방법은 retry 옵션에 함수를 전달하여 에러 상태 코드를 확인하는 것입니다[1][2][3]. 이를 전역 설정(QueryClient)에 적용하면 애플리케이션 전반에서 일관되게 401 에러에 대한 재시도를 방지할 수 있습니다[4]. 권장 설정 예시: // QueryClient 설정 시 전역 적용 예시 const queryClient = new QueryClient({ defaultOptions: { queries: { retry: (failureCount, error) => { // 에러 객체에서 상태 코드를 확인하여 401인 경우 재시도하지 않음 // 사용하는 HTTP 클라이언트(axios, fetch 등)에 따라 에러 구조가 다를 수 있습니다 if (error instanceof Error && 'status' in error && error.status === 401) { return false; } // 401이 아니면 기본 재시도 횟수인 3회까지 재시도 return failureCount < 3; }, }, }, }); 만약 개별 쿼리에서만 이 동작을 적용하고 싶다면 useQuery 옵션에 동일한 함수를 전달하면 됩니다[2]. HTTP 클라이언트로 axios를 사용 중이라면 error.response?.status를 확인하고, fetch를 사용하는 경우 응답 객체의 status를 확인하도록 로직을 조정해야 합니다[4].

Citations:


🏁 Script executed:

# Check if the files exist and their content
fd -t f "useStatistics.ts"

Repository: I5-Project/TALKY-OWL

Length of output: 91


🏁 Script executed:

# Check QueryProvider.tsx
fd -t f "QueryProvider.tsx"

Repository: I5-Project/TALKY-OWL

Length of output: 107


🏁 Script executed:

# Check the content around line 10 in QueryProvider.tsx
cat -n src/components/providers/QueryProvider.tsx | head -20

Repository: I5-Project/TALKY-OWL

Length of output: 664


🏁 Script executed:

# If useStatistics.ts exists, check its useQuery configuration
if [ -f "src/hooks/useStatistics.ts" ]; then
  cat -n src/hooks/useStatistics.ts
else
  echo "File not found, searching for similar files..."
  fd -t f "useStatistics" 
fi

Repository: I5-Project/TALKY-OWL

Length of output: 1295


QueryClient의 기본 retry 정책에서 401 에러를 제외하도록 설정이 필요합니다.

현재 코드에서 useStatistics.tsuseQuery는 retry 설정이 없고, QueryProvider.tsxQueryClient도 기본 옵션만 사용 중입니다. TanStack Query v5는 기본적으로 모든 에러(401 포함)에 대해 3회 자동 재시도를 수행하므로, 비로그인(401) 케이스에서 불필요한 API 호출이 3회 반복됩니다. PR 목표인 "401 시 null 렌더"를 효과적으로 구현하려면 QueryProvider에서 QueryClient 초기화 시 retry 옵션에 함수를 전달하여 401 에러는 재시도하지 않도록 설정하세요.

권장 설정 예시
const [queryClient] = useState(() => 
  new QueryClient({
    defaultOptions: {
      queries: {
        retry: (failureCount, error) => {
          if (error instanceof Error && 'status' in error && error.status === 401) {
            return false;
          }
          return failureCount < 3;
        },
      },
    },
  })
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/providers/QueryProvider.tsx` at line 10, The QueryClient
initialization in the useState hook needs a custom retry policy to prevent
unnecessary retries on 401 unauthorized errors. Modify the QueryClient
constructor to include a defaultOptions parameter with a queries retry function
that checks the error status and returns false if the error status is 401, while
allowing other errors to be retried up to 3 times. This prevents TanStack Query
v5's default behavior of retrying all errors including 401 responses, which
causes unnecessary API calls when users are not logged in.

@wjdalss21

Copy link
Copy Markdown
Collaborator Author

feature/homepage-jm (PR #47)에 통계 섹션이 통합되어 별도 PR이 불필요해졌습니다. PR #47로 대체합니다.

@wjdalss21 wjdalss21 closed this Jun 18, 2026
@evenif99
evenif99 deleted the feature/homepage-statistics-jm branch June 25, 2026 04:13
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.

1 participant