Diary Section 개발 기록
2026-02-14 ~ 2026-02-20
배경
블로그 일기 포스트에 사용할 이미지 갤러리를 만들고 싶었다.
블로그 방치하고 있다가 설 연휴에 할 것도 없고 해서 시작.
이 템플릿을 보고 이거처럼 블로그 포스트에 다이나믹하게 이미지와 글을 표시하고 싶었다.
Three.js를 통해서 이를 구현하면 좋겠다고 생각했는데, 정작 나는 Three.js에 대하여 모르니까
AI(Claude Code)를 통해서 구현하기로 했다.
결론부터 말하면 시행착오를 거쳐 가장 단순한 형태로 수렴했다:
- Three.js 사용하여 3D 공간에 이미지를 배치
- CSS 3D Transform
- 통합 Three.js 갤러리
- Three.js 링 캐러셀
- 단순 이미지 캐러셀.
1. Three.js (WebGL) 버전
@react-three/fiber 기반으로 먼저 구현한 버전.
구현 내용
- WebGL Canvas 위에 이미지를 3D plane으로 배치
- 스크롤 진행도에 따라 plane이 Y축으로 이동 (parallax)
- 카메라가 스크롤에 따라 dolly + vertical travel
smoothstep기반 fade in/out- alphaMap DataTexture로 이미지 가장자리 soft fade
- fog으로 원거리 이미지 자연스럽게 사라짐
문제점
- 단일 Three.js 인스턴스라면 문제없었지만, 일기 페이지에 9개 DiarySection이 각각 독립 WebGL Canvas를 생성하니 GPU 컨텍스트 제한에 도달
- 이미지 배치와 카메라 동선이 부자연스러움
- 모바일에서 심각한 성능 저하
- 스크롤 시 버벅임이 심하고 프레임 드랍이 눈에 띄게 발생
- 아래 데모는 인스턴스가 하나뿐이라 성능 문제가 전혀 없음 — 실제 일기 페이지처럼 9개가 동시에 존재하면 문제가 드러남
2. CSS 3D Transform 버전
Three.js 버전의 경험을 바탕으로 CSS 3D transform + framer-motion으로 재구현.
먼저 Three.js로 만들어보면서 3D 레이아웃과 스크롤 인터랙션의 방향이 잡혀 있었기 때문에,
CSS 3D로 옮기니 원하는 결과물에 어느 정도 근접할 수 있었다.
구현 내용
- CSS
perspective+translateZ / rotateY로 3D 깊이감 - framer-motion
useScroll+useTransform으로 스크롤 기반 애니메이션 - 이미지별 독립 스크롤 윈도우로 순차적 fade in/out
- 컨테이너 scale + translateY로 카메라 시뮬레이션
mask-imagegradient로 soft edge fade- radial gradient fog (vignette)
- focus mode 토글, 썸네일 스트립, 라이트박스
Three.js 대비 개선점
- WebGL Canvas 없이 브라우저 compositor GPU 가속만 사용 → 컨텍스트 제한 문제 해결
- 여러 섹션이 한 페이지에 공존해도 성능 문제 없음
- 추가 최적화가 필요하지만 Three.js 버전 대비 스크롤이 어느 정도 매끄럽게 동작
0 / 4




CSS 3D 버전 데모
스크롤하면 perspective와 translateZ로 깊이감이 표현된 이미지들이 순차적으로 나타납니다.
여전히 이미지 부자연스러운 겹침 문제 존재.
랜덤하게 이미지 위치 및 사이즈 결정되어 특정 콘텐츠의 경우 제대로 보이지 않는 문제가 있습니다.




