[Spring] 파일 업로드 정리

2025. 4. 28. 14:00코딩 도구/백엔드 개발 (Backend Development)

반응형

스프링 MVC 2 - 파일 업로드 정리

이 글은 인프런 김영한님의 "스프링 MVC 2편 - 백엔드 웹 개발 활용 기술" 강의의 '파일 업로드' 파트를 수강하고 정리한 내용이다.

 

이 강의에서는 웹 애플리케이션에서 파일 업로드를 처리하는 다양한 방식을 다루며, multipart/form-data의 구조부터 서블릿 API, 그리고 실무에서 널리 사용하는 MultipartFile 기반의 스프링 방식까지 점진적으로 발전시켜 나간다. 특히 마지막 실무 예제를 통해 실제 파일 업로드, 다운로드 구현 시 고려할 부분들을 세세히 배울 수 있다.


정리 및 흐름 요약

  • 폼 전송 방식 이해: application/x-www-form-urlencoded vs multipart/form-data
  • 서블릿 기반 파일 업로드 처리 (Part API)
  • 스프링의 MultipartFile 활용 방식
  • 실제 파일 저장 로직과 다운로드 처리 구현

1. HTML Form 전송 방식

기본적으로 HTML Form은 두 가지 방식으로 서버에 데이터를 전송할 수 있다:

  • application/x-www-form-urlencoded: 기본 문자 기반 전송 방식
  • multipart/form-data: 파일 업로드를 위한 바이너리 기반 전송 방식

multipart/form-data는 문자열과 파일을 동시에 전송할 수 있어야 할 때 사용하며, 각각의 데이터는 Part 단위로 분리되어 전송되고, 각 Part에는 파일 이름, 타입, 콘텐츠 등의 정보가 포함된다.


2. 서블릿과 파일 업로드 (기초)

HttpServletRequest + Part API 사용

서블릿 3.0 이상부터는 파일 업로드를 위해 request.getParts()를 사용해 multipart 데이터를 직접 다룰 수 있다.

하지만 이 방식은 다음과 같은 한계가 있다:

  • HttpServletRequest 기반으로 작성되어 코드가 복잡하고 직관적이지 않다.
  • 파일 저장 경로 처리, 예외 처리, MIME 타입 처리 등을 개발자가 모두 직접 구현해야 한다.

결론적으로, 기본 개념 학습 목적 외에는 실무에서 잘 사용하지 않는다.


3. 스프링과 파일 업로드 (실무 핵심)

MultipartFile 인터페이스 활용

스프링은 파일 업로드를 보다 쉽게 처리하기 위해 MultipartFile 인터페이스를 제공한다. 이 방식은 컨트롤러에서 다음과 같이 간단히 선언하여 사용할 수 있다:

@PostMapping("/upload")
public String saveFile(@RequestParam String itemName,
                       @RequestParam MultipartFile file) throws IOException {
    log.info("itemName={}", itemName);
    log.info("multipartFile={}", file);

    if (!file.isEmpty()) {
        String fullPath = fileDir + file.getOriginalFilename();
        file.transferTo(new File(fullPath));
    }
    return "upload-form";
}

장점

  • multipart 요청을 자동으로 분석하여 MultipartFile로 주입해준다.
  • 코드가 간결하고 생산성이 높다.
  • 파일 이름, 확장자, 크기, Content-Type 등에 쉽게 접근할 수 있다.
  • transferTo(...) 메서드를 통해 서버 파일로 간단히 저장할 수 있다.

주요 메서드

  • getOriginalFilename(): 클라이언트가 업로드한 파일명 반환
  • getSize(): 파일 크기 반환
  • getContentType(): MIME 타입 반환
  • transferTo(...): 파일 저장 처리

이 방식은 실무에서 가장 많이 사용되는 파일 업로드 처리 방식이다.


4. 실무 예제로 구현한 파일 업로드/다운로드

요구사항

  • 상품 등록 시 첨부파일 1개, 이미지 여러 개 업로드 가능해야 한다.
  • 업로드한 첨부파일은 다운로드 가능해야 한다.
  • 업로드한 이미지들은 브라우저에서 바로 확인 가능해야 한다.

핵심 클래스 설계

1) UploadFile

@Data
public class UploadFile {
    private String uploadFileName; // 사용자가 업로드한 파일명
    private String storeFileName; // 서버에서 관리하는 유니크 파일명

    public UploadFile(String uploadFileName, String storeFileName) { ... }
}

2) FileStore

public class FileStore {
    public UploadFile storeFile(MultipartFile multipartFile) {...}
    public List<UploadFile> storeFiles(List<MultipartFile> multipartFiles) {...}

    private String createStoreFileName(String originalFilename) { ... }
    private String extractExt(String originalFilename) { ... }
}
  • UUID를 사용하여 서버 저장 파일명을 유일하게 생성한다.
  • 원래 확장자를 유지하여 MIME 타입 처리를 일관되게 할 수 있다.

3) Item, ItemForm, ItemRepository

  • 상품 등록 폼에서 MultipartFile, List<MultipartFile>을 입력받도록 구성한다.
  • 업로드된 파일은 서버에 저장하고, 해당 정보는 메모리 기반 저장소에 저장한다.

4) ItemController

@PostMapping("/items/new")
public String saveItem(@ModelAttribute ItemForm form, ...) throws IOException {
    UploadFile attachFile = fileStore.storeFile(form.getAttachFile());
    List<UploadFile> storeImageFiles = fileStore.storeFiles(form.getImageFiles());

    Item item = new Item(...);
    itemRepository.save(item);
    return "redirect:/items/{itemId}";
}
  • 이미지 조회는 <img src="/images/{storeFileName}"> 방식으로 처리한다.
  • 첨부파일 다운로드는 <a href="/attach/{itemId}"> 링크를 제공한다.

마무리 정리

  • 파일 업로드는 단순히 파일만 서버에 저장하는 것이 아니라, 파일 이름 관리, 확장자 처리, MIME 타입 처리, 보안 이슈 등 다양한 요소를 고려해야 한다.
  • 서블릿 기반보다는 MultipartFile을 활용하는 스프링 방식이 실무에서 더 효율적이고 관리하기 쉽다.
  • UUID 기반의 내부 저장 파일명과 사용자가 업로드한 파일명을 분리함으로써 파일명 충돌 및 보안 문제를 예방할 수 있다.
반응형