[feature] 활동사진 확대 모달 구현 및 PhotoList 구조 리팩토링#371
Conversation
- 키보드 내비게이션 및 닫기 기능 추가
|
Warning Rate limit exceeded@oesnuj has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 32 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
""" Walkthrough이 변경사항은 동아리 상세 페이지에서 활동사진을 확대해서 볼 수 있는 모달 기능을 추가하는 대규모 리팩토링 및 신규 구현입니다. 사진 레이아웃 관련 상수와 반응형 레이아웃, 사진 리스트, 모달 상태 관리, 사진 내비게이션을 위한 커스텀 훅들이 새로 도입되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant PhotoList
participant PhotoModal
User->>PhotoList: 사진 카드 클릭(index)
PhotoList->>PhotoModal: 모달 오픈(index, 사진 URL 배열, clubName)
User->>PhotoModal: 좌/우 내비게이션 클릭 또는 썸네일 클릭
PhotoModal->>PhotoModal: 현재 사진 인덱스 변경
User->>PhotoModal: ESC 키 또는 오버레이 클릭
PhotoModal->>PhotoList: 모달 닫기
Assessment against linked issues
Possibly related PRs
Suggested reviewers
✨ Finishing Touches
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. 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
✅ Deploy Preview for moadong ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
There was a problem hiding this comment.
Pull Request Overview
This PR implements an activity photo enlargement modal and refactors the PhotoList component to separate concerns by introducing dedicated hooks for navigation, modal state, and responsive layout.
- Added a PhotoModal component with keyboard navigation and overlay click-to-close behavior.
- Refactored PhotoList to use new hooks (usePhotoNavigation, useResponsiveLayout, and usePhotoModal) and extracted the thumbnail list into a separate PhotoCardList component.
- Updated styles for PhotoList and PhotoModal to improve UX on mobile and desktop screens.
Reviewed Changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx | Adds modal functionality with keyboard and click-based controls. |
| frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts | Provides styling for the modal overlay, content, and navigation buttons. |
| frontend/src/pages/ClubDetailPage/components/PhotoList.tsx | Refactors PhotoList with new hooks and integrates the PhotoModal. |
| frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts | Adjusts styling to support the new layout and navigation logic. |
| frontend/src/pages/ClubDetailPage/components/PhotoCardList/PhotoCardList.tsx | Extracts the thumbnail rendering logic into its own component. |
| frontend/src/hooks/PhotoModal/useModalNavigation.ts | Provides cyclic navigation logic within the modal. |
| frontend/src/hooks/PhotoList/useResponsiveLayout.ts | Manages layout responsiveness by monitoring container width and mobile breakpoints. |
| frontend/src/hooks/PhotoList/usePhotoNavigation.ts | Simplifies card translation logic and navigation for the photo slider. |
| frontend/src/hooks/PhotoList/usePhotoModal.ts | Manages modal open/close state and current index for the photo modal. |
| frontend/src/constants/photoLayout.ts | Defines constants for card dimensions and gap. |
Comments suppressed due to low confidence (1)
frontend/src/hooks/PhotoList/usePhotoNavigation.ts:25
- The translation logic was simplified to '-index * cardWidth' without offset adjustment for the last card. Please verify that on desktop views this behavior is intended and does not result in undesirable empty space when the last card is reached.
const calculateTranslateX = useCallback((index: number) => -index * cardWidth, [cardWidth]);
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (17)
frontend/src/constants/photoLayout.ts (1)
5-6: 가독성 향상을 위한 제안상수 파일에 간단한 주석을 추가하면 이 상수들의 용도와 사용처를 쉽게 이해할 수 있습니다.
+// 카드 간격 및 크기에 대한 레이아웃 상수 export const CARD_GAP = 20; export const DESKTOP_CARD_CONTENT_WIDTH = 400; export const MOBILE_CARD_CONTENT_WIDTH = 350; +// 전체 카드 너비 (컨텐츠 + 간격) export const DESKTOP_CARD_WIDTH = DESKTOP_CARD_CONTENT_WIDTH + CARD_GAP; export const MOBILE_CARD_WIDTH = MOBILE_CARD_CONTENT_WIDTH + CARD_GAP;frontend/src/hooks/PhotoList/usePhotoModal.ts (2)
7-11: 더 안전한 타입 정의 제안인덱스 매개변수에 명시적 타입을 추가하면 좋을 것 같습니다.
- const open = (i: number) => { + const open = (i: number): void => { setIndex(i); setIsOpen(true); }; - const close = () => setIsOpen(false); + const close = (): void => setIsOpen(false);
13-14: 반환 타입 명시를 고려해 보세요훅의 반환 타입을 명시적으로 정의하면 타입 안전성이 향상되고 사용하는 컴포넌트에서 더 명확한 자동 완성 기능을 제공할 수 있습니다.
- export const usePhotoModal = () => { + export const usePhotoModal = (): { + isOpen: boolean; + index: number; + open: (i: number) => void; + close: () => void; + setIndex: React.Dispatch<React.SetStateAction<number>>; + } => { // ... 기존 코드frontend/src/hooks/PhotoModal/useModalNavigation.ts (1)
16-17: 반환 타입 명시를 고려해 보세요반환 객체에 명시적인 타입을 추가하면 타입 안전성과 자동 완성 기능이 향상됩니다.
- return { handlePrev, handleNext }; + return { + handlePrev, + handleNext + } as const;혹은 함수 선언부에 반환 타입을 명시:
export default function useModalNavigation( currentIndex: number, total: number, setIndex: (index: number) => void, - ) { + ): { + handlePrev: () => void; + handleNext: () => void; + } {frontend/src/hooks/PhotoList/useResponsiveLayout.ts (3)
13-29: 이벤트 리스너 최적화 검토리사이즈 이벤트 리스너가 모든 창 크기 변경 시 호출됩니다. 성능 최적화를 위해 디바운싱을 적용하는 것이 좋을 것 같습니다.
+ import { useEffect, useState, useMemo, useCallback } from 'react'; + import { debounce } from 'lodash'; useEffect(() => { const updateIsMobile = () => setIsMobile(window.innerWidth <= breakPoint); const updateContainerWidth = () => { if (ref.current) { setContainerWidth(ref.current.offsetWidth); } }; - const handleResize = () => { + const handleResize = useCallback(() => { updateIsMobile(); updateContainerWidth(); - }; + }, [updateIsMobile, updateContainerWidth]); + + const debouncedHandleResize = debounce(handleResize, 150); handleResize(); - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); + window.addEventListener('resize', debouncedHandleResize); + return () => { + window.removeEventListener('resize', debouncedHandleResize); + debouncedHandleResize.cancel(); + }; }, [ref, breakPoint]);
15-19: ref.current가 null인 경우 처리 강화현재 ref.current 체크가 있지만, 컨테이너 너비 업데이트 로직을 더 견고하게 만들 수 있습니다.
const updateContainerWidth = () => { if (ref.current) { setContainerWidth(ref.current.offsetWidth); + } else { + // 기본값 설정 또는 로깅 + setContainerWidth(window.innerWidth); + console.debug('Container reference is not available yet'); } };
31-34: 카드 너비 계산 로직에 대한 주석 추가카드 너비 계산 로직에 대한 설명을 주석으로 추가하면 코드 이해도가 향상될 것입니다.
+ // 디바이스 타입에 따라 적절한 카드 너비를 설정 + // 모바일에서는 좁은 화면에 맞는 작은 카드 크기를, 데스크톱에서는 더 큰 카드 크기를 사용 const cardWidth = useMemo( () => (isMobile ? MOBILE_CARD_WIDTH : DESKTOP_CARD_WIDTH), [isMobile], );frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts (1)
43-44: 조건부 transform 로직 개선photoCount가 2 이하일 때 'none'을 사용하는 부분이 명확한 이유가 없어 보입니다. 이 부분에 주석을 추가하거나 다른 방식으로 접근하는 것이 좋을 것 같습니다.
transform: ${({ translateX, photoCount }) => - photoCount <= 2 ? 'none' : `translateX(${translateX}px)`}; + // 사진이 2개 이하일 때는 슬라이딩이 필요하지 않으므로 transform을 적용하지 않음 + photoCount <= 2 ? 'none' : `translateX(${translateX}px)`};frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoCardList/PhotoCardList.tsx (2)
20-32: 배열 key 속성으로 index 사용에 대한 제안현재 key로 index를 사용하고 있는데, 가능하다면 고유한 식별자(예: URL 해시나 ID)를 사용하는 것이 React 렌더링 성능에 더 좋습니다.
{photoUrls.map((url, index) => ( - <Styled.PhotoCard key={index} onClick={() => onImageClick(index)}> + <Styled.PhotoCard + key={`photo-${url.split('/').pop() || index}`} + onClick={() => onImageClick(index)}>
22-30: 이미지 오류 처리 로직 강화현재 이미지 오류 처리가 잘 구현되어 있습니다만, 오류 상태일 때의 사용자 경험을 더 개선할 수 있습니다.
{!imageErrors[index] ? ( <LazyImage src={url} alt={`활동 사진 ${index + 1}`} onError={() => onImageError(index)} /> ) : ( - <Styled.NoImageContainer>이미지 준비중..</Styled.NoImageContainer> + <Styled.NoImageContainer> + <div> + <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" fill="#666"/> + </svg> + <p>이미지 준비중..</p> + </div> + </Styled.NoImageContainer> )}이에 맞춰 스타일 파일도 업데이트해야 합니다:
// PhotoList.styles.ts에 추가 export const NoImageContainer = styled.div` width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-color: #f5f5f5; color: #666; div { display: flex; flex-direction: column; align-items: center; gap: 8px; } p { font-size: 14px; } `;frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.tsx (3)
37-50: 키보드 이벤트 핸들링 개선 제안키보드 이벤트 핸들링 로직이 잘 구현되어 있지만, 몇 가지 개선 사항을 제안합니다.
const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (!isOpen) return; - if (e.key === 'Escape') return onClose(); - if (e.key === 'ArrowLeft') return handlePrev(); - if (e.key === 'ArrowRight') return handleNext(); + switch (e.key) { + case 'Escape': + e.preventDefault(); + onClose(); + break; + case 'ArrowLeft': + e.preventDefault(); + handlePrev(); + break; + case 'ArrowRight': + e.preventDefault(); + handleNext(); + break; + } }, [isOpen, onClose, handlePrev, handleNext], );이렇게 하면 방향키 이벤트가 스크롤 등 다른 동작을 트리거하는 것을 방지할 수 있습니다.
54-62: 모달 접근성 개선 제안모달의 접근성을 향상시키기 위한 ARIA 속성을 추가하는 것이 좋을 것 같습니다.
- <Styled.ModalOverlay onClick={onClose}> + <Styled.ModalOverlay + onClick={onClose} + role="dialog" + aria-modal="true" + aria-labelledby="modal-title"> <Styled.ModalContent onClick={(e) => e.stopPropagation()}> <Styled.ModalHeader> - <Styled.ClubName>{clubName}</Styled.ClubName> + <Styled.ClubName id="modal-title">{clubName}</Styled.ClubName> <Styled.CloseButton onClick={onClose} aria-label='닫기'> × </Styled.CloseButton>
85-93: 썸네일 리스트 최적화 제안썸네일 리스트의 경우 사진 수가 많아질 때 성능 문제가 발생할 수 있습니다. 필요에 따라 가상화 또는 페이징 방식을 고려해볼 수 있습니다.
<Styled.ThumbnailList> - {urls.map((url, idx) => ( + {/* 썸네일이 많을 경우 가상화 또는 페이징 고려 */} + {urls.slice(0, 10).map((url, idx) => ( <Styled.Thumbnail key={url} isActive={idx === currentIndex} onClick={() => onChangeIndex(idx)}> <img src={url} alt='썸네일' /> </Styled.Thumbnail> ))} + {urls.length > 10 && ( + <Styled.MoreThumbnails>+{urls.length - 10}</Styled.MoreThumbnails> + )} </Styled.ThumbnailList>frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.tsx (1)
79-88: PhotoModal 컴포넌트 구현
PhotoModal컴포넌트를 추가하고 필요한 props를 전달하는 구현은 잘 되어 있습니다. 사진 확대 모달 기능을 별도 컴포넌트로 분리하여 관심사 분리를 잘 적용했습니다.한 가지 개선점으로는,
photos객체를 구조분해할당으로 직접 전달하는 것이 더 명확할 수 있습니다:<PhotoModal isOpen={isOpen} onClose={close} clubName={clubName} - photos={{ - currentIndex: index, - urls: photoUrls, - onChangeIndex: setIndex, - }} + currentIndex={index} + photoUrls={photoUrls} + onChangeIndex={setIndex} />이렇게 하면
PhotoModal컴포넌트에서 props 사용이 더 직관적이고 타입 안전성도 향상됩니다.frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.styles.ts (3)
46-61: 닫기 버튼 스타일 구현닫기 버튼의 위치와 호버 효과가 적절하게 구현되어 있습니다. 특히 opacity 변화를 통한 자연스러운 상호작용은 좋은 디테일입니다.
다만, 버튼에 접근성을 위한
aria-label속성이 필요할 수 있습니다. 컴포넌트 구현 시 이 점을 고려해 보세요.
120-162: 내비게이션 버튼 스타일내비게이션 버튼의 스타일링이 매우 세밀하게 구현되어 있습니다. 원형 버튼과 그림자 효과, 호버 시 변화, 반응형 크기 조정 등이 사용자 경험을 향상시키는 좋은 디테일입니다.
다만, 좌우 버튼이 화면 밖으로 벗어나거나 이미지에 가려질 수 있는 상황에 대한 고려가 필요할 수 있습니다. z-index 충돌이 발생하지 않는지 테스트해 보세요.
207-236: 썸네일 스타일개별 썸네일의 스타일링이 잘 구현되어 있습니다. 활성 상태 표시를 위한 테두리 색상 변경, 호버 효과, 반응형 크기 조정 등이 사용자 경험을 향상시키는 좋은 디테일입니다.
접근성 측면에서 한 가지 개선 사항으로, 현재 선택된 썸네일임을 스크린 리더에게 알려주는
aria-current="true"속성 추가를 고려해 보세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
frontend/src/constants/photoLayout.ts(1 hunks)frontend/src/hooks/PhotoList/usePhotoModal.ts(1 hunks)frontend/src/hooks/PhotoList/usePhotoNavigation.ts(3 hunks)frontend/src/hooks/PhotoList/useResponsiveLayout.ts(1 hunks)frontend/src/hooks/PhotoModal/useModalNavigation.ts(1 hunks)frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoCardList/PhotoCardList.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts(1 hunks)frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.styles.ts(1 hunks)frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.tsx(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
frontend/src/hooks/PhotoList/useResponsiveLayout.ts (1)
frontend/src/constants/photoLayout.ts (2)
MOBILE_CARD_WIDTH(6-6)DESKTOP_CARD_WIDTH(5-5)
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.tsx (1)
frontend/src/hooks/PhotoModal/useModalNavigation.ts (1)
useModalNavigation(3-17)
🔇 Additional comments (30)
frontend/src/constants/photoLayout.ts (1)
1-6: 상수 정의가 명확하고 잘 구성되어 있습니다.상수를 중앙에서 관리하는 접근 방식은 유지보수성을 높이는 좋은 패턴입니다. 카드 간격과 너비를 명확히 분리하여 정의한 것이 좋습니다.
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
64-68: 새로운 clubName prop 추가가 적절합니다.
PhotoList컴포넌트에clubNameprop을 추가하여 모달에서 클럽 이름을 표시하는 기능이 가능해졌습니다. 이 변경은 간결하고 명확합니다.frontend/src/hooks/PhotoList/usePhotoModal.ts (1)
1-14: 모달 상태 관리 훅이 잘 구현되었습니다.
usePhotoModal훅은 모달의 열림/닫힘 상태와 현재 사진 인덱스를 관리하는 책임을 명확히 분리하여 구현했습니다. 단일 책임 원칙(SRP)을 잘 따르고 있으며, 인터페이스가 깔끔합니다.frontend/src/hooks/PhotoModal/useModalNavigation.ts (2)
3-7: 함수 시그니처가 명확합니다.함수의 매개변수와 반환 타입이 명확하게 정의되어 있습니다. 모달 내비게이션에 필요한 정보를 적절하게 받고 있습니다.
8-14: 원형 내비게이션 로직이 잘 구현되었습니다.이전/다음 사진으로 이동하는 로직이 경계 조건(처음과 끝)을 잘 처리하고 있으며,
useCallback을 사용하여 성능 최적화도 고려했습니다.frontend/src/hooks/PhotoList/useResponsiveLayout.ts (1)
1-37: 훅 전체 코드 구조에 대한 피드백useResponsiveLayout 훅은 컨테이너 참조, 모바일 여부, 컨테이너 너비, 카드 너비 등 여러 관심사를 처리하고 있어 응집도가 높습니다. 코드가 깔끔하게 구현되었고 책임 분리 측면에서도 좋은 접근이라고 생각합니다.
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts (3)
46-46: user-select 속성 적용에 대한 피드백user-select: none 속성 추가는 사용자 경험 향상에 좋은 개선입니다. 사진 슬라이딩 중 텍스트가 선택되는 문제를 방지할 수 있습니다.
57-57: cursor 속성 설정에 대한 피드백PhotoCard에 cursor: pointer 속성을 적용한 것은 좋은 UX 개선입니다. 사용자에게 카드가 클릭 가능함을 시각적으로 알려줍니다.
64-65: 이미지 드래그 방지 스타일 추가에 대한 피드백이미지 요소에 user-select와 -webkit-user-drag 속성을 추가한 것은 사용자 경험을 향상시키는 좋은 개선입니다. 사용자가 실수로 이미지를 드래그하거나 선택하는 것을 방지합니다.
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoCardList/PhotoCardList.tsx (1)
1-37: 컴포넌트 구조 및 책임 분리에 대한 피드백PhotoCardList 컴포넌트의 책임 분리가 잘 되어 있습니다. 단일 책임 원칙을 잘 따르고 있으며, props 인터페이스도 명확하게 정의되어 있습니다. 이미지 에러 핸들링도 적절히 구현되었습니다.
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.tsx (2)
26-35: body overflow 스타일 처리에 대한 피드백모달 오픈/클로즈 시 document.body의 overflow 스타일을 직접 조작하는 부분이 잘 구현되어 있습니다. useEffect 클린업 함수에서도 적절하게 처리되어 있습니다.
1-101: 모달 컴포넌트 전체 구현에 대한 피드백PhotoModal 컴포넌트가 전반적으로 아주 잘 구현되어 있습니다. 키보드 네비게이션 지원, 썸네일 리스트, 사진 이동 버튼 등 사용자 경험을 고려한 다양한 기능들이 포함되어 있습니다. 특히 useModalNavigation 훅을 사용해 순환 탐색 로직을 분리한 것이 좋은 설계 결정입니다.
frontend/src/hooks/PhotoList/usePhotoNavigation.ts (3)
4-4: 네이밍된 export로 변경 및 isMobile 파라미터 추가기본 export에서 네이밍된 export로 변경하고 isMobile 파라미터를 추가한 것은 좋은 방향입니다. 이를 통해 모바일 환경에 맞는 동작을 구현할 수 있고, 명시적인 import 방식으로 코드 가독성이 향상됩니다.
Also applies to: 11-11, 20-20
24-26: calculateTranslateX 함수 단순화이전 코드에서 마지막 사진에 대한 특별한 처리 로직을 제거하고 단순화한 것은 코드 유지보수성 측면에서 좋은 개선입니다. 또한 의존성 배열을
[cardWidth]로 최소화하여 불필요한 재계산을 방지했습니다.
46-49: 스크롤 가능 조건 개선모바일 환경을 고려한 스크롤 조건 개선이 이루어졌습니다. 특히:
canScrollLeft조건을photosLength > 1로 변경하여 사진이 2개 이상일 때 좌측 스크롤이 가능하도록 개선canScrollRight조건을 모바일 여부에 따라 다르게 처리하여 사용자 경험 향상이 변경은 모바일과 데스크톱 환경에서의 네비게이션 경험을 최적화하는 좋은 접근입니다.
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.tsx (10)
1-1: 모듈 분리 및 import 구조 개선컴포넌트와 훅을 적절히 분리하고 필요한 모듈을 명시적으로 import한 것은 좋은 접근입니다. 특히
PhotoModal,PhotoCardList컴포넌트와useResponsiveLayout,usePhotoModal훅을 분리하여 관심사 분리 원칙을 잘 적용했습니다.Also applies to: 4-10
15-15: clubName 속성 추가
clubName속성을 추가하여 모달에서 동아리 이름을 표시할 수 있게 한 것은 사용자 경험 측면에서 좋은 개선입니다.Also applies to: 18-18
19-22: photoUrls 메모이제이션
useMemo를 사용하여 Google Drive URL 변환 로직을 메모이제이션한 것은 성능 최적화에 도움이 됩니다. 이를 통해 불필요한 재계산을 방지할 수 있습니다.
26-27: 상태 관리 개선
imageErrors상태를Record<number, boolean>타입으로 정의하고,usePhotoModal훅을 사용하여 모달 상태를 관리하는 방식은 코드 가독성과 유지보수성을 높여줍니다.
29-31: 반응형 레이아웃 처리
useResponsiveLayout훅을 사용하여 반응형 레이아웃을 처리하는 방식은 기존의 수동 상태 관리 및 window resize 이벤트 처리보다 훨씬 효율적입니다. 코드가 간결해지고 관심사 분리가 잘 이루어졌습니다.
33-43: usePhotoNavigation 훅 활용 개선
usePhotoNavigation훅에 필요한 파라미터를 적절히 전달하고 있습니다. 특히isMobile값을 전달하여 모바일 환경에 맞는 내비게이션 동작을 구현한 것은 좋은 접근입니다.
45-47: handleImageError 최적화
useCallback을 사용하여handleImageError함수를 최적화한 것은 좋은 접근입니다. 이를 통해 불필요한 렌더링을 방지할 수 있습니다.
49-49: openModalAt 콜백 함수 추가
openModalAt콜백 함수를 추가하여 특정 인덱스에서 모달을 열 수 있도록 한 것은 코드의 재사용성과 가독성을 높여줍니다.
59-66: PhotoCardList 컴포넌트 활용
PhotoCardList컴포넌트를 사용하여 사진 카드 렌더링을 분리한 것은 관심사 분리 측면에서 훌륭한 접근입니다. 이를 통해 코드 복잡성이 줄어들고 유지보수성이 향상됩니다.
69-69: 이미지 리소스 활용단순 화살표 대신
SlideButton이미지를 사용하여 네비게이션 버튼의 시각적 품질을 향상시킨 것은 사용자 경험 측면에서 좋은 개선입니다.Also applies to: 74-74
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoModal/PhotoModal.styles.ts (5)
3-25: 모달 오버레이 스타일 구현전체 화면을 커버하는 반투명 오버레이와 블러 효과, 페이드인 애니메이션은 모달 UX를 자연스럽게 만들어 주는 좋은 구현입니다. 이를 통해 사용자는 모달이 열리는 과정을 시각적으로 인지하고 배경과 모달을 명확히 구분할 수 있습니다.
27-44: 모달 콘텐츠 스타일 구현모달 콘텐츠의 반응형 스타일링이 잘 구현되어 있습니다. 특히 모바일 환경(600px 이하)에서 전체 화면으로 전환되는 처리는 작은 화면에서의 사용자 경험을 고려한 좋은 접근입니다.
63-74: 모달 헤더와 클럽 이름 스타일헤더의 고정 위치 처리와 클럽 이름 스타일링이 잘 구현되어 있습니다. 이를 통해 사용자는 현재 보고 있는 동아리의 사진임을 계속 인지할 수 있습니다.
Also applies to: 76-80
82-88: 모달 바디와 이미지 컨테이너, 이미지 스타일모달 바디, 이미지 컨테이너, 이미지 자체의 스타일링이 잘 구현되어 있습니다. 특히:
- 이미지의 최대 크기 제한을 통한 적절한 표시
- 반응형 패딩 조정으로 모바일 환경 대응
- 사용자 드래그 및 선택 방지를 통한 UX 개선
이 모든 스타일링이 사진 확대 모달의 핵심 기능을 잘 지원합니다.
Also applies to: 90-102, 104-118
164-177: 썸네일 컨테이너와 리스트 스타일썸네일 영역의 스타일링이 전체적으로 잘 구현되어 있습니다. 특히:
- 그라데이션 배경을 통한 시각적 구분
- 커스텀 스크롤바 스타일링
- 반응형 높이 및 패딩 조정
이러한 디테일은 사용자가 여러 사진을 쉽게 탐색할 수 있도록 돕습니다.
Also applies to: 179-205
seongwon030
left a comment
There was a problem hiding this comment.
복잡했던 활동사진 로직이 깔끔해져서 좋습니다 ㅎㅎ 정말 고생하셨습니다 궁금한 점 리뷰했습니다...!!!

#️⃣ 연관된 이슈
📝 작업 내용
활동사진을 클릭해 전체화면으로 볼 수 있는 모달 기능을 구현하고, 관련 로직들을 책임 단위로 분리하는 구조적 리팩토링을 진행했습니다.
또한 모바일 환경에서의 카드 크기 및 동작에 대한 UX 개선 작업도 병행
📸 1. 기능 추가
PhotoModal)🧼 2. 리팩토링
PhotoList가 너무 많은 책임을 지고 있었기 때문에 상태와 로직, 컴포넌트를 명확히 분리했습니다.(useState가 10개가 넘었...)
🔧 상태/로직 관련
usePhotoNavigationtranslateX계산useModalNavigationusePhotoModalisOpen) 및 현재 인덱스 관리open(index)/close()및 외부에서setIndex제공useResponsiveLayoutcardWidthmemoization 및isMobile판별 포함🧩 컴포넌트 분리
PhotoModal: 전체화면 확대 보기 컴포넌트PhotoCardList: 썸네일 카드 리스트 전용 컴포넌트🐛 버그 수정
모바일 너비 400px (예: Galaxy S24)에서 카드 짤림 현상 → 너비 300px로 조정
불필요한
lastCard및 placeholder 관련 코드 제거🔍 중점적으로 리뷰받고 싶은 부분
🧠 논의하고 싶은 부분
Summary by CodeRabbit
신규 기능
리팩터링
스타일