Node.js 멀티파트 파일 업로드 처리 시 메모리 이슈 해결

문제 상황

프로덕션 환경에서 파일 업로드 API 호출 시 간헐적으로 서버 메모리가 급증하며 OOM(Out Of Memory) 에러가 발생했다. 모니터링 결과 10MB 이상의 이미지 파일이 동시에 여러 건 업로드될 때 문제가 재현되었다.

원인 분석

기존 코드는 multer의 memoryStorage를 사용하고 있었다.

const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() });

app.post('/upload', upload.single('image'), async (req, res) => {
  const buffer = req.file.buffer; // 전체 파일이 메모리에 적재됨
  await uploadToS3(buffer);
});

memoryStorage는 파일 전체를 버퍼로 메모리에 올린다. 동시 요청이 많아지면 당연히 메모리 사용량이 폭증할 수밖에 없는 구조였다.

해결 방법

diskStorage로 변경하여 임시 디렉토리에 파일을 쓰고, stream으로 S3에 업로드하도록 수정했다.

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

const storage = multer.diskStorage({
  destination: './uploads/temp',
  filename: (req, file, cb) => {
    cb(null, `${Date.now()}-${file.originalname}`);
  }
});

const upload = multer({ storage });

app.post('/upload', upload.single('image'), async (req, res) => {
  const fileStream = fs.createReadStream(req.file.path);
  await uploadToS3Stream(fileStream);
  fs.unlinkSync(req.file.path); // 업로드 후 삭제
});

결과

메모리 사용량이 안정화되었고, 대용량 파일 업로드 시에도 서버가 정상 동작한다. stream 방식이 메모리 효율이 훨씬 좋다는 것을 체감했다.

다만 디스크 I/O가 추가되므로 임시 파일 정리 로직은 꼼꼼히 관리해야 한다. 업로드 실패 시 orphan 파일이 남지 않도록 에러 핸들링도 보완했다.