Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAPI의 RoomMember/Member 타입에 isMyself 플래그를 추가·전파하고, 멤버 클릭 시 플래그 기반 라우팅으로 분기하도록 UI 로직을 수정. 사진 업로드 섹션은 Blob URL을 메모이즈·정리하도록 변경. 일부 스타일에서 caret 색/스크롤/폭 조정 및 사소한 포맷·문법 수정. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as UI (Members 화면)
participant API as getRoomMembers API
participant Conv as convertRoomMembersToMembers
participant Router as Router
UI->>API: fetch room members
API-->>UI: RoomMember[] (includes isMyself)
UI->>Conv: map to Member[]
Conv-->>UI: Member[] (propagate isMyself)
UI->>UI: render list with member.isMyself
UI->>Router: onClick(member)
alt member.isMyself == true
Router-->>UI: navigate /myfeed/{member.id}
else member.isMyself == false
Router-->>UI: navigate /otherfeed/{member.id}
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/createpost/PostContentSection.styled.ts (1)
13-15: readOnly 모드 텍스트 가독성 문제 수정 필요 — WCAG AA 대비 기준(4.5:1) 미달readOnly 상태에서 배경색
#F5F5F5대비 현재 텍스트색#DADADA는 명도 대비 비율이 1.28:1에 불과해(필수 4.5:1 대비 크게 미달) 가독성이 심각히 저하됩니다. 반면 제안된#121212(onlightPrimary)는 17.18:1로 충분히 대비를 확보합니다.수정 대상:
- 파일:
src/components/createpost/PostContentSection.styled.ts- 위치:
TextAreastyled component 내background-color/color설정 부분제안된 코드 변경:
export const TextArea = styled.textarea<{ readOnly?: boolean }>` width: 100%; min-height: 100px; - background-color: ${props => (props.readOnly ? '#f5f5f5' : semanticColors.background.primary)}; - color: ${semanticColors.text.secondary}; + background-color: ${props => (props.readOnly ? '#f5f5f5' : semanticColors.background.primary)}; + color: ${props => + props.readOnly ? semanticColors.text.onlightPrimary : semanticColors.text.secondary};이렇게 수정 시 readOnly 모드에서 WCAG AA 명도 대비 기준(4.5:1)을 충족하며, 사용자 접근성을 보장할 수 있습니다.
🧹 Nitpick comments (13)
src/components/recordwrite/PageRangeSection.styled.ts (4)
53-56: PageInput 기본/최소 너비 축소(12px/9px)로 인한 사용성 이슈 가능현재 기본 12px·최소 9px는 클릭 타깃이 과도하게 작아지고 캐럿/플레이스홀더가 잘려 보일 수 있습니다. 폰트 크기 변화에도 대응 가능한
ch단위를 쓰고 최소 3ch 이상을 확보하는 것을 권장합니다.iOS Safari/Android Chrome에서 빈 값/한글 조합 입력(IME) 시 폭 흔들림과 포커스 타깃 접근성을 확인해 주세요.
적용 제안(diff):
- width: ${props => { - if (!props.inputLength) return '12px'; - return `${Math.max(9, props.inputLength * 8)}px`; - }}; + width: ${({ inputLength }) => { + const len = typeof inputLength === 'number' ? inputLength : 0; + // ch 단위는 글꼴/폰트크기 변화에 자연스럽게 반응합니다. + const ch = Math.max(3, len || 1); + return `${ch}ch`; + }};
23-33: 키보드 접근성: outline 제거로 포커스 가시성이 사라질 수 있음
PageInput에outline: none이 설정되어 있고 컨테이너도 기본적으로 보더가 없어, 키보드 포커스가 사실상 보이지 않을 수 있습니다.:focus-within링으로 시각적 피드백을 추가하는 것을 권장합니다.권장 diff(컨테이너에 포커스 링 추가):
export const PageInputContainer = styled.div<{ hasError?: boolean }>` width: 100%; height: 48px; background-color: ${semanticColors.background.cardDark}; border: ${props => (props.hasError ? '1px solid #FF9496' : 'none')}; border-radius: 12px; padding: 18px 52px 18px 20px; display: flex; align-items: center; position: relative; + &:focus-within { + box-shadow: 0 0 0 2px ${semanticColors.button.fill.primary}; + } `;Also applies to: 45-52
27-27: 하드코딩된 색상 상수와 불필요한 분기 정리 권장
#FF9496,#3d3d3d등 매직 넘버 색상이 다수 사용됩니다. 테마 전역 토큰(semanticColors.*)으로 치환하면 다크모드/브랜딩 변경 시 유지보수가 쉬워집니다.Tooltip의 배경색은 variant가red/green이어도 동일하게#3d3d3d로 분기됩니다(라인 199). 분기 제거 또는 의도된 차별화를 토큰으로 반영해 주세요.TooltipText는 red만 하드코딩(#FF9496)이고 green만 토큰을 사용합니다(라인 210). 양쪽 모두 토큰 기반으로 통일하는 것을 권장합니다.Also applies to: 199-201, 210-214, 233-233
165-168: ToggleSwitch 비활성 배경에 text 토큰 사용비활성 배경으로
semanticColors.text.ghost(텍스트 토큰)를 쓰면 의미가 어색하고 대비가 모호해질 수 있습니다.background.disabled/surface.disabled/button.fill.disabled유형의 표면 토큰이 있다면 그 사용을 고려해 주세요. 대비(AA)도 한 번 확인 부탁드립니다.src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.styled.ts (2)
252-252: 하드코딩된 색상(#e0e0e0) 대신 디자인 토큰 사용을 권장합니다.스타일 일관성과 테마 전환(다크/라이트)에 유리하도록 semanticColors 토큰으로 대체하는 편이 좋습니다. 이 파일에서도 placeholder 등은 이미 semanticColors를 사용 중입니다.
아래처럼 교체하면 됩니다.
export const EmptyText = styled.p` - color: #e0e0e0; + color: ${semanticColors.text.ghost}; font-size: ${typography.fontSize.sm}; font-weight: ${typography.fontWeight.regular}; margin: 0; text-align: center; `
169-169: CSS 유효값: background-color: none → transparent로 수정 권장background-color에 none은 유효한 값이 아니므로 무시됩니다. 의도가 투명 배경이라면 transparent를 명시하세요.
export const BookItem = styled.div` display: flex; align-items: center; padding: 12px 2px; - background-color: none; + background-color: transparent; cursor: pointer; transition: background-color 0.2s ease; border-bottom: 1px solid ${colors.grey[400]};src/components/creategroup/RoomInfoSection.styled.ts (1)
23-23: caret-color 토큰 일관화(semanticColors 사용) 및 import 정리 제안다른 컴포넌트(PostContentSection, BookSearchBottomSheet 등)는 point.green(semanticColors)을 사용합니다. 동일 토큰 사용으로 테마 일관성을 맞추는 것을 권장합니다. colors import도 불필요해집니다.
-import { typography, semanticColors, colors } from '../../styles/global/global'; +import { typography, semanticColors } from '../../styles/global/global'; @@ margin: 0; - caret-color: ${colors.neongreen}; + caret-color: ${semanticColors.text.point.green};Also applies to: 2-2
src/components/createpost/PostContentSection.styled.ts (2)
26-47: 커스텀 스크롤바: 모바일 터치 관성 및 레이아웃 점프 방지 옵션 제안현재 WebKit/Firefox 커스텀은 잘 추가되었습니다. 보완 아이디어:
- iOS 등 터치 환경에서 관성 스크롤을 위해 -webkit-overflow-scrolling: touch; 추가
- 스크롤바 등장/사라짐으로 인한 레이아웃 점프를 줄이기 위해 지원 브라우저에서 scrollbar-gutter 사용
export const TextArea = styled.textarea<{ readOnly?: boolean }>` @@ overflow-y: auto; + -webkit-overflow-scrolling: touch; @@ + /* 지원 브라우저에서 스크롤바 거터 고정으로 레이아웃 점프 최소화 */ + @supports (scrollbar-gutter: stable) { + scrollbar-gutter: stable; + } cursor: ${props => (props.readOnly ? 'not-allowed' : 'text')};
2-2: caret-color 토큰 일관화(semanticColors 사용) 및 import 정리RoomInfoSection과 동일하게 semanticColors.text.point.green을 사용하면 테마 일관성이 좋아지고 디자인 토큰 체계에 맞습니다. colors import도 불필요합니다.
-import { typography, semanticColors, colors } from '../../styles/global/global'; +import { typography, semanticColors } from '../../styles/global/global'; @@ - caret-color: ${colors.neongreen}; + caret-color: ${semanticColors.text.point.green};Also applies to: 24-24
src/components/createpost/PhotoSection.tsx (1)
52-56: 인덱스 키/인덱스 기반 URL 접근은 재정렬·삭제 시 이미지-URL 매칭 오류 위험 → 파일 식별자 기반 키와 URL Map 사용 권장현재
key={new-${index}}와photoUrls[index]는 배열 재정렬/중간 삭제 시 DOM 재사용으로 잘못된 이미지를 표시할 수 있습니다.File의(name,lastModified,size)를 이용한 안정 키와Map<File,string>을 사용하면 안전합니다.아래처럼 보완하는 것을 제안합니다.
- const photoUrls = useMemo(() => { - return photos.map(file => URL.createObjectURL(file)); - }, [photos]); + const photoUrlMap = useMemo(() => { + const map = new Map<File, string>(); + photos.forEach(file => map.set(file, URL.createObjectURL(file))); + return map; + }, [photos]); - useEffect(() => { - return () => { - photoUrls.forEach(url => URL.revokeObjectURL(url)); - }; - }, [photoUrls]); + useEffect(() => { + return () => { + photoUrlMap.forEach(url => URL.revokeObjectURL(url)); + }; + }, [photoUrlMap]); - {photos.map((_, index) => ( + {photos.map((file, index) => ( <div - key={`new-${index}`} + key={`new-${file.name}-${file.lastModified}-${file.size}-${index}`} style={{ position: 'relative', width: '80px', height: '80px' }} > - <PhotoImage src={photoUrls[index]} alt={`새 이미지 ${index + 1}`} /> + <PhotoImage src={photoUrlMap.get(file)!} alt={`새 이미지 ${index + 1}`} /> {!readOnly && ( <RemoveButton onClick={() => onPhotoRemove(index)}> <img src={closeIcon} alt="삭제" /> </RemoveButton> )} </div> ))}추가로, 제거 버튼 클릭 시 해당 URL을 즉시
revokeObjectURL로 해제하면(리스트 리렌더를 기다리지 않고) 메모리 반환 타이밍이 더 빨라집니다. 필요하면 헬퍼를 만들어 드릴 수 있습니다.Also applies to: 57-63, 92-98
src/pages/groupMembers/GroupMembers.tsx (1)
68-75: 주석과 구현이 불일치하며 라우팅 분기가 이중 정의되어 있습니다주석은 “MemberList에서 isMyself에 따른 네비게이션 처리”라고 적혀 있지만, 실제로는 이 핸들러에서 분기/네비게이션을 수행하고 있습니다. 중복 책임은 추후 변경 시 드리프트를 유발할 수 있습니다.
최소 변경으로 혼선을 줄이려면, 아래처럼 주석만 정리하세요.
- // MemberList 컴포넌트에서 isMyself에 따른 네비게이션 처리를 담당하므로 - // 여기서는 기본 동작을 유지 + // 이 페이지에서 isMyself에 따라 네비게이션을 분기합니다.또는, MemberList의 기본 내비게이션을 사용하려면 이 핸들러를 제거하고
onMemberClick을 넘기지 않는 방식으로 단일 책임으로 합칠 수 있습니다(선택).src/components/members/MemberList.tsx (2)
26-36: 기본 내비 분기 로직은 명확합니다. 다만 리스트 컴포넌트가 API 타입 세부사항(isMyself)에 직접 의존프레젠테이셔널 컴포넌트의 결합도를 낮추려면(선택) 라우팅 결정을 상위로 올리고, 여기서는
onMemberClick(member)같은 콜백만 호출하도록 단순화하는 방법도 있습니다. 현재 변경은 실용적이며 동작상 문제는 없습니다.
46-51: 키보드 접근성: Space 키는 onKeyUp에서 트리거하는 것이 네이티브 버튼 동작과 더 유사지금은 onKeyDown에서 Space를 처리하고 있어 스크린리더/키보드 사용성 측면에서 네이티브 버튼과 차이가 날 수 있습니다. 아래처럼 Space는 keyup에서, Enter는 지금처럼 keydown으로 유지하면 UX 일관성이 좋아집니다.
onKeyDown={(e: KeyboardEvent) => { - if (e.key === 'Enter' || e.key === ' ') { + if (e.key === 'Enter') { e.preventDefault(); - handleMemberClick(member); + handleMemberClick(member); } }} + onKeyUp={(e: KeyboardEvent) => { + if (e.key === ' ') { + e.preventDefault(); + handleMemberClick(member); + } + }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
src/api/rooms/getRoomMembers.ts(3 hunks)src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.styled.ts(1 hunks)src/components/creategroup/RoomInfoSection.styled.ts(1 hunks)src/components/createpost/PhotoSection.tsx(3 hunks)src/components/createpost/PostContentSection.styled.ts(2 hunks)src/components/members/MemberList.tsx(2 hunks)src/components/recordwrite/PageRangeSection.styled.ts(2 hunks)src/pages/groupMembers/GroupMembers.tsx(1 hunks)src/pages/pollwrite/PollWrite.styled.ts(1 hunks)src/pages/recordwrite/RecordWrite.styled.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/components/creategroup/RoomInfoSection.styled.ts (1)
src/styles/global/global.ts (1)
colors(4-53)
src/components/createpost/PostContentSection.styled.ts (1)
src/styles/global/global.ts (2)
colors(4-53)semanticColors(80-153)
src/components/createpost/PhotoSection.tsx (1)
src/components/createpost/PhotoSection.styled.ts (1)
PhotoImage(49-54)
src/components/members/MemberList.tsx (2)
src/api/rooms/getRoomMembers.ts (1)
Member(24-31)src/mocks/members.mock.ts (1)
Member(1-8)
🔇 Additional comments (6)
src/components/recordwrite/PageRangeSection.styled.ts (1)
2-2: 불필요한 색상 토큰 import 제거: 👍
colors의존성을 걷어내고typography,semanticColors만 남긴 정리는 타당합니다. 죽은 import 제거로 가독성과 유지보수성이 좋아졌습니다.src/pages/recordwrite/RecordWrite.styled.ts (1)
15-16: 포맷팅 변경만 발생 — 동작 영향 없음빈 줄 추가는 런타임/스타일 결과에 영향 없습니다. 그대로 진행해도 됩니다.
src/pages/pollwrite/PollWrite.styled.ts (1)
15-16: 포맷팅 변경만 발생 — 동작 영향 없음빈 줄 추가는 런타임/스타일 결과에 영향 없습니다. 그대로 진행해도 됩니다.
src/components/createpost/PostContentSection.styled.ts (1)
21-21: Textarea 스크롤 정책 변경(overflow-y: auto) — 적절합니다콘텐츠 초과 시 세로 스크롤 허용으로 입력 UX가 개선됩니다. 별도 이슈 없습니다.
src/api/rooms/getRoomMembers.ts (1)
10-11: Member.isMyself를 boolean으로 강제 처리 제안UI 레이어에서
isMyself가undefined가 될 수 있는 상황을 없애고, 분기 로직의 중복을 제거하기 위해Member.isMyself를 필수(required)boolean으로 변경하는 것을 권장드립니다. 변환 시점에 확실히 boolean으로 캐스팅하는 예시는 다음과 같습니다:export interface Member { id: string; nickname: string; role: string; followersCount?: number; profileImageUrl?: string; - isMyself?: boolean; + isMyself: boolean; } // ... const convertedMembers = roomMembers.map(member => { const convertedMember: Member = { id: member.userId.toString(), nickname: member.nickname || '익명', role: member.aliasName || '독서메이트', followersCount: member.followerCount || 0, profileImageUrl: member.imageUrl || undefined, - isMyself: member.isMyself, + isMyself: !!member.isMyself, }; return convertedMember; });• 확인 결과,
src/mocks/members.mock.ts에만 동일한 이름의Member인터페이스가 정의되어 있으나 프로덕션 코드(.ts/.tsx)에서는 전혀 import되지 않고 있어 타입 충돌 우려는 없습니다.
• UI 컴포넌트(MemberList,GroupMembers.tsx) 전반에서 이 변경사항이 안전하게 적용될 수 있습니다.이 제안을 반영하시면 런타임 안정성과 코드 가독성이 모두 향상될 것으로 보입니다.
src/components/createpost/PhotoSection.tsx (1)
52-56: Blob URL 메모이즈 및 정리 도입 👍 (메모리 누수 방지에 효과적)파일 배열 변경 시 기존 URL을 해제하는 클린업 로직이 잘 들어갔습니다. 실사용 환경에서 다중 업로드/삭제를 반복해도 메모리 릭 가능성이 낮아졌습니다.
Also applies to: 57-63
#️⃣연관된 이슈
📝작업 내용
스크린샷 (선택)
💬리뷰 요구사항(선택)
Summary by CodeRabbit
New Features
Bug Fixes
Style
Chores