Responsive Advertisement

Node.js์—์„œ ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•

ํŒŒ์ผ ์—…๋กœ๋“œ๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. Node.js์—์„œ๋Š” ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํŒŒ์ผ ์ฒ˜๋ฆฌ ๋ชจ๋“ˆ๊ณผ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” Node.js์—์„œ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•˜๊ณ , ์ด๋ฅผ ํ™œ์šฉํ•œ ๊ฐ„๋‹จํ•œ ํŒŒ์ผ ์—…๋กœ๋“œ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ์— ํ•„์š”ํ•œ ํŒจํ‚ค์ง€

Node.js์—์„œ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ช‡ ๊ฐ€์ง€ ํŒจํ‚ค์ง€๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” express ํ”„๋ ˆ์ž„์›Œํฌ์™€ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” multer ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • Express: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ Node.js ํ”„๋ ˆ์ž„์›Œํฌ
  • Multer: multipart/form-data ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด๋กœ, ํŒŒ์ผ ์—…๋กœ๋“œ์— ์‚ฌ์šฉ

ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์„ค์น˜

๋จผ์ €, ํ”„๋กœ์ ํŠธ ํด๋”๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

$ npm init -y
$ npm install express multer

์œ„ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด express์™€ multer ํŒจํ‚ค์ง€๊ฐ€ ์„ค์น˜๋˜๋ฉฐ, ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

Express ์„œ๋ฒ„์—์„œ ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌํ•˜๊ธฐ

์ด์ œ Express ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๊ณ , multer๋ฅผ ์‚ฌ์šฉํ•ด ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. Express ์„œ๋ฒ„ ์„ค์ •

๊ฐ„๋‹จํ•œ Express ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์—…๋กœ๋“œ ๊ฒฝ๋กœ์™€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ผ์šฐํŠธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

// app.js

const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();

// ํŒŒ์ผ ์ €์žฅ ์œ„์น˜ ๋ฐ ํŒŒ์ผ๋ช… ์„ค์ •
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'uploads/');  // ์—…๋กœ๋“œ ํด๋” ๊ฒฝ๋กœ
    },
    filename: (req, file, cb) => {
        cb(null, Date.now() + path.extname(file.originalname));  // ํŒŒ์ผ๋ช… ์„ค์ •
    }
});

const upload = multer({ storage: storage });

app.use(express.static('public'));  // ์ •์  ํŒŒ์ผ ์ œ๊ณต (HTML ํŒŒ์ผ ์œ„์น˜)

// ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ ๋ผ์šฐํŠธ
app.post('/upload', upload.single('file'), (req, res) => {
    res.send('ํŒŒ์ผ ์—…๋กœ๋“œ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
});

// ์„œ๋ฒ„ ์‹คํ–‰
app.listen(3000, () => {
    console.log('์„œ๋ฒ„๊ฐ€ http://localhost:3000 ์—์„œ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.');
});

์œ„ ์ฝ”๋“œ๋Š” ๊ฐ„๋‹จํ•œ Express ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. multer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ํŒŒ์ผ์€ uploads ํด๋”์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ด๋ฆ„์€ ์—…๋กœ๋“œ ์‹œ Date.now()๋ฅผ ์‚ฌ์šฉํ•ด ํ˜„์žฌ ์‹œ๊ฐ„์œผ๋กœ ์„ค์ •๋˜์–ด ์ค‘๋ณต๋˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

2. ํŒŒ์ผ ์—…๋กœ๋“œ ํด๋” ์ƒ์„ฑ

ํŒŒ์ผ์ด ์ €์žฅ๋  uploads ํด๋”๋ฅผ ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

$ mkdir uploads

3. HTML ํŒŒ์ผ ์ž‘์„ฑ

ํด๋ผ์ด์–ธํŠธ์—์„œ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ HTML ์–‘์‹์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ์–‘์‹์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์„ ์„ ํƒํ•˜๊ณ  ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<!-- public/index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ํŒŒ์ผ ์—…๋กœ๋“œ</title>
</head>
<body>
    <h1>ํŒŒ์ผ ์—…๋กœ๋“œ</h1>
    <form action="/upload" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <button type="submit">์—…๋กœ๋“œ</button>
    </form>
</body>
</html>

์ด HTML ํŒŒ์ผ์€ ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์„ ์„ ํƒํ•˜๊ณ  ์„œ๋ฒ„๋กœ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ณธ ์–‘์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. enctype="multipart/form-data" ์†์„ฑ์€ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์œ„ํ•œ ํ•„์ˆ˜ ์„ค์ •์ž…๋‹ˆ๋‹ค.

4. ์„œ๋ฒ„ ์‹คํ–‰ ๋ฐ ํŒŒ์ผ ์—…๋กœ๋“œ ํ…Œ์ŠคํŠธ

์ด์ œ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ๋ธŒ๋ผ์šฐ์ €์—์„œ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ด…๋‹ˆ๋‹ค.

$ node app.js

์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:3000์œผ๋กœ ์ด๋™ํ•˜์—ฌ ํŒŒ์ผ ์—…๋กœ๋“œ ์–‘์‹์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ์„ ํƒํ•œ ํ›„ ์—…๋กœ๋“œ๋ฅผ ํด๋ฆญํ•˜๋ฉด, ์„œ๋ฒ„๋Š” ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  uploads ํด๋”์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์—…๋กœ๋“œ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €์— ์—…๋กœ๋“œ ์™„๋ฃŒ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Multer ์˜ต์…˜: ํŒŒ์ผ ํฌ๊ธฐ ์ œํ•œ ๋ฐ ํ•„ํ„ฐ๋ง

multer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŒŒ์ผ ํฌ๊ธฐ ์ œํ•œ์ด๋‚˜ ํŒŒ์ผ ์œ ํ˜•์„ ํ•„ํ„ฐ๋งํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ ํฌ๊ธฐ ์ œํ•œ

์—…๋กœ๋“œํ•  ํŒŒ์ผ์˜ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•˜๋ ค๋ฉด limits ์˜ต์…˜์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŒŒ์ผ ํฌ๊ธฐ๋ฅผ 2MB๋กœ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const upload = multer({ 
    storage: storage,
    limits: { fileSize: 2 * 1024 * 1024 }  // ํŒŒ์ผ ํฌ๊ธฐ ์ œํ•œ: 2MB
});

ํŒŒ์ผ ํ˜•์‹ ํ•„ํ„ฐ๋ง

ํŠน์ • ํŒŒ์ผ ํ˜•์‹๋งŒ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œํ•œํ•˜๋ ค๋ฉด fileFilter ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ด๋ฏธ์ง€ ํŒŒ์ผ(JPG, PNG)๋งŒ ํ—ˆ์šฉํ•˜๋Š” ํ•„ํ„ฐ๋ง ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

const upload = multer({ 
    storage: storage,
    fileFilter: (req, file, cb) => {
        const fileTypes = /jpeg|jpg|png/;
        const mimeType = fileTypes.test(file.mimetype);
        const extname = fileTypes.test(path.extname(file.originalname).toLowerCase());

        if (mimeType && extname) {
            return cb(null, true);
        } else {
            cb(new Error('์ด๋ฏธ์ง€ ํŒŒ์ผ๋งŒ ์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.'));
        }
    }
});

์œ„ ์ฝ”๋“œ๋Š” ํŒŒ์ผ์˜ MIME ํƒ€์ž…๊ณผ ํ™•์žฅ์ž๋ฅผ ๊ฒ€์‚ฌํ•˜์—ฌ JPG, PNG ํ˜•์‹์˜ ์ด๋ฏธ์ง€ ํŒŒ์ผ๋งŒ ์—…๋กœ๋“œ๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

Node.js์—์„œ Multer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Express ์„œ๋ฒ„์™€ Multer ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—…๋กœ๋“œํ•œ ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์ €์žฅํ•˜๊ณ , ํŒŒ์ผ ํฌ๊ธฐ ์ œํ•œ ๋ฐ ํ˜•์‹ ํ•„ํ„ฐ๋ง๊ณผ ๊ฐ™์€ ์ถ”๊ฐ€ ์„ค์ •๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์—…๋กœ๋“œ๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์ด๋ฏ€๋กœ, ์ด๋ฅผ ์ž˜ ํ™œ์šฉํ•˜์—ฌ ํšจ์œจ์ ์ธ ํŒŒ์ผ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•ด๋ณด์„ธ์š”!

๋Œ“๊ธ€ ์“ฐ๊ธฐ