웹: useEffect를 useQuery로 리팩토링 (PC/모바일 공개 페이지)
This commit is contained in:
parent
a89139f056
commit
990d360520
6 changed files with 347 additions and 321 deletions
|
|
@ -1,22 +1,18 @@
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useState, useEffect } from 'react';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { getAlbums } from '../../../api/public/albums';
|
import { getAlbums } from '../../../api/public/albums';
|
||||||
|
|
||||||
// 모바일 앨범 목록 페이지
|
// 모바일 앨범 목록 페이지
|
||||||
function MobileAlbum() {
|
function MobileAlbum() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [albums, setAlbums] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
// useQuery로 앨범 데이터 로드
|
||||||
getAlbums()
|
const { data: albums = [], isLoading: loading } = useQuery({
|
||||||
.then(data => {
|
queryKey: ['albums'],
|
||||||
setAlbums(data);
|
queryFn: getAlbums,
|
||||||
setLoading(false);
|
});
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { ChevronRight, Clock, Tag } from 'lucide-react';
|
import { ChevronRight, Clock, Tag } from 'lucide-react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { getTodayKST } from '../../../utils/date';
|
import { getTodayKST } from '../../../utils/date';
|
||||||
import { getMembers } from '../../../api/public/members';
|
import { getMembers } from '../../../api/public/members';
|
||||||
|
|
@ -10,27 +10,27 @@ import { getUpcomingSchedules } from '../../../api/public/schedules';
|
||||||
// 모바일 홈 페이지
|
// 모바일 홈 페이지
|
||||||
function MobileHome() {
|
function MobileHome() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [members, setMembers] = useState([]);
|
|
||||||
const [albums, setAlbums] = useState([]);
|
|
||||||
const [schedules, setSchedules] = useState([]);
|
|
||||||
|
|
||||||
// 데이터 로드
|
// useQuery로 멤버 데이터 로드 (활동 중인 멤버만)
|
||||||
useEffect(() => {
|
const { data: members = [] } = useQuery({
|
||||||
// 멤버 로드
|
queryKey: ['members'],
|
||||||
getMembers()
|
queryFn: getMembers,
|
||||||
.then(data => setMembers(data.filter(m => !m.is_former)))
|
select: (data) => data.filter(m => !m.is_former),
|
||||||
.catch(console.error);
|
});
|
||||||
|
|
||||||
// 앨범 로드 (최신 2개)
|
// useQuery로 앨범 로드 (최신 2개)
|
||||||
getAlbums()
|
const { data: albums = [] } = useQuery({
|
||||||
.then(data => setAlbums(data.slice(0, 2)))
|
queryKey: ['albums'],
|
||||||
.catch(console.error);
|
queryFn: getAlbums,
|
||||||
|
select: (data) => data.slice(0, 2),
|
||||||
|
});
|
||||||
|
|
||||||
|
// useQuery로 다가오는 일정 로드
|
||||||
|
const { data: schedules = [] } = useQuery({
|
||||||
|
queryKey: ['upcomingSchedules', 3],
|
||||||
|
queryFn: () => getUpcomingSchedules(3),
|
||||||
|
});
|
||||||
|
|
||||||
// 다가오는 일정 로드
|
|
||||||
getUpcomingSchedules(3)
|
|
||||||
.then(data => setSchedules(data))
|
|
||||||
.catch(console.error);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { Instagram, Calendar, X } from 'lucide-react';
|
import { Instagram, Calendar, X } from 'lucide-react';
|
||||||
import { getMembers } from '../../../api/public/members';
|
import { getMembers } from '../../../api/public/members';
|
||||||
|
|
||||||
// 모바일 멤버 페이지
|
// 모바일 멤버 페이지
|
||||||
function MobileMembers() {
|
function MobileMembers() {
|
||||||
const [members, setMembers] = useState([]);
|
|
||||||
const [formerMembers, setFormerMembers] = useState([]);
|
|
||||||
const [selectedMember, setSelectedMember] = useState(null);
|
const [selectedMember, setSelectedMember] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
// useQuery로 멤버 데이터 로드
|
||||||
getMembers()
|
const { data: allMembers = [] } = useQuery({
|
||||||
.then(data => {
|
queryKey: ['members'],
|
||||||
setMembers(data.filter(m => !m.is_former));
|
queryFn: getMembers,
|
||||||
setFormerMembers(data.filter(m => m.is_former));
|
});
|
||||||
})
|
|
||||||
.catch(console.error);
|
// useMemo로 현재/전 멤버 분리
|
||||||
}, []);
|
const members = useMemo(() => allMembers.filter(m => !m.is_former), [allMembers]);
|
||||||
|
const formerMembers = useMemo(() => allMembers.filter(m => m.is_former), [allMembers]);
|
||||||
|
|
||||||
|
|
||||||
// 나이 계산
|
// 나이 계산
|
||||||
const calculateAge = (birthDate) => {
|
const calculateAge = (birthDate) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Calendar, Music } from 'lucide-react';
|
import { Calendar, Music } from 'lucide-react';
|
||||||
|
|
@ -7,20 +7,13 @@ import { formatDate } from '../../../utils/date';
|
||||||
|
|
||||||
function Album() {
|
function Album() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [albums, setAlbums] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
// useQuery로 앨범 데이터 로드
|
||||||
getAlbums()
|
const { data: albums = [], isLoading: loading } = useQuery({
|
||||||
.then(data => {
|
queryKey: ['albums'],
|
||||||
setAlbums(data);
|
queryFn: getAlbums,
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('앨범 데이터 로드 오류:', error);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 타이틀곡 찾기
|
// 타이틀곡 찾기
|
||||||
const getTitleTrack = (tracks) => {
|
const getTitleTrack = (tracks) => {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from "react";
|
||||||
import { motion } from 'framer-motion';
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Link } from 'react-router-dom';
|
import { motion } from "framer-motion";
|
||||||
import { Calendar, ArrowRight, Clock, Link2, Tag } from 'lucide-react';
|
import { Link } from "react-router-dom";
|
||||||
import { getTodayKST } from '../../../utils/date';
|
import { Calendar, ArrowRight, Clock, Link2, Tag } from "lucide-react";
|
||||||
import { getMembers } from '../../../api/public/members';
|
import { getTodayKST } from "../../../utils/date";
|
||||||
import { getUpcomingSchedules } from '../../../api/public/schedules';
|
import { getMembers } from "../../../api/public/members";
|
||||||
|
import { getUpcomingSchedules } from "../../../api/public/schedules";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
const [members, setMembers] = useState([]);
|
// useQuery로 멤버 데이터 로드
|
||||||
const [upcomingSchedules, setUpcomingSchedules] = useState([]);
|
const { data: members = [] } = useQuery({
|
||||||
|
queryKey: ["members"],
|
||||||
|
queryFn: getMembers,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
// useQuery로 다가오는 일정 로드 (오늘 이후 3개)
|
||||||
// 멤버 데이터 로드
|
const { data: upcomingSchedules = [] } = useQuery({
|
||||||
getMembers()
|
queryKey: ["upcomingSchedules", 3],
|
||||||
.then(data => setMembers(data))
|
queryFn: () => getUpcomingSchedules(3),
|
||||||
.catch(error => console.error('멤버 데이터 로드 오류:', error));
|
});
|
||||||
|
|
||||||
// 다가오는 일정 로드 (오늘 이후 3개)
|
|
||||||
getUpcomingSchedules(3)
|
|
||||||
.then(data => setUpcomingSchedules(data))
|
|
||||||
.catch(error => console.error('일정 데이터 로드 오류:', error));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -37,8 +35,10 @@ function Home() {
|
||||||
<h1 className="text-6xl font-bold mb-4">fromis_9</h1>
|
<h1 className="text-6xl font-bold mb-4">fromis_9</h1>
|
||||||
<p className="text-2xl font-light mb-2">프로미스나인</p>
|
<p className="text-2xl font-light mb-2">프로미스나인</p>
|
||||||
<p className="text-lg opacity-80 mb-8 leading-relaxed">
|
<p className="text-lg opacity-80 mb-8 leading-relaxed">
|
||||||
인사드리겠습니다. 둘, 셋!<br />
|
인사드리겠습니다. 둘, 셋!
|
||||||
이제는 약속해 소중히 간직해,<br />
|
<br />
|
||||||
|
이제는 약속해 소중히 간직해,
|
||||||
|
<br />
|
||||||
당신의 아이돌로 성장하겠습니다!
|
당신의 아이돌로 성장하겠습니다!
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
|
|
@ -68,20 +68,32 @@ function Home() {
|
||||||
viewport={{ once: true, amount: 0.1 }}
|
viewport={{ once: true, amount: 0.1 }}
|
||||||
variants={{
|
variants={{
|
||||||
hidden: { opacity: 1 },
|
hidden: { opacity: 1 },
|
||||||
visible: { opacity: 1, transition: { staggerChildren: 0.1 } }
|
visible: { opacity: 1, transition: { staggerChildren: 0.1 } },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
{ value: "2018.01.24", label: "데뷔일" },
|
{ value: "2018.01.24", label: "데뷔일" },
|
||||||
{ value: `D+${(Math.floor((new Date() - new Date('2018-01-24')) / (1000 * 60 * 60 * 24)) + 1).toLocaleString()}`, label: "D+Day" },
|
{
|
||||||
|
value: `D+${(
|
||||||
|
Math.floor(
|
||||||
|
(new Date() - new Date("2018-01-24")) /
|
||||||
|
(1000 * 60 * 60 * 24)
|
||||||
|
) + 1
|
||||||
|
).toLocaleString()}`,
|
||||||
|
label: "D+Day",
|
||||||
|
},
|
||||||
{ value: "5", label: "멤버 수" },
|
{ value: "5", label: "멤버 수" },
|
||||||
{ value: "flover", label: "팬덤명" }
|
{ value: "flover", label: "팬덤명" },
|
||||||
].map((stat, index) => (
|
].map((stat, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={index}
|
||||||
variants={{
|
variants={{
|
||||||
hidden: { opacity: 0, y: 20 },
|
hidden: { opacity: 0, y: 20 },
|
||||||
visible: { opacity: 1, y: 0, transition: { duration: 0.4, ease: "easeOut" } }
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: { duration: 0.4, ease: "easeOut" },
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
className="bg-gradient-to-br from-primary to-primary-dark rounded-2xl p-6 text-white text-center"
|
className="bg-gradient-to-br from-primary to-primary-dark rounded-2xl p-6 text-white text-center"
|
||||||
>
|
>
|
||||||
|
|
@ -98,17 +110,26 @@ function Home() {
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
<div className="max-w-7xl mx-auto px-6">
|
||||||
<div className="flex justify-between items-center mb-8">
|
<div className="flex justify-between items-center mb-8">
|
||||||
<h2 className="text-3xl font-bold">멤버</h2>
|
<h2 className="text-3xl font-bold">멤버</h2>
|
||||||
<Link to="/members" className="text-primary hover:underline flex items-center gap-1">
|
<Link
|
||||||
|
to="/members"
|
||||||
|
className="text-primary hover:underline flex items-center gap-1"
|
||||||
|
>
|
||||||
전체보기 <ArrowRight size={16} />
|
전체보기 <ArrowRight size={16} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-5 gap-6">
|
<div className="grid grid-cols-5 gap-6">
|
||||||
{members.filter(m => !m.is_former).map((member, index) => (
|
{members
|
||||||
|
.filter((m) => !m.is_former)
|
||||||
|
.map((member, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={member.id}
|
key={member.id}
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.3 + index * 0.1, duration: 0.5, ease: "easeOut" }}
|
transition={{
|
||||||
|
delay: 0.3 + index * 0.1,
|
||||||
|
duration: 0.5,
|
||||||
|
ease: "easeOut",
|
||||||
|
}}
|
||||||
className="group relative rounded-2xl overflow-hidden shadow-sm hover:shadow-xl transition-all duration-300"
|
className="group relative rounded-2xl overflow-hidden shadow-sm hover:shadow-xl transition-all duration-300"
|
||||||
>
|
>
|
||||||
{/* 이미지 컨테이너 */}
|
{/* 이미지 컨테이너 */}
|
||||||
|
|
@ -125,7 +146,9 @@ function Home() {
|
||||||
|
|
||||||
{/* 멤버 정보 */}
|
{/* 멤버 정보 */}
|
||||||
<div className="absolute bottom-0 left-0 right-0 p-5 text-white">
|
<div className="absolute bottom-0 left-0 right-0 p-5 text-white">
|
||||||
<h3 className="font-bold text-xl drop-shadow-lg">{member.name}</h3>
|
<h3 className="font-bold text-xl drop-shadow-lg">
|
||||||
|
{member.name}
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -138,7 +161,10 @@ function Home() {
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
<div className="max-w-7xl mx-auto px-6">
|
||||||
<div className="flex justify-between items-center mb-8">
|
<div className="flex justify-between items-center mb-8">
|
||||||
<h2 className="text-3xl font-bold">다가오는 일정</h2>
|
<h2 className="text-3xl font-bold">다가오는 일정</h2>
|
||||||
<Link to="/schedule" className="text-primary hover:underline flex items-center gap-1">
|
<Link
|
||||||
|
to="/schedule"
|
||||||
|
className="text-primary hover:underline flex items-center gap-1"
|
||||||
|
>
|
||||||
전체보기 <ArrowRight size={16} />
|
전체보기 <ArrowRight size={16} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -155,7 +181,7 @@ function Home() {
|
||||||
viewport={{ once: true, amount: 0.1 }}
|
viewport={{ once: true, amount: 0.1 }}
|
||||||
variants={{
|
variants={{
|
||||||
hidden: { opacity: 1 },
|
hidden: { opacity: 1 },
|
||||||
visible: { opacity: 1, transition: { staggerChildren: 0.1 } }
|
visible: { opacity: 1, transition: { staggerChildren: 0.1 } },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{upcomingSchedules.map((schedule) => {
|
{upcomingSchedules.map((schedule) => {
|
||||||
|
|
@ -167,22 +193,30 @@ function Home() {
|
||||||
const scheduleYear = scheduleDate.getFullYear();
|
const scheduleYear = scheduleDate.getFullYear();
|
||||||
const scheduleMonth = scheduleDate.getMonth();
|
const scheduleMonth = scheduleDate.getMonth();
|
||||||
const isCurrentYear = scheduleYear === currentYear;
|
const isCurrentYear = scheduleYear === currentYear;
|
||||||
const isCurrentMonth = isCurrentYear && scheduleMonth === currentMonth;
|
const isCurrentMonth =
|
||||||
|
isCurrentYear && scheduleMonth === currentMonth;
|
||||||
|
|
||||||
const day = scheduleDate.getDate();
|
const day = scheduleDate.getDate();
|
||||||
const weekdays = ['일', '월', '화', '수', '목', '금', '토'];
|
const weekdays = ["일", "월", "화", "수", "목", "금", "토"];
|
||||||
const weekday = weekdays[scheduleDate.getDay()];
|
const weekday = weekdays[scheduleDate.getDay()];
|
||||||
|
|
||||||
// 멤버 처리
|
// 멤버 처리
|
||||||
const memberList = schedule.member_names ? schedule.member_names.split(',') : [];
|
const memberList = schedule.member_names
|
||||||
const displayMembers = memberList.length >= 5 ? ['프로미스나인'] : memberList;
|
? schedule.member_names.split(",")
|
||||||
|
: [];
|
||||||
|
const displayMembers =
|
||||||
|
memberList.length >= 5 ? ["프로미스나인"] : memberList;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={schedule.id}
|
key={schedule.id}
|
||||||
variants={{
|
variants={{
|
||||||
hidden: { opacity: 0, x: -30 },
|
hidden: { opacity: 0, x: -30 },
|
||||||
visible: { opacity: 1, x: 0, transition: { duration: 0.4, ease: "easeOut" } }
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: { duration: 0.4, ease: "easeOut" },
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
className="flex items-stretch bg-white rounded-2xl shadow-sm hover:shadow-md transition-shadow overflow-hidden"
|
className="flex items-stretch bg-white rounded-2xl shadow-sm hover:shadow-md transition-shadow overflow-hidden"
|
||||||
>
|
>
|
||||||
|
|
@ -201,29 +235,42 @@ function Home() {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className="text-3xl font-bold">{day}</span>
|
<span className="text-3xl font-bold">{day}</span>
|
||||||
<span className="text-sm font-medium opacity-80">{weekday}</span>
|
<span className="text-sm font-medium opacity-80">
|
||||||
|
{weekday}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 내용 영역 */}
|
{/* 내용 영역 */}
|
||||||
<div className="flex-1 p-5 flex flex-col justify-center">
|
<div className="flex-1 p-5 flex flex-col justify-center">
|
||||||
<h3 className="font-bold text-lg text-gray-900 mb-2">{schedule.title}</h3>
|
<h3 className="font-bold text-lg text-gray-900 mb-2">
|
||||||
|
{schedule.title}
|
||||||
|
</h3>
|
||||||
|
|
||||||
<div className="flex flex-wrap items-center gap-3 text-sm text-gray-500">
|
<div className="flex flex-wrap items-center gap-3 text-sm text-gray-500">
|
||||||
{schedule.time && (
|
{schedule.time && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Clock size={14} className="text-primary opacity-60" />
|
<Clock
|
||||||
|
size={14}
|
||||||
|
className="text-primary opacity-60"
|
||||||
|
/>
|
||||||
<span>{schedule.time.slice(0, 5)}</span>
|
<span>{schedule.time.slice(0, 5)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{schedule.category_name && (
|
{schedule.category_name && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Tag size={14} className="text-primary opacity-60" />
|
<Tag
|
||||||
|
size={14}
|
||||||
|
className="text-primary opacity-60"
|
||||||
|
/>
|
||||||
<span>{schedule.category_name}</span>
|
<span>{schedule.category_name}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{schedule.source_name && (
|
{schedule.source_name && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Link2 size={14} className="text-primary opacity-60" />
|
<Link2
|
||||||
|
size={14}
|
||||||
|
className="text-primary opacity-60"
|
||||||
|
/>
|
||||||
<span>{schedule.source_name}</span>
|
<span>{schedule.source_name}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -248,8 +295,6 @@ function Home() {
|
||||||
})}
|
})}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -257,4 +302,3 @@ function Home() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,16 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Instagram, Calendar } from 'lucide-react';
|
import { Instagram, Calendar } from 'lucide-react';
|
||||||
import { getMembers } from '../../../api/public/members';
|
import { getMembers } from '../../../api/public/members';
|
||||||
import { formatDate } from '../../../utils/date';
|
import { formatDate } from '../../../utils/date';
|
||||||
|
|
||||||
function Members() {
|
function Members() {
|
||||||
const [members, setMembers] = useState([]);
|
// useQuery로 멤버 데이터 로드
|
||||||
const [loading, setLoading] = useState(true);
|
const { data: members = [], isLoading: loading } = useQuery({
|
||||||
|
queryKey: ['members'],
|
||||||
useEffect(() => {
|
queryFn: getMembers,
|
||||||
getMembers()
|
|
||||||
.then(data => {
|
|
||||||
setMembers(data);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('데이터 로드 오류:', error);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue