Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🏷️ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (17)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the Walkthrough메인 페이지 헤더를 텍스트에서 세 개의 아이콘으로 교체하고, 마이페이지용 프로필·저장된 이벤트·엽서 컨테이너 및 로그아웃 팝업을 새로 추가했습니다. 공유 컴포넌트 재내보내기가 확장되고 전역 스타일에 스크롤바 숨김 클래스가 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant MyPage as MyPage 컴포넌트
participant ProfilePhoto as ProfilePhoto
participant PostcardContainer as PostcardContainer
participant PopupSet as PopupSet
User->>MyPage: 마이페이지 접속
MyPage->>ProfilePhoto: 프로필 렌더 요청
ProfilePhoto-->>MyPage: 이미지/플레이스홀더 반환
MyPage->>PostcardContainer: 엽서 목록 전달
PostcardContainer-->>MyPage: 슬롯/썸네일 렌더
User->>MyPage: 로그아웃 버튼 클릭
MyPage->>PopupSet: 팝업 표시 상태 변경
PopupSet-->>MyPage: 확인/취소 결과 반환
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
|
🏷️ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
|
🏷️ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/shared/components/button/AddressCopy.tsx (1)
50-60: 키보드 접근성 부족: 클릭 가능한 div에 role/tabIndex/키 핸들러 필요현재 마우스만 동작합니다. Space/Enter로도 복사가 가능하도록 보완해 주세요. 시멘틱 변경(버튼 태그) 대신 비침투적 수정안 제안합니다.
return ( - <div - onClick={handleCopy} + <div + role="button" + tabIndex={0} + onClick={handleCopy} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleCopy(); + } + }} className={cn( addressCopyStyle({ variant }), 'w-full h-[4rem] px-[1.3rem] py-[1rem] gap-[0.4rem]', 'cursor-pointer select-none', className, )} {...props} >
🧹 Nitpick comments (5)
src/pages/mypage/index.tsx (1)
49-49: 엽서 데이터 패칭 로직을 추가하세요.현재 빈 배열이 전달되고 있습니다. 실제 사용자가 저장한 엽서 데이터를 가져오는 로직이 필요합니다.
다음과 같은 구현이 필요합니다:
const [savedPostcards, setSavedPostcards] = useState<string[]>([]); useEffect(() => { // TODO: 저장한 엽서 목록 API 호출 }, []); // JSX에서 <PostcardContainer postcards={savedPostcards} />이 기능 구현을 위한 이슈를 생성해드릴까요?
src/pages/mypage/components/PostcardContainer.tsx (3)
24-31: bordered=false 처리 시border-none대신border-0권장베이스 클래스에
border border-pink-200가 있어border-none만으로는 width가 남을 수 있습니다.border-0가 보다 명시적입니다.- bordered: { - false: 'border-none', - }, + bordered: { + false: 'border-0', + },
86-95: 폼 내부 포함 시 의도치 않은 submit 방지button 기본 type은
submit입니다. 안전하게type="button"을 지정해 주세요.- <button + <button + type="button" key={idx} onClick={() => src && onClickCard?.(idx)}
7-12:postcards를 선택 프로퍼티로 전환하면 사용성이 좋아집니다런타임 기본값을 이미
[]로 처리하고 있어 타입도 선택으로 맞추는 편이 자연스럽습니다.-interface PostcardContainerProps { - postcards: string[]; +interface PostcardContainerProps { + postcards?: string[];src/shared/components/button/AddressCopy.tsx (1)
61-79: 복사 상태를 스크린리더에 알려주세요
copied변화가 시각적으로만 표시됩니다.aria-live로 비가시 메시지를 추가하면 접근성이 좋아집니다.<div className='flex-shrink-0'> <Icon name={copied ? 'Check' : 'CopySimple'} size={18} color={variant === 'mint' ? 'mint-400' : 'gray-400'} /> </div> + {/* SR 전용 상태 알림 */} + <span className="sr-only" aria-live="polite" aria-atomic="true"> + {copied ? '복사됨' : ''} + </span> <span className={cn( 'text-label-lg flex-1 min-w-0', variant === 'mint' ? 'text-mint-400' : 'text-gray-400', truncate && 'truncate whitespace-nowrap overflow-hidden text-ellipsis', )} title={value} > {value} </span>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (17)
src/shared/icons/source/CalendarBlank.svgis excluded by!**/*.svgsrc/shared/icons/source/Caret.svgis excluded by!**/*.svgsrc/shared/icons/source/ChatCircle.svgis excluded by!**/*.svgsrc/shared/icons/source/Check.svgis excluded by!**/*.svgsrc/shared/icons/source/CopySimple.svgis excluded by!**/*.svgsrc/shared/icons/source/Export.svgis excluded by!**/*.svgsrc/shared/icons/source/FadersHorizontal.svgis excluded by!**/*.svgsrc/shared/icons/source/GoogleIcon.svgis excluded by!**/*.svgsrc/shared/icons/source/HeartStraight.svgis excluded by!**/*.svgsrc/shared/icons/source/HouseSimple.svgis excluded by!**/*.svgsrc/shared/icons/source/KakaoIcon.svgis excluded by!**/*.svgsrc/shared/icons/source/MapPin.svgis excluded by!**/*.svgsrc/shared/icons/source/MapPin_.svgis excluded by!**/*.svgsrc/shared/icons/source/Save.svgis excluded by!**/*.svgsrc/shared/icons/source/User.svgis excluded by!**/*.svgsrc/shared/icons/source/backto.svgis excluded by!**/*.svgsrc/shared/icons/source/x.svgis excluded by!**/*.svg
📒 Files selected for processing (11)
src/pages/auth/index.tsx(4 hunks)src/pages/index.tsx(1 hunks)src/pages/mypage/components/PostcardContainer.tsx(1 hunks)src/pages/mypage/components/ProfilePhoto.tsx(1 hunks)src/pages/mypage/index.tsx(1 hunks)src/shared/components/button/AddressCopy.tsx(3 hunks)src/shared/components/container/Card.tsx(1 hunks)src/shared/components/index.ts(1 hunks)src/shared/icons/components/icon.tsx(3 hunks)src/styles/globals.css(1 hunks)svgo.config.mjs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/shared/components/button/AddressCopy.tsx (1)
src/shared/lib/utils.ts (1)
cn(71-73)
src/pages/mypage/index.tsx (4)
src/pages/mypage/components/ProfilePhoto.tsx (1)
ProfilePhoto(11-35)src/pages/mypage/components/PostcardContainer.tsx (1)
PostcardContainer(59-111)src/shared/components/tab/BottomNav.tsx (1)
BottomNav(21-54)src/shared/components/set/PopupSet.tsx (1)
PopupSet(11-35)
src/pages/index.tsx (1)
src/shared/icons/components/icon.tsx (1)
Icon(55-110)
src/pages/auth/index.tsx (1)
src/pages/auth/components/LoginButton.tsx (1)
LoginButton(33-67)
src/pages/mypage/components/ProfilePhoto.tsx (1)
src/shared/lib/utils.ts (1)
cn(71-73)
src/pages/mypage/components/PostcardContainer.tsx (1)
src/shared/lib/utils.ts (1)
cn(71-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-deploy
🔇 Additional comments (11)
src/styles/globals.css (1)
222-223: LGTM!스크롤바 숨김 유틸리티 클래스가 잘 구현되었습니다. 크로스 브라우저 지원도 적절합니다.
svgo.config.mjs (1)
1-7: LGTM!SVG 속성 자동 조작 플러그인 제거로 Icon 컴포넌트에서
fillColorprop을 통한 명시적 제어가 가능해졌습니다. 이는 더 예측 가능한 동작을 제공합니다.src/pages/auth/index.tsx (1)
72-104: LGTM!최근 사용한 로그인 플랫폼을 추적하고 시각적으로 표시하는 기능이 잘 구현되었습니다. 각 버튼마다 독립적으로
RecentLoginBubble을 조건부 렌더링하는 구조가 깔끔합니다.src/shared/icons/components/icon.tsx (3)
48-48: LGTM!
fillColorprop 추가로 아이콘의 채우기 색상을 독립적으로 제어할 수 있게 되었습니다. 기존colorprop(stroke용)과 함께 사용하여 더 세밀한 스타일링이 가능합니다.
89-94: LGTM!CSS 변수를 활용한 스타일링 접근 방식이 우수합니다.
color와fillColor를 CSS 변수로 변환하여 SVG 내부에서 참조할 수 있도록 했으며, 기존styleprop과도 잘 병합됩니다.
97-98: LGTM!조건부
fill/stroke속성 처리가 적절합니다:
fillColor가 제공되면 CSS 변수 참조, 아니면 'none'color가 제공되면 'currentColor', 아니면 'none'이를 통해 명시적이고 예측 가능한 동작이 보장됩니다.
src/pages/mypage/index.tsx (1)
8-12: 로그아웃 로직을 구현하세요.현재 팝업만 표시되고 실제 로그아웃 처리가 없습니다. 팝업의 확인 버튼 클릭 시 다음 작업이 필요합니다:
- 인증 토큰 제거
- 사용자 상태 초기화
- 로그인 페이지로 리다이렉트
const handleConfirmLogout = async () => { // TODO: 로그아웃 API 호출 // TODO: 로컬 스토리지/쿠키에서 토큰 제거 // TODO: /auth로 리다이렉트 router.push('/auth'); };src/shared/components/container/Card.tsx (1)
14-16: 검증 불완전 - 코드베이스에서 제한된 정보만 확인됨현재 Card.tsx의 코드는 이미
w-full이 적용된 상태입니다. 다만 다음 이유로 영향 검증이 불충분합니다:
- 사용처 제한: EventCard는 mypage에서만 발견되었고, LocationCard는 실제 사용 사례를 찾을 수 없습니다.
- 단일 컨텍스트: 발견된 EventCard 사용처가 하나뿐이므로, 다양한 레이아웃 시나리오에서의 동작을 확인할 수 없습니다.
- 기존 상태 미확인: 고정 너비(
w-[17rem],w-[35.4rem])로부터의 변경 전 상태를 현재 코드에서 확인할 수 없습니다.개발자의 수동 검증이 필요합니다. 변경사항이 다양한 화면 크기와 부모 컨테이너에서 의도대로 동작하는지 확인하고, 모바일/태블릿/데스크톱 환경에서 카드 레이아웃이 정상인지 테스트해주세요.
src/shared/components/index.ts (2)
11-11: 검증 완료: 현재 코드가 정상입니다BottomNav는 named export(
export const BottomNav = () => { ... })로 정의되어 있으므로, 현재의 재‑export 방식이 올바릅니다.export { BottomNav } from './tab/BottomNav';제안된 수정(
export { default as BottomNav })은 불필요하며, 현재 코드는 변경할 사항이 없습니다.
5-10: 순환 의존성 검증 완료 - 문제 없음검증 결과, 내보낸 컴포넌트들이
@/shared/components바렐(index.ts)을 다시 import하지 않습니다. 모든 내부 import는 다음과 같이 특정 서브모듈을 지정합니다:
- PopupSet.tsx:
@/shared/components/container/Popup- LocationBubbleSet.tsx:
@/shared/components/container/LocationBubble- LocationCard.tsx, EventCard.tsx:
@/shared/components/container/Card순환 의존성이 없으므로 바렐 export 추가는 안전합니다.
src/shared/components/button/AddressCopy.tsx (1)
23-28: 검증 완료: 리뷰 의견이 코드베이스와 일치하지 않음검증 결과, 다음을 확인했습니다:
AddressCopy컴포넌트는 코드베이스 전체에서 사용되지 않음 (호출 사이트 0개)- 현재 인터페이스에
labelprop이 존재하지 않음 (이미 제거되었거나 처음부터 없었음)- 마이그레이션이 필요한 기존 호출부 없음
리뷰 의견의 우려사항(기존 호출부 파괴 가능성)은 적용되지 않습니다.
Likely an incorrect or invalid review comment.
| const handleLoginClick = (platform: string) => { | ||
| alert(`${platform} 로그인 준비중`); | ||
| console.log(`${platform} 로그인 버튼 클릭`); | ||
| setRecentPlatform(platform); | ||
| }; |
There was a problem hiding this comment.
프로덕션 코드에서 alert와 console.log를 제거하세요.
개발/테스트용 코드가 포함되어 있습니다:
- Line 12:
alert()는 사용자 경험을 해치며 프로덕션에 적합하지 않습니다. - Line 13:
console.log()는 배포 전에 제거되어야 합니다.
다음과 같이 수정하세요:
const handleLoginClick = (platform: string) => {
- alert(`${platform} 로그인 준비중`);
- console.log(`${platform} 로그인 버튼 클릭`);
+ // TODO: 실제 로그인 로직 구현
setRecentPlatform(platform);
};📝 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 handleLoginClick = (platform: string) => { | |
| alert(`${platform} 로그인 준비중`); | |
| console.log(`${platform} 로그인 버튼 클릭`); | |
| setRecentPlatform(platform); | |
| }; | |
| const handleLoginClick = (platform: string) => { | |
| // TODO: 실제 로그인 로직 구현 | |
| setRecentPlatform(platform); | |
| }; |
🤖 Prompt for AI Agents
In src/pages/auth/index.tsx around lines 11 to 15, remove the development-only
alert() and console.log() calls from handleLoginClick; preserve the
setRecentPlatform(platform) call and, if user feedback is required in
production, replace alert() with a proper non-blocking UI notification/toast and
send console logs to a telemetry/logger utility instead of using console.log().
| <Icon | ||
| name='CopySimple' | ||
| size={24} | ||
| color='mint-500' | ||
| fillColor='mint-200' | ||
| /> | ||
| <Icon | ||
| name='HeartStraight' | ||
| size={24} | ||
| color='red-300' | ||
| fillColor='red-400' | ||
| /> | ||
|
|
||
| <Icon | ||
| name='Stamp' | ||
| size={24} | ||
| color='pink-100' | ||
| /> |
There was a problem hiding this comment.
프로덕션 코드가 아닌 테스트 코드가 포함되어 있습니다.
홈페이지의 실제 콘텐츠가 Icon 컴포넌트 테스트용 코드로 교체되었습니다. 이는 새로운 fillColor 기능을 테스트하기 위한 임시 코드로 보이며, 실제 배포 전에 원래 콘텐츠로 복원되어야 합니다.
다음과 같이 수정하세요:
- 원래 홈페이지 콘텐츠로 복원
- 또는 Icon 컴포넌트 테스트가 목적이라면 별도의 테스트 페이지나 Storybook으로 이동
🤖 Prompt for AI Agents
In src/pages/index.tsx around lines 8 to 25, the homepage content has been
replaced with Icon component test snippets; restore the original production
homepage markup (or remove these test Icons) so end users don't see test code,
or if testing the new fillColor behavior is required move these Icon examples to
a dedicated test page or Storybook story and remove them from this file before
merging.
| {src && ( | ||
| <Image | ||
| src={src} | ||
| alt={`엽서 ${idx + 1}`} | ||
| width={200} | ||
| height={200} | ||
| className="w-full h-full object-cover" | ||
| /> | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
원격 이미지 도메인 허용 여부 확인
Next/Image 사용 시 next.config.(js|ts)의 images.domains 또는 remotePatterns에 엽서 이미지 도메인이 등록되어 있어야 합니다. 누락 시 빌드/런타임 에러가 납니다.
🏁 Script executed:
#!/bin/bash
# next.config 파일에서 이미지 설정 확인
fd -a 'next.config.*' | xargs -I{} sh -c "echo '--- {} ---'; cat {}"
rg -nP "images\s*:\s*\{|\bdomains\b|\bremotePatterns\b" -n --glob 'next.config.*'Length of output: 856
next.config.ts에 원격 이미지 도메인 설정 필요
현재 next.config.ts에 images.domains 또는 remotePatterns 설정이 없습니다. PostcardContainer.tsx의 Next/Image 컴포넌트가 원격 이미지를 로드할 때 에러가 발생합니다. 사용 중인 이미지 도메인을 next.config.ts의 images 설정에 추가하세요.
const nextConfig: NextConfig = {
images: {
domains: ['your-image-domain.com'], // 또는 remotePatterns 사용
},
// ... 기타 설정
};🤖 Prompt for AI Agents
In src/pages/mypage/components/PostcardContainer.tsx around lines 96 to 104 the
Next/Image component may load remote images and currently Next.js config lacks
allowed remote image domains, causing runtime errors; open your project's
next.config.ts and add the image host(s) you use under the images key (either as
domains array or remotePatterns) so Next/Image can fetch them, then restart the
dev server to apply the changes.
| return ( | ||
| <div className={cn(baseStyle, className)}> | ||
| {src ? ( | ||
| <img src={src} className='w-full h-full object-cover' /> |
There was a problem hiding this comment.
Next.js Image 컴포넌트를 사용하고 접근성을 개선하세요.
현재 구현의 문제점:
- 네이티브
<img>태그 사용으로 Next.js의 이미지 최적화 혜택(자동 리사이징, 포맷 변환, 지연 로딩 등)을 받지 못합니다. alt속성이 누락되어 접근성 기준(WCAG)을 충족하지 못합니다.
다음과 같이 수정하세요:
+'use client';
+
+import Image from 'next/image';
import { Icon } from '@/shared/icons';
import { cn } from '@/shared/lib';
// ... interface ...
export default function ProfilePhoto({
src,
className,
}: ProfilePhotoProps) {
// ... baseStyle ...
return (
<div className={cn(baseStyle, className)}>
{src ? (
- <img src={src} className='w-full h-full object-cover' />
+ <Image
+ src={src}
+ alt="프로필 사진"
+ width={146}
+ height={146}
+ className='w-full h-full object-cover'
+ priority
+ />
) : (
<div className='p-[6.1rem] flex items-center justify-center'>
<Icon name='User' size={24} color='gray-500' />
</div>
)}
</div>
);
}📝 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.
| <img src={src} className='w-full h-full object-cover' /> | |
| 'use client'; | |
| import Image from 'next/image'; | |
| import { Icon } from '@/shared/icons'; | |
| import { cn } from '@/shared/lib'; | |
| // ... interface ... | |
| export default function ProfilePhoto({ | |
| src, | |
| className, | |
| }: ProfilePhotoProps) { | |
| // ... baseStyle ... | |
| return ( | |
| <div className={cn(baseStyle, className)}> | |
| {src ? ( | |
| <Image | |
| src={src} | |
| alt="프로필 사진" | |
| width={146} | |
| height={146} | |
| className='w-full h-full object-cover' | |
| priority | |
| /> | |
| ) : ( | |
| <div className='p-[6.1rem] flex items-center justify-center'> | |
| <Icon name='User' size={24} color='gray-500' /> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/pages/mypage/components/ProfilePhoto.tsx around line 27, replace the
native <img> with Next.js' Image component and add an appropriate alt attribute
to satisfy accessibility: import Image from 'next/image' at the top, swap the
<img src={src} className='w-full h-full object-cover' /> for <Image> using
either width/height or layout='fill' (with parent position:relative) to preserve
sizing and object-fit behavior, pass alt (use a descriptive string or alt="" if
purely decorative), keep any needed className or style for object-cover, and
ensure loading behavior (lazy/priority) is set according to importance.
| return ( | ||
| <main className='w-full min-h-screen bg-white flex flex-col items-center'> | ||
| <div className='w-full flex flex-col items-center px-[2.3rem] pb-[2rem]'> | ||
| {/* 프로필 */} | ||
| <section className='w-full flex flex-col items-center mt-[2.4rem] gap-[1.0rem]'> | ||
| <ProfilePhoto/> | ||
| <p className='text-title-md'>이름</p> | ||
| </section> | ||
|
|
||
| {/* 저장한 행사 */} | ||
| <section className='w-full mt-[1.6rem]'> | ||
| <p className='text-label-lg mb-[1rem] pl-[1rem]'>저장한 행사</p> | ||
| <div className='max-h-[18rem] overflow-y-auto no-scrollbar space-y-[1rem]'> | ||
| <EventCard | ||
| name='골반 통신 이상 감지' | ||
| address='우리집' | ||
| description='내 골반이 멈추지 않아서 일까?' | ||
| variant='gray' | ||
| size='large' | ||
| imageSrc='' | ||
| /> | ||
| <EventCard | ||
| name='이혼 숙려 캠프' | ||
| address='우리집' | ||
| description='앙 이라는 감정' | ||
| variant='gray' | ||
| size='large' | ||
| imageSrc='' | ||
| /> | ||
| </div> | ||
| </section> | ||
|
|
||
| {/* 저장한 엽서 */} | ||
| <section className='w-full mt-[1.8rem]'> | ||
| <p className='text-label-lg mb-[0.6rem] pl-[1rem]'>저장한 엽서</p> | ||
| <PostcardContainer postcards={[]} /> | ||
| </section> | ||
|
|
||
| {/* 로그아웃 */} | ||
| <button | ||
| type='button' | ||
| onClick={handleLogout} | ||
| className='mt-[2.1em] text-label-md text-gray-400 cursor-pointer underline underline-offset-[0.25rem]' | ||
| > | ||
| 로그아웃 | ||
| </button> | ||
| </div> | ||
| <BottomNav /> | ||
|
|
||
| {showLogoutPopup && ( | ||
| <PopupSet | ||
| text="로그아웃 하시겠습니까?" | ||
| onClose={() => { | ||
| setShowLogoutPopup(false); | ||
| }} | ||
| /> | ||
| )} | ||
| </main> | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
로딩 및 에러 상태 처리를 추가하세요.
현재 구현에는 다음이 누락되어 있습니다:
- 데이터 로딩 중 표시할 스켈레톤 UI
- API 에러 발생 시 에러 메시지 표시
- 네트워크 실패 시 재시도 로직
기본적인 로딩 상태 처리 예시:
if (isLoading) {
return <LoadingSkeleton />;
}
if (error) {
return <ErrorMessage message={error.message} onRetry={refetch} />;
}| <EventCard | ||
| name='골반 통신 이상 감지' | ||
| address='우리집' | ||
| description='내 골반이 멈추지 않아서 일까?' | ||
| variant='gray' | ||
| size='large' | ||
| imageSrc='' | ||
| /> | ||
| <EventCard | ||
| name='이혼 숙려 캠프' | ||
| address='우리집' | ||
| description='앙 이라는 감정' | ||
| variant='gray' | ||
| size='large' | ||
| imageSrc='' | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
테스트 데이터를 실제 데이터 패칭으로 교체하세요.
하드코딩된 테스트 데이터("골반 통신 이상 감지", "이혼 숙려 캠프")가 포함되어 있습니다. 실제 배포 전에 API 연동 및 데이터 패칭 로직으로 교체되어야 합니다.
실제 이벤트 데이터 패칭 구현 예시:
const [savedEvents, setSavedEvents] = useState<Event[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// TODO: API 호출로 저장한 이벤트 목록 가져오기
fetchSavedEvents()
.then(setSavedEvents)
.finally(() => setIsLoading(false));
}, []);🏁 Script executed:
#!/bin/bash
# Examine the target file and lines
if [ -f "src/pages/mypage/index.tsx" ]; then
echo "=== File found: src/pages/mypage/index.tsx ==="
echo "=== Lines 1-50 (including target lines 27-42) ==="
head -n 50 "src/pages/mypage/index.tsx"
else
echo "File not found at src/pages/mypage/index.tsx"
find . -name "index.tsx" -path "*/mypage/*" 2>/dev/null | head -20
fiLength of output: 1822
🏁 Script executed:
#!/bin/bash
# Check for common API/data fetching patterns
if [ -d "src" ]; then
echo "=== Searching for API integration patterns ==="
rg -n "fetch|axios|useEffect|API|api" src/pages/mypage/ 2>/dev/null || echo "No results found"
else
echo "src directory not found"
fiLength of output: 63
🏁 Script executed:
#!/bin/bash
# Search for EventCard component definition and usage
echo "=== Searching for EventCard component ==="
rg -n "EventCard" -A 5 -B 2 --type tsx 2>/dev/null | head -50Length of output: 41
🏁 Script executed:
#!/bin/bash
# Check file structure around the target area
if [ -f "src/pages/mypage/index.tsx" ]; then
echo "=== Context around lines 20-50 ==="
sed -n '20,50p' "src/pages/mypage/index.tsx"
fiLength of output: 1017
저장한 행사 데이터를 API 연동으로 교체하세요.
파일 src/pages/mypage/index.tsx 27-42줄에 하드코딩된 테스트 데이터가 포함되어 있습니다. 현재 상태로는 배포 불가능하며, 실제 저장된 행사 데이터를 서버에서 가져오는 로직으로 반드시 교체되어야 합니다. 추가로 PostcardContainer도 빈 배열을 받고 있으므로 함께 수정이 필요합니다.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/pages/mypage/index.tsx (2)
27-42: 저장한 행사 데이터를 API 연동으로 교체하세요.이전 리뷰에서 지적된 것과 동일하게, 하드코딩된 테스트 데이터("골반 통신 이상 감지", "이혼 숙려 캠프")가 그대로 남아있습니다. 배포 전 반드시 실제 API에서 저장된 행사 목록을 가져오는 로직으로 교체되어야 합니다.
14-72: 로딩 및 에러 상태 처리를 추가하세요.이전 리뷰에서 지적된 것과 동일하게, 데이터 로딩 중 스켈레톤 UI, API 에러 발생 시 에러 메시지 표시, 네트워크 실패 시 재시도 로직이 여전히 누락되어 있습니다. 프로덕션 배포를 위해서는 이러한 상태 처리가 필수적입니다.
기본 구현 예시:
if (isLoading) { return <LoadingSkeleton />; } if (error) { return <ErrorMessage message={error.message} onRetry={refetch} />; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/mypage/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/mypage/index.tsx (4)
src/pages/mypage/components/ProfilePhoto.tsx (1)
ProfilePhoto(11-35)src/pages/mypage/components/PostcardContainer.tsx (1)
PostcardContainer(59-111)src/shared/components/tab/BottomNav.tsx (1)
BottomNav(21-54)src/shared/components/set/PopupSet.tsx (1)
PopupSet(11-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-deploy
🔇 Additional comments (2)
src/pages/mypage/index.tsx (2)
1-6: 'use client' 지시어와 임포트가 올바르게 설정되었습니다.클라이언트 상태 관리(
useState)를 사용하므로 'use client' 지시어가 적절하며, 모든 필요한 컴포넌트가 정확하게 임포트되었습니다.
8-12: 상태 관리가 깔끔하고 적절합니다.로그아웃 팝업 표시를 위한 상태 관리가 명확하고 간결하게 구현되었습니다.
| <section className='w-full flex flex-col items-center mt-[2.4rem] gap-[1.0rem]'> | ||
| <ProfilePhoto/> | ||
| <p className='text-title-md'>이름</p> | ||
| </section> |
There was a problem hiding this comment.
사용자 프로필 데이터를 API 연동으로 교체하세요.
현재 사용자 이름이 "이름"으로 하드코딩되어 있고, ProfilePhoto에 실제 이미지 경로가 전달되지 않습니다. 사용자 정보를 가져오는 API 연동이 필요합니다.
구현 예시:
const [userData, setUserData] = useState({ name: '', profileImage: '' });
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchUserProfile()
.then(setUserData)
.finally(() => setIsLoading(false));
}, []);
// 렌더링:
<ProfilePhoto src={userData.profileImage} />
<p className='text-title-md'>{userData.name}</p>🤖 Prompt for AI Agents
In src/pages/mypage/index.tsx around lines 18 to 21, the user name is hardcoded
as "이름" and ProfilePhoto is not receiving an image path; replace this with
API-backed user data by adding useState for userData ({ name: '', profileImage:
''}) and loading state, call your fetchUserProfile (or equivalent) inside
useEffect to populate userData (handle errors and set loading false), then
render <ProfilePhoto src={userData.profileImage} /> and <p
className='text-title-md'>{userData.name}</p>, showing a loader or placeholder
while loading or when image/name are absent.
| <section className='w-full mt-[1.8rem]'> | ||
| <p className='text-label-lg mb-[0.6rem] pl-[1rem]'>저장한 엽서</p> | ||
| <PostcardContainer postcards={[]} /> | ||
| </section> |
There was a problem hiding this comment.
저장한 엽서 데이터를 API 연동으로 교체하세요.
PostcardContainer에 빈 배열이 전달되고 있습니다. 사용자가 저장한 엽서 목록을 API에서 가져오는 로직을 구현해야 합니다.
구현 예시:
const [postcards, setPostcards] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchSavedPostcards()
.then(setPostcards)
.finally(() => setIsLoading(false));
}, []);
// 렌더링:
<PostcardContainer postcards={postcards} />| {showLogoutPopup && ( | ||
| <PopupSet | ||
| text="로그아웃 하시겠습니까?" | ||
| onClose={() => { | ||
| setShowLogoutPopup(false); | ||
| }} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
로그아웃 확인 후 실제 로그아웃 로직을 추가하세요.
현재 로그아웃 팝업이 표시되지만, 사용자가 확인을 클릭해도 실제 로그아웃 처리(토큰 제거, 세션 종료, 리다이렉트 등)가 수행되지 않습니다. PopupSet 컴포넌트에 확인 액션 핸들러를 추가하거나, 별도의 로그아웃 로직을 구현해야 합니다.
구현 예시:
const handleConfirmLogout = async () => {
try {
await logout(); // API 호출
// 토큰 제거, 상태 초기화 등
router.push('/login');
} catch (error) {
console.error('로그아웃 실패:', error);
}
setShowLogoutPopup(false);
};
// PopupSet에 onConfirm prop이 있다면:
<PopupSet
text="로그아웃 하시겠습니까?"
onConfirm={handleConfirmLogout}
onClose={() => setShowLogoutPopup(false)}
/>🤖 Prompt for AI Agents
In src/pages/mypage/index.tsx around lines 63 to 70, the logout confirmation
popup is shown but no actual logout logic runs when the user confirms; add an
onConfirm handler to PopupSet that calls the logout flow (await logout API call
if applicable), clears auth tokens/storage, resets any user state,
navigates/redirects to the login (or home) page via the router, handles errors
(log or show a message), and finally closes the popup by setting
setShowLogoutPopup(false); wire the onConfirm prop into the existing PopupSet
invocation and keep the current onClose to just dismiss the dialog.
jjangminii
left a comment
There was a problem hiding this comment.
수고하셨습니다-! 따로 좋은 수정 방안 있을거같아 하나 남겨놨어요 확인해주세요-!
| {recentPlatform === '카카오' && ( | ||
| <div | ||
| className='absolute -top-[3.8rem] left-1/2 -translate-x-1/2 | ||
| w-auto min-w-max h-auto flex-shrink-0 pointer-events-none' | ||
| > | ||
| <RecentLoginBubble /> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
이 부분이 반복되고 있는거 같아요 혹시 따로 컴포넌트로 분리하거나, 플랫폼 리스트를 배열로 선언 후 map으로 돌리는 방법은 어떤가요?
There was a problem hiding this comment.
미처 생각 못했네요.. 너무 좋은 방법인 거 같습니다! 이 부분은 로그인 페이지 브랜치에서 수정해보도록 하겠습니다!
| <EventCard | ||
| name='골반 통신 이상 감지' | ||
| address='우리집' | ||
| description='내 골반이 멈추지 않아서 일까?' | ||
| variant='gray' | ||
| size='large' | ||
| imageSrc='' | ||
| /> | ||
| <EventCard | ||
| name='이혼 숙려 캠프' | ||
| address='우리집' | ||
| description='앙 이라는 감정' | ||
| variant='gray' | ||
| size='large' | ||
| imageSrc='' | ||
| /> |
| export { default as LocationBubbleSet } from './set/LocationBubbleSet'; | ||
| export { default as PopupSet } from './set/PopupSet'; | ||
| export { default as LocationCard } from './container/LocationCard'; | ||
| export { default as EventCard } from './container/EventCard'; | ||
| export { default as AddressCopy } from './button/AddressCopy'; | ||
| export { default as CommonButton } from './button/CommonButton'; | ||
| export { BottomNav } from './tab/BottomNav'; |
| viewBox="0 0 24 24" | ||
| className={combined} | ||
| style={{ ...(color && { color: `var(--color-${color})` }), ...style }} | ||
| style={iconStyle} |
There was a problem hiding this comment.
여기 코드 svg pr에 올라온 부분이랑 중복된거같아요
| .no-scrollbar::-webkit-scrollbar { display: none; } | ||
| .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; } No newline at end of file |
There was a problem hiding this comment.
저희 스크롤바는 따로 안보이게 하는건가요?
There was a problem hiding this comment.
피그마 디자인 상에 기능적으로 스크롤이 들어가야하는 부분은 있지만, 따로 스크롤바는 보이지 않아
일단은 보이지 않도로고 해놓긴 했습니다!
The merge-base changed after approval.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/shared/components/button/AddressCopy.tsx (1)
68-78: 텍스트 렌더링이 잘 구현되었으나 클래스 중복이 있습니다.
flex-1 min-w-0와title속성을 통한 접근성 향상은 잘 되어 있습니다. 다만 Lines 72-73의 조건부 클래스에서 Tailwind의truncate유틸리티가 이미overflow-hidden,text-overflow: ellipsis,white-space: nowrap를 포함하고 있어 중복입니다.다음과 같이 단순화할 수 있습니다:
className={cn( 'text-label-lg flex-1 min-w-0', variant === 'mint' ? 'text-mint-400' : 'text-gray-400', - truncate && - 'truncate whitespace-nowrap overflow-hidden text-ellipsis', + truncate && 'truncate', )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/shared/components/button/AddressCopy.tsx(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/shared/components/button/AddressCopy.tsx (2)
src/shared/utils/copy.ts (1)
Copy(1-12)src/shared/lib/utils.ts (1)
cn(71-73)
🔇 Additional comments (4)
src/shared/components/button/AddressCopy.tsx (4)
8-21: 베이스 스타일 정의가 잘 되어 있습니다.
overflow-hidden추가로 컨테이너 내에서 콘텐츠가 넘치는 것을 방지하고, rounded-full과 함께 깔끔한 UI를 유지합니다.
23-28: 인터페이스 변경이 적절합니다.
truncateprop 추가로 텍스트 말줄임 표시를 선택적으로 제어할 수 있어 컴포넌트 유연성이 향상되었습니다.
30-48: Props 및 핸들러 로직이 올바릅니다.
label을 복사 값으로 사용하고,truncate기본값이true로 설정되어 일관성 있는 동작을 제공합니다. 복사 성공 시 1.5초 동안 피드백을 표시하는 것도 좋은 UX입니다.
61-67: 아이콘 렌더링이 적절합니다.복사 상태에 따라 아이콘이 전환되어(
Check↔CopySimple) 사용자에게 명확한 피드백을 제공합니다.flex-shrink-0래퍼로 아이콘 크기가 보존됩니다.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/main/Node.tsx (1)
2-2: 배럴 파일에서 import하도록 변경하세요.
AddressCopy를 직접 경로에서 import하고 있지만, PR에서 공유 컴포넌트용 배럴 파일을 추가했으므로 일관성을 위해 다른 컴포넌트(Header)처럼 배럴 파일에서 import해야 합니다.다음 diff를 적용하세요:
import { Header } from '@/shared/components'; -import AddressCopy from '@/shared/components/button/AddressCopy'; +import { AddressCopy } from '@/shared/components'; import LocationCard from '@/shared/components/container/LocationCard';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/main/Node.tsx(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Next.js CI/CD with Vercel
src/pages/main/Node.tsx
[error] 64-64: Type '{ variant: "mint"; value: string; label: string; }' is not assignable to type 'IntrinsicAttributes & AddressCopyProps'. Property 'label' does not exist on type 'IntrinsicAttributes & AddressCopyProps'.
| <AddressCopy | ||
| variant='mint' | ||
| value='인천광역시 계양구 오조산로 31길(복사되어야할 주소)' | ||
| label={label ? String(label) : '노드'} | ||
| value='(복사되어야할 주소)' | ||
| /> |
There was a problem hiding this comment.
하드코딩된 플레이스홀더를 실제 주소 데이터로 교체하세요.
value prop에 플레이스홀더 문자열 "(복사되어야할 주소)"가 하드코딩되어 있습니다. 사용자가 이 버튼을 클릭하면 의미 없는 플레이스홀더 텍스트가 클립보드에 복사되어 잘못된 사용자 경험을 제공합니다.
Line 55의 LocationCard와 마찬가지로 실제 주소 데이터(API 응답, 라우터 쿼리 등)를 사용하도록 구현하거나, 데이터가 준비될 때까지 이 컴포넌트를 조건부로 렌더링하세요.
예시:
<AddressCopy
variant='mint'
- value='(복사되어야할 주소)'
+ value={actualAddress || '주소 정보를 불러올 수 없습니다'}
/>Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 GitHub Actions: Next.js CI/CD with Vercel
[error] 64-64: Type '{ variant: "mint"; value: string; label: string; }' is not assignable to type 'IntrinsicAttributes & AddressCopyProps'. Property 'label' does not exist on type 'IntrinsicAttributes & AddressCopyProps'.
🤖 Prompt for AI Agents
In src/pages/main/Node.tsx around lines 61 to 64, the AddressCopy component is
using a hardcoded placeholder "(복사되어야할 주소)"; replace that hardcoded string with
the actual address variable (e.g., from props, component state populated by the
API response, or router query) and ensure the component only renders or is
enabled once the address is available—either conditionally render AddressCopy
(matching the approach used for LocationCard at line 55) or pass a safe
fallback/disabled state until the real address is loaded.
🔥 작업 내용
🤔 추후 작업 사항
🔗 이슈
PR Point (To Reviewer)
mypage 하위 폴더 파일들 + global.css 만 보시면 될 거 같습니다
MyPage 레이아웃 작업
페이지 레이아웃 작업해놨습니다.
엽서 저장 컨테이너는 8개 이상이 될 조건부로 가로 스크롤로 가능하도록 해놨습니다.
2행 고정 + 가로 확장형 그리드:
grid-rows-2 + grid-flow-col + auto-cols-[70px]Barrel 파일 & global.css 스크롤 숨김 추가
공동컴포넌트들을 Barrel 파일로 불러왔습니다. 또 스크롤바가 나오면 디자인이 틀어져, global.css 에 스크롤바를 숨기도록 아래처럼 추가해뒀습니다.
📸 피그마 스크린샷 or 기능 GIF
KakaoTalk_20251028_011213062.mp4
Summary by CodeRabbit
릴리스 노트
새로운 기능
스타일
변경