-
Notifications
You must be signed in to change notification settings - Fork 1
Fix/113 헤더, 푸터, 메인 페이지 이슈 해결 #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough이 변경사항은 검색 기능의 URL 기반 상태 관리로의 전환, 배너/검색바/경험 리스트 컴포넌트의 prop 구조 변경, 로딩/스켈레톤 UI 추가, 헤더/푸터의 UI 및 소셜 아이콘 개편, 마이페이지와 전역 사용자 상태 동기화, 미들웨어의 로그인 상태 판별 로직 개선 등을 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant SearchBar
participant Router
participant BannerSection
participant ExperienceList
User->>SearchBar: 검색어 입력 후 제출
SearchBar->>Router: /?q=검색어로 이동
Router->>BannerSection: keyword prop 전달
Router->>ExperienceList: keyword prop 전달
ExperienceList->>ExperienceList: keyword 기반 데이터 fetch 및 렌더링
sequenceDiagram
participant User
participant ExperienceList
participant SkeletonCard
User->>ExperienceList: 페이지 진입/검색
ExperienceList->>SkeletonCard: 로딩 중 스켈레톤 카드 8개 렌더
ExperienceList->>ExperienceList: 데이터 수신 시 실제 카드 렌더
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~18 minutes Possibly related issues
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
🧰 Additional context used🧬 Code Graph Analysis (1)src/app/(with-header)/page.tsx (1)
🔇 Additional comments (4)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🔭 Outside diff range comments (2)
src/components/Footer.tsx (1)
10-10: 모바일 버전 저작권 텍스트 업데이트가 누락되었습니다.데스크톱 버전은 "@NoSleepNoBugs - 2025"로 업데이트되었지만, 모바일 버전은 여전히 "©codeit - 2023"으로 남아있어 일관성이 부족합니다.
다음과 같이 수정해주세요:
- <div className='text-center md:text-left'>©codeit - 2023</div> + <div className='text-center md:text-left'>@NoSleepNoBugs - 2025</div>src/components/Header.tsx (1)
66-71: 메인 헤더의 로고 링크에 onClick 핸들러가 누락되었습니다.하이드레이션 후 렌더링되는 메인 헤더의 로고에는 onClick 핸들러가 없어서 검색 후 로고 클릭 시 쿼리 파라미터가 제거되지 않을 것 같습니다.
다음과 같이 수정하여 일관성을 유지하세요:
<Link + onClick={handleLogoClick} href='/' className='flex items-center gap-2 text-xl font-bold text-gray-800' >
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
public/assets/img/main-banner.jpgis excluded by!**/*.jpg
📒 Files selected for processing (13)
public/assets/svg/github.tsx(1 hunks)src/app/(with-header)/components/BannerSection.tsx(3 hunks)src/app/(with-header)/components/ExperienceList.tsx(6 hunks)src/app/(with-header)/components/PopularExperiences.tsx(3 hunks)src/app/(with-header)/components/SearchBar.tsx(1 hunks)src/app/(with-header)/components/Skeletons/ExperienceCardSkeleton.tsx(1 hunks)src/app/(with-header)/components/Skeletons/PopularCardSkeleton.tsx(1 hunks)src/app/(with-header)/page.tsx(1 hunks)src/components/Footer.tsx(2 hunks)src/components/Header.tsx(3 hunks)src/components/ProfileDropdown.tsx(1 hunks)src/hooks/useMyPageQueries.ts(5 hunks)src/middleware.ts(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/middleware.ts (1)
src/app/(non-header)/login/action.ts (1)
Login(41-100)
src/app/(with-header)/components/ExperienceList.tsx (3)
src/app/api/experiences/getExperiences.ts (2)
ExperienceListResult(19-22)getExperiences(28-51)src/app/(with-header)/components/Skeletons/ExperienceCardSkeleton.tsx (1)
ExperienceCardSkeleton(1-15)src/app/(with-header)/components/ExperienceCard.tsx (1)
ExperienceCard(11-44)
src/app/(with-header)/components/PopularExperiences.tsx (3)
src/app/api/experiences/getPopularExperiences.ts (1)
getPopularExperiences(11-21)src/app/(with-header)/components/Skeletons/PopularCardSkeleton.tsx (1)
PopularCardSkeleton(1-15)src/app/(with-header)/components/PopularCard.tsx (1)
PopularCard(11-40)
🪛 Biome (2.1.2)
public/assets/svg/github.tsx
[error] 4-11: Alternative text title element cannot be empty
For accessibility purposes, SVGs should have an alternative text, provided via title element. If the svg element has role="img", you should add the aria-label or aria-labelledby attribute.
(lint/a11y/noSvgWithoutTitle)
🪛 GitHub Actions: CI
src/hooks/useMyPageQueries.ts
[error] 51-51: @typescript-eslint/no-unused-vars: 'setGlobalUser' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]/u.
src/app/(with-header)/page.tsx
[error] 3-3: @typescript-eslint/no-unused-vars: 'useRouter' is defined but never used. Allowed unused vars must match /^[A-Z_]/u.
[error] 3-3: unused-imports/no-unused-imports: 'useRouter' is defined but never used.
🔇 Additional comments (34)
public/assets/svg/github.tsx (1)
1-21: 전반적인 컴포넌트 구조는 적절합니다.GitHub 아이콘 SVG 컴포넌트의 구현이 잘 되어있고, props 구조와 기본값 설정이 적절합니다.
src/components/Footer.tsx (3)
14-19: 내비게이션 링크 기능이 제거된 이유를 확인해주세요.Privacy Policy와 FAQ 링크가 앵커 태그에서 일반 div로 변경되어 클릭 기능이 사라졌습니다. 의도적인 변경인지 확인이 필요합니다.
만약 일시적으로 링크를 비활성화하려는 것이라면, 다음과 같이 접근성을 고려한 구현을 권장합니다:
- <div className='transition-colors'> + <button className='transition-colors cursor-not-allowed opacity-50' disabled> Privacy Policy - </div> + </button>Also applies to: 31-36
41-43: GitHub 링크 구현이 적절합니다.새 탭에서 열리도록 하는 target='_blank'와 보안을 위한 rel='noopener noreferrer' 속성이 올바르게 설정되었고, 호버 효과도 잘 적용되었습니다.
1-1: GitHub 아이콘 컴포넌트 import 경로가 적절합니다.새로 생성된 IconGithub 컴포넌트가 올바르게 import되고 사용되었습니다.
src/app/(with-header)/components/Skeletons/ExperienceCardSkeleton.tsx (1)
1-15: 스켈레톤 컴포넌트 구현이 잘 되었습니다.실제 ExperienceCard와 동일한 구조를 가지고 있으며, 반응형 높이값도 적절히 설정되어 있습니다.
animate-pulse클래스를 사용한 로딩 애니메이션과 회색 배경으로 로딩 상태를 명확하게 표현했습니다.src/app/(with-header)/components/Skeletons/PopularCardSkeleton.tsx (1)
1-15: 인기 카드 스켈레톤 구현이 우수합니다.그라디언트 오버레이와 절대 위치 지정을 통해 실제 PopularCard의 구조를 잘 모방했습니다. 반응형 크기 조정과 적절한 텍스트 플레이스홀더 배치로 로딩 중에도 일관된 UI를 제공합니다.
src/components/ProfileDropdown.tsx (1)
45-45: 프로필 이미지 스타일링 개선이 적절합니다.명시적인 크기 지정(
w-32 h-32)과object-cover속성 추가로 이미지의 일관된 크기와 비율을 보장합니다. 다양한 크기의 프로필 이미지가 깔끔하게 표시될 것입니다.src/app/(with-header)/components/ExperienceList.tsx (4)
11-11: 스켈레톤 컴포넌트 import 추가가 적절합니다.로딩 상태 개선을 위한 ExperienceCardSkeleton 컴포넌트를 올바르게 import했습니다.
30-40: useQuery 간소화와 키워드 처리 개선이 좋습니다.
error구조분해를 제거하고keyword처리를undefined로 명시적으로 설정한 것이 깔끔합니다. API 호출 시 falsy 값 처리가 더 명확해졌습니다.
57-59: 검색 결과 없음 메시지 조건 개선이 우수합니다.
!isLoading조건을 추가하여 로딩 중에는 "검색 결과가 없습니다" 메시지가 표시되지 않도록 한 것이 사용자 경험을 개선합니다.
92-108: 스켈레톤 로딩 구현이 훌륭합니다.항상 그리드 컨테이너를 렌더링하고 로딩 상태에 따라 스켈레톤 또는 실제 카드를 조건부로 렌더링하는 방식이 레이아웃 안정성을 크게 향상시킵니다. 8개의 스켈레톤 카드로 일관된 로딩 경험을 제공합니다.
src/app/(with-header)/components/BannerSection.tsx (4)
5-9: Props 인터페이스 변경이 아키텍처 개선에 적절합니다.
onSearch콜백에서keyword문자열로 변경한 것은 URL 기반 상태 관리로의 전환을 잘 반영합니다. 컴포넌트가 더 단순해지고 props drilling이 줄어듭니다.
18-18: 배너 이미지 경로 업데이트가 적절합니다.테스트 이미지(
/test/image1.png)에서 실제 메인 배너 이미지(/assets/img/main-banner.jpg)로 변경한 것이 프로덕션 준비에 적합합니다.
27-32: 배너 콘텐츠 업데이트가 매력적입니다.오로라와 북극 감성을 테마로 한 새로운 텍스트가 시각적으로 매력적이고 브랜드 메시지를 잘 전달합니다. 이모지 사용도 친근한 느낌을 줍니다.
35-35: SearchBar props 전달 방식 개선이 일관성 있습니다.
keywordprop을 전달하는 방식이 다른 컴포넌트들의 변경사항과 일관성을 유지하며, URL 기반 검색 상태 관리 패턴에 잘 맞습니다.src/components/Header.tsx (5)
4-4: useRouter 임포트가 적절히 추가되었습니다.로고 클릭 시 쿼리 파라미터를 제거하여 검색어를 초기화하는 기능을 위해 useRouter가 올바르게 추가되었습니다.
16-16: 사용자 스토어 구조분해할당이 개선되었습니다.hasHydrated 상태를 포함하여 한 번의 호출로 필요한 값들을 구조분해할당하는 것이 더 효율적입니다.
23-25: 로고 클릭 핸들러가 올바르게 구현되었습니다.검색 후 로고 클릭 시 메인 페이지로 이동하면서 쿼리 파라미터를 제거하여 검색어를 초기화하는 기능이 명확하게 구현되었습니다.
38-60: 하이드레이션 전 스켈레톤 UI가 잘 구현되었습니다.하이드레이션 완료 전까지 스켈레톤 UI를 보여주어 레이아웃 시프트를 방지하고 사용자 경험을 개선하는 좋은 접근법입니다. 스켈레톤의 크기와 애니메이션도 적절합니다.
44-50: 스켈레톤 상태에서도 로고 클릭 기능이 유지됩니다.하이드레이션 전에도 로고 클릭으로 메인 페이지 이동이 가능하도록 handleLogoClick이 연결된 것이 좋습니다.
src/app/(with-header)/components/SearchBar.tsx (5)
5-6: 필요한 훅들이 적절히 임포트되었습니다.useRouter와 useEffect 임포트가 새로운 URL 기반 상태 관리 패턴에 맞게 올바르게 추가되었습니다.
8-10: 인터페이스가 URL 기반 상태 관리에 맞게 개선되었습니다.콜백 기반에서 keyword prop을 받는 방식으로 변경되어 더 예측 가능하고 URL과 동기화된 상태 관리가 가능해졌습니다.
12-14: 컴포넌트 초기화가 올바르게 구현되었습니다.keyword prop으로 searchValue를 초기화하고 useRouter를 클라이언트에서 적절히 선언했습니다.
17-22: 검색 제출 로직이 잘 구현되었습니다.빈 검색어 검증, URL 인코딩, 라우터 네비게이션이 모두 적절히 처리되었습니다. 쿼리 파라미터를 사용한 상태 관리 패턴이 일관성 있게 적용되었습니다.
25-27: 외부 keyword 변경에 대한 동기화가 적절합니다.useEffect를 통해 외부에서 keyword prop이 변경될 때 내부 상태를 동기화하는 것이 올바른 패턴입니다.
src/middleware.ts (3)
7-8: 미들웨어 로직 변경에 대한 명확한 문서화가 되었습니다.기존 로그인/회원가입 페이지 접근 차단 로직을 제거한 이유가 주석으로 잘 설명되어 있습니다.
21-24: 하이드레이션 타이밍 문제에 대한 적절한 해결책입니다.서버 사이드에서 accessToken 존재 여부만으로 로그인 상태를 판단하면 클라이언트 하이드레이션 전에 잘못된 리다이렉트가 발생할 수 있습니다. 이를 클라이언트 사이드로 위임하는 것이 올바른 접근법입니다.
27-33: 보호된 경로에 대한 접근 제어가 적절히 유지되었습니다./mypage 경로에 대한 토큰 기반 접근 제어 로직은 그대로 유지되어 필요한 보안 기능은 계속 작동합니다.
src/hooks/useMyPageQueries.ts (2)
12-12: 전역 상태 동기화를 위한 스토어 임포트가 적절합니다.마이페이지에서 업데이트된 사용자 정보를 헤더에 반영하기 위한 전역 상태 동기화 접근법이 좋습니다.
96-96: useUploadProfileImage의 전역 상태 동기화가 올바르게 구현되었습니다.setGlobalUser 선언과 의존성 배열 추가가 적절히 처리되었습니다.
Also applies to: 150-150
src/app/(with-header)/page.tsx (2)
9-11: URL 기반 상태 관리로의 전환이 잘 구현되었습니다.useSearchParams를 사용하여 URL에서 검색 키워드를 가져오고 검색 모드를 판단하는 로직이 명확하고 올바릅니다.
15-23: 컴포넌트 구조와 조건부 렌더링이 적절합니다.keyword를 BannerSection으로 전달하고 검색 모드에 따라 다른 UI를 보여주는 로직이 잘 구현되었습니다.
src/app/(with-header)/components/PopularExperiences.tsx (2)
7-11: import 구문 정리가 잘 되었습니다.아이콘 import를 그룹화하고 새로운 PopularCardSkeleton 컴포넌트를 추가한 것이 좋습니다. 코드 구성이 더욱 명확해졌습니다.
41-41: JSX 주석 추가로 가독성 향상섹션별로 주석을 추가하여 코드 가독성이 개선되었습니다.
Also applies to: 50-50
| @@ -0,0 +1,20 @@ | |||
| import React from 'react'; | |||
|
|
|||
| const IconGithub = ({ size = 24, color = '#fff', ...props }) => ( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
TypeScript 타입 정의 추가가 필요합니다.
컴포넌트의 props에 대한 TypeScript 인터페이스가 없어 타입 안전성이 보장되지 않습니다.
다음과 같이 props 타입을 정의해주세요:
+interface IconGithubProps extends React.SVGProps<SVGSVGElement> {
+ size?: number;
+ color?: string;
+}
+
-const IconGithub = ({ size = 24, color = '#fff', ...props }) => (
+const IconGithub: React.FC<IconGithubProps> = ({ size = 24, color = '#fff', ...props }) => (📝 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.
| const IconGithub = ({ size = 24, color = '#fff', ...props }) => ( | |
| interface IconGithubProps extends React.SVGProps<SVGSVGElement> { | |
| size?: number; | |
| color?: string; | |
| } | |
| const IconGithub: React.FC<IconGithubProps> = ({ size = 24, color = '#fff', ...props }) => ( |
🤖 Prompt for AI Agents
In public/assets/svg/github.tsx at line 3, the IconGithub component lacks
TypeScript type definitions for its props, reducing type safety. Define a
TypeScript interface for the props specifying size as a number and color as a
string, then apply this interface to the component's props parameter to ensure
proper type checking.
| <svg | ||
| width={size} | ||
| height={size} | ||
| viewBox="0 0 98 96" | ||
| fill={color} | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| {...props} | ||
| > | ||
| <path | ||
| fillRule="evenodd" | ||
| clipRule="evenodd" | ||
| d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a47 47 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0" | ||
| /> | ||
| </svg> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
접근성 개선을 위해 title 요소 또는 aria-label 추가가 필요합니다.
정적 분석 도구에서 지적한 대로, 접근성을 위해 SVG에 대한 대체 텍스트가 필요합니다.
다음 중 하나의 방법으로 해결할 수 있습니다:
<svg
width={size}
height={size}
viewBox="0 0 98 96"
fill={color}
xmlns="http://www.w3.org/2000/svg"
+ aria-label="GitHub"
{...props}
>
+ <title>GitHub</title>
<path📝 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.
| <svg | |
| width={size} | |
| height={size} | |
| viewBox="0 0 98 96" | |
| fill={color} | |
| xmlns="http://www.w3.org/2000/svg" | |
| {...props} | |
| > | |
| <path | |
| fillRule="evenodd" | |
| clipRule="evenodd" | |
| d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a47 47 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0" | |
| /> | |
| </svg> | |
| <svg | |
| width={size} | |
| height={size} | |
| viewBox="0 0 98 96" | |
| fill={color} | |
| xmlns="http://www.w3.org/2000/svg" | |
| aria-label="GitHub" | |
| {...props} | |
| > | |
| <title>GitHub</title> | |
| <path | |
| fillRule="evenodd" | |
| clipRule="evenodd" | |
| d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a47 47 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0" | |
| /> | |
| </svg> |
🧰 Tools
🪛 Biome (2.1.2)
[error] 4-11: Alternative text title element cannot be empty
For accessibility purposes, SVGs should have an alternative text, provided via title element. If the svg element has role="img", you should add the aria-label or aria-labelledby attribute.
(lint/a11y/noSvgWithoutTitle)
🤖 Prompt for AI Agents
In public/assets/svg/github.tsx around lines 4 to 17, the SVG element lacks
accessibility features such as a title element or aria-label, which are needed
for screen readers. To fix this, add a <title> element inside the SVG with a
descriptive text or add an aria-label attribute to the SVG tag describing the
image. This will provide alternative text for assistive technologies and improve
accessibility.
| const { data, isLoading } = useQuery<ExperienceListResult>({ | ||
| queryKey: ['experiences', currentPage, selectedCategory, sortOption, keyword], | ||
| queryFn: () => | ||
| getExperiences({ | ||
| page: currentPage, | ||
| sort: sortOption, | ||
| category: selectedCategory, | ||
| keyword, | ||
| keyword: keyword || undefined, | ||
| }), | ||
| placeholderData: (prev) => prev, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
에러 처리 고려사항
error 구조분해를 제거했는데, 사용자에게 API 에러 상황을 알려주는 방법을 고려해보시기 바랍니다. 현재는 로딩이 실패해도 빈 목록만 표시됩니다.
다음 스크립트로 에러 처리 패턴을 확인해보겠습니다:
🏁 Script executed:
#!/bin/bash
# Description: Check error handling patterns in similar components
# Search for error handling in other list components
rg -A 10 -B 5 "useQuery.*error" --type tsxLength of output: 72
다음 스크립트로 TSX 파일들에서 useQuery 호출 및 error 구조분해/사용 패턴을 확인해 보겠습니다.
🏁 Script executed:
#!/bin/bash
# Description: TSX 컴포넌트에서 useQuery 호출 및 error 구조분해 패턴 검색
# 1. useQuery 사용 파일 목록
echo "■ useQuery 사용 TSX 파일:"
rg -l "useQuery(" -g '*.tsx' || true
echo ""
# 2. useQuery 호출 내 error 구조분해 또는 error 사용 패턴
echo "■ useQuery 호출에서 error 구조분해/사용 패턴:"
rg -n -C2 "{[^}]*error" -g '*.tsx' || trueLength of output: 378
TSX 파일에서 useQuery 호출과 error 구조분해/사용 패턴을 올바르게 검색하도록 정규식을 수정한 스크립트를 실행해 보겠습니다.
🏁 Script executed:
#!/bin/bash
# TSX 컴포넌트에서 useQuery 호출 및 error 구조분해/사용 패턴 검색
echo "■ useQuery 사용 파일:"
rg -l "useQuery" -g '*.tsx' || true
echo ""
echo "■ useQuery 호출 라인:"
rg -n "useQuery" -g '*.tsx' || true
echo ""
echo "■ error 구조분해 사용 패턴 (예: const { data, error } = useQuery(...)):"
rg -n "const \\{[^\\}]*error[^\\}]*\\}" -g '*.tsx' || trueLength of output: 2270
ExperienceList.tsx에 에러 핸들링 추가 필요
다른 목록 컴포넌트(e.g. PopularExperiences.tsx)에서는 useQuery 호출 시 error를 구조분해하고 에러 UI를 렌더링하고 있습니다. ExperienceList도 동일하게 처리해 주세요.
• 파일: src/app/(with-header)/components/ExperienceList.tsx
- 30번째 줄:
- const { data, isLoading } = useQuery<ExperienceListResult>({ + const { data, isLoading, error } = useQuery<ExperienceListResult>({
error가 있을 때 사용자에게 안내 메시지나 에러 컴포넌트를 보여주도록 추가 처리if (error) { return <ErrorMessage message="경험 목록을 불러오는 중 오류가 발생했습니다. 다시 시도해 주세요." />; }
🤖 Prompt for AI Agents
In src/app/(with-header)/components/ExperienceList.tsx around lines 30 to 40,
the useQuery hook destructures data and isLoading but does not handle the error
state. Update the destructuring to also include error from useQuery, then add a
conditional check for error. If error exists, return an ErrorMessage component
with a user-friendly message indicating that loading the experience list failed
and suggesting to retry.
| {error ? ( | ||
| <p className="text-red-500 text-sm">인기 체험을 불러오는 데 실패했습니다 😢</p> | ||
| ) : isLoading || !data ? ( | ||
| Array.from({ length: 4 }).map((_, idx) => ( | ||
| <div key={idx} className="flex-shrink-0 card"> | ||
| <PopularCardSkeleton /> | ||
| </div> | ||
| )) | ||
| ) : ( | ||
| data.activities.map((exp) => ( | ||
| <div key={exp.id} className='flex-shrink-0 card'> | ||
| <Link | ||
| href={`/activities/${exp.id}`} | ||
| className='flex-shrink-0 card' | ||
| > | ||
| <PopularCard | ||
| imageUrl={exp.bannerImageUrl} | ||
| price={exp.price} | ||
| rating={exp.rating} | ||
| reviews={exp.reviewCount} | ||
| title={exp.title} | ||
| /> | ||
| </Link> | ||
| </div> | ||
| )) | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
로딩 상태 처리 개선이 우수합니다.
early return 패턴에서 조건부 렌더링으로 변경한 것이 좋습니다. 다음과 같은 장점이 있습니다:
- 일관된 레이아웃 구조 유지
- 로딩 중 4개의 스켈레톤 UI로 사용자 경험 향상
- 에러 상태에 대한 명확한 피드백 제공
다만 한 가지 개선사항이 있습니다:
65-68번 라인에서 card 클래스가 중복 적용되고 있습니다:
- <div key={exp.id} className='flex-shrink-0 card'>
- <Link
- href={`/activities/${exp.id}`}
- className='flex-shrink-0 card'
- >
+ <div key={exp.id} className='flex-shrink-0 card'>
+ <Link href={`/activities/${exp.id}`}>📝 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.
| {error ? ( | |
| <p className="text-red-500 text-sm">인기 체험을 불러오는 데 실패했습니다 😢</p> | |
| ) : isLoading || !data ? ( | |
| Array.from({ length: 4 }).map((_, idx) => ( | |
| <div key={idx} className="flex-shrink-0 card"> | |
| <PopularCardSkeleton /> | |
| </div> | |
| )) | |
| ) : ( | |
| data.activities.map((exp) => ( | |
| <div key={exp.id} className='flex-shrink-0 card'> | |
| <Link | |
| href={`/activities/${exp.id}`} | |
| className='flex-shrink-0 card' | |
| > | |
| <PopularCard | |
| imageUrl={exp.bannerImageUrl} | |
| price={exp.price} | |
| rating={exp.rating} | |
| reviews={exp.reviewCount} | |
| title={exp.title} | |
| /> | |
| </Link> | |
| </div> | |
| )) | |
| )} | |
| {error ? ( | |
| <p className="text-red-500 text-sm">인기 체험을 불러오는 데 실패했습니다 😢</p> | |
| ) : isLoading || !data ? ( | |
| Array.from({ length: 4 }).map((_, idx) => ( | |
| <div key={idx} className="flex-shrink-0 card"> | |
| <PopularCardSkeleton /> | |
| </div> | |
| )) | |
| ) : ( | |
| data.activities.map((exp) => ( | |
| <div key={exp.id} className='flex-shrink-0 card'> | |
| <Link href={`/activities/${exp.id}`}> | |
| <PopularCard | |
| imageUrl={exp.bannerImageUrl} | |
| price={exp.price} | |
| rating={exp.rating} | |
| reviews={exp.reviewCount} | |
| title={exp.title} | |
| /> | |
| </Link> | |
| </div> | |
| )) | |
| )} |
🤖 Prompt for AI Agents
In src/app/(with-header)/components/PopularExperiences.tsx between lines 55 and
80, the 'card' class is applied twice on the same element in lines 65-68,
causing redundant class usage. Remove the duplicate 'card' class from either the
wrapping div or the Link component to avoid unnecessary repetition while
preserving the intended styling and layout.
| export const useUpdateProfile = () => { | ||
| const queryClient = useQueryClient(); | ||
| const { setUser, setLoading, setError } = useMyPageStore(); | ||
| const setGlobalUser = useUserStore((state) => state.setUser); // 헤더 상태 갱신 함수 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useUpdateProfile에서 setGlobalUser가 사용되지 않고 있습니다.
파이프라인 오류에서 보듯이 setGlobalUser가 선언되었지만 사용되지 않고 있습니다. 프로필 업데이트 후 전역 상태도 동기화해야 합니다.
useEffect 내에서 setGlobalUser를 호출하도록 수정하세요:
if (mutation.isSuccess && mutation.data) {
setUser(mutation.data);
+ setGlobalUser(mutation.data); // 전역 상태도 동기화
setLoading(false);
// 캐시 업데이트
queryClient.setQueryData(QUERY_KEYS.PROFILE, mutation.data);
alert('프로필이 성공적으로 업데이트되었습니다!');
}그리고 의존성 배열에도 setGlobalUser를 추가해야 합니다:
}, [
mutation.isPending,
mutation.isSuccess,
mutation.isError,
mutation.data,
mutation.error,
queryClient,
setUser,
+ setGlobalUser,
setLoading,
setError,
]);📝 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.
| const setGlobalUser = useUserStore((state) => state.setUser); // 헤더 상태 갱신 함수 | |
| useEffect(() => { | |
| if (mutation.isSuccess && mutation.data) { | |
| setUser(mutation.data); | |
| setGlobalUser(mutation.data); // 전역 상태도 동기화 | |
| setLoading(false); | |
| // 캐시 업데이트 | |
| queryClient.setQueryData(QUERY_KEYS.PROFILE, mutation.data); | |
| alert('프로필이 성공적으로 업데이트되었습니다!'); | |
| } | |
| }, [ | |
| mutation.isPending, | |
| mutation.isSuccess, | |
| mutation.isError, | |
| mutation.data, | |
| mutation.error, | |
| queryClient, | |
| setUser, | |
| setGlobalUser, | |
| setLoading, | |
| setError, | |
| ]); |
🧰 Tools
🪛 GitHub Actions: CI
[error] 51-51: @typescript-eslint/no-unused-vars: 'setGlobalUser' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]/u.
🤖 Prompt for AI Agents
In src/hooks/useMyPageQueries.ts at line 51, the setGlobalUser function is
declared but not used, causing the global user state to not update after profile
changes. To fix this, call setGlobalUser inside the useEffect hook that handles
profile updates to synchronize the global state, and add setGlobalUser to the
useEffect dependency array to ensure proper effect execution.
| setLoading(false); | ||
| setGlobalUser(updatedUser); // [추가] 헤더 상태(authStore)도 동기화 | ||
|
|
||
| queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 중복이 발생했습니다.
Lines 128-132에서 setLoading(false)와 queryClient.setQueryData 호출이 중복되어 있습니다.
중복된 코드를 제거하세요:
setUser(updatedUser);
queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser);
- setLoading(false);
setGlobalUser(updatedUser); // [추가] 헤더 상태(authStore)도 동기화
- queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser);
-
setLoading(false);
alert('프로필 이미지가 성공적으로 업로드되었습니다!');📝 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.
| setLoading(false); | |
| setGlobalUser(updatedUser); // [추가] 헤더 상태(authStore)도 동기화 | |
| queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); | |
| setUser(updatedUser); | |
| queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); | |
| setGlobalUser(updatedUser); // [추가] 헤더 상태(authStore)도 동기화 | |
| setLoading(false); | |
| alert('프로필 이미지가 성공적으로 업로드되었습니다!'); |
🤖 Prompt for AI Agents
In src/hooks/useMyPageQueries.ts around lines 128 to 132, the calls to
setLoading(false) and queryClient.setQueryData are duplicated. Remove the
redundant calls so that each function is only called once to eliminate code
duplication.
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/app/(with-header)/page.tsx(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: CI
src/app/(with-header)/page.tsx
[warning] 3-17: simple-import-sort/imports and react/jsx-sort-props: Run autofix to sort imports; shorthand props must be listed before all other props.
src/app/(with-header)/page.tsx
Outdated
| {isSearchMode ? ( | ||
| <ExperienceList keyword={keyword} isSearchMode /> | ||
| ) : ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSX prop 순서로 인한 react/jsx-sort-props 경고 수정
Shorthand prop(값 없이 존재 자체가 true인 prop)은 다른 prop보다 앞에 와야 합니다.
- <ExperienceList keyword={keyword} isSearchMode />
+ <ExperienceList isSearchMode keyword={keyword} />📝 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.
| {isSearchMode ? ( | |
| <ExperienceList keyword={keyword} isSearchMode /> | |
| ) : ( | |
| {isSearchMode ? ( | |
| <ExperienceList isSearchMode keyword={keyword} /> | |
| ) : ( |
🤖 Prompt for AI Agents
In src/app/(with-header)/page.tsx around lines 16 to 18, the shorthand prop
isSearchMode should be placed before the keyword prop to comply with the
react/jsx-sort-props rule. Reorder the props so that isSearchMode appears first,
followed by keyword.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/hooks/useMyPageQueries.ts (2)
51-87: 이전 리뷰 코멘트의 문제가 여전히 해결되지 않았습니다.setGlobalUser가 여전히 사용되지 않고 있어 프로필 업데이트 후 전역 상태가 동기화되지 않습니다. useEffect 내에서 setGlobalUser를 호출해야 합니다.
mutation.isSuccess 블록에서 setGlobalUser를 호출하도록 수정하세요:
if (mutation.isSuccess && mutation.data) { setUser(mutation.data); + setGlobalUser(mutation.data); // 전역 상태도 동기화 setLoading(false); // 캐시 업데이트 queryClient.setQueryData(QUERY_KEYS.PROFILE, mutation.data); alert('프로필이 성공적으로 업데이트되었습니다!'); }
129-133: 코드 중복이 발생했습니다.Lines 129, 132, 134에서 setLoading(false)와 queryClient.setQueryData 호출이 중복되어 있습니다.
중복된 코드를 제거하세요:
setUser(updatedUser); queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); - setLoading(false); setGlobalUser(updatedUser); // [추가] 헤더 상태(authStore)도 동기화 - queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); - setLoading(false); alert('프로필 이미지가 성공적으로 업로드되었습니다!');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/hooks/useMyPageQueries.ts(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/hooks/useMyPageQueries.ts (1)
src/app/(with-header)/mypage/profile/page.tsx (1)
ProfilePage(14-183)
🪛 GitHub Actions: CI
src/hooks/useMyPageQueries.ts
[warning] 3-3: simple-import-sort/imports: Run autofix to sort these imports.
🔇 Additional comments (1)
src/hooks/useMyPageQueries.ts (1)
97-97: 전역 상태 동기화 구현이 올바릅니다.useUploadProfileImage hook에서 setGlobalUser 함수를 올바르게 선언했습니다. 이는 프로필 이미지 업로드 후 헤더의 사용자 상태를 실시간으로 업데이트하는 데 필요합니다.
| import { UpdateProfileRequest } from '@/types/mypageTypes'; | ||
| import useMyPageStore from '@/stores/MyPage/useMyPageStore'; | ||
| import { useEffect } from 'react'; | ||
| import useUserStore from '@/stores/authStore'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import 정렬 문제 수정이 필요합니다.
파이프라인 실패 로그에서 지적된 대로 import 구문의 정렬을 수정해야 합니다.
-import useUserStore from '@/stores/authStore';이 import를 다른 import들과 함께 정렬된 순서로 배치하세요.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/hooks/useMyPageQueries.ts at line 12, the import statement for
useUserStore is not properly sorted. Rearrange this import together with the
other import statements in the file so that all imports are in a consistent,
sorted order, typically alphabetically or following the project's import sorting
conventions.
evaain706
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다!
헤더 플리커링 이슈도 hasHydrated를 사용해서 잘 해결해주셨고
푸터와 스켈레톤도 잘 수정해주셨네요!
minimo-9
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
메인 배너가 너무 이쁘네요!
LeeCh0129
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다!
📌 변경 사항 개요
멘토링 때 발견된 헤더, 푸터, 메인 페이지 이슈 해결
📝 상세 내용
🔗 관련 이슈
🖼️ 스크린샷(선택사항)
💡 참고 사항
Summary by CodeRabbit
New Features
버그 수정
UI/UX 개선
기능 변경
기타