2024. 1. 15. 23:39ㆍReact 정리
React와 Express를 활용한 사진 앨범 만들기
안녕하세요. 오늘은 React와 Express를 활용하여 간단한 사진 앨범 웹 어플리케이션을 만드는 방법에 대해 자세히 알아보겠습니다.
1. 프로젝트 구조
프로젝트는 크게 두 부분으로 나뉘어져 있습니다. 프론트엔드는 React를 사용하며, 백엔드는 Express.js와 MySQL을 사용합니다.
프론트엔드는 다음과 같은 구조로 이루어져 있습니다:
App.js: 라우팅을 담당하는 메인 컴포넌트입니다.
pages: 각 페이지 컴포넌트를 담고 있습니다. (Home, MyPage, UploadPage 등)
components: 재사용 가능한 컴포넌트들을 담고 있습니다.
백엔드는 Express.js를 사용하여 API를 구축하며, 데이터는 MySQL 데이터베이스에 저장됩니다.
2. 코드 분석
2.1 App.js
이 파일은 React 앱의 진입점입니다. 이곳에서 react-router-dom을 사용하여 라우팅을 설정합니다. 각 경로에 대한 페이지를 매핑하여, 사용자가 특정 URL을 방문했을 때 어떤 컴포넌트를 보여줄지 결정합니다.
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Header from "./components/Header";
import Home from "./pages/Home";
import MyPage from "./pages/MyPage";
import MapPoto from "./pages/MapPoto"; // MapPoto import
import UploadPage from "./pages/UploadPage"; // UploadPage import
function App() {
return (
<BrowserRouter>
<Header />
<Routes>
<Route index element={<Home />} />
<Route path="mypage" element={<MyPage />} />
<Route path="upload" element={<UploadPage />} />
<Route path="MapPoto" element={<MapPoto />} />
</Routes>
</BrowserRouter>
);
}
export default App;
2.2 Home.js
이 페이지는 사용자에게 사진을 업로드할 수 있는 버튼을 제공합니다. useNavigate 훅을 사용하여 버튼 클릭 시 MapPoto 페이지로 이동하는 함수를 만듭니다.
import React from "react";
import { useNavigate } from "react-router-dom";
import styled, { keyframes } from "styled-components";
import mapImage from "../components/images/mapimg.png";
const imageAppear = keyframes`
0% {
opacity: 0;
}
100% {
opacity: 1;
}
`;
const textAppear = keyframes`
0% {
transform: scale(0.8);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
`;
const buttonGlow = keyframes`
0% {
box-shadow: 0 0 5px #333;
}
50% {
box-shadow: 0 0 20px #333, 0 0 30px #333;
}
100% {
box-shadow: 0 0 5px #333;
}
`;
const Home = () => {
const navigate = useNavigate();
const handleButtonClick = () => {
navigate("/MapPoto");
};
return (
<HeroContainer>
<HeroContent>
<HeroButton onClick={handleButtonClick}>사진 저장함</HeroButton>
</HeroContent>
</HeroContainer>
);
};
const HeroContainer = styled.section`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-image: url(${mapImage});
background-size: 50%;
background-position: center;
background-repeat: no-repeat;
animation: ${imageAppear} 3s ease-in-out forwards; // 배경 이미지에 효과를 추가합니다.
`;
const HeroContent = styled.div`
text-align: center;
animation: ${textAppear} 5s ease-in-out forwards;
`;
const HeroButton = styled.button`
padding: 0.8rem 2rem;
font-size: 1.1rem;
color: white;
background-color: #333;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
animation: ${buttonGlow} 2s infinite;
&:hover {
background-color: #777;
}
`;
export default Home;
2.3 MyPage.js
이 페이지는 사용자가 업로드한 사진들을 보여줍니다. useState와 useEffect 훅을 사용하여 백엔드 서버로부터 사진 데이터를 받아와 상태로 관리합니다.
import React, { useState, useEffect } from "react";
import axios from "axios";
import styled from "styled-components";
const Image = styled.img`
margin: 10px;
width: 200px;
height: 200px;
`;
function MyPage() {
const [photos, setPhotos] = useState([]);
useEffect(() => {
axios
.get("http://localhost:5000/photos")
.then((response) => {
setPhotos(response.data);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div>
{photos.map((photo) => (
<Image
key={photo.id}
src={photo.url} // URL이 완전한 형태로 저장되어 있다고 가정
alt="사진"
/>
))}
</div>
);
}
export default MyPage;
2.4 Express 서버
Express 서버는 API를 제공하며, 사진을 업로드하고 저장하는 데 사용됩니다. multer 미들웨어를 사용하여 파일 업로드를 처리하며, cors 미들웨어를 사용하여 CORS 문제를 해결합니다.
import express from "express";
import multer from "multer";
import cors from "cors";
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";
import mysql from "mysql2/promise";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = express();
const port = 5000;
app.use(cors());
app.use("/uploads", express.static(path.join(__dirname, "uploads")));
app.get("/", (req, res) => {
res.send("Hello World!");
});
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname)); // appending extension
},
});
const upload = multer({ storage: storage });
const pool = mysql.createPool({
host: "localhost",
user: "root",
password: "",
database: "photo_album",
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
app.post("/upload", upload.array("photo"), async (req, res) => {
if (!req.files) {
return res.status(400).send("No files were uploaded.");
}
try {
const insertPromises = req.files.map((file) => {
const url = `./uploads/${file.filename}`;
return pool.query(
"INSERT INTO photos (upload_date, file_name, file_path, url) VALUES (NOW(), ?, ?, ?)",
[file.originalname, `/uploads/${file.originalname}`, url]
);
});
await Promise.all(insertPromises);
res.send(req.files.map((file) => ({ url: `/uploads/${file.filename}` })));
} catch (err) {
console.error(err);
res.status(500).send("Server Error");
}
});
app.get("/photos", async (req, res) => {
try {
const [rows] = await pool.query("SELECT * FROM photos");
res.send(rows);
} catch (err) {
console.error(err);
res.status(500).send("Server Error");
}
});
app.listen(port, () => {
console.log(`Server is listening at http://localhost:${port}`);
});
2.5 Vite 설정
Vite는 프론트엔드 개발 환경을 구성하는 데 사용됩니다. 여기서는 프록시 설정을 통해 백엔드 API에 접근하도록 설정되어 있습니다.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
"/api": "http://localhost:5000",
},
},
});
db.sql
# DB 생성
DROP DATABASE IF EXISTS photo_album;
CREATE DATABASE photo_album;
USE photo_album;
# 테이블 생성
CREATE TABLE photos (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
upload_date DATETIME NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_path VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL -- 'url' 컬럼 추가
);
# 데이터 생성 예시
INSERT INTO photos
SET upload_date = NOW(),
file_name = 'image01.jpg',
file_path = '/uploads/image01.jpg',
url = 'http://localhost:5000/uploads/image01.jpg'; -- 'url' 값 추가
INSERT INTO photos
SET upload_date = NOW(),
file_name = 'image02.jpg',
file_path = '/uploads/image02.jpg',
url = 'http://localhost:5000/uploads/image02.jpg'; -- 'url' 값 추가
INSERT INTO photos
SET upload_date = NOW(),
file_name = 'image03.jpg',
file_path = '/uploads/image03.jpg',
url = 'http://localhost:5000/uploads/image03.jpg'; -- 'url' 값 추가
'React 정리' 카테고리의 다른 글
나만의 레시피 기록 사이트 만들기(프론트+ 백엔드) (0) | 2024.02.03 |
---|---|
React로 리뷰사이트 만들기(프론트 + 백엔드) (0) | 2024.01.22 |
React - fly.io로 pg 생성후 todo 만들기 (0) | 2024.01.19 |
React로 Todo 만들기 (0) | 2024.01.15 |
React 란? (0) | 2023.08.28 |