[Express] 프로필 사진을 변경하려면..?

짧은 프로젝트를 진행하던 중, 대수롭지 않게 여겼던 기능 중 하나가 발목을 잡았습니다.

유저가 회원가입할 때, 유저의 프로필 사진을 클라이언트에서 업로드 받아 이를 저장하여 유저가 로그인할 때마다 그 프로필 사진을 보여주는 것이었습니다.

처음엔 클라이언트에서 그 이미지의 주소를 상태로 관리하는 것을 생각했으나, 다양한 유저의 프로필 사진을 보기 위해선 서버 또는 DB에 저장해야할 것 같다고 생각했습니다.

DB에 이미지 저장은 NO

이를 구현하기 위해 수많은 구글링을 한 결과, DB는 데이터를 저장하는 공간이 아니라, 데이터를 효과적으로 관리하고 다룰 수 있게 만든 도구임을 알게됐습니다.

이미지는 바이너리 파일로, DB에서 그 이미지의 파일 자체를 관리하게 된다면 DB의 성능 저하가 발생한다는 것을 알게됐습니다.

즉, DB에 파일을 저장하는 것이 아니라, DB에는 그 이미지의 경로(String)만 저장하는 것이 효과적이라는 것을 알게됐습니다.

그리고, 클라이언트로부터 업로드 받은 이미지는 서버에 저장하여 응답하는것이 효과적이라는 것을 알게됐습니다.

BLOB

처음 DB Schema를 기획할 땐 userPic이란 테이블에 BLOB 타입을 줄 생각이었습니다.

Binary large object(BLOB)는 데이터베이스 관리 시스템의 하나의 엔티티로서 저장되는 이진 데이터의 모임입니다. BLOB은 일반적으로 그림, 오디오, 또는 기타 멀티미디어 오브젝트로 보통 이루어져 있습니다.

BLOB 타입은 이름마다 저장할 수 있는 용량이 제한되어 있습니다.

이름용량
TINYBLOBmaximum length of 255 bytes
BLOBmaximum length of 65,535 bytes
MEDIUMBLOBmaximum length of 16,777,215 bytes
LONGBLOBmaximum length of 4,294,967,295 bytes

유저마다 업로드하는 프로필 사진의 용량은 모두 제각각일 것이기 때문에, BLOB은 비효율적이라 생각하게 됐습니다.

또한, DB 용량 자체도 AWS RDS 로 배포할 것이기 때문에, 용량이 늘어나면 과금되는 부분도 영향을 미쳤습니다.

How to?

서두에 말한 것 처럼, 이미지 파일은 다른 저장 공간에 저장하고, MySQL DB에는 그 파일의 경로만 저장하기로 했습니다.

이를 위해, node.jsmulter 모듈을 사용할 수 있었습니다.

Multer는 파일 업로드를 위해 사용되는 multipart/form-data 를 다루기 위한 node.js 의 미들웨어 입니다.

공식문서

npm install --save multer

App.js

const express = require('express');
const imgRouter = require('./routes/img');

const app = express();

app.use('/', imgRouter);

app.listen(4000, () =>{
    console.log('4000번 포트 서버');
})

routes/img.js

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

const router = express.Router();

fs.readdir('uploads', (error) => {
    // uploads 폴더 없으면 생성
    if (error) {
        fs.mkdirSync('uploads');
    }
})

const upload = multer({
    storage: multer.diskStorage({
        destination(req, file, cb) {
            cb(null, 'uploads/');
        },
        filename(req, file, cb) {
            const ext = path.extname(file.originalname);
            cb(null, path.basename(file.originalname, ext) + Date.now() + ext);
        },
    }),
    limits: { fileSize: 5 * 1024 * 1024 },
})
// 이미지 업로드를 위한 API
// upload의 single 메서드는 하나의 이미지를 업로드할 때 사용
router.post('/upload', upload.single('img'), (req, res) => {
    console.log(req.file);
    res.json({ url : `/${req.file.filename}`});
})

module.exports = router;

Multer options

  • Storage
    파일 저장 방식, 경로, 파일 명 설정

    • diskStorage : 이미지가 서버에 저장 되도록 함. destination 메소드로 저장 경로를 설정할 수 있습니다.
    • filename : 예시에 따르면 기존 이름(originalname) + 업로드 날짜(Date.now()) + 기존 확장자(ext) 를 합쳐서 파일명을 만들 수 있는 메소드입니다.
  • Limit
    최대 이미지 파일 용량 허용치입니다.

    • fieldSize : 필드값 사이즈 최대 값입니다. 기본값은 1mb입니다.
    • fileSize : multipart 형식 폼에서 최대 파일 사이즈입니다.(bytes) 기본값은 무제한입니다.

사이즈 제한 옵션을 지정하면 DDOS 공격으로부터 사이트를 보호하는데 도움이 됩니다!

참조한 글

좋은 웹페이지 즐겨찾기