diff --git a/backend/lib/db.js b/backend/lib/db.js new file mode 100644 index 0000000..0749f70 --- /dev/null +++ b/backend/lib/db.js @@ -0,0 +1,15 @@ +import mysql from "mysql2/promise"; + +// MariaDB 연결 풀 생성 +const pool = mysql.createPool({ + host: process.env.DB_HOST || "mariadb", + port: parseInt(process.env.DB_PORT) || 3306, + user: process.env.DB_USER || "fromis9", + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME || "fromis9", + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, +}); + +export default pool; diff --git a/backend/routes/members.js b/backend/routes/members.js new file mode 100644 index 0000000..5e91739 --- /dev/null +++ b/backend/routes/members.js @@ -0,0 +1,35 @@ +import express from "express"; +import pool from "../lib/db.js"; + +const router = express.Router(); + +// 전체 멤버 조회 +router.get("/", async (req, res) => { + try { + const [rows] = await pool.query( + "SELECT id, name, name_en, birth_date, position, image_url, instagram FROM members ORDER BY id" + ); + res.json(rows); + } catch (error) { + console.error("멤버 조회 오류:", error); + res.status(500).json({ error: "멤버 정보를 가져오는데 실패했습니다." }); + } +}); + +// 특정 멤버 조회 +router.get("/:id", async (req, res) => { + try { + const [rows] = await pool.query("SELECT * FROM members WHERE id = ?", [ + req.params.id, + ]); + if (rows.length === 0) { + return res.status(404).json({ error: "멤버를 찾을 수 없습니다." }); + } + res.json(rows[0]); + } catch (error) { + console.error("멤버 조회 오류:", error); + res.status(500).json({ error: "멤버 정보를 가져오는데 실패했습니다." }); + } +}); + +export default router; diff --git a/backend/server.js b/backend/server.js index 18a5018..8ac2ea1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,6 +1,7 @@ import express from "express"; import path from "path"; import { fileURLToPath } from "url"; +import membersRouter from "./routes/members.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -14,11 +15,13 @@ app.use(express.json()); // 정적 파일 서빙 (프론트엔드 빌드 결과물) app.use(express.static(path.join(__dirname, "dist"))); -// API 라우트 (추후 구현) +// API 라우트 app.get("/api/health", (req, res) => { res.json({ status: "ok", timestamp: new Date().toISOString() }); }); +app.use("/api/members", membersRouter); + // SPA 폴백 - 모든 요청을 index.html로 app.get("*", (req, res) => { res.sendFile(path.join(__dirname, "dist", "index.html")); diff --git a/frontend/src/pages/pc/Home.jsx b/frontend/src/pages/pc/Home.jsx index 229046f..44a5b0c 100644 --- a/frontend/src/pages/pc/Home.jsx +++ b/frontend/src/pages/pc/Home.jsx @@ -1,9 +1,19 @@ +import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Link } from 'react-router-dom'; import { Calendar, Users, Disc3, ArrowRight } from 'lucide-react'; -import { members, schedules, albums } from '../../data/dummy'; +import { schedules, albums } from '../../data/dummy'; function Home() { + const [members, setMembers] = useState([]); + + useEffect(() => { + fetch('/api/members') + .then(res => res.json()) + .then(data => setMembers(data)) + .catch(error => console.error('멤버 데이터 로드 오류:', error)); + }, []); + return (
{/* 히어로 섹션 */} @@ -92,14 +102,14 @@ function Home() { >
{member.name}

{member.name}

-

{member.position.split(',')[0]}

+

{member.position?.split(',')[0]}

))} diff --git a/frontend/src/pages/pc/Members.jsx b/frontend/src/pages/pc/Members.jsx index f5862d0..8571a63 100644 --- a/frontend/src/pages/pc/Members.jsx +++ b/frontend/src/pages/pc/Members.jsx @@ -1,8 +1,39 @@ +import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Instagram, Calendar } from 'lucide-react'; -import { members } from '../../data/dummy'; function Members() { + const [members, setMembers] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch('/api/members') + .then(res => res.json()) + .then(data => { + setMembers(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')}`; + }; + + if (loading) { + return ( +
+
+
+ ); + } + return (
@@ -39,7 +70,7 @@ function Members() { {/* 이미지 */}
{member.name} @@ -52,7 +83,7 @@ function Members() {
- {member.birthDate} + {formatDate(member.birth_date)}
{/* 인스타그램 링크 */} @@ -106,3 +137,4 @@ function Members() { } export default Members; +