🛠 프로젝트 개요
이번 포스트에서는 Django 백엔드와 React 프론트엔드를 활용하여 이미지의 메타 태그를 제거하는 웹 애플리케이션을 구축하는 과정을 정리합니다. 이전 포스트에서 Django 환경 설정 및 API 개발을 진행했으며, 이번에는 Vite React를 활용한 UI 개발을 진행했습니다.
Django + PostgreSQL 기반 블로그 분석 프로젝트 시작하기
📌 개요이번 글에서는 Django와 PostgreSQL을 활용한 블로그 분석 프로젝트를 시작하는 방법을 다룹니다. 프로젝트 설정부터 환경 구성, 데이터베이스 연결, 기본 API 테스트까지 순차적으로 진행하
migo-dev.tistory.com
📌 개발 목표
✅ 사용자가 다수의 이미지를 업로드 ✅ 업로드한 파일 목록 표시 ✅ Drag & Drop 파일 업로드 기능 지원 ✅ 메타 태그 제거 버튼 클릭 시 백엔드 API 호출 ✅ 변환된 이미지 ZIP 파일 다운로드 기능 추가 ✅ 깔끔하고 직관적인 UI 적용
1️⃣ 프로젝트 구조 및 React 프로젝트 생성 및 설치
📂 폴더 구조
blog_analysis/
├── backend/ # Django 백엔드
│ ├── analysis/ # API 개발
│ ├── config/ # Django 프로젝트 설정
│ ├── manage.py
│ ├── db.sqlite3
│
├── frontend/ # React 프론트엔드
│ ├── src/
│ │ ├── components/ # 공통 UI 컴포넌트
│ │ ├── pages/ # 개별 페이지
│ │ │ ├── MetaCleaner.jsx # 메타 태그 제거 UI
│ │ ├── App.jsx # 전체 라우팅 관리
│ │ ├── main.jsx # React 루트 컴포넌트
│ ├── public/
│ ├── package.json # 프로젝트 설정
│
└── .gitignore # Git 관리 파일
📌 Vite를 활용한 React 프로젝트 생성
React 프로젝트는 빠른 개발 환경을 위해 Vite를 사용하여 생성합니다.
# Django 프로젝트 폴더로 이동
cd ~/Workspace/blog_analysis
# React 프로젝트 생성
npx create-vite@latest frontend --template react
# React 프로젝트 디렉토리로 이동
cd frontend
# 패키지 설치
npm install
📌 React 실행
설치가 완료되면 아래 명령어로 개발 서버를 실행합니다.
npm run dev
브라우저에서 http://localhost:5173/에 접속하여 기본 화면이 보이면 설치가 완료된 것입니다.
2️⃣ 네비게이션 바 추가 (상단 메뉴)
사용자가 쉽게 "메타 태그 삭제" 기능을 사용할 수 있도록 상단 메뉴를 추가했습니다.
📌 📂 src/components/Navbar.jsx
import React from "react";
import { Link } from "react-router-dom";
function Navbar() {
return (
<nav style={styles.navbar}>
<div style={styles.navContainer}>
<h1 style={styles.logo}>🛠 블로그 툴</h1>
<ul style={styles.navLinks}>
<li><Link to="/" style={styles.link}>홈</Link></li>
<li><Link to="/tools/meta-cleaner" style={styles.link}>메타 태그 삭제</Link></li>
<li><Link to="/tools/watermark" style={styles.link}>워터마크 삽입</Link></li>
</ul>
</div>
</nav>
);
}
const styles = {
navbar: {
position: "fixed",
top: 0,
left: 0,
width: "100%",
background: "#282c34",
color: "white",
padding: "10px 0",
zIndex: 1000,
},
navContainer: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
maxWidth: "1100px",
margin: "0 auto",
padding: "0 20px",
},
logo: {
margin: 0,
fontSize: "24px",
},
navLinks: {
listStyle: "none",
display: "flex",
gap: "20px",
margin: 0,
padding: 0,
},
link: {
textDecoration: "none",
color: "white",
fontSize: "18px",
}
};
export default Navbar;
📌 📂 src/App.jsx에 네비게이션 추가
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Navbar from "./components/Navbar";
import MetaCleaner from "./pages/MetaCleaner";
function App() {
return (
<Router>
<Navbar />
<Routes>
<Route path="/tools/meta-cleaner" element={<MetaCleaner />} />
</Routes>
</Router>
);
}
export default App;
3️⃣ 메타 태그 제거 UI 개발 (Drag & Drop 지원)
사용자가 이미지를 업로드하고 메타 태그를 삭제할 수 있는 UI를 제작했습니다.
📌 📂 src/pages/MetaCleaner.jsx
import React, { useState } from "react";
function MetaCleaner() {
const [files, setFiles] = useState([]);
const [processing, setProcessing] = useState(false);
const [zipUrl, setZipUrl] = useState(null);
const [isDragging, setIsDragging] = useState(false);
const handleFileChange = (event) => {
setFiles([...event.target.files]);
};
const handleDragOver = (event) => {
event.preventDefault();
setIsDragging(true);
};
const handleDragLeave = () => {
setIsDragging(false);
};
const handleDrop = (event) => {
event.preventDefault();
setIsDragging(false);
if (event.dataTransfer.files.length > 0) {
setFiles([...files, ...event.dataTransfer.files]);
}
};
const handleUpload = async () => {
if (files.length === 0) {
alert("파일을 업로드해주세요!");
return;
}
setProcessing(true);
setZipUrl(null);
setTimeout(() => {
setZipUrl("https://example.com/download.zip");
setProcessing(false);
}, 2000);
};
return (
<div style={styles.pageContainer}>
<div style={styles.contentBox}>
<h2 style={styles.title}>🖼️ 이미지 메타 태그 삭제</h2>
<div
style={isDragging ? styles.uploadBoxDragging : styles.uploadBox}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<p style={styles.uploadText}>파일을 여기에 드래그하거나 클릭하여 선택하세요.</p>
<input type="file" multiple onChange={handleFileChange} style={styles.fileInput} />
</div>
<ul style={styles.fileList}>
{files.map((file, index) => (
<li key={index} style={styles.fileItem}>{file.name}</li>
))}
</ul>
<button onClick={handleUpload} disabled={processing} style={styles.button}>
{processing ? "처리 중..." : "메타 태그 삭제"}
</button>
{zipUrl && (
<div style={styles.downloadSection}>
<a href={zipUrl} download="cleaned_images.zip" style={styles.downloadLink}>
📥 변환된 ZIP 다운로드
</a>
</div>
)}
</div>
</div>
);
}
const styles = {
pageContainer: {
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100vw",
height: "100vh",
minHeight: "100vh",
background: "#1e1e1e",
},
contentBox: {
textAlign: "center",
width: "500px",
background: "#fff",
padding: "20px",
borderRadius: "10px",
boxShadow: "0px 4px 6px rgba(0, 0, 0, 0.1)",
},
title: {
fontSize: "24px",
marginBottom: "10px",
color: "#000",
},
uploadBox: {
border: "2px dashed #007bff",
padding: "20px",
cursor: "pointer",
borderRadius: "8px",
marginBottom: "10px",
background: "#f8f9fa",
},
uploadBoxDragging: {
border: "2px solid #007bff",
background: "#e0f2ff",
padding: "20px",
cursor: "pointer",
borderRadius: "8px",
marginBottom: "10px",
},
uploadText: {
color: "#333", // 업로드 영역 안의 텍스트 가독성 향상
fontSize: "16px",
},
fileInput: {
width: "100%",
opacity: 0,
position: "absolute",
top: 0,
left: 0,
height: "100%",
cursor: "pointer",
},
fileList: {
listStyle: "none",
padding: 0,
marginBottom: "10px",
},
fileItem: {
background: "#ffffff", // 파일 리스트 배경색을 흰색으로 변경
color: "#000", // 텍스트 색상을 검은색으로 변경하여 가독성 향상
padding: "8px",
margin: "5px 0",
borderRadius: "5px",
border: "1px solid #ccc", // 테두리 추가하여 구분감 강화
fontWeight: "bold",
transition: "background 0.3s ease", // 마우스 호버 시 부드러운 애니메이션 추가
},
button: {
padding: "10px 15px",
background: "#007bff",
color: "#fff",
border: "none",
borderRadius: "5px",
cursor: "pointer",
fontSize: "16px",
},
downloadSection: {
marginTop: "20px",
},
downloadLink: {
color: "#28a745",
fontWeight: "bold",
textDecoration: "none",
},
};
export default MetaCleaner;
여기까지 따라 오셨다면 /tools/meta-cleaner 접속시. 아래와 같은 프론트 화면이 만들어졌을 것 입니다.
🚀 다음 단계
✅ UI 개선 완료 → React 기반으로 파일 업로드 & UI 완성 ✅ Django API 개발 → 업로드된 이미지의 메타 태그 삭제 기능 개발 ✅ React와 Django 연동 → React에서 Django API 호출하여 변환된 파일 다운로드
다음 포스트에서는 Django를 활용한 백엔드 API 개발 과정을 다룰 예정입니다. 🚀