웹: AlbumDetail, AlbumGallery 페이지 useQuery로 리팩토링

This commit is contained in:
caadiq 2026-01-12 17:44:28 +09:00
parent 443bd203ca
commit dc65858d6b
4 changed files with 65 additions and 89 deletions

View file

@ -1,6 +1,7 @@
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { Play, Calendar, Music2, Clock, X, Download, ChevronDown, ChevronUp, FileText, ChevronRight } from 'lucide-react'; import { Play, Calendar, Music2, Clock, X, Download, ChevronDown, ChevronUp, FileText, ChevronRight } from 'lucide-react';
import { Swiper, SwiperSlide } from 'swiper/react'; import { Swiper, SwiperSlide } from 'swiper/react';
import { Virtual } from 'swiper/modules'; import { Virtual } from 'swiper/modules';
@ -9,17 +10,21 @@ import { getAlbumByName } from '../../../api/public/albums';
import { formatDate } from '../../../utils/date'; import { formatDate } from '../../../utils/date';
import LightboxIndicator from '../../../components/common/LightboxIndicator'; import LightboxIndicator from '../../../components/common/LightboxIndicator';
//
function MobileAlbumDetail() { function MobileAlbumDetail() {
const { name } = useParams(); const { name } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [album, setAlbum] = useState(null);
const [loading, setLoading] = useState(true);
const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true }); const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true });
const [showAllTracks, setShowAllTracks] = useState(false); const [showAllTracks, setShowAllTracks] = useState(false);
const [showDescriptionModal, setShowDescriptionModal] = useState(false); const [showDescriptionModal, setShowDescriptionModal] = useState(false);
const swiperRef = useRef(null); const swiperRef = useRef(null);
// useQuery
const { data: album, isLoading: loading } = useQuery({
queryKey: ['album', name],
queryFn: () => getAlbumByName(name),
enabled: !!name,
});
// - // -
const openLightbox = useCallback((images, index, options = {}) => { const openLightbox = useCallback((images, index, options = {}) => {
setLightbox({ open: true, images, index, showNav: options.showNav !== false, teasers: options.teasers }); setLightbox({ open: true, images, index, showNav: options.showNav !== false, teasers: options.teasers });
@ -85,18 +90,6 @@ function MobileAlbumDetail() {
return () => { document.body.style.overflow = ''; }; return () => { document.body.style.overflow = ''; };
}, [lightbox.open, showDescriptionModal]); }, [lightbox.open, showDescriptionModal]);
useEffect(() => {
getAlbumByName(name)
.then(data => {
setAlbum(data);
setLoading(false);
})
.catch(error => {
console.error('앨범 로드 오류:', error);
setLoading(false);
});
}, [name]);
// //
const getTotalDuration = () => { const getTotalDuration = () => {
if (!album?.tracks) return ''; if (!album?.tracks) return '';

View file

@ -1,5 +1,6 @@
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { X, Download, ChevronRight, Info, Users, Tag } from 'lucide-react'; import { X, Download, ChevronRight, Info, Users, Tag } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { Swiper, SwiperSlide } from 'swiper/react'; import { Swiper, SwiperSlide } from 'swiper/react';
@ -12,34 +13,29 @@ import LightboxIndicator from '../../../components/common/LightboxIndicator';
function MobileAlbumGallery() { function MobileAlbumGallery() {
const { name } = useParams(); const { name } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [album, setAlbum] = useState(null);
const [photos, setPhotos] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedIndex, setSelectedIndex] = useState(null); const [selectedIndex, setSelectedIndex] = useState(null);
const [showInfo, setShowInfo] = useState(false); const [showInfo, setShowInfo] = useState(false);
const swiperRef = useRef(null); const swiperRef = useRef(null);
useEffect(() => { // useQuery
getAlbumByName(name) const { data: album, isLoading: loading } = useQuery({
.then(data => { queryKey: ['album', name],
setAlbum(data); queryFn: () => getAlbumByName(name),
const allPhotos = []; enabled: !!name,
if (data.conceptPhotos && typeof data.conceptPhotos === 'object') { });
Object.entries(data.conceptPhotos).forEach(([concept, photos]) => {
photos.forEach(p => allPhotos.push({ //
...p, const photos = useMemo(() => {
concept: concept !== 'Default' ? concept : null if (!album?.conceptPhotos) return [];
})); const allPhotos = [];
}); Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => {
} conceptPhotos.forEach(p => allPhotos.push({
setPhotos(allPhotos); ...p,
setLoading(false); concept: concept !== 'Default' ? concept : null
}) }));
.catch(error => { });
console.error('앨범 데이터 로드 오류:', error); return allPhotos;
setLoading(false); }, [album]);
});
}, [name]);
// - // -
const openLightbox = useCallback((index) => { const openLightbox = useCallback((index) => {

View file

@ -1,5 +1,6 @@
import { useState, useEffect, useCallback, memo } from 'react'; import { useState, useEffect, useCallback, memo } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { Calendar, Music2, Clock, X, ChevronLeft, ChevronRight, Download, MoreVertical, FileText } from 'lucide-react'; import { Calendar, Music2, Clock, X, ChevronLeft, ChevronRight, Download, MoreVertical, FileText } from 'lucide-react';
import { getAlbumByName } from '../../../api/public/albums'; import { getAlbumByName } from '../../../api/public/albums';
@ -9,15 +10,20 @@ import LightboxIndicator from '../../../components/common/LightboxIndicator';
function AlbumDetail() { function AlbumDetail() {
const { name } = useParams(); const { name } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [album, setAlbum] = useState(null);
const [loading, setLoading] = useState(true);
const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0 }); const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0 });
const [slideDirection, setSlideDirection] = useState(0); const [slideDirection, setSlideDirection] = useState(0);
const [imageLoaded, setImageLoaded] = useState(false); const [imageLoaded, setImageLoaded] = useState(false);
const [preloadedImages] = useState(() => new Set()); // URL const [preloadedImages] = useState(() => new Set());
const [showDescriptionModal, setShowDescriptionModal] = useState(false); const [showDescriptionModal, setShowDescriptionModal] = useState(false);
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
// useQuery
const { data: album, isLoading: loading } = useQuery({
queryKey: ['album', name],
queryFn: () => getAlbumByName(name),
enabled: !!name,
});
// //
const goToPrev = useCallback(() => { const goToPrev = useCallback(() => {
if (lightbox.images.length <= 1) return; if (lightbox.images.length <= 1) return;
@ -125,18 +131,6 @@ function AlbumDetail() {
}); });
}, [lightbox.open, lightbox.index, lightbox.images, preloadedImages]); }, [lightbox.open, lightbox.index, lightbox.images, preloadedImages]);
useEffect(() => {
getAlbumByName(name)
.then(data => {
setAlbum(data);
setLoading(false);
})
.catch(error => {
console.error('앨범 데이터 로드 오류:', error);
setLoading(false);
});
}, [name]);
// URL - API // URL - API
// //

View file

@ -1,5 +1,6 @@
import { useState, useEffect, useCallback, memo } from 'react'; import { useState, useEffect, useCallback, memo, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { X, ChevronLeft, ChevronRight, Download } from 'lucide-react'; import { X, ChevronLeft, ChevronRight, Download } from 'lucide-react';
import { RowsPhotoAlbum } from 'react-photo-album'; import { RowsPhotoAlbum } from 'react-photo-album';
@ -40,42 +41,34 @@ const galleryStyles = `
function AlbumGallery() { function AlbumGallery() {
const { name } = useParams(); const { name } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [album, setAlbum] = useState(null);
const [photos, setPhotos] = useState([]);
const [loading, setLoading] = useState(true);
const [lightbox, setLightbox] = useState({ open: false, index: 0 }); const [lightbox, setLightbox] = useState({ open: false, index: 0 });
const [imageLoaded, setImageLoaded] = useState(false); const [imageLoaded, setImageLoaded] = useState(false);
const [slideDirection, setSlideDirection] = useState(0); const [slideDirection, setSlideDirection] = useState(0);
const [preloadedImages] = useState(() => new Set()); // URL const [preloadedImages] = useState(() => new Set());
useEffect(() => { // useQuery
getAlbumByName(name) const { data: album, isLoading: loading } = useQuery({
.then(data => { queryKey: ['album', name],
setAlbum(data); queryFn: () => getAlbumByName(name),
const allPhotos = []; enabled: !!name,
});
if (data.conceptPhotos && typeof data.conceptPhotos === 'object') {
Object.entries(data.conceptPhotos).forEach(([concept, photos]) => { //
photos.forEach(p => allPhotos.push({ const photos = useMemo(() => {
// API URL if (!album?.conceptPhotos) return [];
mediumUrl: p.medium_url, const allPhotos = [];
originalUrl: p.original_url, Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => {
width: p.width || 800, conceptPhotos.forEach(p => allPhotos.push({
height: p.height || 1200, mediumUrl: p.medium_url,
title: concept, originalUrl: p.original_url,
members: p.members ? p.members.split(', ') : [] width: p.width || 800,
})); height: p.height || 1200,
}); title: concept,
} members: p.members ? p.members.split(', ') : []
}));
setPhotos(allPhotos); });
setLoading(false); return allPhotos;
}) }, [album]);
.catch(error => {
console.error('앨범 데이터 로드 오류:', error);
setLoading(false);
});
}, [name]);
// //
const openLightbox = (index) => { const openLightbox = (index) => {