3. 통합 Three.js 갤러리
링 캐러셀의 “복수 Canvas”가 성능적으로 부하를 많이 일으킨다고 판단하여, 페이지 전체를 하나의 Three.js Canvas로 통합하는 접근도 시도했다.
컨셉
DiaryGalleryUnified: 모든 섹션의 이미지를 하나의 Canvas에 렌더링- 섹션 간 전환은 카메라 이동으로 처리
useUnifiedScroll훅으로 스크롤 진행도 → 섹션 인덱스 매핑- 텍스처 캐시 공유로 메모리 효율화
폐기 사유
- Astro island 아키텍처와 근본적으로 충돌 —
client:visible로 lazy hydration하는 패턴과 단일 Canvas가 양립 불가 - 페이지 진입 시 모든 이미지 텍스처를 한번에 로딩해야 하는 문제
- 텍스트 콘텐츠를 3D Canvas 위에 오버레이하는 것의 접근성 문제
0 /4
섹션 1
통합 갤러리 데모 — 모든 섹션의 이미지가 하나의 Canvas에 렌더링됩니다. 스크롤에 따라 카메라가 이동하며 이미지가 순차적으로 나타납니다.
섹션 2
두 번째 섹션입니다. 우측 도트로 현재 섹션을 확인하고, 이미지를 클릭하면 라이트박스가 열립니다.
4. Three.js 링 캐러셀
CSS 3D 버전으로는 구현의 한계가 있고, 1번째에서 시도한 Three.js는 모바일에서 crash가 너무 자주 발생하여,
예시 템플릿에서 보이는 방식으로 사진을 많이 넣어서 갤러리처럼 보이게 하는 것은 불가능하다고 판단하여
다른 방향으로 가보자라는 생각에 다른 예시를 찾아보다가 Three.js 링 캐러셀 예시를 찾아보게 되었다.
컨셉
@react-three/fiber기반 3D 링(원형) 캐러셀- 이미지를 원통형으로 배치하고, 섹션 도트를 클릭하면 링이 회전
calc(100dvh - 150px)높이의 몰입형 뷰- alphaMap으로 이미지 가장자리 soft fade
- 스크롤이 아닌 클릭 기반 네비게이션
구현 시도
CarouselRing.tsx: R3F Scene — 이미지를 원형으로 배치,atan2로 카메라를 향하도록 회전UnifiedImagePlane.tsx: 개별 이미지 plane — 텍스처 로딩, alphaMap, opacity 애니메이션buildCarouselLayout.ts: 원형 배치 수학 (반지름, 각도 계산)carouselAlphaMap.ts: soft-edge DataTexture 생성 (RGBAFormat 필수 — GREEN 채널 이슈 학습)texturePreload.ts: 동시성 제한 텍스처 로더,createImageBitmap+ canvas fallback
0 / 6
섹션 1
R3F 링 캐러셀 데모 — 스크롤하면 3D 링이 회전하며 이미지가 전환됩니다. 하단 도트를 클릭해 섹션 이동도 가능합니다.
섹션 2
두 번째 섹션입니다. 링 위 이미지를 클릭하면 라이트박스가 열립니다.
5. 최종: DiaryCarousel
Three.js 링 캐러셀의 경우에는 성능 이슈도 어느 정도 해결되어서 모바일 구동도 문제가 없었다.
다만 스크롤 방식과 보이는 방식이 PC 브라우저에서는 괜찮았는데,
모바일에서는 스크롤 불편, 컨텐츠 보기도 불편하여 과연 이렇게 구현하는게 의미가 있을까하는 의문이 들었다.
그러던 중 내가 팔로우하고 있는 anthony-fu 의 블로그에서 하나의 포스트가 단순히 캐러셀 + 텍스트 단락으로 구성되어 있는 것을 보고
굳이 복잡하게 갈 필요가 있을까 하는 생각이 들었고, 모바일에서는 오히려 단순한 것이 더 보기 편해서 단순하게 가는 것으로 결정햇다.
구현 내용
embla-carousel-react기반 캐러셀 (프로젝트에서 이미 사용 중이던 ui/carousel 컴포넌트 재활용)- 이미지 클릭 → 라이트박스 (
ImageLightbox) - 비디오 지원 (
videosprop) - 카운터 뱃지 (현재 슬라이드 / 전체)
- 데스크탑: 좌우 화살표, 모바일: 스와이프
장점
- 추가 번들 없음 — 이미 존재하는
Carousel+ImageLightbox조합 - 100줄 미만의 단일 컴포넌트
- 3D 갤러리 대비 번들 사이즈 대폭 감소 (Three.js, R3F, framer-motion 3D 로직 제거)
- 모바일 네이티브 수준의 스와이프 UX
client:visible+ self-closing JSX로 MDX 작성이 단순- 접근성 우수 — 키보드 네비게이션, 스크린 리더 지원



