2019. 07. 21 수정


이번 글에서는 파일을 업로드하는 방법을 알아보도록 하겠습니다.

  • 개발환경

    • express-generator 4.16.1

    • multer 1.4.2




1. 준비작업

1) npm 설치

body-parser 모듈과 multer 모듈을 사용해서 파일 업로드를 구현할 수 있습니다.

body-parser 모듈은 express-generator로 설치하면 built-in 되어 있으므로, multer 모듈만 추가적으로 설치하면 됩니다.

# npm install multer




2) upload를 위한 라우터 등록

다음으로 파일 업로드에 대한 기능을 처리하기 위한 라우터 미들웨어를 등록합니다.

뒤에서 /routes/upload.js 파일을 생성할 것이며, 해당 라우터를 app.js 파일에 미들웨어로 등록해야 합니다.

아래와 같이 /upload에 대한 부분을 추가해주세요.

...
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var fileUploadRouter = require('./routes/upload');

...

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/upload', fileUploadRouter);
...



3) 파일의 저장위치인 upload 폴더 생성 및 static 등록

다음으로 업로드된 파일이 저장되는 upload 폴더를 루트 경로에 생성합니다.



해당 폴더에 대한 경로를 단순히 하기 위해 app.js 파일에서 static으로 설정하겠습니다.

app.use('/upload', express.static('uploads'));

이렇게 가상 경로를 설정하면, static 파일이 있는 upload 폴더는 내부적으로 /upload라는 가상 경로로 접근할 수 있습니다.



이상으로 라우터 파일을 생성하고, 가상 경로를 설정했으므로 준비 작업은 끝났습니다.

이제 본격적으로 파일 업로드를 구현해보겠습니다.





2. 파일 업로드 form 작성

먼저 파일 업로드 뷰 페이지를 만듭니다.

/views/board.ejs

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>

<form action="/upload/create" method="post" enctype="multipart/form-data">
<input type="file" name="imgFile">
<input type="submit" value="파일 업로드하기">
</form>

</body>
</html>

form 태그의 속성 중 enctype( 인코딩 타입 )을 주목해주세요.

파일 업로드를 하려면 enctype을 muiltipart 포맷으로 명시해줘야 합니다.


multipart 포맷이란, 음악, 사진 , 파일 등을 일반 데이터와 함께 웹 서버로 보내려고 만든 표준입니다.

따라서 웹 서버에서 파일을 업로드 할 때는 multipart 포맷을 사용해야 합니다.





3. multer 미들웨어 등록 및 라우터 함수 추가

multer 미들웨어에 대한 문서는 여기를 참고해주세요.


multer 미들웨어를 등록하면 요청 객체( req )에 file, files 객체가 추가됩니다.

file 또는 files 객체에는 form으로 업로드 된 파일들을 갖고있습니다.


/routes/upload.js 라우터 파일에 코드를 작성하면서, multer 모듈을 어떻게 사용하는지 살펴보겠습니다.

const express = require('express');
const router = express.Router();
const multer = require("multer");

// 1. multer 미들웨어 등록
let upload = multer({
dest: "upload/"
})

// 뷰 페이지 경로
router.get('/show', function(req, res, next) {
res.render("board")
});

// 2. 파일 업로드 처리
router.post('/create', upload.single("imgFile"), function(req, res, next) {
// 3. 파일 객체
let file = req.file

// 4. 파일 정보
let result = {
originalName : file.originalname,
size : file.size,
}

res.json(result);
});

module.exports = router;
  1. multer 미들웨어 등록
    • multer 미들웨어를 등록할 때 인자로 목적지( dest ) 프로퍼티가 추가된 객체를 전달했습니다.
      • multer 미들웨어 등록 시 추가 할 수 있는 옵션의 정보는 여기를 참고해주세요.
    • dest 프로퍼티의 값으로 upload/ 를 작성했는데, 준비 작업에서 uploads에 대한 폴더는 /upload 라는 가상 경로로 접근할 수 있도록 해주었기 때문입니다. 따라서 파일이 저장될 목적지는 upload/****가 됩니다.
      • uploads 폴더가 생성되어 있어야 업로드 된 파일이 저장됩니다. 
  2. 파일 업로드 처리
    • 파일 업로드의 경우에는 router 함수의 매개변수가 3개입니다.
      • 두 번째 인자로 upload 객체의 single() 메서드를 호출했습니다.
        • single() 메서드의 인자로는 form에서 파일을 넘겨주는 요소의 name 애트리뷰트인 imgFile를 작성하면 됩니다.
        • single() 메서드는 파일을 하나만 받으며, single() 메서드 이외에도 array(), fields() 등의 메서드가 있습니다.
  3. 파일 객체
    • multer 미들웨어를 등록하면 요청 객체에 file 또는 files 객체가 추가된다고 했습니다.
    • single() 메서드를 호출했기 때문에 req.file 객체에는 업로드된 파일의 정보가 담겨있습니다.
  4. 파일 정보
    • file의 정보는 프로퍼티를 통해 가져올 수 있습니다.
    • file 객체에 대한 API는 여기를 참고해주세요.



여기까지 작성하셨다면 브라우저에서 http://localhost:3000/upload/show 으로 요청하여, 이미지 파일을 업로드해보세요.

그리고 나서 upload 폴더를 확인하시기 바랍니다.





4. DiskStorage

위와 같은 방법으로 파일 업로드를 구현할 수 있습니다.


추가적으로 multer 모듈에는 파일을 저장하는 여러 방식을 지원하는데,

이 글에서는 실제 로컬의 디스크에 파일을 저장하는 DiskStorage에 대해 알아보도록 하겠습니다.

multer 모듈 Storage와 관련된 내용은 여기를 참고해주세요.


DiskStorage는 파일을 디스크에 저장하기 위한 모든 제어 기능을 제공합니다.


코드를 먼저 살펴보겠습니다.

아래의 코드는 multer 모듈의 깃헙에 있는 예제 코드를 살짝 수정한 것이며, 기존의 라우터 파일 상단에 "multer 미들웨어 등록" 부분을 수정하면 됩니다.

/routes/upload.js

let storage = multer.diskStorage({
destination: function(req, file ,callback){
callback(null, "upload/")
},
filename: function(req, file, callback){
callback(null, file.originalname + " - " + Date.now())
}
})

// 1. multer 미들웨어 등록
let upload = multer({
storage: storage
})

라우터 함수에 대한 내용은 같으므로 multer 미들웨어를 등록하는 부분만 수정했습니다.

  • multer 객체의 diskStorage() 메서드를 호출하여, storage를 생성할 수 있습니다.
    • destination 속성은 파일이 저장될 경로를 작성하고,
    • filename 속성은 file의 이름을 변경해서 저장합니다.
      • 예제에서는 "원래 파일이름 - 현재시간"의 양식으로 파일 이름이 저장되도록 했습니다.
      • filename 속성을 작성하지 않으면, 이전에 올렸던 파일 이름처럼 랜덤화된 파일 이름이 부여됩니다.

Storage를 이용하여, 다시 파일 업로드를 진행해보세요!

upload 폴더에 이름이 바뀐상태로 저장된 것을 확인할 수 있습니다.



그런데 파일이름을 보면, 확장자가 없기 때문에 조금 어색하므로 조금 더 수정해보도록 하겠습니다.

/routes/upload.js

const multer = require("multer");
const path = require("path");

let storage = multer.diskStorage({
destination: function(req, file ,callback){
callback(null, "upload/")
},
filename: function(req, file, callback){
let extension = path.extname(file.originalname);
let basename = path.basename(file.originalname, extension);
callback(null, basename + "-" + Date.now() + extension);
}
});

// 1. 미들웨어 등록
let upload = multer({
storage: storage
});





이상으로 multer 모듈로 파일을 업로드하는 기능을 구현해보았습니다.

이 밖에도 multer 모듈의 깃헙페이지를 보시면 파일의 개수, 파일의 크기, 파일 필터링( 특정 파일 확장자 거부 ) 등의 옵션을 사용할 수 있으니 참고하시기 바랍니다.