Multipart 파일에서 시작된 스노우볼

개발세발·2025년 4월 16일

📚 Multipart 파일 업로드: 개념부터 클라우드 저장까지 정리


1️⃣ HTTP 기반 Multipart 업로드 개요

항목설명
전송 방식HTTP POST 요청, Content-Type: multipart/form-data
구조각 form 필드가 boundary로 구분된 파트로 구성됨
구분 기준Content-Disposition 헤더의 filename 존재 여부로 "파일/텍스트" 구분
예시파일 + 텍스트 데이터가 함께 전달됨 (Input, textarea, file 등)

2️⃣ Spring에서 Multipart 파일 처리

항목설명
핵심 클래스MultipartFile, MultipartResolver, MultipartHttpServletRequest
주요 메서드getOriginalFilename(), getBytes(), getInputStream(), transferTo()
처리 과정HTTP 요청 → MultipartResolver 파싱 → MultipartFile 객체 생성
파일 수신 예@RequestParam("file") MultipartFile file
다중 파일MultipartFile[], List<MultipartFile>로 처리

3️⃣ MultipartFile vs File 비교

구분MultipartFileFile
정의업로드된 파일을 추상화한 객체 (Spring 제공)서버 로컬의 물리적 파일 (java.io.File)
생성 위치클라이언트 요청 시 자동 생성개발자가 저장 경로 지정 후 생성
용도요청으로 받은 파일 처리디스크에 저장하거나 조작할 때
변환 방법file.transferTo(new File(...)) 또는 InputStream 직접 저장

4️⃣ MultipartFile → File 변환 과정

단계설명
① 수신클라이언트가 multipart/form-data로 파일 전송
② 파싱Spring이 이를 MultipartFile로 파싱
③ 추출바이너리: getInputStream(), getBytes()
④ 저장transferTo(File) 또는 직접 FileOutputStream 사용

5️⃣ 클라우드 저장소 업로드 전략

✅ 단일 업로드 (Single PUT Upload)

  • 사용 예: 5MB 이하의 파일
  • 코드 간단 (putObject 한 번 호출)

✅ 분할 업로드 (Multipart Upload)

  • 사용 예: 대용량 파일, 안정성 중요할 때
  • 구조:
    1. 업로드 초기화 (uploadId)
    2. 파일을 일정 크기로 분할
    3. 조각을 병렬로 업로드
    4. 업로드 완료 요청 (completeMultipartUpload)
  • 장점:
    • 병렬 처리로 빠름
    • 실패 시 해당 파트만 재업로드 가능
    • 메모리 효율 좋음

6️⃣ 연결 흐름 다이어그램 (요약)

scss
복사편집
[클라이언트]
    ↓  (multipart/form-data 요청)
[Spring 서버]
    ↓  (MultipartResolver 파싱)
[MultipartFile 객체]
    ↓
[File 변환 (transferTo or stream)]
    ↓
[로컬 저장 or 클라우드 전송 (S3 등)]
    ↳ (단일 업로드 or 분할 업로드)

📦 파일 다운로드 뷰란?

Spring MVC에서 클라이언트가 파일을 다운로드할 수 있도록 응답을 구성해주는 뷰(View) 객체 또는 컨트롤러 로직.

  • 업로드: 클라이언트 → 서버
  • 다운로드: 서버 → 클라이언트 (브라우저가 저장 창 띄우는 거)

Spring에서는 파일을 HTTP 응답(ResponseEntity 또는 StreamingResponseBody)로 만들어서 다운로드하도록 처리해.


✅ 기본적인 파일 다운로드 방법 (Spring 방식)

📄 1. 일반적인 파일 다운로드 컨트롤러

java
복사편집
@GetMapping("/download")
public ResponseEntity<Resource> download(@RequestParam String filename) throws IOException {
    Path path = Paths.get("upload-dir/" + filename);
    Resource resource = new UrlResource(path.toUri());

    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
            .body(resource);
}

🔍 설명:

요소의미
Resource다운로드할 파일 자원 (UrlResource, FileSystemResource 등)
Content-Dispositionattachment; filename="..." → 다운로드 창 뜨게 함
MediaType보통 application/octet-stream (범용 이진 파일 타입)
ResponseEntity직접 HTTP 응답 구성 가능

💎 옛날 방식: View 객체 사용 (예: AbstractView 상속)

Spring 3 이전 또는 커스터마이징된 View로 다운로드 구현할 때 사용하던 방식

java
복사편집
public class FileDownloadView extends AbstractView {

    public FileDownloadView() {
        setContentType("application/download; charset=utf-8");
    }

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        File file = (File) model.get("downloadFile");

        response.setContentType(getContentType());
        response.setContentLength((int) file.length());
        response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");

        try (InputStream in = new FileInputStream(file);
             OutputStream out = response.getOutputStream()) {

            FileCopyUtils.copy(in, out);
        }
    }
}
  • 지금은 거의 쓰지 않고, ResponseEntity<Resource>로 대체하는 추세

🧠 정리

구분설명
업로드클라이언트가 파일을 multipart/form-data로 전송
다운로드서버가 파일을 HTTP 응답의 body에 실어서 내려줌
파일 다운로드 뷰파일을 내려주는 응답(파일 스트림 + Content-Disposition 설정)
추천 방식ResponseEntity<Resource> 또는 StreamingResponseBody

📚 파일 업로드 & 다운로드 + 첨부파일 시스템 설계 정리


🧱 1. 기본 구조: 파일 업로드와 다운로드 개념

구분설명
📤 업로드클라이언트 → 서버, 파일 전송 (multipart/form-data)
📥 다운로드서버 → 클라이언트, 파일 바이너리 전송 (Content-Disposition: attachment)
📎 첨부파일 기능사용자가 업로드한 파일을 저장하고, 이후에 다시 열람하거나 다운로드할 수 있는 기능

🧩 2. Spring에서 MultipartFile 처리 방식

항목설명
MultipartFileSpring에서 HTTP 요청의 파일 파트를 추상화한 객체
FileJava에서 실제 디스크에 존재하는 파일
처리 흐름MultipartFile → InputStream or byte[] → File or 클라우드 저장소로 전송
파일 저장 방식transferTo(File), getInputStream(), 또는 클라우드 SDK 이용

🗃️ 3. 첨부파일 저장 전략

✅ 주요 전략 2가지

전략설명
DB 직접 저장 (BLOB)파일을 DB에 직접 넣는 방식 (소규모 시스템에서만 사용)
DB + 파일시스템/S3파일은 서버 디스크나 S3에 저장하고, DB엔 메타정보만 저장 (대세)

📁 DB에 저장되는 메타정보 예시

sql
복사편집
CREATE TABLE file (
    id BIGINT,
    original_name VARCHAR,
    s3_key VARCHAR,
    content_type VARCHAR,
    size BIGINT,
    uploaded_by BIGINT,
    uploaded_at DATETIME
);

🚚 4. 다운로드 처리

방식설명
ResponseEntity가장 보편적인 Spring 방식
HttpServletResponse + Stream대용량 파일 스트리밍에 유리
Presigned URL클라이언트가 직접 클라우드(S3 등)에서 다운로드 (서버 무부하)

⚙️ 5. S3에 저장하는 다양한 전략


✅ 전략 1: 서버에서 S3로 바로 업로드

  • 서버가 MultipartFile을 받아 s3Client.putObject()로 업로드
  • 간단하지만 서버 리소스 소모

✅ 전략 2: Presigned URL (서명된 URL)을 통한 직접 업로드

  • 서버가 클라이언트에 업로드용 Presigned URL을 발급
  • 클라이언트가 해당 URL로 S3에 직접 PUT
  • 서버는 업로드 확정 시점에만 개입
java
복사편집
// Presigned URL 생성
PresignedPutObjectRequest url = presigner.presignPutObject(...);
js
복사편집
// 클라이언트에서 직접 S3로 업로드
fetch(presignedUrl, { method: "PUT", body: fileBlob });

🧠 6. 임시 저장 전략 정리

전략설명활용 시점
메모리 임시 저장MultipartFile.getBytes() → 임시 저장파일 작을 때만
디스크 임시 저장파일을 /tmp 등에 저장 후 확정 시 클라우드 전송중간 검토/승인 필요할 때
S3 임시 업로드upload/temp/... 경로에 저장 → 확정 시 이동서버리스/대용량 시스템에 유리
S3 Presigned URL 업로드클라이언트가 직접 S3에 업로드, 서버는 확정만 처리확장성과 효율성이 가장 좋음

🧰 7. 3번 + Presigned URL 전략 (추천)

사용자 업로드 → S3에 임시 저장 (temp/) → 확정 시 S3 내부 이동 + DB 저장

흐름 요약

markdown
복사편집
1. 서버: presigned PUT URL 생성 → 프론트로 전달
2. 프론트: S3에 직접 파일 PUT (upload/temp/)
3. 서버: 확정 요청 시 → S3 경로 이동 or 태그 변경
4. 서버: DB에 정식 파일 정보 저장
  • 서버 무부하
  • 사용자 "확정" 전까지 불필요한 저장 방지
  • 정리 작업도 자동화 가능 (temp 경로 정기 삭제)

✅ 최종 정리 요약표

항목추천 전략
파일 업로드 처리MultipartFile → S3 or File
대용량 업로드Presigned URL + 클라이언트 직접 PUT
확정되기 전 상태 유지S3에 임시 저장 (temp/) or 서버 디스크에 임시 파일
첨부파일 시스템 구성S3 Key + 파일 정보만 DB에 저장, 실제 파일은 클라우드
다운로드 처리S3에서 서버 스트리밍 or Presigned GET URL

0개의 댓글