refactor: 모든 Admin 페이지 API 모듈화 완료

- AdminMemberEdit API 모듈 적용
- AdminScheduleForm API 모듈 적용
- AdminAlbumPhotos API 모듈 적용 (업로드는 SSE 유지)
- api/admin/albums.js에 getAlbumTeasers, deleteAlbumTeaser 추가

총 적용 완료:
- Public 페이지: 7개
- Admin 페이지: 10개 전체 완료
This commit is contained in:
caadiq 2026-01-09 22:26:37 +09:00
parent f006309ef4
commit 8124a1abe1
4 changed files with 49 additions and 97 deletions

View file

@ -48,3 +48,15 @@ export async function deleteAlbumPhoto(albumId, photoId) {
method: "DELETE", method: "DELETE",
}); });
} }
// 앨범 티저 목록 조회
export async function getAlbumTeasers(albumId) {
return fetchAdminApi(`/api/admin/albums/${albumId}/teasers`);
}
// 앨범 티저 삭제
export async function deleteAlbumTeaser(albumId, teaserId) {
return fetchAdminApi(`/api/admin/albums/${albumId}/teasers/${teaserId}`, {
method: "DELETE",
});
}

View file

@ -8,6 +8,10 @@ import {
Tag, FolderOpen, Save Tag, FolderOpen, Save
} from 'lucide-react'; } from 'lucide-react';
import Toast from '../../../components/Toast'; import Toast from '../../../components/Toast';
import * as authApi from '../../../api/admin/auth';
import { getAlbum } from '../../../api/public/albums';
import { getMembers } from '../../../api/public/members';
import * as albumsApi from '../../../api/admin/albums';
function AdminAlbumPhotos() { function AdminAlbumPhotos() {
const { albumId } = useParams(); const { albumId } = useParams();
@ -145,67 +149,47 @@ function AdminAlbumPhotos() {
useEffect(() => { useEffect(() => {
// //
const token = localStorage.getItem('adminToken'); if (!authApi.hasToken()) {
const userData = localStorage.getItem('adminUser');
if (!token || !userData) {
navigate('/admin'); navigate('/admin');
return; return;
} }
setUser(JSON.parse(userData)); setUser(authApi.getCurrentUser());
fetchAlbumData(); fetchAlbumData();
}, [navigate, albumId]); }, [navigate, albumId]);
const fetchAlbumData = async () => { const fetchAlbumData = async () => {
try { try {
const token = localStorage.getItem('adminToken');
// //
const albumRes = await fetch(`/api/albums/${albumId}`); const albumData = await getAlbum(albumId);
if (!albumRes.ok) throw new Error('앨범을 찾을 수 없습니다');
const albumData = await albumRes.json();
setAlbum(albumData); setAlbum(albumData);
// //
const membersRes = await fetch('/api/members'); try {
if (membersRes.ok) { const membersData = await getMembers();
const membersData = await membersRes.json();
setMembers(membersData); setMembers(membersData);
} } catch (e) { /* 무시 */ }
// //
const photosRes = await fetch(`/api/admin/albums/${albumId}/photos`, {
headers: { 'Authorization': `Bearer ${token}` }
});
let photosData = []; let photosData = [];
if (photosRes.ok) { try {
photosData = await photosRes.json(); photosData = await albumsApi.getAlbumPhotos(albumId);
setPhotos(photosData); setPhotos(photosData);
} } catch (e) { /* 무시 */ }
// //
const teasersRes = await fetch(`/api/admin/albums/${albumId}/teasers`, {
headers: { 'Authorization': `Bearer ${token}` }
});
let teasersData = []; let teasersData = [];
if (teasersRes.ok) { try {
teasersData = await teasersRes.json(); teasersData = await albumsApi.getAlbumTeasers(albumId);
setTeasers(teasersData); setTeasers(teasersData);
} } catch (e) { /* 무시 */ }
// ( + 1) //
// ,
const maxPhotoOrder = photosData.length > 0 const maxPhotoOrder = photosData.length > 0
? Math.max(...photosData.map(p => p.sort_order || 0)) ? Math.max(...photosData.map(p => p.sort_order || 0))
: 0; : 0;
const maxTeaserOrder = teasersData.length > 0
? Math.max(...teasersData.map(t => t.sort_order || 0))
: 0;
//
setStartNumber(maxPhotoOrder + 1); setStartNumber(maxPhotoOrder + 1);
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {
console.error('앨범 로드 오류:', error); console.error('앨범 로드 오류:', error);
@ -230,8 +214,7 @@ function AdminAlbumPhotos() {
}, [photoType, photos, teasers]); }, [photoType, photos, teasers]);
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem('adminToken'); authApi.logout();
localStorage.removeItem('adminUser');
navigate('/admin'); navigate('/admin');
}; };
@ -504,7 +487,6 @@ function AdminAlbumPhotos() {
// ( /) // ( /)
const handleDelete = async () => { const handleDelete = async () => {
setDeleting(true); setDeleting(true);
const token = localStorage.getItem('adminToken');
try { try {
// ID ID // ID ID
@ -515,24 +497,12 @@ function AdminAlbumPhotos() {
// //
for (const photoId of photoIds) { for (const photoId of photoIds) {
const res = await fetch(`/api/admin/albums/${albumId}/photos/${photoId}`, { await albumsApi.deleteAlbumPhoto(albumId, photoId);
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) {
throw new Error('사진 삭제 실패');
}
} }
// //
for (const teaserId of teaserIds) { for (const teaserId of teaserIds) {
const res = await fetch(`/api/admin/albums/${albumId}/teasers/${teaserId}`, { await albumsApi.deleteAlbumTeaser(albumId, teaserId);
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) {
throw new Error('티저 삭제 실패');
}
} }
// UI // UI

View file

@ -6,6 +6,8 @@ import {
Home, ChevronRight, ChevronLeft, ChevronDown, User, Instagram, Calendar, Briefcase Home, ChevronRight, ChevronLeft, ChevronDown, User, Instagram, Calendar, Briefcase
} from 'lucide-react'; } from 'lucide-react';
import Toast from '../../../components/Toast'; import Toast from '../../../components/Toast';
import * as authApi from '../../../api/admin/auth';
import * as membersApi from '../../../api/admin/members';
// //
function CustomDatePicker({ value, onChange }) { function CustomDatePicker({ value, onChange }) {
@ -272,28 +274,18 @@ function AdminMemberEdit() {
useEffect(() => { useEffect(() => {
// //
const token = localStorage.getItem('adminToken'); if (!authApi.hasToken()) {
const userData = localStorage.getItem('adminUser');
if (!token || !userData) {
navigate('/admin'); navigate('/admin');
return; return;
} }
setUser(JSON.parse(userData)); setUser(authApi.getCurrentUser());
fetchMember(); fetchMember();
}, [navigate, name]); }, [navigate, name]);
const fetchMember = async () => { const fetchMember = async () => {
try { try {
const token = localStorage.getItem('adminToken'); const data = await membersApi.getMember(encodeURIComponent(name));
const res = await fetch(`/api/admin/members/${encodeURIComponent(name)}`, {
headers: { Authorization: `Bearer ${token}` }
});
if (!res.ok) throw new Error('멤버 조회 실패');
const data = await res.json();
setFormData({ setFormData({
name: data.name || '', name: data.name || '',
birth_date: data.birth_date ? data.birth_date.split('T')[0] : '', birth_date: data.birth_date ? data.birth_date.split('T')[0] : '',
@ -311,8 +303,7 @@ function AdminMemberEdit() {
}; };
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem('adminToken'); authApi.logout();
localStorage.removeItem('adminUser');
navigate('/admin'); navigate('/admin');
}; };
@ -331,7 +322,6 @@ function AdminMemberEdit() {
setSaving(true); setSaving(true);
try { try {
const token = localStorage.getItem('adminToken');
const formDataToSend = new FormData(); const formDataToSend = new FormData();
formDataToSend.append('name', formData.name); formDataToSend.append('name', formData.name);
@ -344,14 +334,7 @@ function AdminMemberEdit() {
formDataToSend.append('image', imageFile); formDataToSend.append('image', imageFile);
} }
const res = await fetch(`/api/admin/members/${encodeURIComponent(name)}`, { await membersApi.updateMember(encodeURIComponent(name), formDataToSend);
method: 'PUT',
headers: { Authorization: `Bearer ${token}` },
body: formDataToSend
});
if (!res.ok) throw new Error('수정 실패');
setToast({ message: '멤버 정보가 수정되었습니다.', type: 'success' }); setToast({ message: '멤버 정보가 수정되었습니다.', type: 'success' });
setTimeout(() => navigate('/admin/members'), 1000); setTimeout(() => navigate('/admin/members'), 1000);
} catch (error) { } catch (error) {

View file

@ -26,6 +26,10 @@ import {
} from "lucide-react"; } from "lucide-react";
import Toast from "../../../components/Toast"; import Toast from "../../../components/Toast";
import Lightbox from "../../../components/common/Lightbox"; import Lightbox from "../../../components/common/Lightbox";
import * as authApi from "../../../api/admin/auth";
import * as categoriesApi from "../../../api/admin/categories";
import * as schedulesApi from "../../../api/admin/schedules";
import { getMembers } from "../../../api/public/members";
// (AdminMemberEdit.jsx ) // (AdminMemberEdit.jsx )
function CustomDatePicker({ value, onChange, placeholder = "날짜 선택" }) { function CustomDatePicker({ value, onChange, placeholder = "날짜 선택" }) {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -840,8 +844,7 @@ function AdminScheduleForm() {
// //
const fetchCategories = async () => { const fetchCategories = async () => {
try { try {
const res = await fetch("/api/admin/schedule-categories"); const data = await categoriesApi.getCategories();
const data = await res.json();
setCategories(data); setCategories(data);
// //
if (data.length > 0 && !formData.category) { if (data.length > 0 && !formData.category) {
@ -861,15 +864,12 @@ function AdminScheduleForm() {
}, [toast]); }, [toast]);
useEffect(() => { useEffect(() => {
const token = localStorage.getItem("adminToken"); if (!authApi.hasToken()) {
const userData = localStorage.getItem("adminUser");
if (!token || !userData) {
navigate("/admin"); navigate("/admin");
return; return;
} }
setUser(JSON.parse(userData)); setUser(authApi.getCurrentUser());
fetchMembers(); fetchMembers();
fetchCategories(); fetchCategories();
@ -883,18 +883,7 @@ function AdminScheduleForm() {
const fetchSchedule = async () => { const fetchSchedule = async () => {
setLoading(true); setLoading(true);
try { try {
const token = localStorage.getItem("adminToken"); const data = await schedulesApi.getSchedule(id);
const res = await fetch(`/api/admin/schedules/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!res.ok) {
throw new Error("일정을 찾을 수 없습니다.");
}
const data = await res.json();
// //
setFormData({ setFormData({
@ -957,8 +946,7 @@ function AdminScheduleForm() {
const fetchMembers = async () => { const fetchMembers = async () => {
try { try {
const res = await fetch("/api/members"); const data = await getMembers();
const data = await res.json();
setMembers(data.filter((m) => !m.is_former)); setMembers(data.filter((m) => !m.is_former));
} catch (error) { } catch (error) {
console.error("멤버 로드 오류:", error); console.error("멤버 로드 오류:", error);
@ -966,8 +954,7 @@ function AdminScheduleForm() {
}; };
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem("adminToken"); authApi.logout();
localStorage.removeItem("adminUser");
navigate("/admin"); navigate("/admin");
}; };