Vite React 활용한 이미지 업로드 UI 개발

🛠 프로젝트 개요

이번 포스트에서는 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/에 접속하여 기본 화면이 보이면 설치가 완료된 것입니다.

vite react 설정 완료 화면


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 개발 과정을 다룰 예정입니다. 🚀