From 67e9992cf1d6303720371386d01b53947719d62d Mon Sep 17 00:00:00 2001 From: caadiq Date: Wed, 21 Jan 2026 12:05:36 +0900 Subject: [PATCH] =?UTF-8?q?=EB=9D=BC=EC=9D=B4=ED=8A=B8=EB=B0=95=EC=8A=A4?= =?UTF-8?q?=20=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PC/모바일 앨범 상세, 갤러리 페이지에 뒤로가기 처리 - PC/모바일 X 일정 상세 페이지에 뒤로가기 처리 - 라이트박스 열릴 때 history.pushState 호출 - popstate 이벤트로 라이트박스 닫기 Co-Authored-By: Claude Opus 4.5 --- .../pages/mobile/public/ScheduleDetail.jsx | 17 +++++- frontend/src/pages/pc/public/AlbumDetail.jsx | 55 +++++++++++++------ frontend/src/pages/pc/public/AlbumGallery.jsx | 25 +++++++-- .../pc/public/schedule-sections/XSection.jsx | 25 +++++++-- 4 files changed, 92 insertions(+), 30 deletions(-) diff --git a/frontend/src/pages/mobile/public/ScheduleDetail.jsx b/frontend/src/pages/mobile/public/ScheduleDetail.jsx index 8abea95..a2cfcb2 100644 --- a/frontend/src/pages/mobile/public/ScheduleDetail.jsx +++ b/frontend/src/pages/mobile/public/ScheduleDetail.jsx @@ -315,6 +315,7 @@ function XSection({ schedule }) { const openLightbox = (index) => { setLightboxIndex(index); setLightboxOpen(true); + window.history.pushState({ lightbox: true }, ''); }; const goToPrev = () => { @@ -341,6 +342,18 @@ function XSection({ schedule }) { }; }, [lightboxOpen]); + // 뒤로가기 처리 + useEffect(() => { + const handlePopState = () => { + if (lightboxOpen) { + setLightboxOpen(false); + } + }; + + window.addEventListener('popstate', handlePopState); + return () => window.removeEventListener('popstate', handlePopState); + }, [lightboxOpen]); + // 링크 데코레이터 (새 탭에서 열기) const linkDecorator = (href, text, key) => ( setLightboxOpen(false)} + onClick={() => window.history.back()} > {/* 닫기 버튼 */} diff --git a/frontend/src/pages/pc/public/AlbumDetail.jsx b/frontend/src/pages/pc/public/AlbumDetail.jsx index e1b1346..d7532e9 100644 --- a/frontend/src/pages/pc/public/AlbumDetail.jsx +++ b/frontend/src/pages/pc/public/AlbumDetail.jsx @@ -45,10 +45,30 @@ function AlbumDetail() { })); }, [lightbox.images.length]); + // 라이트박스 열기 - 히스토리 추가 + const openLightbox = useCallback((images, index, options = {}) => { + setLightbox({ open: true, images, index, teasers: options.teasers }); + window.history.pushState({ lightbox: true }, ''); + }, []); + const closeLightbox = useCallback(() => { setLightbox(prev => ({ ...prev, open: false })); }, []); + // 뒤로가기 처리 + useEffect(() => { + const handlePopState = () => { + if (showDescriptionModal) { + setShowDescriptionModal(false); + } else if (lightbox.open) { + setLightbox(prev => ({ ...prev, open: false })); + } + }; + + window.addEventListener('popstate', handlePopState); + return () => window.removeEventListener('popstate', handlePopState); + }, [showDescriptionModal, lightbox.open]); + // 라이트박스 열릴 때 body 스크롤 숨기기 useEffect(() => { if (lightbox.open) { @@ -98,7 +118,7 @@ function AlbumDetail() { goToNext(); break; case 'Escape': - closeLightbox(); + window.history.back(); break; default: break; @@ -202,11 +222,10 @@ function AlbumDetail() { animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.4 }} className="w-80 h-80 flex-shrink-0 rounded-2xl overflow-hidden shadow-lg cursor-pointer group" - onClick={() => setLightbox({ - open: true, - images: [album.cover_original_url || album.cover_medium_url], - index: 0 - })} + onClick={() => openLightbox( + [album.cover_original_url || album.cover_medium_url], + 0 + )} > { setShowDescriptionModal(true); + window.history.pushState({ description: true }, ''); setShowMenu(false); }} className="w-full flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50 transition-colors" @@ -291,16 +311,15 @@ function AlbumDetail() {

티저 이미지

{album.teasers.map((teaser, index) => ( -
setLightbox({ - open: true, - images: album.teasers.map(t => + onClick={() => openLightbox( + album.teasers.map(t => t.media_type === 'video' ? (t.video_url || t.original_url) : t.original_url ), index, - teasers: album.teasers // media_type 정보 전달 - })} + { teasers: album.teasers } + )} className="w-24 h-24 bg-gray-200 rounded-lg overflow-hidden cursor-pointer transition-all duration-300 ease-out hover:scale-105 hover:shadow-xl hover:z-10 relative" > <> @@ -397,9 +416,9 @@ function AlbumDetail() {
{previewPhotos.map((photo, idx) => ( -
setLightbox({ open: true, images: [photo.original_url], index: 0 })} + onClick={() => openLightbox([photo.original_url], 0)} className="aspect-square bg-gray-200 rounded-xl overflow-hidden cursor-pointer transition-all duration-300 ease-out hover:scale-[1.03] hover:shadow-xl hover:z-10" > {/* 닫기 버튼 */} - @@ -537,7 +556,7 @@ function AlbumDetail() { animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" - onClick={() => setShowDescriptionModal(false)} + onClick={() => window.history.back()} >

앨범 소개

- diff --git a/frontend/src/pages/pc/public/schedule-sections/XSection.jsx b/frontend/src/pages/pc/public/schedule-sections/XSection.jsx index 498dad1..692aa2b 100644 --- a/frontend/src/pages/pc/public/schedule-sections/XSection.jsx +++ b/frontend/src/pages/pc/public/schedule-sections/XSection.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { motion } from 'framer-motion'; import Linkify from 'react-linkify'; import { decodeHtmlEntities } from './utils'; @@ -33,10 +33,27 @@ function XSection({ schedule }) { const [lightboxOpen, setLightboxOpen] = useState(false); const [lightboxIndex, setLightboxIndex] = useState(0); - const openLightbox = (index) => { + const openLightbox = useCallback((index) => { setLightboxIndex(index); setLightboxOpen(true); - }; + window.history.pushState({ lightbox: true }, ''); + }, []); + + const closeLightbox = useCallback(() => { + setLightboxOpen(false); + }, []); + + // 뒤로가기 처리 + useEffect(() => { + const handlePopState = () => { + if (lightboxOpen) { + setLightboxOpen(false); + } + }; + + window.addEventListener('popstate', handlePopState); + return () => window.removeEventListener('popstate', handlePopState); + }, [lightboxOpen]); // 링크 데코레이터 (새 탭에서 열기) const linkDecorator = (href, text, key) => ( @@ -160,7 +177,7 @@ function XSection({ schedule }) { images={schedule.imageUrls || []} currentIndex={lightboxIndex} isOpen={lightboxOpen} - onClose={() => setLightboxOpen(false)} + onClose={() => window.history.back()} onIndexChange={setLightboxIndex} />