feat: 참여 후 모임방 상세 화면 구현#79
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthrough새로운 그룹 상세 페이지(참여 그룹용)와 관련된 주요 UI 섹션(기록, 코멘트, 핫토픽, 책 등) 컴포넌트 및 스타일 파일이 추가되었습니다. 그룹 상세 페이지의 라우팅이 확장되었고, 일부 기존 스타일에 미세 조정이 이루어졌습니다. 또한 그룹 액션용 바텀 시트 UI와 컴포넌트가 새로 도입되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Router
participant ParticipatedGroupDetail
participant SectionComponents
participant GroupActionBottomSheet
User->>Router: "group/detail/joined" 경로로 이동
Router->>ParticipatedGroupDetail: 컴포넌트 렌더링
ParticipatedGroupDetail->>SectionComponents: (RecordSection, CommentSection, HotTopicSection, GroupBookSection) 렌더링
SectionComponents-->>ParticipatedGroupDetail: UI 및 상호작용 제공
ParticipatedGroupDetail->>GroupActionBottomSheet: 그룹 액션 바텀 시트 오픈/클로즈 제어
GroupActionBottomSheet-->>ParticipatedGroupDetail: 액션 콜백 실행 및 상태 변경
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ 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 comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/components/group/HotTopicSection.styled.ts (1)
4-13: 다른 섹션들과 동일한 코드 중복 이슈앞서 언급했듯이
HotTopicSection의 기본 스타일들도 다른 섹션들과 중복되는 패턴을 보입니다. 공통 베이스 컴포넌트 사용을 고려해보세요.
🧹 Nitpick comments (9)
src/components/group/CommentSection.tsx (1)
16-28: 컴포넌트 구현 품질 개선 제안전반적인 구현은 깔끔하고 접근성 고려사항(alt 텍스트)도 잘 적용되었습니다. 하지만 컴포넌트명과 styled 컴포넌트명이 동일하여 혼란을 야기할 수 있습니다.
import { - CommentSection as StyledCommentSection, + CommentSectionWrapper, CommentSectionHeader, CommentSectionTitle, CommentSectionChevron, CommentContent, CommentText, } from './CommentSection.styled'; const CommentSection = ({ message, onClick }: CommentSectionProps) => { return ( - <StyledCommentSection> + <CommentSectionWrapper> <CommentSectionHeader onClick={onClick}> <CommentSectionTitle>오늘의 한마디</CommentSectionTitle> <CommentSectionChevron src={rightChevron} alt="한마디 이동 버튼" /> </CommentSectionHeader> <CommentContent> <CommentText>{message}</CommentText> </CommentContent> - </StyledCommentSection> + </CommentSectionWrapper> ); };src/components/group/GroupBookSection.tsx (1)
16-26: 컴포넌트 구현 품질 개선 제안컴포넌트 구현이 깔끔하고 접근성도 잘 고려되었습니다. CommentSection과 동일한 네이밍 개선을 적용하여 일관성을 유지하는 것이 좋겠습니다.
import { - GroupBookSection as StyledGroupBookSection, + GroupBookSectionWrapper, BookTitle, BookAuthor, ChevronIcon, RightSection, } from './GroupBookSection.styled'; const GroupBookSection = ({ title, author, onClick }: GroupBookSectionProps) => { return ( - <StyledGroupBookSection onClick={onClick}> + <GroupBookSectionWrapper onClick={onClick}> <BookTitle>{title}</BookTitle> <RightSection> <BookAuthor>{author}</BookAuthor> <ChevronIcon src={rightChevron} alt="책 이동 버튼" /> </RightSection> - </StyledGroupBookSection> + </GroupBookSectionWrapper> ); };src/components/group/RecordSection.tsx (1)
31-34: 인라인 스타일을 styled component로 리팩토링진행도 텍스트와 퍼센트 기호를 감싸는 컨테이너에 인라인 스타일이 사용되고 있습니다. 일관성을 위해 styled component를 사용하는 것을 권장합니다.
RecordSection.styled.ts파일에 새로운 styled component를 추가하세요:export const ProgressTextContainer = styled.div` display: flex; align-items: baseline; `;그리고 컴포넌트에서 사용하세요:
- <div style={{ display: 'flex', alignItems: 'baseline' }}> + <ProgressTextContainer> <ProgressText>{progress}</ProgressText> <PercentText>%</PercentText> - </div> + </ProgressTextContainer>src/pages/groupDetail/ParticipatedGroupDetail.tsx (2)
42-42: 빈 핸들러 함수 구현 필요
handleMoreButton함수가 비어있습니다. 더보기 버튼의 기능을 구현하거나 TODO 주석을 추가하세요.const handleMoreButton = () => { + // TODO: 더보기 메뉴 기능 구현 + console.log('More button clicked'); };
67-113: 모킹 데이터를 별도 파일로 분리컴포넌트 내부에 모킹 데이터가 정의되어 있습니다. 코드의 가독성과 재사용성을 위해 별도의 모킹 파일로 분리하는 것을 권장합니다.
src/mocks/participatedGroupDetail.mock.ts파일을 생성하여 데이터를 분리하세요:export const mockRecordData = { bookAuthor: '최정화 저', currentPage: 1, progress: 30, }; export const mockCommentData = { message: '모임방 멤버들과 간단한 인사를 나눠보세요!', }; export const mockPolls: Poll[] = [ // 현재 데이터들... ];src/components/group/CommentSection.styled.ts (1)
4-13: 다른 섹션들과의 코드 중복 개선 고려
CommentSection,RecordSection,HotTopicSection의 스타일드 컴포넌트들 사이에 상당한 중복이 있습니다. 공통 스타일들을 기본 섹션 컴포넌트로 추출하는 것을 고려해보세요.공통 스타일을 위한 베이스 컴포넌트를 생성할 수 있습니다:
// src/components/group/shared/BaseSection.styled.ts export const BaseSection = styled.section` display: flex; flex-direction: column; width: 90%; gap: 12px; background: ${colors.darkgrey.dark}; margin: 20px 20px 0 20px; padding: 16px 12px; border-radius: 12px; `; export const BaseSectionHeader = styled.div` display: flex; justify-content: space-between; align-items: center; cursor: pointer; `; export const BaseSectionTitle = styled.h3` color: ${colors.white}; font-size: ${typography.fontSize.base}; font-weight: ${typography.fontWeight.semibold}; margin: 0; `;Also applies to: 15-20, 22-27, 29-33
src/components/group/HotTopicSection.tsx (3)
108-121: 터치 이벤트에서 preventDefault 사용 주의모든 터치 이벤트에서
preventDefault()를 호출하면 스크롤과 같은 기본 브라우저 동작을 차단할 수 있습니다.더 정교한 처리를 위해 다음과 같이 개선하는 것을 고려하세요:
const handleTouchStart = (e: React.TouchEvent) => { - e.preventDefault(); handleDragStart(e.touches[0].clientX); }; const handleTouchMove = (e: React.TouchEvent) => { - e.preventDefault(); + if (isDragging) { + e.preventDefault(); + } handleDragMove(e.touches[0].clientX); };이렇게 하면 드래그가 시작된 후에만 기본 동작을 차단합니다.
130-148: 전역 이벤트 리스너의 메모리 누수 방지 개선현재 구현은 올바르지만, 컴포넌트가 언마운트될 때도 이벤트 리스너가 제거되도록 보장해야 합니다.
다음과 같이 개선할 수 있습니다:
useEffect(() => { const handleMouseMove = (e: MouseEvent) => { handleDragMove(e.clientX); }; const handleMouseUp = () => { handleDragEnd(); }; if (isDragging) { document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); + } else { + // 드래그가 끝났을 때도 리스너 제거 보장 + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); } return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging, handleDragMove]);
191-195: 인라인 스타일 객체 최적화 고려매 렌더링마다 새로운 스타일 객체가 생성되어 불필요한 리렌더링을 유발할 수 있습니다.
성능 최적화를 위해 스타일 객체를 useMemo로 메모이제이션하세요:
+const slideContainerStyle = useMemo( + () => ({ + transform: `translateX(${translateX}%)`, + transition: isDragging ? 'none' : 'transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)', + cursor: isDragging ? 'grabbing' : 'grab', + }), + [translateX, isDragging], +); <SlideContainer ref={slideRef} onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} onMouseDown={handleMouseDown} - style={{ - transform: `translateX(${translateX}%)`, - transition: isDragging ? 'none' : 'transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)', - cursor: isDragging ? 'grabbing' : 'grab', - }} + style={slideContainerStyle} >
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/assets/group/right-chevron.svgis excluded by!**/*.svg
📒 Files selected for processing (12)
src/components/group/CommentSection.styled.ts(1 hunks)src/components/group/CommentSection.tsx(1 hunks)src/components/group/GroupBookSection.styled.ts(1 hunks)src/components/group/GroupBookSection.tsx(1 hunks)src/components/group/HotTopicSection.styled.ts(1 hunks)src/components/group/HotTopicSection.tsx(1 hunks)src/components/group/RecordSection.styled.ts(1 hunks)src/components/group/RecordSection.tsx(1 hunks)src/pages/groupDetail/GroupDetail.styled.ts(1 hunks)src/pages/groupDetail/ParticipatedGroupDetail.styled.ts(1 hunks)src/pages/groupDetail/ParticipatedGroupDetail.tsx(1 hunks)src/pages/index.tsx(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/components/group/GroupBookSection.styled.ts (1)
src/styles/global/global.ts (3)
colors(4-53)typography(56-77)semanticColors(80-153)
src/pages/groupDetail/ParticipatedGroupDetail.styled.ts (1)
src/styles/global/global.ts (1)
colors(4-53)
src/components/group/CommentSection.tsx (1)
src/components/group/CommentSection.styled.ts (6)
CommentSection(4-13)CommentSectionHeader(15-20)CommentSectionTitle(22-27)CommentSectionChevron(29-33)CommentContent(35-38)CommentText(40-45)
src/components/group/GroupBookSection.tsx (1)
src/components/group/GroupBookSection.styled.ts (5)
GroupBookSection(4-14)BookTitle(16-22)RightSection(36-40)BookAuthor(24-28)ChevronIcon(30-34)
src/components/group/HotTopicSection.styled.ts (2)
src/styles/global/global.ts (2)
colors(4-53)typography(56-77)src/components/group/HotTopicSection.tsx (1)
VoteOption(20-23)
src/pages/groupDetail/ParticipatedGroupDetail.tsx (8)
src/mocks/groupDetail.mock.ts (1)
mockGroupDetail(1-53)src/components/group/HotTopicSection.tsx (1)
Poll(25-30)src/pages/groupDetail/ParticipatedGroupDetail.styled.ts (1)
ParticipatedWrapper(4-16)src/pages/groupDetail/GroupDetail.styled.ts (14)
TopBackground(19-28)Header(30-49)BannerSection(51-59)GroupTitle(61-66)SubTitle(68-73)Intro(75-80)MetaInfo(82-86)Meta(88-100)MetaDate(102-106)MetaMember(108-112)MetaTotalMember(114-118)TagRow(120-123)Tag(125-135)TagGenre(137-139)src/components/group/GroupBookSection.styled.ts (1)
GroupBookSection(4-14)src/components/group/RecordSection.styled.ts (1)
RecordSection(4-13)src/components/group/CommentSection.styled.ts (1)
CommentSection(4-13)src/components/group/HotTopicSection.styled.ts (1)
HotTopicSection(4-13)
src/components/group/RecordSection.tsx (1)
src/components/group/RecordSection.styled.ts (10)
RecordSection(4-13)RecordSectionHeader(15-20)RecordSectionTitle(22-27)RecordSectionChevron(29-33)RecordSectionContent(35-39)CurrentPage(41-45)ProgressText(71-75)PercentText(65-69)ProgressBar(47-55)ProgressBarFill(57-63)
src/components/group/HotTopicSection.tsx (1)
src/components/group/HotTopicSection.styled.ts (15)
VoteOption(64-77)HotTopicSection(4-13)VoteOptionNumber(79-83)VoteOptionText(85-89)HotTopicContent(29-34)EmptyVoteContainer(111-118)EmptyVoteTitle(120-125)SlideContainer(36-43)SlideItem(45-49)HotTopicText(51-56)VoteOptionsList(58-62)Pagination(91-96)PaginationDot(98-109)HotTopicSectionHeader(15-20)HotTopicSectionTitle(22-27)
src/components/group/RecordSection.styled.ts (1)
src/styles/global/global.ts (3)
colors(4-53)typography(56-77)semanticColors(80-153)
src/components/group/CommentSection.styled.ts (1)
src/styles/global/global.ts (2)
colors(4-53)typography(56-77)
🪛 Biome (2.1.2)
src/components/group/HotTopicSection.tsx
[error] 20-20: Shouldn't redeclare 'VoteOption'. Consider to delete it or rename it.
'VoteOption' is defined here:
(lint/suspicious/noRedeclare)
🔇 Additional comments (22)
src/pages/groupDetail/GroupDetail.styled.ts (1)
79-79: 텍스트 가독성 개선 승인
line-height: 20px추가로 텍스트의 줄 간격이 개선되어 가독성이 향상되었습니다.src/pages/index.tsx (2)
38-38: 컴포넌트 import 승인
ParticipatedGroupDetail컴포넌트가 올바르게 import되었습니다.
55-55: 라우팅 구조 개선 승인참여 후 그룹 상세 화면을 위한
/group/detail/joined라우트 추가가 적절합니다. PR 목표에 따라 참여 전과 후 화면을 명확히 분리하는 좋은 구조입니다.src/pages/groupDetail/ParticipatedGroupDetail.styled.ts (1)
1-16: 일관된 스타일 컴포넌트 구현 승인
ParticipatedWrapper스타일 컴포넌트가 기존 패턴과 일관되게 구현되었습니다. 반응형 디자인을 위한 min/max width 제약과 적절한 레이아웃 속성들이 잘 적용되었습니다.src/components/group/CommentSection.tsx (1)
11-14: Props 인터페이스 정의 승인타입 안전성을 위한 적절한 Props 인터페이스가 정의되었습니다.
src/components/group/GroupBookSection.tsx (1)
10-14: Props 인터페이스 정의 승인명확하고 타입 안전한 Props 인터페이스가 잘 정의되었습니다.
src/components/group/RecordSection.tsx (1)
22-41: 컴포넌트 구현 승인컴포넌트의 전체적인 구조와 기능이 잘 구현되어 있습니다. 클릭 이벤트 처리, 진행도 표시, 그리고 일관된 스타일링이 적용되어 있습니다.
src/components/group/GroupBookSection.styled.ts (1)
1-40: styled components 구현 승인GroupBookSection을 위한 styled components가 일관성 있게 잘 구현되어 있습니다. 전역 색상 및 타이포그래피 시스템을 적절히 활용하고 있으며, 다른 그룹 섹션들과 일관된 디자인 패턴을 따르고 있습니다.
src/pages/groupDetail/ParticipatedGroupDetail.tsx (2)
33-66: PR 목표와 불일치하는 구현PR 설명에서
useCallback과useEffect훅의 사용에 대한 리뷰를 요청했지만, 실제 코드에서는 이러한 훅들이 사용되지 않고 있습니다. 드래그 이벤트 처리와 성능 최적화가 필요한 부분이 있다면 해당 훅들의 사용을 고려해보세요.Likely an incorrect or invalid review comment.
115-176: 컴포넌트 구조 및 JSX 구현 승인전체적인 컴포넌트 구조와 JSX 구현이 잘 되어 있습니다. 각 섹션 컴포넌트들이 적절히 배치되어 있고, props 전달과 이벤트 핸들링이 올바르게 구현되어 있습니다.
src/components/group/CommentSection.styled.ts (1)
1-45: 전반적인 styled components 구현 승인CommentSection을 위한 styled components가 일관성 있게 잘 구현되어 있습니다. 전역 스타일 시스템을 적절히 활용하고 있습니다.
src/components/group/HotTopicSection.styled.ts (3)
36-43: 터치 및 드래그 최적화 구현 승인
SlideContainer에서touch-action: pan-x와user-select: none속성들이 적절히 설정되어 있어 수평 스와이프 기능이 최적화되어 있습니다. 이는 PR 목표에서 언급된 터치 및 마우스 드래그 스크롤 기능을 지원합니다.
98-109: 동적 스타일링과 상호작용 효과 구현 승인
PaginationDot컴포넌트에서activeprop을 기반으로 한 조건부 스타일링과 호버 효과가 잘 구현되어 있습니다. 사용자 경험을 향상시키는 부드러운 트랜지션도 포함되어 있습니다.
1-125: 복잡한 UI 컴포넌트 스타일링 승인HotTopicSection을 위한 종합적인 스타일드 컴포넌트들이 잘 구현되어 있습니다. 슬라이드 기능, 투표 옵션, 페이지네이션, 빈 상태 등 모든 요소가 일관성 있게 스타일링되어 있고, 사용자 상호작용을 위한 적절한 효과들이 포함되어 있습니다.
src/components/group/HotTopicSection.tsx (3)
62-72: snapToClosest 함수의 useCallback 사용 우수snapToClosest 함수를 useCallback으로 메모이제이션한 것은 성능 최적화에 좋은 접근입니다. dependency array도 적절하게 설정되어 있습니다.
39-54: 전반적인 구현 품질 우수컴포넌트의 전반적인 구조와 로직이 잘 설계되어 있습니다. 드래그 상태에 따른 클릭 방지 로직(
!isDragging체크)과 투표 옵션 렌더링이 적절히 구현되어 있습니다.Also applies to: 157-168
82-97: handleDragMove의 dependency array 최적화 필요useCallback의 dependency array에 불필요한 의존성들이 포함되어 성능 저하를 일으킬 수 있습니다.
다음과 같이 최적화하세요:
const handleDragMove = useCallback( (clientX: number) => { if (!isDragging) return; const deltaX = clientX - startX; const newTranslateX = startTranslateX + (deltaX / window.innerWidth) * 100; // 드래그 범위 제한 (약간의 오버스크롤 허용) const minTranslate = getTargetTranslateX(polls.length - 1) - 20; const maxTranslate = getTargetTranslateX(0) + 20; const limitedTranslateX = Math.max(minTranslate, Math.min(maxTranslateX, newTranslateX)); setTranslateX(limitedTranslateX); }, - [isDragging, startX, startTranslateX, polls.length], + [isDragging, startX, startTranslateX, polls.length], );실제로는 dependency array가 적절합니다. 하지만
polls.length가 자주 변경되지 않는다면, 이를 별도 ref로 관리하여 불필요한 재생성을 방지할 수 있습니다.Likely an incorrect or invalid review comment.
src/components/group/RecordSection.styled.ts (5)
1-3: Import 구조 적절함필요한 스타일 토큰들을 명확하게 import하고 있으며, 전역 스타일 시스템을 적절히 활용하고 있습니다.
4-13: 일관된 레이아웃 패턴 사용다른 섹션 컴포넌트들(
HotTopicSection등)과 동일한 레이아웃 패턴(width: 90%, margin, padding, border-radius)을 사용하여 UI 일관성을 유지하고 있습니다.
57-63: ProgressBarFill 애니메이션 구현 우수progress prop을 받아서 동적으로 width를 설정하고, smooth transition을 적용한 것이 좋습니다. 사용자 경험을 향상시키는 적절한 애니메이션입니다.
29-33: Chevron 아이콘 마진 검토 필요음수 마진(
margin-right: -8px)을 사용한 이유가 명확하지 않습니다. 의도된 디자인인지 확인이 필요합니다.다음이 의도된 디자인인지 확인해 주세요:
- 아이콘이 컨테이너 경계를 넘어서는 것이 의도된 것인지
- 다른 섹션들과의 일관성을 위해 마진 조정이 필요한지
65-75: 색상 사용의 일관성 우수텍스트 요소들이 purple.main 색상을 일관되게 사용하여 브랜드 아이덴티티를 잘 반영하고 있습니다. 타이포그래피 설정도 전역 스타일 시스템을 적절히 활용하고 있습니다.
| interface RecordSectionProps { | ||
| bookAuthor: string; | ||
| currentPage: number; | ||
| progress: number; | ||
| onClick: () => void; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
사용되지 않는 props 제거 필요
bookAuthor prop이 인터페이스에 정의되어 있지만 컴포넌트 내에서 실제로 사용되지 않고 있습니다.
사용되지 않는 prop을 제거하여 인터페이스를 정리하세요:
interface RecordSectionProps {
- bookAuthor: string;
currentPage: number;
progress: number;
onClick: () => void;
}또한 함수 시그니처에서도 제거하세요:
-const RecordSection = ({ currentPage, progress, onClick }: RecordSectionProps) => {
+const RecordSection = ({ currentPage, progress, onClick }: RecordSectionProps) => {Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/components/group/RecordSection.tsx between lines 15 and 20, the
bookAuthor prop is defined in the RecordSectionProps interface but is not used
anywhere in the component. Remove bookAuthor from the interface definition and
also remove it from any function signatures or component props where it is
declared to keep the interface clean and accurate.
| }; | ||
|
|
||
| const handleBookSectionClick = () => { | ||
| navigate(`/book/123`); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
하드코딩된 값들을 상수로 분리
책 ID(/book/123)와 쿼리 매개변수(filter=poll)가 하드코딩되어 있습니다. 이러한 값들을 상수로 분리하거나 props/context에서 받아오는 것을 권장합니다.
+const BOOK_ID = '123'; // 실제로는 props나 context에서 가져와야 함
+const POLL_FILTER = 'poll';
const handleBookSectionClick = () => {
- navigate(`/book/123`);
+ navigate(`/book/${BOOK_ID}`);
};
const handlePollClick = (pageNumber: number) => {
- navigate(`/memory?page=${pageNumber}&filter=poll`);
+ navigate(`/memory?page=${pageNumber}&filter=${POLL_FILTER}`);
};Also applies to: 64-64
🤖 Prompt for AI Agents
In src/pages/groupDetail/ParticipatedGroupDetail.tsx at lines 58 and 64, the
book ID and query parameter values are hardcoded in the navigate calls. Refactor
these lines to replace the hardcoded strings with constants defined at the top
of the file or passed in via props or context. This will improve maintainability
and flexibility by centralizing these values.
heeeeyong
left a comment
There was a problem hiding this comment.
고생하셨습니다~ 반응형 요구사항이 있는데 피그마에서 해당부분 페이지 늘려서 확인한번 해주시면 감사하겠습니다!
| const handleDragMove = useCallback( | ||
| (clientX: number) => { | ||
| if (!isDragging) return; | ||
|
|
||
| const deltaX = clientX - startX; | ||
| const newTranslateX = startTranslateX + (deltaX / window.innerWidth) * 100; | ||
|
|
||
| // 드래그 범위 제한 (약간의 오버스크롤 허용) | ||
| const minTranslate = getTargetTranslateX(polls.length - 1) - 20; | ||
| const maxTranslate = getTargetTranslateX(0) + 20; | ||
|
|
||
| const limitedTranslateX = Math.max(minTranslate, Math.min(maxTranslate, newTranslateX)); | ||
| setTranslateX(limitedTranslateX); | ||
| }, | ||
| [isDragging, startX, startTranslateX, polls.length], | ||
| ); |
There was a problem hiding this comment.
startX와 startTranslateX 를 useCallback의 의존성관리에서 빼서 useRef로 관리하는건 어떨까요?
There was a problem hiding this comment.
오! 좋은 지적 감사합니다!
useCallback 의존성 배열에서 빼고 dragStateRef로 관리하도록 수정하겠습니다! 👏🏻
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/pages/groupDetail/ParticipatedGroupDetail.styled.ts (1)
18-27: 터치 디바이스를 고려한 인터랙션 개선을 권장합니다.현재 hover 효과는 데스크톱에서는 좋은 UX를 제공하지만, 터치 디바이스에서는 hover 상태가 제대로 동작하지 않습니다. 모바일 우선 디자인을 고려할 때 active 상태도 추가하는 것이 좋겠습니다.
다음과 같이 터치 친화적인 상태를 추가해보세요:
export const ClickableMeta = styled.div` display: flex; flex-direction: column; width: 100%; cursor: pointer; &:hover { opacity: 0.8; } + + &:active { + opacity: 0.7; + transform: scale(0.98); + } `;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/pages/groupDetail/ParticipatedGroupDetail.styled.ts(1 hunks)src/pages/groupDetail/ParticipatedGroupDetail.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/pages/groupDetail/ParticipatedGroupDetail.tsx
🔇 Additional comments (2)
src/pages/groupDetail/ParticipatedGroupDetail.styled.ts (2)
29-32: 깔끔하고 명확한 아이콘 컴포넌트입니다.24px의 고정 크기는 UI 아이콘으로 적절하며, 구현이 간결하고 명확합니다.
34-39: 헤더 레이아웃을 위한 적절한 구조입니다.
justify-content: space-between과align-items: center의 조합이 메타 정보를 양쪽 끝에 배치하는 일반적인 헤더 패턴에 잘 맞습니다. 구현이 표준적이고 효과적입니다.
| export const ParticipatedWrapper = styled.div` | ||
| display: flex; | ||
| position: relative; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| justify-content: center; | ||
| min-width: 320px; | ||
| max-width: 767px; | ||
| height: 100%; | ||
| margin: 0 auto; | ||
| background-color: ${colors.black.main}; | ||
| overflow: hidden; | ||
| `; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
메인 컨테이너의 수직 정렬 방식을 재검토해주세요.
justify-content: center로 인해 콘텐츠가 화면 중앙에 위치하게 되는데, 일반적으로 그룹 상세 페이지의 콘텐츠는 상단부터 자연스럽게 흘러야 합니다. 콘텐츠가 많을 때 레이아웃 문제가 발생할 수 있습니다.
다음과 같이 수정을 권장합니다:
export const ParticipatedWrapper = styled.div`
display: flex;
position: relative;
flex-direction: column;
align-items: center;
- justify-content: center;
+ justify-content: flex-start;
min-width: 320px;
max-width: 767px;
height: 100%;
margin: 0 auto;
background-color: ${colors.black.main};
overflow: hidden;
`;📝 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.
| export const ParticipatedWrapper = styled.div` | |
| display: flex; | |
| position: relative; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| min-width: 320px; | |
| max-width: 767px; | |
| height: 100%; | |
| margin: 0 auto; | |
| background-color: ${colors.black.main}; | |
| overflow: hidden; | |
| `; | |
| export const ParticipatedWrapper = styled.div` | |
| display: flex; | |
| position: relative; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: flex-start; | |
| min-width: 320px; | |
| max-width: 767px; | |
| height: 100%; | |
| margin: 0 auto; | |
| background-color: ${colors.black.main}; | |
| overflow: hidden; | |
| `; |
🤖 Prompt for AI Agents
In src/pages/groupDetail/ParticipatedGroupDetail.styled.ts between lines 4 and
16, the main container uses justify-content: center which vertically centers the
content, causing layout issues when there is a lot of content. Change
justify-content from center to flex-start to align the content naturally from
the top and prevent overflow or layout problems.
#️⃣연관된 이슈
따로 등록하지 않았습니다.
📝작업 내용
참여 후 모임방 상세 화면을 새로 구현하였습니다. 사용자가 모임에 참여한 후 활용할 수 있는 다양한 섹션들을 포함하고 있습니다.
주요 구현 내용
1. 라우팅 구조 추가
/group/detail/group/detail/joined경로로 분리하여 명확한 화면 구분을 구현했습니다.2. 컴포넌트 기반 아키텍처 설계
GroupBookSection: 모임방의 책 제목 및 저자 표시RecordSection: 현재 독서 진행률과 페이지 정보를 프로그레스바와 함께 제공CommentSection: 오늘의 한마디 기능 연결 섹션HotTopicSection: 모임방의 뜨거운 감자(투표) 기능을 포함한 인터랙티브 섹션3. 투표 시스템 구현
스크린샷 (선택)
2025-08-07.4.27.08.mov
💬리뷰 요구사항(선택)
드래그 이벤트 처리에서
useCallback과useEffect를 적절히 활용했는지, 그리고 불필요한 리렌더링이 발생하지 않는지 성능 관점에서 검토 부탁드립니다.Summary by CodeRabbit
New Features
Style
Bug Fixes
Chores