diff --git a/backend/routes/albums.js b/backend/routes/albums.js new file mode 100644 index 0000000..b5ea92c --- /dev/null +++ b/backend/routes/albums.js @@ -0,0 +1,57 @@ +import express from "express"; +import pool from "../lib/db.js"; + +const router = express.Router(); + +// 전체 앨범 조회 (트랙 포함) +router.get("/", async (req, res) => { + try { + // 앨범 목록 조회 + const [albums] = await pool.query( + "SELECT id, title, album_type, release_date, cover_url FROM albums ORDER BY release_date DESC" + ); + + // 각 앨범에 트랙 정보 추가 + for (const album of albums) { + const [tracks] = await pool.query( + "SELECT id, track_number, title, is_title_track, duration, lyricist, composer, arranger FROM tracks WHERE album_id = ? ORDER BY track_number", + [album.id] + ); + album.tracks = tracks; + } + + res.json(albums); + } catch (error) { + console.error("앨범 조회 오류:", error); + res.status(500).json({ error: "앨범 정보를 가져오는데 실패했습니다." }); + } +}); + +// 특정 앨범 조회 (트랙 및 상세 정보 포함) +router.get("/:id", async (req, res) => { + try { + const [albums] = await pool.query("SELECT * FROM albums WHERE id = ?", [ + req.params.id, + ]); + + if (albums.length === 0) { + return res.status(404).json({ error: "앨범을 찾을 수 없습니다." }); + } + + const album = albums[0]; + + // 트랙 정보 조회 (가사 포함) + const [tracks] = await pool.query( + "SELECT * FROM tracks WHERE album_id = ? ORDER BY track_number", + [album.id] + ); + album.tracks = tracks; + + res.json(album); + } catch (error) { + console.error("앨범 조회 오류:", error); + res.status(500).json({ error: "앨범 정보를 가져오는데 실패했습니다." }); + } +}); + +export default router; diff --git a/backend/routes/stats.js b/backend/routes/stats.js new file mode 100644 index 0000000..30af305 --- /dev/null +++ b/backend/routes/stats.js @@ -0,0 +1,28 @@ +import express from "express"; +import pool from "../lib/db.js"; + +const router = express.Router(); + +// 통계 조회 (멤버 수, 앨범 수) +router.get("/", async (req, res) => { + try { + const [memberCount] = await pool.query( + "SELECT COUNT(*) as count FROM members" + ); + const [albumCount] = await pool.query( + "SELECT COUNT(*) as count FROM albums" + ); + + res.json({ + memberCount: memberCount[0].count, + albumCount: albumCount[0].count, + debutYear: 2018, + fandomName: "flover", + }); + } catch (error) { + console.error("통계 조회 오류:", error); + res.status(500).json({ error: "통계 정보를 가져오는데 실패했습니다." }); + } +}); + +export default router; diff --git a/backend/server.js b/backend/server.js index 8ac2ea1..b2490a9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2,6 +2,8 @@ import express from "express"; import path from "path"; import { fileURLToPath } from "url"; import membersRouter from "./routes/members.js"; +import albumsRouter from "./routes/albums.js"; +import statsRouter from "./routes/stats.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -21,6 +23,8 @@ app.get("/api/health", (req, res) => { }); app.use("/api/members", membersRouter); +app.use("/api/albums", albumsRouter); +app.use("/api/stats", statsRouter); // SPA 폴백 - 모든 요청을 index.html로 app.get("*", (req, res) => { diff --git a/frontend/src/pages/pc/Discography.jsx b/frontend/src/pages/pc/Discography.jsx index 0ef4276..003ca5f 100644 --- a/frontend/src/pages/pc/Discography.jsx +++ b/frontend/src/pages/pc/Discography.jsx @@ -1,8 +1,54 @@ +import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Calendar, Music } from 'lucide-react'; -import { albums } from '../../data/dummy'; function Discography() { + const [albums, setAlbums] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch('/api/albums') + .then(res => res.json()) + .then(data => { + setAlbums(data); + setLoading(false); + }) + .catch(error => { + console.error('앨범 데이터 로드 오류:', error); + setLoading(false); + }); + }, []); + + // 날짜 포맷팅 + const formatDate = (dateStr) => { + if (!dateStr) return ''; + const date = new Date(dateStr); + return `${date.getFullYear()}.${String(date.getMonth() + 1).padStart(2, '0')}.${String(date.getDate()).padStart(2, '0')}`; + }; + + // 타이틀곡 찾기 + const getTitleTrack = (tracks) => { + if (!tracks || tracks.length === 0) return ''; + const titleTrack = tracks.find(t => t.is_title_track); + return titleTrack ? titleTrack.title : tracks[0].title; + }; + + // 앨범 타입별 개수 계산 + const albumStats = { + 정규: albums.filter(a => a.album_type === '정규').length, + 미니: albums.filter(a => a.album_type === '미니').length, + 싱글: albums.filter(a => a.album_type === '싱글').length, + 총: albums.length + }; + + if (loading) { + return ( +
앨범 상세보기
+{album.tracks?.length || 0}곡 수록
- {album.titleTrack} + {getTitleTrack(album.tracks)}
1
+{albumStats.정규}
정규 앨범
2
+{albumStats.미니}
미니 앨범
1
+{albumStats.싱글}
싱글 앨범
4+
+{albumStats.총}
총 앨범
{member.position}
+{member.position || '\u00A0'}
2018
+{stats.debutYear}
데뷔 연도
5
+{stats.memberCount}
멤버 수
7+
+{stats.albumCount}
앨범 수
flover
+{stats.fandomName}
팬덤명