Skip to content

feat: 마이페이지 전체 구현#63

Merged
heeeeyong merged 9 commits into
developfrom
feature/mypage
Jul 28, 2025
Merged

feat: 마이페이지 전체 구현#63
heeeeyong merged 9 commits into
developfrom
feature/mypage

Conversation

@heeeeyong
Copy link
Copy Markdown
Collaborator

@heeeeyong heeeeyong commented Jul 27, 2025

#️⃣연관된 이슈

#62 [UI] 마이페이지

📝작업 내용

  • 마이페이지 메인화면 구현
image image
  • 마이페이지 저장 페이지 구현
image image image
  • 마이페이지 회원탈퇴 플로우 구현
image image image image
  • 마이페이지 알림설정 페이지 구현
image image
  • 마이페이지 프로필 편집 페이지 구현
image image

💬리뷰 요구사항

특별한 내용 없습니다.

Summary by CodeRabbit

  • 신규 기능

    • 마이페이지 및 하위 페이지(알림 설정, 저장함, 회원탈퇴, 프로필 편집 등) 추가
    • 사용자 프로필, 활동 내역, 메뉴, 로그아웃/회원탈퇴 등 마이페이지 주요 기능 제공
    • 알림 설정 페이지에서 푸시 알림 토글 및 스낵바 안내 제공
    • 저장함에서 피드/책 탭별 저장 콘텐츠 확인 및 저장/해제 가능
    • 회원탈퇴 및 탈퇴 완료 페이지 추가
    • 프로필 편집 페이지에서 닉네임 및 장르 타이틀 변경 가능
  • UI/스타일

    • 탭 버튼, 메뉴 버튼, 프로필 등 다양한 UI 컴포넌트 및 스타일 추가
    • 반응형 레이아웃 및 일관된 디자인 적용
  • 데이터/모델

    • 책, 장르, 피드 등 목업 데이터 및 타입 정의 추가
  • 버그 수정

    • 내 정보(프로필) 네비게이션 경로를 '/my'에서 '/mypage'로 변경
  • 리팩터

    • 장르 타입을 외부 모듈로 통합하여 일관성 향상

@vercel
Copy link
Copy Markdown

vercel Bot commented Jul 27, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
thip ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 28, 2025 0:05am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 27, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

이번 변경 사항은 "마이페이지" 관련 신규 기능 개발 및 데이터 타입 추가에 집중되어 있습니다. 마이페이지, 저장, 알림, 회원탈퇴, 프로필 편집 등 여러 페이지가 새로 추가되었으며, 관련 컴포넌트와 스타일, 라우팅, 목데이터, 타입 정의 등이 포함됩니다. 일부 기존 UI 컴포넌트와 데이터 구조도 수정되었습니다.

Changes

Cohort / File(s) Change Summary
마이페이지 신규 페이지 및 컴포넌트 추가
src/pages/mypage/Mypage.tsx, src/components/Mypage/MenuButton.tsx, src/pages/mypage/SavePage.tsx, src/pages/mypage/AlertPage.tsx, src/pages/mypage/WithdrawPage.tsx, src/pages/mypage/WithdrawDonePage.tsx, src/pages/mypage/EditPage.tsx, src/pages/mypage/EditPage.styled.ts
마이페이지, 저장, 알림, 회원탈퇴, 회원탈퇴 완료, 프로필 편집 등 신규 React 컴포넌트 및 스타일 파일 추가. 메뉴 버튼, 알림 설정, 저장 컨텐츠 탭, 탈퇴 확인 및 완료, 프로필 편집 등 다양한 유저 인터페이스 구현 및 스타일링 포함.
라우팅 및 네비게이션
src/pages/index.tsx, src/components/common/NavBar.tsx
마이페이지 및 하위 페이지 라우트 추가. 네비게이션 바에서 마이페이지 경로를 /my에서 /mypage로 변경.
피드/저장/책 데이터 및 타입 추가
src/data/postData.ts, src/data/bookData.ts, src/types/book.ts
피드 및 책 관련 목데이터 추가, 피드 데이터 구조 일부 변경(showHeaderimages), 책 데이터 타입 및 목데이터 추가.
장르 타입 및 적용
src/types/genre.ts, src/pages/signup/SignupGenre.tsx
장르(Genre) 타입 신규 정의 및 기존 파일에서 타입 import로 일원화.
피드/탭바 UI 개선
src/components/feed/TabBar.tsx
탭 버튼의 고정 너비(60px) 스타일 추가.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant Mypage
    participant SavePage
    participant AlertPage
    participant WithdrawPage
    participant EditPage

    User->>Router: /mypage 접근
    Router->>Mypage: Mypage 컴포넌트 렌더링
    User->>Mypage: 메뉴 버튼 클릭 (예: 저장)
    Mypage->>Router: /mypage/save 이동
    Router->>SavePage: SavePage 렌더링

    User->>Mypage: 알림 버튼 클릭
    Mypage->>Router: /mypage/alert 이동
    Router->>AlertPage: AlertPage 렌더링

    User->>Mypage: 편집 버튼 클릭
    Mypage->>Router: /mypage/edit 이동
    Router->>EditPage: EditPage 렌더링

    User->>Mypage: 회원탈퇴 클릭
    Mypage->>Router: /mypage/withdraw 이동
    Router->>WithdrawPage: WithdrawPage 렌더링
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Suggested reviewers

  • ljh130334

Poem

🐇
마이페이지 열렸네, 토끼가 깡총!
저장도 알림도, 탈퇴도 뿅!
프로필 편집하며 닉네임 고민,
장르 고르며 책 향기 맡지.
새로워진 메뉴, 토끼도 신나—
리뷰할 때는 당근 케이크 하나!
🥕✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3aabbf7 and 727660f.

📒 Files selected for processing (1)
  • src/pages/index.tsx (2 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/mypage

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@heeeeyong heeeeyong self-assigned this Jul 27, 2025
@heeeeyong heeeeyong added ✨ Feature 기능 개발 🎨 Html&css 마크업 & 스타일링 labels Jul 27, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (9)
src/components/Mypage/MenuButton.tsx (1)

10-20: 클릭 이벤트 핸들러 추가를 고려해 보세요.

MenuButton이라는 이름과 버튼처럼 보이는 스타일을 가지고 있지만 클릭 이벤트 핸들러가 없습니다. 사용자 인터랙션을 위한 onClick prop을 추가하는 것을 고려해 보세요.

 export interface MenuButtonProps {
   src: string;
   name: string;
+  onClick?: () => void;
 }

-const MenuButton = ({ src, name }: MenuButtonProps) => {
+const MenuButton = ({ src, name, onClick }: MenuButtonProps) => {
   return (
-    <Wrapper>
+    <Wrapper onClick={onClick} role="button" tabIndex={0}>
       <div className="main">
         <img src={src} alt={name} />
         <div>{name}</div>
       </div>
       <img src={rightArrow} alt="" />
     </Wrapper>
   );
 };
src/components/common/TitleHeader.tsx (1)

16-29: 스타일링 일관성을 개선할 수 있습니다.

HeaderWrapper는 글로벌 색상 객체를 사용하지만, NextButtonInnerHeader는 여전히 CSS 변수를 사용하고 있습니다. 일관성을 위해 모든 스타일을 글로벌 색상 시스템으로 통일하는 것을 고려해 보세요.

 const NextButton = styled.div<{ active: boolean }>`
   cursor: ${({ active }) => (active ? 'pointer' : 'default')};
   width: 49px;
   height: 28px;
   padding: 4px 12px;
   border-radius: 20px;
   background-color: ${({ active }) =>
-    active ? 'var(--color-purple-main)' : 'var(--color-grey-300)'};
-  color: var(--color-white);
+    active ? colors.purple.main : colors.grey[300]};
+  color: ${colors.white};
   font-size: var(--font-size-sm);
   font-weight: var(--font-weight-semibold);
   line-height: 20px;
   text-align: center;
 `;
src/components/feed/FeedDetailPostBody.tsx (1)

106-106: 이미지 접근성 개선을 위한 alt 속성 추가를 권장합니다.

현재 alt={이미지 ${i + 1}}로 설정되어 있는데, 더 구체적인 설명을 제공하는 것이 좋겠습니다.

-<img key={i} src={src} alt={`이미지 ${i + 1}`} onClick={() => handleImageClick(i)} />
+<img key={i} src={src} alt={`게시글 이미지 ${i + 1}`} onClick={() => handleImageClick(i)} />
src/pages/mypage/Mypage.tsx (2)

21-21: 편집 기능 구현이 필요합니다.

onClickEdit 핸들러가 빈 함수로 되어 있습니다. 프로필 편집 페이지로의 네비게이션이나 모달 열기 등의 기능 구현이 필요합니다.

프로필 편집 기능 구현에 도움이 필요하시면 새로운 이슈를 생성하여 작업을 추적할 수 있습니다.


75-93: 헤더 스타일링에서 일관성 문제가 있습니다.

헤더 컴포넌트에서 글로벌 색상 상수(colors.black.main, colors.white)와 타이포그래피 상수를 사용하는 것은 좋지만, 다른 부분에서는 CSS 변수(var(--color-white))를 혼용하고 있습니다.

스타일링 일관성을 위해 전체적으로 글로벌 상수 사용을 권장합니다:

.username {
-  color: var(--color-white);
+  color: ${colors.white};
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  line-height: normal;
}
src/components/common/Modal/ImageViewer.tsx (4)

44-67: 드래그 제스처 구현에서 성능 최적화가 필요합니다.

handleMouseMovehandleTouchMove에서 불필요한 상태 업데이트가 발생할 수 있습니다. 드래그 감지 로직을 개선하는 것을 권장합니다.

-const handleMouseMove = () => {
+const handleMouseMove = (e: React.MouseEvent) => {
  if (dragStartRef.current === null) return;
+  const deltaX = Math.abs(e.clientX - dragStartRef.current);
+  if (deltaX > 5) { // 최소 이동 거리 체크
    isDraggingRef.current = true;
+  }
};

-const handleTouchMove = () => {
+const handleTouchMove = (e: React.TouchEvent) => {
  if (dragStartRef.current === null) return;
+  const touch = e.touches[0];
+  const deltaX = Math.abs(touch.clientX - dragStartRef.current);
+  if (deltaX > 5) { // 최소 이동 거리 체크
    isDraggingRef.current = true;
+  }
};

104-104: 이전 버튼 아이콘 방향이 혼란을 야기할 수 있습니다.

rightArrow 아이콘을 180도 회전시켜 이전 버튼으로 사용하고 있는데, 전용 leftArrow 아이콘을 사용하는 것이 더 명확할 것 같습니다.

-<ButtonIcon src={rightArrow} alt="이전 이미지" isNext />
+<ButtonIcon src={leftArrow} alt="이전 이미지" />

그리고 ButtonIcon 컴포넌트에서 isNext prop을 제거할 수 있습니다.


140-150: 모달 오버레이 스타일에서 주석 처리된 코드를 정리해야 합니다.

주석 처리된 CSS 코드가 남아있습니다. 필요하지 않다면 제거하는 것이 좋겠습니다.

const ModalOverlay = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
-  /* min-width: 320px;
-  max-width: 767px;
-  margin: 0 auto; */
-  /* background-color: ${colors.black.main}; */
  background-color: rgba(18, 18, 18, 0.8);
  backdrop-filter: blur(1.5px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
`;

233-240: 주석 처리된 hover/active 스타일을 정리하거나 활성화해야 합니다.

버튼에 대한 hover와 active 스타일이 주석 처리되어 있습니다. 사용자 경험 향상을 위해 활성화하거나 완전히 제거하는 것을 권장합니다.

hover 효과를 활성화하여 사용자 피드백을 개선하는 것을 권장합니다:

-  /* &:hover {
-    background-color: rgba(0, 0, 0, 0.7);
-  }
-
-  &:active {
-    transform: translateY(-50%) scale(0.95);
-  } */
+  &:hover {
+    background-color: rgba(0, 0, 0, 0.3);
+    border-radius: 50%;
+  }
+
+  &:active {
+    transform: translateY(-50%) scale(0.95);
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0b0b34 and aa3ae65.

⛔ Files ignored due to path filters (10)
  • src/assets/common/test.jpg is excluded by !**/*.jpg
  • src/assets/common/test2.jpg is excluded by !**/*.jpg
  • src/assets/mypage/alert.svg is excluded by !**/*.svg
  • src/assets/mypage/guide.svg is excluded by !**/*.svg
  • src/assets/mypage/heart.svg is excluded by !**/*.svg
  • src/assets/mypage/logout.svg is excluded by !**/*.svg
  • src/assets/mypage/save.svg is excluded by !**/*.svg
  • src/assets/mypage/service.svg is excluded by !**/*.svg
  • src/assets/mypage/ver.svg is excluded by !**/*.svg
  • src/assets/mypage/withdraw.svg is excluded by !**/*.svg
📒 Files selected for processing (8)
  • src/components/Mypage/MenuButton.tsx (1 hunks)
  • src/components/common/Modal/ImageViewer.tsx (1 hunks)
  • src/components/common/TitleHeader.tsx (1 hunks)
  • src/components/feed/FeedDetailPostBody.tsx (5 hunks)
  • src/data/postData.ts (2 hunks)
  • src/pages/index.tsx (2 hunks)
  • src/pages/mypage/Mypage.tsx (1 hunks)
  • src/types/post.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/components/common/TitleHeader.tsx (1)
src/styles/global/global.ts (1)
  • colors (4-53)
src/components/Mypage/MenuButton.tsx (1)
src/styles/global/global.ts (2)
  • colors (4-53)
  • typography (56-77)
src/components/common/Modal/ImageViewer.tsx (1)
src/styles/global/global.ts (1)
  • colors (4-53)
🔇 Additional comments (13)
src/components/Mypage/MenuButton.tsx (1)

22-44: 스타일링 구현이 잘 되어 있습니다.

글로벌 스타일 시스템을 적절히 활용하고 있으며, 반응형 디자인과 접근성을 고려한 스타일링이 잘 구현되어 있습니다.

src/types/post.ts (1)

27-27: 타입 정의 변경이 적절합니다.

showHeader 프로퍼티를 선택적으로 변경한 것은 좋은 결정입니다. 이는 컴포넌트 사용 시 더 많은 유연성을 제공하며, 목 데이터 변경사항과도 일치합니다.

src/pages/index.tsx (2)

27-27: 임포트 구문이 올바르게 추가되었습니다.

새로운 Mypage 컴포넌트의 임포트가 적절히 추가되었습니다.


53-53: 라우트 설정이 적절합니다.

마이페이지 라우트가 기존 패턴을 따라 올바르게 추가되었습니다. 경로명도 직관적이고 명확합니다.

src/components/common/TitleHeader.tsx (2)

2-2: 글로벌 색상 시스템 사용이 좋습니다.

CSS 변수 대신 글로벌 색상 객체를 사용하는 것은 일관성 있는 스타일링을 위한 좋은 변경입니다.

Also applies to: 13-13


10-10: max-width 변경 사유를 확인해 주세요.

767px에서 766px로 1px 감소한 이유가 명확하지 않습니다. 특정 디자인 요구사항이나 반응형 브레이크포인트를 위한 의도적인 변경인지 확인해 주세요.

src/data/postData.ts (2)

2-3: 이미지 임포트가 적절히 추가되었습니다.

테스트용 이미지 파일들이 올바르게 임포트되었습니다.


59-59: 이미지 배열 구성이 잘 되어 있습니다.

로컬 이미지와 플레이스홀더 URL을 조합한 images 배열이 새로운 이미지 뷰어 기능을 테스트하기에 적합하게 구성되어 있습니다.

src/components/feed/FeedDetailPostBody.tsx (3)

83-96: 이미지 뷰어 상태 관리 구현이 적절합니다.

상태 초기화와 이벤트 핸들러 구현이 깔끔하고 명확합니다. 모달 열기/닫기와 이미지 인덱스 관리가 잘 분리되어 있습니다.


37-38: 이미지 상호작용을 위한 CSS 스타일링이 적절합니다.

object-fit: covercursor: pointer 속성 추가로 사용자 경험이 개선되었습니다.


123-130: ImageViewer 컴포넌트 통합이 올바르게 구현되었습니다.

조건부 렌더링과 props 전달이 적절하며, 컴포넌트 간의 상태 관리가 잘 연결되어 있습니다.

src/pages/mypage/Mypage.tsx (1)

177-181: 반응형 디자인이 잘 구현되었습니다.

572px 이상에서 2열 그리드로 변경되는 반응형 레이아웃이 적절하게 구현되었습니다.

src/components/common/Modal/ImageViewer.tsx (1)

21-32: body 스크롤 차단 구현이 적절합니다.

모달이 열릴 때 배경 스크롤을 방지하고 cleanup에서 복구하는 로직이 올바르게 구현되었습니다.

Comment on lines +15 to +16
const ImageViewer = ({ images, initialIndex, isOpen, onClose }: ImageViewerProps) => {
const [currentIndex, setCurrentIndex] = useState(initialIndex);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

initialIndex 초기화 시점에 대한 고려가 필요합니다.

initialIndex가 변경될 때 currentIndex 상태가 자동으로 업데이트되지 않습니다. 같은 모달에서 다른 이미지를 선택했을 때의 동작을 확인해야 합니다.

필요하다면 useEffect를 추가하여 initialIndex 변경에 대응할 수 있습니다:

const ImageViewer = ({ images, initialIndex, isOpen, onClose }: ImageViewerProps) => {
  const [currentIndex, setCurrentIndex] = useState(initialIndex);
+  
+  useEffect(() => {
+    setCurrentIndex(initialIndex);
+  }, [initialIndex]);

🏁 Script executed:

#!/bin/bash
# FeedDetailPostBody에서 ImageViewer 사용 패턴 확인
echo "=== ImageViewer 사용 패턴 확인 ==="
rg -A 10 -B 5 "ImageViewer" src/components/feed/FeedDetailPostBody.tsx
echo -e "\n=== selectedImageIndex 상태 관리 확인 ==="
rg -A 5 -B 5 "selectedImageIndex" src/components/feed/FeedDetailPostBody.tsx

Length of output: 2487


initialIndex prop 변경 시 currentIndex 상태 동기화 필요

ImageViewerinitialIndexcurrentIndex를 한 번만 초기화하기 때문에, 모달이 열린 상태에서 다른 이미지를 클릭해 initialIndex가 바뀌어도 뷰어에 반영되지 않습니다.
FeedDetailPostBody에서 모달을 열고(isOpen=true) 다른 이미지를 선택하면, 컴포넌트가 언마운트되지 않으므로 useState(initialIndex)만으로는 업데이트가 불가능합니다.

수정 제안:

  • src/components/common/Modal/ImageViewer.tsx에 useEffect를 추가해 initialIndex 변경 시 currentIndex를 동기화합니다.
 const ImageViewer = ({ images, initialIndex, isOpen, onClose }: ImageViewerProps) => {
   const [currentIndex, setCurrentIndex] = useState(initialIndex);
+  
+  useEffect(() => {
+    setCurrentIndex(initialIndex);
+  }, [initialIndex]);
📝 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 ImageViewer = ({ images, initialIndex, isOpen, onClose }: ImageViewerProps) => {
const [currentIndex, setCurrentIndex] = useState(initialIndex);
const ImageViewer = ({ images, initialIndex, isOpen, onClose }: ImageViewerProps) => {
const [currentIndex, setCurrentIndex] = useState(initialIndex);
useEffect(() => {
setCurrentIndex(initialIndex);
}, [initialIndex]);
// ...rest of the component
};
🤖 Prompt for AI Agents
In src/components/common/Modal/ImageViewer.tsx around lines 15 to 16, the
currentIndex state is initialized only once from the initialIndex prop, so
changes to initialIndex while the modal is open do not update currentIndex. To
fix this, add a useEffect hook that listens for changes to initialIndex and
updates currentIndex accordingly, ensuring the viewer reflects the new
initialIndex whenever it changes.

Comment thread src/components/Mypage/MenuButton.tsx
Comment thread src/components/Mypage/MenuButton.tsx Outdated
<img src={src} />
<div>{name}</div>
</div>
<img src={rightArrow} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

장식용 이미지에 빈 alt 속성을 추가해 주세요.

우측 화살표 아이콘은 장식용이므로 빈 alt 속성을 추가하여 스크린 리더가 무시하도록 해 주세요.

-      <img src={rightArrow} />
+      <img src={rightArrow} alt="" />
📝 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
<img src={rightArrow} />
<img src={rightArrow} alt="" />
🤖 Prompt for AI Agents
In src/components/Mypage/MenuButton.tsx at line 17, the img element for the
right arrow icon lacks an alt attribute. Since this image is decorative, add an
empty alt attribute (alt="") to ensure screen readers ignore it.

Comment on lines +12 to +17
const mockProfile = {
profileImgUrl: 'https://placehold.co/54x54',
userName: '문학하는 고래',
userTitle: '문학가',
titleColor: '#a1d5ff',
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

실제 사용자 데이터 연동 준비가 필요합니다.

현재 하드코딩된 mock 데이터를 사용하고 있습니다. 실제 사용자 프로필 데이터와 연동할 계획을 수립해야 합니다.

다음 스크립트로 사용자 데이터 관련 타입이나 API가 이미 정의되어 있는지 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# 사용자 프로필 관련 타입이나 API 찾기
echo "=== 사용자 관련 타입 검색 ==="
rg -A 5 "interface.*User" --type ts
echo -e "\n=== 프로필 관련 API 검색 ==="
rg -A 3 "profile|Profile" --type ts
echo -e "\n=== API 관련 파일 검색 ==="
fd -e ts -e js api

Length of output: 17144


실제 사용자 프로필 연동 준비 필요
현재 src/pages/mypage/Mypage.tsx에서 직접 정의한 mockProfile을 사용 중입니다.

  • src/types/user.ts에 정의된 UserProfileItemProps 타입을 재사용해 데이터 구조를 통일하세요.
  • 실제 사용자 프로필 API 모듈(e.g. src/api/user.ts)을 생성해 fetch 로직을 추가하고, 컴포넌트에서 호출하도록 구현 계획을 세워주세요.
  • 로딩 및 에러 상태 처리(스켈레톤, 에러 메시지 등)도 함께 고려 부탁드립니다.
🤖 Prompt for AI Agents
In src/pages/mypage/Mypage.tsx around lines 12 to 17, replace the hardcoded
mockProfile object with a properly typed user profile by importing and using the
UserProfileItemProps type from src/types/user.ts. Create a new API module in
src/api/user.ts to handle fetching the actual user profile data asynchronously.
Update the Mypage component to call this API, manage loading and error states by
showing appropriate skeleton UI or error messages, and render the fetched
profile data accordingly.

Comment thread src/pages/mypage/Mypage.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/components/Mypage/MenuButton.tsx (2)

17-17: 접근성을 위한 alt 속성이 여전히 누락되어 있습니다.

메인 아이콘 이미지에 alt 속성을 추가하여 스크린 리더 사용자의 접근성을 향상시켜 주세요.

-        <img src={src} />
+        <img src={src} alt={name} />

20-20: 장식용 이미지에 빈 alt 속성 추가가 필요합니다.

우측 화살표 아이콘은 장식용이므로 빈 alt 속성을 추가하여 스크린 리더가 무시하도록 해 주세요.

-      {isButton && <img src={rightArrow} />}
+      {isButton && <img src={rightArrow} alt="" />}
🧹 Nitpick comments (1)
src/components/Mypage/MenuButton.tsx (1)

5-11: 사용하지 않는 textColor 프로퍼티를 정리해 주세요.

textColor 프로퍼티가 인터페이스에 정의되어 있지만 컴포넌트에서 실제로 사용되지 않고 있습니다. 텍스트 색상 커스터마이징 기능을 구현하거나, 사용하지 않는다면 인터페이스에서 제거하는 것을 고려해 주세요.

 export interface MenuButtonProps {
   src: string;
   name: string;
-  textColor?: string;
   isButton?: boolean;
   onClick?: () => void;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa3ae65 and 9838b83.

⛔ Files ignored due to path filters (2)
  • src/assets/mypage/notice.svg is excluded by !**/*.svg
  • src/assets/mypage/terms.svg is excluded by !**/*.svg
📒 Files selected for processing (2)
  • src/components/Mypage/MenuButton.tsx (1 hunks)
  • src/pages/mypage/Mypage.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/mypage/Mypage.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/components/Mypage/MenuButton.tsx (1)
src/styles/global/global.ts (2)
  • colors (4-53)
  • typography (56-77)
🔇 Additional comments (3)
src/components/Mypage/MenuButton.tsx (3)

1-3: 임포트 구조가 잘 정리되어 있습니다.

글로벌 스타일 시스템 사용, Emotion 스타일링, SVG 에셋 임포트가 적절하고 일관성 있게 구성되어 있습니다.


25-48: 스타일링이 잘 구현되어 있습니다.

글로벌 디자인 시스템을 적절히 활용하고, flexbox 레이아웃으로 일관성 있는 UI를 구성했습니다. 반응형 고려사항과 접근성을 위한 커서 스타일링도 적절합니다.


50-50: 컴포넌트 익스포트가 적절합니다.

React 컴포넌트의 일반적인 default export 패턴을 따르고 있습니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/pages/mypage/AlertPage.tsx (2)

1-8: import 경로 일관성을 고려해보세요.

상대 경로와 절대 경로(@alias)가 혼재되어 있습니다. 프로젝트 전체에서 일관된 import 패턴을 유지하는 것이 좋겠습니다.


72-147: 스타일링이 잘 구현되었으나 일관성을 개선할 수 있습니다.

디자인 시스템 활용과 반응형 설계가 좋습니다. 다만 하드코딩된 배경색 대신 colors.black.main을 사용하는 것이 일관성 측면에서 더 좋겠습니다.

- background-color: #121212;
+ background-color: ${colors.black.main};
src/pages/mypage/WithdrawPage.tsx (1)

24-40: 프로덕션 코드에서 console.log를 제거하세요.

회원탈퇴 로직은 잘 구현되었지만, 31번 라인의 console.log는 실제 API 호출로 대체되거나 제거되어야 합니다.

-          // 회원탈퇴 API 호출 부분 (비워둠)
-          console.log('회원탈퇴 API 호출');
+          // TODO: 회원탈퇴 API 호출 구현 필요
src/pages/mypage/SavePage.tsx (2)

47-91: 조건부 렌더링 구조를 개선할 수 있습니다.

현재 JSX의 중첩된 조건문이 복잡합니다. 가독성을 위해 렌더링 로직을 별도 함수로 분리하는 것을 고려해보세요.

다음과 같이 렌더링 로직을 분리할 수 있습니다:

+  const renderFeedTab = () => {
+    if (!hasPosts) {
+      return (
+        <EmptyState>
+          <div className="title">아직 저장한 피드가 없어요</div>
+          <div className="sub-title">마음에 드는 책을 THIP 해보세요!</div>
+        </EmptyState>
+      );
+    }
+    return (
+      <FeedContainer>
+        {mockPosts.map(post => (
+          <FeedPost key={post.feedId} showHeader={true} isMyFeed={false} {...post} />
+        ))}
+      </FeedContainer>
+    );
+  };
+
+  const renderBookTab = () => {
+    if (!hasBooks) {
+      return (
+        <EmptyState>
+          <div className="title">아직 저장한 책이 없어요</div>
+          <div className="sub-title">마음에 드는 책을 THIP 해보세요!</div>
+        </EmptyState>
+      );
+    }
+    return (
+      <BookList>
+        {mockBooks.map(book => (
+          <BookItem key={book.isbn}>
+            {/* ... 기존 book 렌더링 로직 */}
+          </BookItem>
+        ))}
+      </BookList>
+    );
+  };

-      {activeTab === '피드' ? (
-        <>
-          {hasPosts ? (
-            <FeedContainer>
-              {mockPosts.map(post => (
-                <FeedPost key={post.feedId} showHeader={true} isMyFeed={false} {...post} />
-              ))}
-            </FeedContainer>
-          ) : (
-            <EmptyState>
-              <div className="title">아직 저장한 피드가 없어요</div>
-              <div className="sub-title">마음에 드는 책을 THIP 해보세요!</div>
-            </EmptyState>
-          )}
-        </>
-      ) : hasBooks ? (
-        <>
-          <BookList>
-            {mockBooks.map(book => (
-              <BookItem key={book.isbn}>
-                <LeftSection>
-                  <Cover src={book.coverUrl} alt={`${book.title} 커버`} />
-                  <BookInfo>
-                    <Title>{book.title}</Title>
-                    <Subtitle>
-                      {book.author} 저 · {book.publisher}
-                    </Subtitle>
-                  </BookInfo>
-                </LeftSection>
-                <SaveIcon onClick={() => handleSaveToggle(book.isbn)}>
-                  <img
-                    src={savedBooks[book.isbn] ? activeSave : save}
-                    alt={savedBooks[book.isbn] ? '저장됨' : '저장'}
-                  />
-                </SaveIcon>
-              </BookItem>
-            ))}
-          </BookList>
-        </>
-      ) : (
-        <EmptyState>
-          <div className="title">아직 저장한 책이 없어요</div>
-          <div className="sub-title">마음에 드는 책을 THIP 해보세요!</div>
-        </EmptyState>
-      )}
+      {activeTab === '피드' ? renderFeedTab() : renderBookTab()}

22-22: 저장된 책 상태 지속성을 고려해보세요.

현재 savedBooks 상태가 컴포넌트 재마운트 시 초기화됩니다. 사용자 경험 향상을 위해 localStorage나 서버 동기화를 통한 상태 지속성을 고려해보세요.

다음과 같이 localStorage를 활용할 수 있습니다:

-  const [savedBooks, setSavedBooks] = useState<{ [isbn: number]: boolean }>({});
+  const [savedBooks, setSavedBooks] = useState<{ [isbn: number]: boolean }>(() => {
+    const saved = localStorage.getItem('savedBooks');
+    return saved ? JSON.parse(saved) : {};
+  });

  const handleSaveToggle = (isbn: number) => {
-    setSavedBooks(prev => ({
+    setSavedBooks(prev => {
+      const newState = {
-      ...prev,
-      [isbn]: !prev[isbn],
-    }));
+        ...prev,
+        [isbn]: !prev[isbn],
+      };
+      localStorage.setItem('savedBooks', JSON.stringify(newState));
+      return newState;
+    });
  };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de8395c and 7a90ddc.

⛔ Files ignored due to path filters (2)
  • src/assets/mypage/charactor.svg is excluded by !**/*.svg
  • src/assets/mypage/check.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • src/components/feed/TabBar.tsx (1 hunks)
  • src/data/bookData.ts (1 hunks)
  • src/data/postData.ts (3 hunks)
  • src/pages/index.tsx (2 hunks)
  • src/pages/mypage/AlertPage.tsx (1 hunks)
  • src/pages/mypage/Mypage.tsx (1 hunks)
  • src/pages/mypage/SavePage.tsx (1 hunks)
  • src/pages/mypage/WithdrawDonePage.tsx (1 hunks)
  • src/pages/mypage/WithdrawPage.tsx (1 hunks)
  • src/types/book.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • src/components/feed/TabBar.tsx
  • src/types/book.ts
  • src/data/bookData.ts
  • src/pages/mypage/WithdrawDonePage.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pages/index.tsx
  • src/data/postData.ts
  • src/pages/mypage/Mypage.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/pages/mypage/AlertPage.tsx (2)
src/hooks/usePopupActions.ts (1)
  • usePopupActions (4-25)
src/styles/global/global.ts (2)
  • colors (4-53)
  • typography (56-77)
🔇 Additional comments (12)
src/pages/mypage/AlertPage.tsx (4)

9-17: 깔끔한 컴포넌트 구조와 확장 가능한 설계입니다.

useState를 통한 토글 상태 관리와 향후 확장을 위한 주석 처리된 알림 타입들이 잘 설계되어 있습니다.


19-35: 이벤트 핸들러가 잘 구현되었습니다.

TypeScript 타입 안전성(keyof typeof)과 사용자 피드백(스낵바)이 적절히 구현되어 있고, 한국어 메시지도 자연스럽습니다.


37-41: 설정 데이터 구조가 잘 설계되었습니다.

as const를 사용한 타입 안전성과 확장 가능한 구조가 좋습니다. 향후 추가될 기능들이 주석으로 잘 정리되어 있습니다.


43-70: JSX 구조가 깔끔하고 의미있게 구성되었습니다.

컴포넌트 구조가 직관적이고, map 함수에서 key prop 사용과 이벤트 핸들링이 적절히 구현되어 있습니다.

src/pages/mypage/WithdrawPage.tsx (5)

1-9: Import 구조가 적절합니다.

필요한 의존성들이 올바르게 import되어 있고, 구조가 깔끔합니다.


11-15: 컴포넌트 구조가 잘 설계되었습니다.

함수형 컴포넌트와 훅 사용이 적절하며, 상태 관리가 간단하고 명확합니다.


16-23: 핸들러 함수들이 적절하게 구현되었습니다.

뒤로가기와 체크박스 토글 로직이 명확하고 올바릅니다.


59-72: 체크박스와 버튼 구현이 잘 되어 있습니다.

사용자 확인 플로우가 적절하게 구현되었고, 접근성을 위한 alt 텍스트도 포함되어 있습니다.


74-180: 스타일링이 일관성 있게 잘 구현되었습니다.

반응형 디자인 고려사항, 테마 색상 활용, 그리고 조건부 스타일링이 적절하게 적용되었습니다. 고정 위치의 버튼 구현도 UX 관점에서 좋습니다.

src/pages/mypage/SavePage.tsx (3)

1-15: 임포트 구조가 잘 정리되어 있습니다.

임포트 순서와 경로 별칭 사용이 적절하며, 상수를 컴포넌트 외부에 정의한 것도 좋은 관행입니다.


16-37: 상태 관리와 로직 구현이 적절합니다.

React 훅 사용법이 올바르고, savedBooks 상태를 ISBN을 키로 관리하는 것이 적절합니다. 탭 변경 시 스크롤을 상단으로 이동시키는 UX 개선도 좋습니다.


96-199: 스타일 컴포넌트 구현이 우수합니다.

디자인 시스템과 일관성을 유지하고 있으며, 반응형 디자인과 접근성을 고려한 구현이 좋습니다. 컴포넌트 이름도 직관적이고 적절합니다.

Comment on lines +11 to +12
import { mockBooks } from '@/data/bookData';
import { mockPosts } from '@/data/postData';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

프로덕션 준비를 위한 데이터 소스 전환 계획을 수립하세요.

현재 목 데이터를 사용하고 있습니다. 실제 API 연동 시 로딩 상태, 에러 처리, 빈 데이터 상태 등을 고려한 구현이 필요합니다.

다음 스크립트로 목 데이터 파일들의 구조를 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# 목 데이터 파일들의 구조 확인
echo "=== Book Data Structure ==="
cat src/data/bookData.ts | head -20

echo -e "\n=== Post Data Structure ==="
cat src/data/postData.ts | head -20

Length of output: 1264


프로덕션 준비: mock 데이터 → 실제 API 전환 계획 수립 필요

SavePage.tsx (src/pages/mypage/SavePage.tsx: 11–12)에서 현재 다음 mock 데이터를 사용하고 있습니다. 프로덕션 연동 전 아래 사항을 반영한 전환 계획을 수립해주세요.

  • API 서비스 계층 설계 및 비동기 호출 로직 구현
  • 로딩 상태(UI 스켈레톤/스피너) 처리
  • 네트워크 에러 및 예외 상황 처리(UI 피드백)
  • 빈 배열 또는 데이터 부재 시 대체 UI/메시지 처리
  • 타입 일치 여부 검증 (src/data/bookData.ts의 mockBooks: Book[] / src/data/postData.ts의 mockPosts: PostData[] 구조 참고)
🤖 Prompt for AI Agents
In src/pages/mypage/SavePage.tsx around lines 11 to 12, the current usage of
mockBooks and mockPosts mock data needs to be replaced with real API integration
before production. Implement an API service layer to fetch book and post data
asynchronously, add loading state handling with UI skeletons or spinners, and
include error handling for network failures with user feedback. Also, handle
cases where the fetched data is empty by showing appropriate fallback UI or
messages. Finally, verify that the data types returned from the API match the
expected Book[] and PostData[] structures as defined in src/data/bookData.ts and
src/data/postData.ts.

Comment on lines +42 to +58
return (
<Wrapper>
<TitleHeader
title="회원탈퇴"
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBack}
/>
<Container>
<Content>
<ContentTitle>회원탈퇴 주의사항</ContentTitle>
<ContentText>
회원탈퇴 시 계정정보는 복구 불가능하며 90일 이후 재가입이 가능합니다.
</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
</Content>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

중복된 컨텐츠를 수정하세요.

55-57번 라인에 동일한 텍스트가 중복되어 있습니다. 실제 주의사항 내용으로 수정이 필요합니다.

-          <ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
-          <ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
-          <ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
+          <ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
+          <ContentText>탈퇴 후에는 동일한 이메일로 재가입할 수 없습니다.</ContentText>
+          <ContentText>저장된 북마크와 설정 정보가 모두 삭제됩니다.</ContentText>
📝 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
return (
<Wrapper>
<TitleHeader
title="회원탈퇴"
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBack}
/>
<Container>
<Content>
<ContentTitle>회원탈퇴 주의사항</ContentTitle>
<ContentText>
회원탈퇴 계정정보는 복구 불가능하며 90 이후 재가입이 가능합니다.
</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
</Content>
return (
<Wrapper>
<TitleHeader
title="회원탈퇴"
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBack}
/>
<Container>
<Content>
<ContentTitle>회원탈퇴 주의사항</ContentTitle>
<ContentText>
회원탈퇴 계정정보는 복구 불가능하며 90 이후 재가입이 가능합니다.
</ContentText>
<ContentText>등록된 기록 및 게시물은 삭제되지 않습니다.</ContentText>
<ContentText>탈퇴 후에는 동일한 이메일로 재가입할 수 없습니다.</ContentText>
<ContentText>저장된 북마크와 설정 정보가 모두 삭제됩니다.</ContentText>
</Content>
🤖 Prompt for AI Agents
In src/pages/mypage/WithdrawPage.tsx between lines 42 and 58, there are three
consecutive ContentText components (lines 55-57) with identical text about
records and posts not being deleted. Replace the duplicate texts with the
correct and distinct withdrawal caution messages to avoid redundancy and provide
accurate information.

@heeeeyong heeeeyong changed the title feat: 마이페이지 메인 화면 구현 feat: 마이페이지 전체 구현 Jul 28, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/pages/mypage/EditPage.tsx (2)

13-28: 검증 로직 개선 및 API 연동 필요

현재 console.log로 완료 처리되고 있어 실제 API 연동이 필요합니다. 또한 검증 중 로딩 상태 관리와 에러 처리를 개선할 수 있습니다.

  const onRightClick = () => {
+   setIsLoading(true);
    // 닉네임 유효성 검사
    if (nickname.trim() === '') {
      setNicknameError('변경할 닉네임을 입력해주세요.');
+     setIsLoading(false);
      return;
    }

    if (nickname === currentNickname) {
      setNicknameError('현재 닉네임과 동일합니다.');
+     setIsLoading(false);
      return;
    }

    // 에러가 없으면 서버에 요청
    setNicknameError('');
-   console.log('편집완료', { nickname, genreId: selectedId });
+   // TODO: API 호출 구현 필요
+   // updateProfile({ nickname, genreId: selectedId })
+   //   .then(() => navigate('/mypage'))
+   //   .catch(error => setNicknameError('프로필 업데이트에 실패했습니다.'))
+   //   .finally(() => setIsLoading(false));
  };

API 연동 구현을 도와드릴까요?


60-112: JSX 구조가 잘 구성되어 있습니다

컴포넌트 구조, 에러 처리, 이벤트 핸들링이 적절하게 구현되었습니다. 인라인 스타일 사용을 CSS 클래스로 변경하는 것을 고려해보세요.

                <div className="genreTitle">{g.title}</div>
-               <div className="genreSub" style={{ color: g.color }}>
+               <div className="genreSub" style={{ '--genre-color': g.color } as React.CSSProperties}>
                  {g.subTitle}
                </div>

스타일 파일에 CSS 변수를 활용하도록 추가:

.genreSub {
  color: var(--genre-color, #adadad);
}

전체적으로 사용자 경험을 고려한 검증 로직과 직관적인 UI 구성이 좋습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a90ddc and 3aabbf7.

📒 Files selected for processing (5)
  • src/pages/index.tsx (2 hunks)
  • src/pages/mypage/EditPage.styled.ts (1 hunks)
  • src/pages/mypage/EditPage.tsx (1 hunks)
  • src/pages/signup/SignupGenre.tsx (1 hunks)
  • src/types/genre.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • src/pages/signup/SignupGenre.tsx
  • src/types/genre.ts
  • src/pages/mypage/EditPage.styled.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/index.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/pages/mypage/EditPage.tsx (2)
src/types/genre.ts (1)
  • Genre (1-7)
src/pages/mypage/EditPage.styled.ts (5)
  • Wrapper (3-16)
  • Container (18-186)
  • InputBox (188-201)
  • StyledInput (203-216)
  • CharCount (218-225)
🔇 Additional comments (1)
src/pages/mypage/EditPage.tsx (1)

1-8: 임포트 구조가 잘 정리되어 있습니다

컴포넌트 임포트, 타입 정의, 스타일 컴포넌트가 논리적으로 잘 구성되어 있습니다.

Comment on lines +29 to +43
const [nickname, setNickname] = useState('');
const [currentNickname] = useState('문학하는고래'); // 서버에서 받아온 현재 닉네임
const [nicknameError, setNicknameError] = useState('');
const maxLength = 10;

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
const filteredValue = inputValue.replace(/[^ㄱ-ㅎ가-힣a-zA-Z0-9]/g, '');
setNickname(filteredValue);

// 입력이 변경되면 에러 상태 초기화
if (nicknameError) {
setNicknameError('');
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 값 수정 필요 및 입력 처리 로직 양호

입력 필터링과 에러 처리 로직은 잘 구현되었으나, 현재 닉네임이 하드코딩되어 있습니다.

- const [currentNickname] = useState('문학하는고래'); // 서버에서 받아온 현재 닉네임
+ const [currentNickname, setCurrentNickname] = useState('');
+
+ useEffect(() => {
+   // TODO: 사용자 정보 API 호출
+   // fetchUserProfile().then(data => setCurrentNickname(data.nickname));
+ }, []);

입력 값 필터링 정규식 [^ㄱ-ㅎ가-힣a-zA-Z0-9]는 한글, 영문, 숫자만 허용하는 로직이 정확합니다.

📝 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 [nickname, setNickname] = useState('');
const [currentNickname] = useState('문학하는고래'); // 서버에서 받아온 현재 닉네임
const [nicknameError, setNicknameError] = useState('');
const maxLength = 10;
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
const filteredValue = inputValue.replace(/[^--a-zA-Z0-9]/g, '');
setNickname(filteredValue);
// 입력이 변경되면 에러 상태 초기화
if (nicknameError) {
setNicknameError('');
}
};
const [nickname, setNickname] = useState('');
const [currentNickname, setCurrentNickname] = useState('');
useEffect(() => {
// TODO: 사용자 정보 API 호출
// fetchUserProfile().then(data => setCurrentNickname(data.nickname));
}, []);
const [nicknameError, setNicknameError] = useState('');
const maxLength = 10;
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
const filteredValue = inputValue.replace(/[^--a-zA-Z0-9]/g, '');
setNickname(filteredValue);
// 입력이 변경되면 에러 상태 초기화
if (nicknameError) {
setNicknameError('');
}
};
🤖 Prompt for AI Agents
In src/pages/mypage/EditPage.tsx around lines 29 to 43, the currentNickname
state is hardcoded with a fixed string. Replace this hardcoded value by fetching
the actual current nickname from the server or props when the component mounts,
using an effect hook or appropriate data fetching method, and initialize
currentNickname with that dynamic value instead.

Comment on lines +44 to +58
const currentGenreId = 'literature'; // 임시 하드코딩

const [genres, setGenres] = useState<Genre[]>([]);
const [selectedId, setSelectedId] = useState<string | null>('currentGenreId');

useEffect(() => {
fetch('/genres.json')
.then(res => res.json())
.then((data: Genre[]) => {
setGenres(data);
// 서버에서 받아온 현재 장르 ID로 선택 상태 설정
setSelectedId(currentGenreId);
})
.catch(console.error);
}, [currentGenreId]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

초기화 버그 및 하드코딩 문제 수정 필요

selectedId 초기값이 변수가 아닌 문자열 리터럴로 설정되어 있고, 장르 ID가 하드코딩되어 있습니다.

  const currentGenreId = 'literature'; // 임시 하드코딩

  const [genres, setGenres] = useState<Genre[]>([]);
- const [selectedId, setSelectedId] = useState<string | null>('currentGenreId');
+ const [selectedId, setSelectedId] = useState<string | null>(null);
+ const [currentGenreId, setCurrentGenreId] = useState<string>('');
+
  useEffect(() => {
+   // TODO: 사용자 현재 장르 정보 가져오기
+   // fetchUserProfile().then(data => setCurrentGenreId(data.genreId));
+
    fetch('/genres.json')
      .then(res => res.json())
      .then((data: Genre[]) => {
        setGenres(data);
-       // 서버에서 받아온 현재 장르 ID로 선택 상태 설정
-       setSelectedId(currentGenreId);
      })
-     .catch(console.error);
- }, [currentGenreId]);
+     .catch(error => {
+       console.error('장르 데이터 로딩 실패:', error);
+     });
+ }, []);
+
+ useEffect(() => {
+   if (currentGenreId) {
+     setSelectedId(currentGenreId);
+   }
+ }, [currentGenreId]);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/pages/mypage/EditPage.tsx around lines 44 to 58, the selectedId state is
incorrectly initialized with the string literal 'currentGenreId' instead of the
variable currentGenreId, and the currentGenreId is hardcoded as 'literature'.
Fix this by initializing selectedId with the variable currentGenreId (without
quotes) and replace the hardcoded currentGenreId with a dynamic value or prop as
appropriate to avoid hardcoding.

@heeeeyong heeeeyong merged commit ade28f2 into develop Jul 28, 2025
2 of 3 checks passed
@heeeeyong heeeeyong deleted the feature/mypage branch July 28, 2025 15:54
Copy link
Copy Markdown
Collaborator

@ho0010 ho0010 left a comment

Choose a reason for hiding this comment

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

LGTM!
다만 콘솔에 id 값 같은거 찍혀서 나가는건 수정이 필요해 보입니다!

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

Labels

✨ Feature 기능 개발 🎨 Html&css 마크업 & 스타일링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants