Node.JS - Multer로 파일 업로드

소스코드

이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.

git clone https://github.com/a-mean-blogger/google-oauth.git
cd google-oauth
git reset --hard ec58098
npm install
atom .

- Github에서 소스코드 보기: https://github.com/a-mean-blogger/google-oauth/tree/ec580985b4af566f951130360d4f39c3c9fcbe0e


Node.JS Express 서버로 파일을 업로드 하는 방법을 알아봅시다. Multer라는 package를 사용합니다.

이 강의는 Node.JS/Express로 기본적인 웹사이트를 만들 수 있는 분들을 대상으로 합니다. (최소 MEAN Stack 강의 시리즈의 Hello World!까지를 읽은 분들을 대상으로 합니다.)

multer의 기능을 확인해 볼 수 있는 간단한 웹페이지를 만들텐데 먼저 완성된 사이트를 살펴봅시다.

한 페이지에 4개의 form이 존재합니다.

  1. Single File Upload: 하나의 파일을 업로드 합니다. multer의 기본설정으로 파일 이름이 무작위값으로 바뀝니다.
  2. Single File Upload (Keep Original Filename): 하나의 파일을 업로드 합니다. multer에 설정을 추가하여 파일이름을 그대로 유지하도록합니다.
  3. Multiple File Upload: 여러개 파일을 한번에 업로드 합니다. multer의 기본설정으로 파일 이름이 무작위값으로 바뀝니다.
  4. Multiple File Upload (Keep Original Filename): 여러개 파일을 한번에 업로드 합니다. multer에 설정을 추가하여 파일이름을 그대로 유지하도록합니다.

여기서 볼 수 있듯이 multer는 기본 설정이 파일명을 무작위값으로 변경하는 것입니다. 오히려 기존의 파일명을 유지하려면 추가적인 설정이 필요하죠. 보안을 생각하여 이렇게 만든 것 같습니다. 어쨌든 파일을 업로드하면 서버에 파일이 생성되고 아래와 같은 confirmation 페이지로 이동하게 됩니다.

confirmation 페이지는 서버에 업로드된 파일의 정보를 보여줍니다. 위 페이지의 내용에 대해서는 본문에서 자세히 살펴보도록 하겠습니다.

프로젝트 생성 및 Package 설치

프로젝트 폴더를 생성합니다. 해당 폴더에서 command line(cmd, git bash 등)에 아래 명령어 입력하여 node.js 프로젝트를 생성합니다.

$ npm init --yes

프로젝트에 필요한 package들도 설치해줍니다.

$ npm install express ejs multer --save

multer가 이번 강의의 핵심 패키지입니다. 나머지 패키지들은 이미 MEAN Stack 강의 시리즈에서 설명을 하였습니다.

폴더 구조


코드

// index.js

var express   = require('express');
var app       = express();
var fs        = require('fs'); // 1

app.set('view engine', 'ejs');

// Routes
app.use('/', require('./routes/main'));

// Port setting
var port = 3000;
app.listen(port, function(){
  var dir = './uploadedFiles';
  if (!fs.existsSync(dir)) fs.mkdirSync(dir); // 2

  console.log('server on! http://localhost:'+port);
});

1. fs는 node.js에 들어 있는 module로 file system의 약자입니다. 서버의 파일/폴더에 접근할 수 있는 함수들이 들어 있습니다. fs 모듈은 파일업로드와 직접적인 연관이 있는 모듈은 아니고, 업로드될 파일을 저장할 폴더를 생성하기 위해서만 사용했습니다.

2. fs.existsSync()함수로 폴더가 존재하는지 확인하고, 없으면 fs.mkdirSync()함수로 폴더를 생성해 줍니다.

// routers/main.js

var express  = require('express');
var router   = express.Router();
var multer   = require('multer'); // 1

var storage  = multer.diskStorage({ // 2
  destination(req, file, cb) {
    cb(null, 'uploadedFiles/');
  },
  filename(req, file, cb) {
    cb(null, `${Date.now()}__${file.originalname}`);
  },
});
var upload = multer({ dest: 'uploadedFiles/' }); // 3-1
var uploadWithOriginalFilename = multer({ storage: storage }); // 3-2

router.get('/', function(req,res){
  res.render('upload');
});

router.post('/uploadFile', upload.single('attachment'), function(req,res){ // 4 
  res.render('confirmation', { file:req.file, files:null });
});

router.post('/uploadFileWithOriginalFilename', uploadWithOriginalFilename.single('attachment'), function(req,res){ // 5
  res.render('confirmation', { file:req.file, files:null });
});

router.post('/uploadFiles', upload.array('attachments'), function(req,res){ // 6
  res.render('confirmation', { file: null, files:req.files} );
});

router.post('/uploadFilesWithOriginalFilename', uploadWithOriginalFilename.array('attachments'), function(req,res){ // 7
  res.render('confirmation', { file:null, files:req.files });
});

module.exports = router;

1. multer module을 불러옵니다. 참고로 제 강의에 사용된 모든 multer 관련 코드는 공식문서 https://github.com/expressjs/multer/blob/master/doc/README-ko.md 를 활용하여 작성하였습니다.

2. 업로드한 파일의 이름을 유지하기 위해서는 multer에 storage 세팅을 해줘야 하는데 이때 사용될 변수입니다. 업로드된 파일명과 서버의 파일명이 완전히 동일하게 되면 중복된 파일 업로드에서 문제가 생길 수 있으니 파일명 앞에 시간을 정수로 달아줬습니다. 즉 똑같은 파일명이 업로드되더라도 앞에 시간 정수가 있기 때문에 구별됩니다.

3-1. multer로 파일이 저장될 위치만을 설정(dest: 'uploadedFiles)하여 upload 미들웨어를 만들었습니다. 이렇게 만든 upload 미들웨어는 저장되는 파일의 이름을 무작위로 변경하게 됩니다.

3-2. 이번에는 2번에서 만든 storage를 넣어서 저장될 파일의 이름을 유지하는 미들웨어를 만들었습니다. 3-1의 uploaduploadWithOriginalFilename은 파일명 변경/유지 외에는 하는 일이 완전히 똑같습니다. 즉 실제 프로젝트에서 파일명을 임의로 바뀌게 하여 보안성을 높이고 싶다면 upload처럼 설정하고, 파일명을 유지하여 관리하기 쉽게 하고 싶다면 uploadWithOriginalFilename처럼 설정하시면 됩니다. 이 예제는 강의용으로 둘 다 사용해 봅시다.

4. 기본 설정으로 하나의 파일업로드를 처리하는 route입니다. 파일명이 바뀌도록 upload를 사용하였고 하나의 파일을 처리하기 위해 upload.single()을 사용했습니다. upload.single()에는 html form에서 사용된 파일 input 필드의 이름(name)이 들어갑니다.

5. 변경된 storage 설정으로 하나의 파일업로드를 처리하는 route입니다. 파일명이 바뀌지 않도록 uploadWithOriginalFilename을 사용하였고 하나의 파일을 처리하기 위해 uploadWithOriginalFilename.single()을 사용했습니다.

6. 기본 설정으로 여러개의 파일업로드를 처리하는 route입니다. 파일명이 바뀌도록 upload를 사용하였고 여러개의 파일을 처리하기 위해 upload.array()를 사용했습니다. upload.array()에도 마찬가지로 html form에서 사용된 파일 input 필드의 이름(name)이 들어갑니다.

7. 변경된 storage 설정으로 여러개의 파일업로드를 처리하는 route입니다. 파일명이 바뀌지 않도록 uploadWithOriginalFilename을 사용하였고 여러개의 파일을 처리하기 위해 uploadWithOriginalFilename.array()를 사용했습니다.

즉, 파일명에 따라 upload/uploadWithOriginalFilename, 파일이 하나인지 여러개인지에 따라 .single()/.array()를 조합하여 총 4개의 route을 만들었습니다.

.single()을 사용해서 하나의 파일을 올린 경우 req.file에 업로드된 하나의 파일의 정보가 저장되고, .array()를 사용해서 여러개의 파일을 올린 경우 req.files에 업로드된 파일들의 정보가 배열로 저장됩니다.

생성된 req.file, req.files를 {file:... files:...}에 넣어서 confirmation 페이지로 보냅니다. 이 오브젝트의 구조는 다음과 같습니다

  • fieldname: 파일이 form의 어느 field에서 왔는지를 알려줍니다.
  • originalname: client에서 업로드된 파일이름입니다.
  • encoding: 파일의 인코딩입니다.
  • mimetype: 파일 타입입니다.
  • destination: 파일의 저장된 위치(폴더)입니다.
  • filename: 실제 저장된 파일의 이름입니다.
  • path: 실제 파일의 위치입니다.
  • size: 파일의 크기(바이트)입니다.

실전에서 이 정보들을 이용하여 DB에 파일 위치를 저장하거나 할 수 있는 중요한 정보입니다.

다음으로 template 파일들을 살펴봅시다.

<!-- views/upload.ejs -->

<h1><i>Express</i> and <i>Multer</i> File Upload Example</h1>
<hr>

<h3>1. Single File Upload</h3>
<form action="/uploadFile" enctype="multipart/form-data" method="post">
  <input type="file" name="attachment">
  <button type="submit" class="btn btn-primary">Upload</button>
</form>
<hr>

<h3>2. Single File Upload (Keep Original Filename)</h3>
<form action="/uploadFileWithOriginalFilename" enctype="multipart/form-data" method="post">
  <input type="file" name="attachment">
  <button type="submit" class="btn btn-primary">Upload</button>
</form>
<hr>

<h3>3. Multiple File Upload</h3>
<form action="/uploadFiles" enctype="multipart/form-data" method="post">
  <input type="file" name="attachments" multiple>
  <button type="submit" class="btn btn-primary">Upload</button>
</form>
<hr>

<h3>4. Multiple File Upload (Keep Original Filename)</h3>
<form action="/uploadFilesWithOriginalFilename" enctype="multipart/form-data" method="post">
  <input type="file" name="attachments" multiple>
  <button type="submit" class="btn btn-primary">Upload</button>
</form>

form에 파일을 업로드하려면 enctype="multipart/form-data" 항목(attribute)을 추가해야 합니다. 그리고 input의 type은 file로 하고, name을 적어줍니다. 이 name이 upload 미들웨어에 사용됩니다. 파일을 여러개 선택할 수 있게 하려면 input에 multiple 항목(attribute)을 추가해줍니다.

<!-- views/confirmation.ejs -->

<h1>Success!</h1>

<% if(file){ %>
<pre><%=JSON.stringify(file, null, 2)%></pre>
<% }%>

<% if(files){ %>
<pre><%=JSON.stringify(files, null, 2)%></pre>
<% }%>

<a href='/'>Back</a>

마지막으로 파일업로드가 된 후 이동할 confirmation 페이지에는 file이나 files의 정보를 표시합니다.

실행결과

nodemon을 사용하여 서버를 실행합니다.

1번 form을 이용하여 파일을 업로드해 봅시다.

req.file이 하나의 파일 정보를 표시하고 있으며, 파일명이 바뀌었습니다.

다음으로 4번 form을 이용하여 2개의 파일을 업로드해 봅시다.


req.files가 배열이고 두개의 파일 정보를 표시하고 있으며, 파일명이 유지되었습니다..

마치며..

공식문서 https://github.com/expressjs/multer/blob/master/doc/README-ko.md를 반드시 읽어보시기 바랍니다. 흔하지 않게 한글로 된 문서를 제공합니다! 공식문서를 살펴보시면 위 강의에서는 설명되지 않은 업로드파일의 용량 제한 등 더욱 자세한 설정들을 하는 방법을 제공합니다.

이 강의에서는 어떻게 파일을 올리는지만 설명하였고, 서버에 업로드된 파일을 활용하려면 fs 모듈에 대해 더 알아야합니다. 역시 공식문서 https://nodejs.org/api/fs.html 를 참고할 수 있습니다. 또한 게시판 만들기(고급)/게시판 - 파일첨부 기능 만들기도 활용해 보시기 바랍니다.

댓글

댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP