Skip to content

[Refactor/#89] 아카이빙 사진 뷰어를 공용 PhotoGallery로 통합 - #90

Open
LSJ102510 wants to merge 31 commits into
mainfrom
refactor/#89-archives-photo-gallery
Open

LSJ102510 wants to merge 31 commits into
mainfrom
refactor/#89-archives-photo-gallery

Conversation

@LSJ102510

@LSJ102510 LSJ102510 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
스크린샷 2026-10-01 오후 7 07 36 image image image

#️⃣연관된 이슈

base가 feat/#31-archive-list-screen입니다(PR #40 위에 쌓은 PR). #40이 머지되면 base가 자동으로 main으로 바뀝니다.

🎯 해결하려는 문제가 무엇인가요?

아카이빙 사진 뷰어(ArchivesPhotoViewerScreen)와 공용 components/ui/PhotoGallery.tsx가 가로 스크롤 스냅 + PageCounter를 각자 구현하고 있었습니다. 뷰어를 공용 갤러리로 합칩니다.

❓ 왜 해결해야 하나요?

같은 인터랙션이 두 곳에 있으면 한쪽만 고치는 일이 생깁니다. 실제로 "세게 밀어도 한 장만 넘어가는" 처리(PR #40 리뷰 반영)는 뷰어에만 들어가 있었습니다.

⭐ 어떻게 해결했나요?

PhotoGallery에 아래를 추가하고, 뷰어를 그 위에서 다시 조립했습니다. 기존 호출부(공지 상세·행사 상세)는 그대로 동작합니다.

  • photos — 실제 사진 배열({ src, alt }). 기존 photoCount(회색 placeholder)와는 유니온 타입이라 둘 중 하나만 받습니다
  • initialPage / onPageChange — 누른 사진에서 열고, 현재 장을 바깥에 알립니다(뷰어의 흐린 배경이 따라갑니다)
  • background — 슬라이드 뒤에 깔리는 슬롯(뷰어의 흐린 사진)
  • snapAlign / snapStop — 뷰어는 center + 한 장씩 멈춤, 공지·행사는 기존 start 유지
  • className / photoClassName / counterClassName / counterAlternative — 전체 화면 배치와 하단 가운데 카운터

여기에 더해 사진 로딩도 정리했습니다.

  • components/ui/LazyImage.tsx 추가 — 사진이 오기 전에는 회색 자리(thumbnail-placeholder)를 보여주고 도착하면 0.3s 페이드인합니다. 받는 시점은 브라우저 기본 지연 로딩(loading="lazy")에 맡겼습니다
  • 카드가 길게 이어지는 아카이빙 목록과 현장 사진 격자 두 곳에만 적용했습니다. 상세 대표 사진·3칸 미리보기·뷰어는 평범한 <img>입니다

리뷰에서 나온 것들도 함께 고쳤습니다.

  • 오버스크롤 버그 — scrollLeft를 클램프하지 않아 마지막 장을 세게 밀면 카운터가 "27 / 26"이 되고, 뷰어가 photos[26](undefined)을 흐린 배경 src로 넣어 배경이 사라졌습니다. FeedbacksDetailScreen에 이미 있던 같은 처리를 공용 컴포넌트에도 넣었습니다
  • onPageChange를 스크롤 이벤트마다가 아니라 장이 바뀔 때만 호출합니다
  • 뷰어 사진을 자르지 않습니다(object-contain) — 비율이 다른 사진이 좌우로 잘리던 문제
  • 하단 Home Bar 여백 복붙 3곳을 components/ui/ScreenBottomSafeArea.tsx로 묶었습니다
  • 필터 칩에 aria-pressed를 넣었습니다(아카이빙 연도 필터 + 빌릴게·행사 필터)
  • 목록 열을 ArchivesPhotoColumn으로 분리하고, 정보 그리드 라벨 칸을 minmax(51px,auto)로 바꿨습니다

🧩 이 PR의 한계 & 트레이드오프

  • PhotoGallery의 props가 늘었습니다. 화면마다 배치가 달라 레이아웃 클래스를 받는 prop이 몇 개 생겼습니다
  • 지연 로딩은 긴 화면(목록)에서만 효과가 뚜렷합니다. 현장 사진 격자는 전체 높이가 브라우저의 선로딩 거리 안이라 대부분 미리 받습니다
  • 뷰어에서 사진 비율이 제각각이면 넘길 때 높이가 바뀝니다. 틀을 고정하면 사라지지만 좌우에 여백이 생겨서, 가로를 꽉 채우는 쪽을 택했습니다
  • PhotoGallery·FeedbacksDetailScreen·FeedbacksListScreen에 스냅 스크롤 계산이 각각 들어 있습니다. 공용 훅(useSnapPager)으로 빼는 건 열린피드백 화면까지 건드려야 해서 이번 범위에서 뺐습니다
  • 블러업(저해상도 썸네일 → 원본)은 썸네일이 없어서 페이드인으로 대체했습니다

⛓️ 기존 기능에 미치는 영향

  • 공용 PhotoGallery를 수정했습니다. 공지 상세·행사 상세는 photos·background를 넘기지 않아 placeholder 동작이 그대로지만, 리뷰 시 한 번 봐주시면 좋겠습니다
  • 슬라이드 영역에 relative를 추가했습니다(배경 슬롯이 일반 흐름 슬라이드 위에 그려지는 문제 때문). 기존 화면 배치에는 영향이 없습니다

🔀 Edge Case & 실패 시나리오

  • 주소의 photoIndex가 숫자가 아니거나 범위를 벗어나면 첫 장/마지막 장으로 맞춥니다
  • 캐시된 사진은 React가 onLoad를 붙이기 전에 로딩이 끝나 이벤트가 오지 않습니다 — 마운트 시 complete를 확인해 회색 자리에 멈추지 않게 했습니다
  • 사진이 1장이면 화살표와 스크롤 핸들러가 붙지 않습니다

📋 검토한 대안과 선택 이유

  • 스크롤 스냅·페이지 계산만 훅으로 빼고 레이아웃은 화면마다 두는 방안도 있었지만, 공용 컴포넌트가 이미 있어서 거기에 prop을 더하는 쪽을 골랐습니다
  • 지연 로딩에 IntersectionObserver를 직접 두는 방안은 뺐습니다 — loading="lazy"와 같은 일을 두 번 하게 되고, 스크롤 컨테이너가 중첩된 구조라 기준을 또 관리해야 합니다

💬 리뷰 포인트

  • [r] PhotoGallery의 늘어난 props 범위 — 더 줄일 여지가 있는지
  • [c] 공지 상세·행사 상세 갤러리가 전과 같은지
  • [a] LazyImage의 캐시 처리(complete 확인)

확인 방법

헤드리스 Chrome(390×844)으로 목데이터를 임시로 늘려 측정했습니다(임시 패치는 커밋하지 않았습니다).

  • 카드 40장 기준 — DOM <img> 40개, 스크롤 전 이미지 요청 15건, 맨 아래까지 스크롤 후 24건
  • 사진 요청을 막은 상태 — 회색 자리 rgb(217,217,217), 사진 opacity: 0, 도착 후 opacity: 1(transition: opacity 0.3s)

이승준 added 23 commits September 14, 2026 18:55
…-screen

# Conflicts:
#	src/app/router.tsx
#	src/components/ui/ScreenHeader.tsx
화면이 직접 깔던 흰 배경도 라우트 background 옵션으로 옮긴다
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 938071bd-fc80-46d5-8300-15510d251b31

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Comment @coderabbitai help to get the list of available commands.

이승준 added 3 commits October 1, 2026 18:26
Safari 탄성 오버스크롤에서 scrollLeft가 범위를 벗어나 마지막 장을 세게 밀면 카운터가
"27 / 26"이 되고 뷰어의 흐린 배경이 사라졌다. FeedbacksDetailScreen과 같은 방식으로 클램프한다.
장이 실제로 바뀔 때만 onPageChange를 호출하고, 뷰어 사진은 자르지 않고 가로를 꽉 채운다.
- 지연 로딩은 목록이 긴 아카이빙 목록·현장 사진 목록에만 둔다
- 하단 Home Bar 여백 복붙 3곳을 ScreenBottomSafeArea로 묶는다
- 필터 칩 선택 상태를 aria-pressed로 알린다
- 목록 열을 ArchivesPhotoColumn 컴포넌트로 분리한다
- 정보 그리드 라벨 칸을 minmax로 바꿔 긴 라벨이 넘치지 않게 한다
이승준 added 3 commits October 11, 2026 22:51
main의 entities/archives(검색 결과가 쓰는 목데이터)와 features/archives의 목데이터가 따로
있어서 id 체계가 어긋났다. 아카이빙 목록·상세가 같은 entities를 보게 하고, 다른 화면과 같은
방식(entities API + use() + Suspense 스켈레톤)으로 맞춘다. 검색 결과의 아카이빙 카드도 상세로 연결한다.
…s-photo-gallery

# Conflicts:
#	src/features/archives/ArchivesDetailScreen.tsx
#	src/features/archives/ArchivesListScreen.tsx
#	src/features/archives/ArchivesPhotoViewerScreen.tsx
#	src/features/archives/ArchivesPhotosScreen.tsx
#	src/features/archives/components/ArchivesPhotoColumn.tsx
#	src/features/archives/constants/archivesDetail.ts
alt={photo.alt}
className={photoClassName}
src={photo.src}
/>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

뷰어를 열었을때 슬라이드에 있는 가 전부 바로 요청되는것으로 이해했는데, 지금은 목업파일이라 요청이 적지만 API 연동했을때 첫 사진이 늦게 뜰 수도 있을거 같습니다. 슬라이드 사진에 loading="lazy"를 주거나, 이번 PR에서 만드신LazyImage를 쓰는거 어떨까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다 (7876a14). 다만 전부 lazy로 두면 다른 문제가 생겨서 절충했습니다.

가로 스크롤러 안의 사진을 전부 loading="lazy"로 두면, 다음 장이 화면에 들어오는 순간에야 요청이 시작돼 넘길 때마다 빈 자리가 보입니다(전에 뷰어 전체를 lazy로 했다가 겪었습니다). 그래서 **보고 있는 장과 앞뒤 한 장만 eager, 나머지는 lazy**로 바꿨습니다.

loading={Math.abs(index + 1 - currentPage) <= 1 ? "eager" : "lazy"}

헤드리스 Chrome(390×844)에서 /archives/2/photos/4를 열어 확인했습니다 — 슬라이드 26장 중 eager 3장 / lazy 23장입니다(나머지 1장은 흐린 배경).

LazyImage를 쓰지 않은 이유도 적어둡니다. 그 컴포넌트는 도착 전 회색 자리를 보여주는 게 목적이라, 사진 한 장이 화면을 꽉 채우는 뷰어에서는 회색 화면이 그대로 보입니다. 목록·격자처럼 자리가 작은 곳에 맞는 방식이라고 판단했습니다.

className="h-full"
counterAlternative
/* Figma Page Indicator/Counter — 화면 하단에서 47px 위, 가운데 */
counterClassName="inset-x-0 bottom-[47px] flex justify-center"

@sangrae2325 sangrae2325 Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

이렇게 px을 하드코딩한 부분 다른 코드들에서도 정리하려고 해서 이번 PR에서도 component-convention.md에 따라 정리해주시면 좋을거같아요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다 (a2d901c). component-convention.md의 "크기·위치·그림자" 순서대로 정리했습니다.

  • bottom-[47px] → bottom-11.75 (지적해 주신 자리)
  • h-[187px](상세 대표 사진 그라데이션) → h-46.75
  • 카드 높이 h-[258px]/h-[220px]/h-[184px] → h-64.5/h-55/h-46
  • sm:h-[34px](하단 Home Bar) → sm:h-8.5
  • FilterChip의 rounded-[20px] → rounded-full — Figma는 20px이지만 칩 높이가 32px이라 브라우저가 16px로 깎아서 결과가 같습니다(주석으로 남겼습니다)

남은 임의값은 흐린 배경의 blur-[21.55px] 하나입니다. Tailwind blur 단계로 나타낼 수 없어 컨벤션 4번(예외)으로 두고, 어느 Figma 값인지 주석을 달았습니다.

@sangrae2325
sangrae2325 deleted the branch main October 12, 2026 06:09
@sangrae2325 sangrae2325 reopened this Oct 12, 2026
@sangrae2325
sangrae2325 changed the base branch from feat/#31-archive-list-screen to main October 12, 2026 06:11
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.

아카이빙 사진 뷰어를 공용 PhotoGallery로 통합

2 participants