본문 바로가기
프로그래밍/JavaScript

Express에서 Multer로 파일 처리하기

by 페이지다운 2021. 12. 20.
반응형

웹 서비스를 운영하다 보면 사용자가 업로드한 파일을 처리해야 할 일이 반드시 생간다.

 

다만 파일같은 경우에는 용량이나 파일의 종류 등 제한하고 싶은 점이 많을 것이다. 오늘은 Express에서 multer를 통해 파일을 처리하는 법을 알아보자.

Multer

multer는 Express를 위해 개발된 파일 처리 미들웨어이다. 파일을 업로드받을 때 해야 할 귀찮은 일들을 대신 처리해 준다고 보면 된다.

 

설치는 간단하다. npm i multer

구조

일단 프로젝트 구조부터 보자. 익스프레스는 워낙 자유도가 높은 프레임워크라 쓰기 나름이지만, 그래도 어느 정도는 표준적인 구조는 존재한다. 나같은 경우에는 다름과 같은 구조로 프로젝트를 구성한다.

└project
 └constants
  └error.js
 └controllers
  └file.js
 └services
  └file.js
 └models
  └file.js
 └middlewares
 └routers
  └files.js
  └route.js
 └app.js

하나 하나 설명을 해보자면

constants - 각종 상수를 정의해 놓는다. 나같은 경우에는 각종 에러와 그에 대한 응답 코드와 메시지를 error,js에다가 작성해 놓고 가져다가 쓴다.

controllers - 컨트롤러를 작성한다. 내 코드가 MVC 모델에 제대로 들어맞는다고 생각하지는 않지만, 역할은 비슷하기에 흉내는 내 보았다. 컨트롤러에서는 핵심적인 로직을 구현한다. 나는 React를 쓰는 관계로 View는 없기 때문에 json으로 쏴준다.

services - 서비스를 작성한다. 나같은 경우에는 메일 작성이나 자주 재사용되는 인증 로직 같은 것들을 여기다가 구현해 놨는데 이것도 맞는건지는 솔직히 잘... 모르겠다

models - 모델, 즉 DB다. 나는 MongoDB를 쓰는데 당연히 뭘 쓰는 상관은 없지만 난 이걸 기준으로 작성할 예정이다.

middewares - 미들웨어를 작성한다. 사실 컨트롤러도 정의상 미들웨어랑 크게 다를 건 없는데, 익스프레스의 시스템에 MVC 개념을 끼워맞추다 보니 그렇게 된 듯 싶다. 여기서는 각종 인증을 처리한다. 여기서는 구현하지 않았다.

routers - 라우터를 작성한다. 인터넷 보면 라우터를 한 파일에 몰아넣는 경우가 있는데 별로 추천하지는 않는다. 나는 역할이 확실하게 분리되는 컨트롤러마다 라우터를 한개씩 할당해 놓았다. 여기에는 file 컨트롤러밖에 없으므로 file 라우터밖에 없다. 그리고 route.js는 상위 라우터로 하위 라우터로 연결해주는 역할을 하게 된다.

사용법

multer는 사전 세팅이 필요하다.

storage

storage는 말 그대로 저장소인데, 두 종류가 있다. diskStorage와 memoryStorage가 있는데 diskStorage는 내부 저장소에 저장하고 memoryStorage는 메모리에 저장한다...는게 뭔 소리냐면 그냥 변수로써 데이터를 가지고 있겠다는 뜻이다. 즉, memoryStorage에서 데이터를 받으면 다음 미들웨어로 데이터가 넘어간다. 나는 추가적인 처리를 해줄 예정이므로 memoryStorage를 이용했다.

multer

storage를 넘기고 upload 미들웨어를 정의한다. limits에는 몇가지 옵션이 있는데 다음과 같다. 출처는 multer 공식 GItHub 레포지토리다.

속성 설명 기본값
fieldNameSize 필드명 사이즈 최대값 100 bytes
fieldSize 필드값 사이즈 최대값 1MB
fields 파일형식이 아닌 필드의 최대 개수 무제한
fileSize multipart 형식 폼에서 최대 파일 사이즈(bytes) 무제한
files multipart 형식 폼에서 파일 필드의 최대 개수 무제한
parts For multipart forms, the max number of parts (fields + files) 무제한
headerPairs multipart 형식 폼에서 파싱할 헤더의 key=>value 쌍의 최대 개수 2000

나는 fileSize만 제한을 걸어놨다. byte 단위인 관계로 MB는 1024 * 1024를, GB면 1024 * 1024 * 1024를 곱해줘야 한다.

 

이렇게 multer를 세팅하면 다음과 같이 라우터에서 미들웨어 체인에 끼워넣으면 된다.

 

routers/files.js

multer는 여기까지가 다고, 나머지는 추가적으로 파일을 처리하는 로직인데 몇가지만 설명하겠다.

 

services/file.js

models/file.js

controllers/file.js

나머지 코드는 https://github.com/dlcjsdltlq/simple-board 이곳에서 확인 바람.

 

코드를 요약하면

 

업로드

1. 파일을 업로드 받으면 해시가 중복되는 파일이 있는지 확인

2. 만양 파일이 없다면 파일을 업로드하고 해시를 DB에 저장함. 있으면 3단계로 패스

3. 파일 이름을 URL-safe한 base64 문자열로 변환해서 반환되는 파일 URL에 Query String으로 붙여줌.

 

파일 다운로드

1. 요청한 파일이 DB에 존재하는지 확인

2. 없으면 에러 반환. 있으면 3단계로 패스

3. Query String에 URL-safe한 base64 문자열로 파일 이름이 있는지 확인. 없으면 파일 이름은 해시가 된다. 있으면 이게 파일 이름이 된다.

 

3번의 존재에 의아함을 느낄 수 있다. 이는 중복된 파일(해시가 같은 파일)은 단 한개밖에 저장할 수 없다는 점 때문에 추가된 기능이다.

 

파일과 파일 이름을 DB에 저장했다고 가정하자. 만약에 누군가 파일은 똑같지만 이름은 다른 파일을 업로드했다면? 그럼 DB에 이 수많은 파일 이름을 다 저장하고 그때 그때 다른 파일 이름을 가져다가 반환해줄 수는 없을 노릇이다.

 

그래서 내가 고안한 해결책(사실 이미 다른 데에서도 쓰고 있을 가능성이 높지만)은 아예 사용자가 파일을 요청할 때 파일 이름을 같이 주는 것이다. 사용자가 따로 URL을 건드리지 않는다는 가정하에 각자 자기가 업로드한 이름대로 파일을 받아볼 수 있다. 모종의 사유로 파일 이름이 사라졌다면? 그럼 파일 이름은 해시가 된다.

 

사실 이런 이유에서 DB에 파일 이름은 필요 엾지만, 내가 저 필드를 추가한 이유는 최초로 업로드된 이름을 저장하기 위해서이다. 이건 내가 다른 서비스를 만드는 과정에서 필요한 부분이라 넣었다.

반응형

댓글