Skip to content

feat: 독서메이트 목록 페이지 구현#81

Merged
ljh130334 merged 4 commits intodevelopfrom
feat/member
Aug 10, 2025
Merged

feat: 독서메이트 목록 페이지 구현#81
ljh130334 merged 4 commits intodevelopfrom
feat/member

Conversation

@ljh130334
Copy link
Member

@ljh130334 ljh130334 commented Aug 10, 2025

#️⃣ 연관된 이슈

따로 등록하지 않았습니다.

📝 작업 내용

기존 ParticipatedGroupDetail.tsx에서 독서메이트 클릭 시 이동할 수 있는 독서메이트 목록 화면을 새롭게 개발했습니다.

📄 주요 구현 사항

1. 독서메이트 목록 페이지 구현

  • src/pages/groupmembers/GroupMembers.tsx 페이지 생성

2. MemberList 컴포넌트 개발

  • src/components/members/MemberList.tsx 컴포넌트 분리
  • props로 members 배열과 onMemberClick 콜백 함수를 받아 사용 가능
  • 콜백이 없으면 기본적으로 /user/${memberId} 경로로 이동하도록 구현

3. 목업 데이터 및 타입 정의

  • src/mocks/members.mock.ts에 Member 인터페이스와 mockMembers 배열 생성
  • 프로필 이미지, 닉네임, 역할(칭호), 팔로워 수 정보 포함

스크린샷

2025-08-10.1.53.39.mov

💬리뷰 요구사항(선택)

딱히 없습니다!

Summary by CodeRabbit

  • 신규 기능

    • 그룹 멤버 목록 페이지가 추가되었습니다.
    • 멤버 리스트에 프로필 이미지, 닉네임, 역할, 팔로워 수, 상태 등이 표시됩니다.
    • 멤버 클릭 시 개별 멤버 페이지로 이동하거나, 커스텀 클릭 핸들러를 사용할 수 있습니다.
  • 스타일

    • 멤버 리스트와 그룹 멤버 페이지에 대한 새로운 스타일이 적용되었습니다.
  • 테스트/모킹 데이터

    • 다양한 멤버 정보가 포함된 목업 데이터가 추가되었습니다.

@vercel
Copy link

vercel bot commented Aug 10, 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 Aug 10, 2025 5:11am

@coderabbitai
Copy link

coderabbitai bot commented Aug 10, 2025

Walkthrough

새로운 그룹 멤버 목록 UI 기능이 추가되었습니다. 멤버 정보를 위한 타입과 목데이터가 생성되었고, 멤버 리스트와 개별 멤버 아이템을 위한 스타일 컴포넌트와 React 컴포넌트가 구현되었습니다. 그룹 멤버 페이지와 해당 라우트가 생성되어, 실제로 멤버 목록을 확인할 수 있게 되었습니다.

Changes

Cohort / File(s) Change Summary
멤버 타입 및 목데이터 추가
src/mocks/members.mock.ts
Member 인터페이스와 7개의 목데이터(mockMembers)가 추가되었습니다.
멤버 리스트 스타일 컴포넌트
src/components/members/MemberList.styled.ts
멤버 리스트 UI를 위한 다양한 styled-components가 새로 정의 및 export되었습니다.
멤버 리스트 컴포넌트
src/components/members/MemberList.tsx
MemberList 컴포넌트가 추가되어, 멤버 배열을 받아 리스트를 렌더링하고 클릭 및 키보드 이벤트를 처리합니다.
그룹 멤버 페이지 스타일
src/pages/groupMembers/GroupMembers.styled.ts
그룹 멤버 페이지의 레이아웃을 위한 Wrapper 스타일 컴포넌트가 추가되었습니다.
그룹 멤버 페이지 컴포넌트
src/pages/groupMembers/GroupMembers.tsx
GroupMembers 페이지 컴포넌트가 추가되어, 헤더와 멤버 리스트를 렌더링하고, 멤버 클릭 시 상세 페이지로 이동합니다.
라우팅 추가
src/pages/index.tsx
"group/members" 경로에 대해 GroupMembers 페이지를 렌더링하는 라우트가 추가되었습니다.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant GroupMembersPage
    participant MemberList
    participant MemberItem

    User->>Router: "group/members" 경로로 이동
    Router->>GroupMembersPage: GroupMembers 컴포넌트 렌더링
    GroupMembersPage->>MemberList: mockMembers 전달
    MemberList->>MemberItem: 각 멤버 데이터로 렌더링
    User->>MemberItem: 멤버 클릭 또는 키보드 활성화
    MemberItem->>GroupMembersPage: onMemberClick 호출
    GroupMembersPage->>Router: 해당 멤버 상세 페이지로 이동
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Suggested labels

✨ Feature, 🎨 Html&css

Suggested reviewers

  • heeeeyong

Poem

🥕
새로운 멤버 리스트, 토끼도 신이 나,
그룹 속 친구들, 한눈에 다 보여라!
클릭하면 이동해, 상세로 점프~
스타일도 예쁘게, 검은 배경 품고,
오늘도 개발자, 멋진 코드 꿈꾸네!
🐇

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 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 e78e988 and f504497.

📒 Files selected for processing (3)
  • src/components/members/MemberList.styled.ts (1 hunks)
  • src/components/members/MemberList.tsx (1 hunks)
  • src/mocks/members.mock.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/members/MemberList.tsx
  • src/mocks/members.mock.ts
  • src/components/members/MemberList.styled.ts
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/member

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
  • 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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.

Copy link

@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: 8

🔭 Outside diff range comments (1)
src/pages/index.tsx (1)

1-91: 라우트 정의 누락: /user/:userId 경로 추가 필요

GroupMembersMemberList 컴포넌트에서

navigate(`/user/${memberId}`)

를 사용하고 있지만, src/pages/index.tsx에는 /user/:userId 경로가 정의되어 있지 않습니다. 아래와 같이 누락된 라우트를 추가하세요.

파일: src/pages/index.tsx

 import Signup from './signup/Signup';
 // …생략…
 import WithdrawDonePage from './mypage/WithdrawDonePage';
 import EditPage from './mypage/EditPage';
+import UserPage from './user/UserPage';    // 실제 컴포넌트 경로/이름으로 조정

 const Router = () => {
   const router = createBrowserRouter(
     createRoutesFromElements(
       <>
         <Route path="/" element={<Login />} />
         {/* …생략… */}
         <Route path="notice" element={<Notice />} />
+        <Route path="user/:userId" element={<UserPage />} />
       </>,
     ),
   );
   return (
     <>
       <ScrollToTop />
       <RouterProvider router={router} />
     </>
   );
 };

 export default Router;
  • UserPage 컴포넌트가 없다면 새로 생성하거나, 기존 사용자 프로필 페이지 컴포넌트를 사용해 주세요.
  • 위 변경 이후 GroupMembersMemberList의 네비게이션 경로가 정상 작동합니다.
🧹 Nitpick comments (12)
src/mocks/members.mock.ts (2)

1-8: 프로필 이미지 필드: 빈 문자열 대신 옵셔널로 처리 권장

UI에서 기본 이미지로 폴백할 예정이라면 빈 문자열 센티널 값보다 profileImage?: string로 옵셔널 처리하고, 목데이터에서는 해당 필드를 생략하는 편이 명확합니다.

 export interface Member {
   id: string;
   nickname: string;
   role: string;
-  profileImage: string;
+  profileImage?: string;
   points: number;
-  followers: string;
+  followersCount: number;
 }

 export const mockMembers: Member[] = [
   {
     id: '1',
     nickname: 'Thiper',
     role: '칭호칭호',
-    profileImage: '', // 빈 문자열로 기본 이미지 처리
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
   {
     id: '2',
     nickname: 'thipthip',
     role: '공식 인플루언서',
-    profileImage: '',
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
   {
     id: '3',
     nickname: 'Thiper',
     role: '청춘청춘',
-    profileImage: '',
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
   {
     id: '4',
     nickname: 'thip01',
     role: '작가',
-    profileImage: '',
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
   {
     id: '5',
     nickname: 'thip01',
     role: '작가',
-    profileImage: '',
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
   {
     id: '6',
     nickname: 'thip01',
     role: '작가',
-    profileImage: '',
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
   {
     id: '7',
     nickname: 'thip01',
     role: '작가',
-    profileImage: '',
     points: 0,
-    followers: '00명이 띱 하는 중',
+    followersCount: 0,
   },
 ];

Also applies to: 15-15, 23-23, 31-31, 39-39, 47-47, 55-55, 63-63


1-8: 타입 정의를 mocks에서 분리하세요

도메인 타입(Member)을 mock 모듈에서 import하는 구조는 확장성/재사용성에 불리합니다. src/types/member.ts 등으로 이동하고, mocks에서는 해당 타입을 import만 하세요.

원하시면 타입 파일 생성과 import 경로 일괄 치환 PR 보조 가능합니다.

src/pages/groupMembers/GroupMembers.styled.ts (1)

10-13: 모바일 주소창 이슈 대응을 위해 100dvh 추가 + 폭 명시 추천

모바일 Safari 등에서 100vh가 뷰포트 UI에 영향을 받아 점프 현상이 생길 수 있습니다. 100dvh를 함께 선언해 폴백을 두는 걸 권장합니다. 또한 폭을 명시하면 레이아웃 안정성이 좋아집니다.

   min-height: 100vh;
+  min-height: 100dvh;
   margin: 0 auto;
+  width: 100%;
   background-color: ${colors.black.main};
src/pages/index.tsx (1)

1-6: (선택) 신규 페이지 라우트는 지연 로딩으로 번들 크기 최적화 가능

규모가 커질 경우 React.lazy + Suspense로 GroupMembers를 지연 로딩하면 초기 번들 크기를 줄일 수 있습니다.

원하시면 라우트 레이지 로딩 패턴 코드 예시 드리겠습니다.

src/pages/groupMembers/GroupMembers.tsx (1)

20-30: onMemberClick 중복 정의 최소화(선택)

MemberList가 기본 내비게이션(fallback)을 제공한다면, 동일한 동작을 이 페이지에서 다시 넘길 필요는 없습니다. 경로 정합성만 맞춘 뒤 onMemberClick을 제거해 단순화할 수 있습니다.

-      <Wrapper>
-        <MemberList members={mockMembers} onMemberClick={handleMemberClick} />
-      </Wrapper>
+      <Wrapper>
+        <MemberList members={mockMembers} />
+      </Wrapper>

단, MemberList의 기본 경로가 실제 라우트와 일치하도록 먼저 정리되어야 합니다.

src/components/members/MemberList.tsx (4)

45-45: Chevron 아이콘은 장식용: 스크린리더 노이즈 제거

장식 아이콘은 alt를 비우고 aria-hidden을 주는 게 접근성에 유리합니다.

-          <ChevronIcon src={rightChevron} alt="이동" />
+          <ChevronIcon src={rightChevron} alt="" aria-hidden="true" />

37-45: 프로필 이미지 필드 활용 및 폴백 처리(선택)

현재 프로필 이미지를 렌더링하지 않습니다. 값이 있으면 이미지, 없으면 플레이스홀더를 보여주세요.

-            <ProfileImage />
+            {member.profileImage ? (
+              <ProfileImage as="img" src={member.profileImage} alt={`${member.nickname} 프로필`} />
+            ) : (
+              <ProfileImage aria-hidden="true" />
+            )}

참고: Emotion styled는 as prop으로 엘리먼트 전환이 가능합니다. 별도 styled.img를 추가하는 방식도 OK입니다.


1-4: 타입 의존성 위치

컴포넌트가 mocks에서 타입을 import하는 구조는 재사용성에 불리합니다. src/types/member.ts 등으로 타입을 이동해 이 파일이 types에서 import하도록 바꾸는 걸 권장합니다.

원하시면 타입 이동 및 경로 일괄 치환 스크립트 제공하겠습니다.


34-49: 빈 목록 처리(선택)

members가 빈 배열일 때의 빈 상태 UI(예: “표시할 멤버가 없습니다”)를 제공하면 UX가 좋아집니다.

필요 시 간단한 빈 상태 블록 추가를 제안드립니다.

src/components/members/MemberList.styled.ts (3)

71-75: 하드코딩된 색상/폰트 크기를 디자인 토큰으로 통일

color: white;font-size: 11px;는 전역 토큰과 일관성이 떨어집니다. 전역 semanticColorstypography 토큰을 사용해 주세요.

 export const MemberStatus = styled.div`
-  color: white;
-  font-size: 11px;
+  color: ${semanticColors.text.primary};
+  font-size: ${typography.fontSize['2xs']};
   font-weight: ${typography.fontWeight.regular};
 `

19-21: 호버 배경 컬러도 의미 기반 토큰 사용 검토

현재 colors.darkgrey['50']를 직접 사용 중입니다. 프로젝트 전반의 일관성을 위해 의미 기반 토큰(semantic) 우선 적용을 검토해 주세요. 적절한 토큰이 없다면 디자인 시스템 측면에서 추가 정의를 고려해 보세요.


77-80: 장식용 체브론 아이콘: 드래그/선택 방지 및 접근성 처리 제안

아이콘은 장식용일 가능성이 높습니다. 스타일에서 선택 방지를, 컴포넌트에서는 alt=""aria-hidden="true"를 적용해 스크린 리더 노이즈를 줄이세요.

 export const ChevronIcon = styled.img`
   width: 24px;
   height: 24px;
+  display: block;
+  user-select: none;
 `
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 17eb99a and e78e988.

⛔ Files ignored due to path filters (1)
  • src/assets/member/right-chevron.svg is excluded by !**/*.svg
📒 Files selected for processing (6)
  • src/components/members/MemberList.styled.ts (1 hunks)
  • src/components/members/MemberList.tsx (1 hunks)
  • src/mocks/members.mock.ts (1 hunks)
  • src/pages/groupMembers/GroupMembers.styled.ts (1 hunks)
  • src/pages/groupMembers/GroupMembers.tsx (1 hunks)
  • src/pages/index.tsx (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/pages/groupMembers/GroupMembers.styled.ts (1)
src/styles/global/global.ts (1)
  • colors (4-53)
src/pages/groupMembers/GroupMembers.tsx (2)
src/pages/groupMembers/GroupMembers.styled.ts (1)
  • Wrapper (4-13)
src/mocks/members.mock.ts (1)
  • mockMembers (10-67)
src/components/members/MemberList.tsx (2)
src/mocks/members.mock.ts (1)
  • Member (1-8)
src/components/members/MemberList.styled.ts (9)
  • Container (4-8)
  • MemberItem (10-36)
  • ProfileSection (38-43)
  • ProfileImage (45-51)
  • MemberInfo (53-57)
  • MemberName (59-63)
  • MemberRole (65-69)
  • MemberStatus (71-75)
  • ChevronIcon (77-80)
src/components/members/MemberList.styled.ts (1)
src/styles/global/global.ts (3)
  • colors (4-53)
  • semanticColors (80-153)
  • typography (56-77)
🔇 Additional comments (1)
src/components/members/MemberList.styled.ts (1)

4-8: 레이아웃 설정은 깔끔합니다

세로 플렉스와 가로 100% 컨테이너 구성 적절합니다.

Comment on lines 10 to 36
export const MemberItem = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
cursor: pointer;
transition: background-color 0.2s;
position: relative;

&:hover {
background-color: ${colors.darkgrey['50']};
}

&::after {
content: '';
position: absolute;
bottom: 0;
left: 20px;
right: 20px;
height: 1px;
background-color: ${colors.darkgrey.dark};
}

&:last-child::after {
display: none;
}
`;
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

키보드 포커스 스타일 추가로 접근성 보완 필요

호버 스타일만 있고 포커스 표시가 없습니다. 클릭 가능한 행이라면 키보드 사용자도 인지 가능하도록 :focus-visible 스타일을 추가하세요. 또한 컴포넌트(MemberList.tsx)에서 tabIndex=0, role="button"(또는 실제 버튼) 및 Enter/Space 키 핸들링을 고려해 주세요.

 export const MemberItem = styled.div`
   display: flex;
   align-items: center;
   justify-content: space-between;
   padding: 20px;
   cursor: pointer;
   transition: background-color 0.2s;
   position: relative;

   &:hover {
     background-color: ${colors.darkgrey['50']};
   }
+
+  &:focus-visible {
+    outline: 2px solid ${semanticColors.text.point.green};
+    outline-offset: -2px;
+  }
 
   &::after {
     content: '';
     position: absolute;
     bottom: 0;
     left: 20px;
     right: 20px;
     height: 1px;
     background-color: ${colors.darkgrey.dark};
   }
 
   &:last-child::after {
     display: none;
   }
 `

MemberList.tsx에서 MemberItem이 실제로 포커스 가능하게 설정되어 있는지 확인해 보겠습니다. 아래 스크립트를 실행해 주세요.


🏁 Script executed:

#!/bin/bash
# MemberList.tsx 파일 경로 찾기
file=$(fd --hidden -e tsx MemberList.tsx)
echo "Found: $file"

# MemberItem 사용 위치 확인
grep -n "MemberItem" -n "$file" || echo "MemberItem 사용이 없습니다."

# 파일 상위 200라인 출력
echo -e "\n===== $file (1~200) ====="
sed -n '1,200p' "$file"

# tabIndex, role, onKeyDown/Enter/Space 핸들러 검색
echo -e "\n===== tabIndex, role, onKeyDown, onKeyPress 검색 ====="
grep -En "tabIndex|role=|onKeyDown|onKeyPress|onKeyUp" "$file" || echo "관련 속성/핸들러 미발견"

Length of output: 2135


키보드 접근성 향상을 위한 MemberItem 컴포넌트 업데이트 필요

현재 MemberItemonClick만 지원하고 있어 키보드 사용자가 접근·인식할 수 없습니다. 다음을 적용하세요:

• src/components/members/MemberList.styled.ts

  • :focus-visible 스타일 추가 (아웃라인/오프셋)

• src/components/members/MemberList.tsx

  • <MemberItem>tabIndex={0}role="button" 속성 추가
  • onKeyDown 핸들러로 Enter/Space 키 입력 시 handleMemberClick 호출
--- a/src/components/members/MemberList.styled.ts
+++ b/src/components/members/MemberList.styled.ts
@@ export const MemberItem = styled.div`
   &:hover {
     background-color: ${colors.darkgrey['50']};
   }
+
+  &:focus-visible {
+    outline: 2px solid ${semanticColors.text.point.green};
+    outline-offset: -2px;
+  }
--- a/src/components/members/MemberList.tsx
+++ b/src/components/members/MemberList.tsx
@@ members.map(member => (
-        <MemberItem key={member.id} onClick={() => handleMemberClick(member.id)}>
+        <MemberItem
+          key={member.id}
+          role="button"
+          tabIndex={0}
+          onClick={() => handleMemberClick(member.id)}
+          onKeyDown={e => {
+            if (e.key === 'Enter' || e.key === ' ') {
+              e.preventDefault();
+              handleMemberClick(member.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.

Suggested change
export const MemberItem = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
&:hover {
background-color: ${colors.darkgrey['50']};
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 20px;
right: 20px;
height: 1px;
background-color: ${colors.darkgrey.dark};
}
&:last-child::after {
display: none;
}
`;
file=src/components/members/MemberList.styled.ts
export const MemberItem = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
&:hover {
background-color: ${colors.darkgrey['50']};
}
&:focus-visible {
outline: 2px solid ${semanticColors.text.point.green};
outline-offset: -2px;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 20px;
right: 20px;
height: 1px;
background-color: ${colors.darkgrey.dark};
}
&:last-child::after {
display: none;
}
`;
Suggested change
export const MemberItem = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
&:hover {
background-color: ${colors.darkgrey['50']};
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 20px;
right: 20px;
height: 1px;
background-color: ${colors.darkgrey.dark};
}
&:last-child::after {
display: none;
}
`;
file=src/components/members/MemberList.tsx
members.map(member => (
<MemberItem
key={member.id}
role="button"
tabIndex={0}
onClick={() => handleMemberClick(member.id)}
onKeyDown={e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleMemberClick(member.id);
}
}}
>
{/* existing member content */}
</MemberItem>
))
🤖 Prompt for AI Agents
In src/components/members/MemberList.styled.ts lines 10 to 36, add a
:focus-visible style to MemberItem to improve keyboard accessibility by showing
an outline and offset when focused. In src/components/members/MemberList.tsx,
update the MemberItem component to include tabIndex={0} and role="button"
attributes, and add an onKeyDown handler that calls handleMemberClick when Enter
or Space keys are pressed.

Comment on lines +45 to +51
export const ProfileImage = styled.div`
width: 48px;
height: 48px;
border-radius: 50%;
background-color: ${colors.grey['400']};
flex-shrink: 0;
`;
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

프로필 이미지는 div 대신 img 사용 권장(alt 제공 및 object-fit)

현재 div는 대체 텍스트를 제공할 수 없어 접근성이 떨어집니다. styled.img로 전환하고 object-fit: cover를 적용하면 비율 유지와 자르기가 자연스럽습니다. 컴포넌트에서 alt를 의미 있게 제공하거나 장식용이면 alt=""를 지정하세요.

-export const ProfileImage = styled.div`
+export const ProfileImage = styled.img`
   width: 48px;
   height: 48px;
   border-radius: 50%;
+  object-fit: cover;
   background-color: ${colors.grey['400']};
   flex-shrink: 0;
 `

ProfileImage 컴포넌트가 실제로 img 태그로 교체해도 문제가 없는지, 사용 위치와 전달되는 props(src, alt 등)를 확인하기 위해 아래 스크립트를 실행하겠습니다.


🏁 Script executed:

#!/bin/bash
grep -R "ProfileImage" -n src

Length of output: 2953


프로필 이미지가 실제로 srcalt 같은 props를 받아 렌더링되는지 확인이 필요합니다. 아래 스크립트로 MemberList.tsx 사용 부분을 살펴보겠습니다.


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/components/members/MemberList.tsx

Length of output: 1382


프로필 URL이 실제로 Member 타입에 포함되어 있는지 확인하기 위해 src/mocks/members.mock.ts를 조회해 보겠습니다.


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/mocks/members.mock.ts

Length of output: 1160


프로필 이미지를 <img>로 전환하고 src/alt를 전달하세요

현재 ProfileImage는 styled.div로 고정된 회색 원형만 렌더링되어 실제 유저 이미지와 대체 텍스트를 제공하지 않습니다. 접근성과 화면 비율 유지를 위해 다음을 적용해주세요:

• src/components/members/MemberList.styled.ts

  • styled.divstyled.img
  • object-fit: cover 추가
-export const ProfileImage = styled.div`
+export const ProfileImage = styled.img`
  width: 48px;
  height: 48px;
  border-radius: 50%;
+   object-fit: cover;
  background-color: ${colors.grey['400']};
  flex-shrink: 0;
`;

• src/components/members/MemberList.tsx

  • <ProfileImage /> 사용부에 src와 유의미한 alt 전달
{members.map(member => (
  <MemberItem key={member.id} onClick={() => handleMemberClick(member.id)}>
    <ProfileSection>
-       <ProfileImage />
+       <ProfileImage
+         src={member.profileImage || undefined}
+         alt={member.nickname ? `${member.nickname} 프로필` : ''}
+       />
      <MemberInfo>
        <MemberName>{member.nickname}</MemberName>
        <MemberRole>{member.role}</MemberRole>
      </MemberInfo>
    </ProfileSection>
    <MemberStatus>{member.followers}</MemberStatus>
    <ChevronIcon src={rightChevron} alt="이동" />
  </MemberItem>
))}
🤖 Prompt for AI Agents
In src/components/members/MemberList.styled.ts lines 45 to 51, change
ProfileImage from styled.div to styled.img, add object-fit: cover to maintain
image aspect ratio, and remove background-color since the image will be shown.
Then in src/components/members/MemberList.tsx, update all uses of <ProfileImage
/> to pass appropriate src and alt props for accessibility and correct image
rendering.

Comment on lines +15 to +18
const handleMemberClick = (memberId: string) => {
// 특정 사용자 페이지로 이동
navigate(`/user/${memberId}`);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

사용자 페이지 경로 불일치 수정 필요

현재 /user/${memberId}로 이동하지만 라우터에는 해당 경로가 없습니다. 기존 사용자 상세 라우트(otherfeed/:userId)와 맞추시는 걸 권장합니다.

옵션 B를 채택할 경우 다음과 같이 변경하세요.

-  navigate(`/user/${memberId}`);
+  navigate(`/otherfeed/${memberId}`);

라우트 추가(옵션 A)를 선택한다면 본 변경은 불필요합니다.

📝 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 handleMemberClick = (memberId: string) => {
// 특정 사용자 페이지로 이동
navigate(`/user/${memberId}`);
};
const handleMemberClick = (memberId: string) => {
// 특정 사용자 페이지로 이동
navigate(`/otherfeed/${memberId}`);
};
🤖 Prompt for AI Agents
In src/pages/groupMembers/GroupMembers.tsx lines 15 to 18, the navigation path
for user pages is incorrect as it uses `/user/${memberId}`, which does not match
any existing route. To fix this, update the navigate call to use the existing
route pattern `/otherfeed/${memberId}` so it aligns with the defined router
paths.

import EditPage from './mypage/EditPage';
import Notice from './notice/Notice';
import ParticipatedGroupDetail from './groupDetail/ParticipatedGroupDetail';
import GroupMembers from './groupMembers/GroupMembers';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

내비게이션 경로 불일치: '/user/:id' 라우트가 없어 클릭 시 404 가능성

MemberList/GroupMembers에서 /user/${memberId}로 이동하지만, 현재 라우터에는 해당 경로가 없습니다. 현 상태로는 클릭 시 404가 날 수 있습니다. 아래 두 가지 중 하나로 정합성을 맞춰주세요.

옵션 A) 사용자 라우트를 추가해 기존 내비게이션을 살립니다.

       <Route path="otherfeed/:userId" element={<OtherFeedPage />} />
+      <Route path="user/:userId" element={<OtherFeedPage />} />
       <Route path="follow/:type/:userId" element={<FollowerListPage />} />

옵션 B) 내비게이션 경로를 기존 사용자 상세 라우트로 통일합니다.

  • MemberList: /user/${memberId} -> /otherfeed/${memberId}
  • GroupMembers: 동일 변경

Also applies to: 57-57

🤖 Prompt for AI Agents
In src/pages/index.tsx at lines 39 and 57, the navigation path '/user/:id' used
in MemberList and GroupMembers does not exist in the router, causing potential
404 errors. To fix this, either add a new user route matching '/user/:id' to the
router (Option A) or update the navigation paths in MemberList and GroupMembers
from '/user/${memberId}' to the existing route '/otherfeed/${memberId}' to
maintain consistency (Option B). Choose one approach and apply it consistently
to both locations.

@ljh130334 ljh130334 merged commit e1611cf into develop Aug 10, 2025
3 checks passed
@ljh130334 ljh130334 deleted the feat/member branch August 10, 2025 06:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant