Contents
see ListSpring Boot에서 파일 업로드가 특정 크기부터 실패하거나, 실패한 이유가 사용자 화면에 보이지 않는다면 업로드 제한을 애플리케이션·프록시·오류 응답까지 같은 기준으로 설정해야 합니다. 핵심은 파일 1개 제한과 요청 전체 제한을 분리하고, 제한 초과를 413 또는 명확한 400 계열 응답으로 처리하며, 실제 배포 경로에서 재현 시험하는 것입니다.
업로드 제한을 하나의 숫자로만 정하면 생기는 문제
관리자 화면에서 상품 이미지, 계약서, 작업 사진을 받는 기능은 파일 수와 파일별 크기가 함께 늘어납니다. 파일 한 개를 10MB로 제한해도 한 요청에 3개를 보내면 총 전송량은 30MB를 넘을 수 있습니다. 반대로 요청 전체 제한만 두면 한 개의 비정상적으로 큰 파일을 막기 어렵습니다. 화면 안내, Spring Boot 설정, Nginx 같은 앞단 프록시의 제한값이 다르면 사용자는 저장 버튼을 눌렀지만 서버 오류 화면만 보게 됩니다.
- 파일별 제한: 한 첨부 파일이 허용되는 최대 크기
- 요청 전체 제한: multipart 요청에 포함된 모든 파일과 폼 필드의 합계
- 파일 개수 제한: 업무 규칙으로 별도 검사할 값
- 허용 형식: 확장자가 아니라 서버가 확인한 MIME 타입과 실제 내용으로 판단할 값
예를 들어 이미지 3개까지, 파일당 10MB, 요청당 32MB라는 업무 규칙을 정할 수 있습니다. multipart 형식에는 경계 문자열과 폼 필드가 포함되므로 요청 전체 제한은 파일 합계보다 약간 여유 있게 잡습니다. 10MB 파일 3개를 허용하려면 32MB처럼 여유를 둔 요청 제한을 검토하고, 운영 환경에서 실제 브라우저 업로드로 확인합니다.
Spring Boot multipart 설정
Spring Boot의 기본 multipart 처리는 application.yml 또는 application.properties에서 제한할 수 있습니다. 아래 설정은 파일 하나는 10MB, 한 요청은 32MB로 제한합니다. 숫자 뒤의 단위를 생략하지 않아야 의도를 분명하게 유지할 수 있습니다.
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 32MB
file-size-threshold: 1MB
location: /var/app/upload-tmp
file-size-threshold는 업로드 내용을 임시 저장소로 넘기기 전 메모리에 둘 기준입니다. 이 값을 무조건 크게 잡으면 동시 업로드 때 힙 사용량이 늘 수 있습니다. location은 애플리케이션 계정이 쓰기 가능한 전용 임시 경로여야 하며, 운영체제의 임시 파일 정리 정책과 디스크 여유 공간도 확인해야 합니다. 여러 인스턴스가 컨테이너로 실행된다면 이 경로는 영구 보관 위치가 아니므로, 검증이 끝난 파일을 별도 스토리지로 옮긴 뒤 임시 파일을 정리하는 흐름이 필요합니다.
컨트롤러에서 파일 개수와 빈 파일을 함께 검사하기
Spring의 용량 제한만으로는 업무 규칙을 모두 표현할 수 없습니다. 아래처럼 업로드 전 파일 개수, 비어 있는 파일, 개별 크기를 검사하면 화면에 필요한 안내를 돌려줄 수 있습니다. 클라이언트의 검사만 믿지 말고 서버에서도 같은 기준을 적용해야 합니다.
@PostMapping("/admin/attachments")
public ResponseEntity<Map<String, Object>> upload(
@RequestParam("files") List<MultipartFile> files) {
if (files.isEmpty() || files.size() > 3) {
return ResponseEntity.badRequest().body(Map.of("code", "FILE_COUNT_INVALID"));
}
for (MultipartFile file : files) {
if (file.isEmpty() || file.getSize() > 10L * 1024 * 1024) {
return ResponseEntity.badRequest().body(Map.of("code", "FILE_SIZE_INVALID"));
}
}
return ResponseEntity.ok(Map.of("status", "ACCEPTED", "count", files.size()));
}
원본 파일명은 경로로 사용하지 않습니다. 서버가 생성한 식별자로 저장 이름을 만들고, 표시용 파일명은 별도 컬럼에 보관합니다. image/png처럼 허용할 형식 목록을 정한 뒤 파일의 실제 내용도 검사해야 하며, 업로드한 파일을 웹 루트에 바로 공개하지 않는 방식이 안전합니다.
제한 초과를 일관된 응답으로 바꾸기
용량 초과는 컨트롤러 메서드에 도달하기 전에 발생할 수 있습니다. 공통 예외 처리기에서 MaxUploadSizeExceededException을 잡아 상태 코드와 오류 코드를 고정하면 화면은 재시도 여부와 안내 문구를 일관되게 처리할 수 있습니다. 응답에는 서버 경로나 내부 예외 메시지를 넣지 않습니다.
@RestControllerAdvice
public class UploadExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
ResponseEntity<Map<String, Object>> tooLarge() {
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).body(Map.of(
"code", "UPLOAD_TOO_LARGE",
"message", "파일은 1개당 10MB까지, 요청 전체는 32MB까지 업로드할 수 있습니다."
));
}
}
프록시가 애플리케이션보다 낮은 제한을 가지면 이 예외 처리기는 실행되지 않습니다. Nginx를 사용한다면 배포 설정에 client_max_body_size 32m처럼 요청 전체 제한을 맞추고, 설정 반영 뒤에는 nginx -t로 문법을 확인한 다음 reload합니다. 로드밸런서, 웹 방화벽, CDN이 앞에 있다면 해당 계층의 요청 크기 제한도 같은 기준으로 확인해야 합니다.
배포 전 확인 순서
- 9MB 파일 1개와 10MB 경계 파일을 업로드해 성공 기준을 확인합니다.
- 10MB를 초과한 파일 1개가 UPLOAD_TOO_LARGE와 413 응답을 받는지 확인합니다.
- 10MB 파일 3개와 폼 필드를 포함한 요청이 32MB 제한 안에서 처리되는지 확인합니다.
- 4개 파일 요청이 FILE_COUNT_INVALID로 거절되는지 확인합니다.
- 프록시를 거친 실제 도메인에서도 상태 코드와 JSON 응답 형식이 같은지 확인합니다.
- 임시 경로의 디스크 사용량, 권한, 실패한 업로드 뒤의 임시 파일 정리 상태를 점검합니다.
파일 업로드는 단순한 화면 기능이 아니라 저장 공간, 보안, 오류 안내가 연결된 운영 기능입니다. 파일별 크기·요청 전체 크기·개수·형식 기준을 먼저 문서화하고, Spring Boot와 프록시에 같은 제한을 적용하면 장애 원인을 빠르게 구분할 수 있습니다.
소프트모아의 서비스와 포트폴리오는 softmoa.com에서 확인할 수 있습니다.
소프트모아는 해당 시스템을 구축합니다. 문의하기
spring
| No | 작성일 | Title |
|---|---|---|
| 3488 | 2026. 09. 18. | Spring Boot 파일 업로드 용량 제한과 오류 응답 설정 방법 |
| 3456 | 2026. 09. 09. | Spring Boot API 오류 응답을 일관되게 만드는 방법: ProblemDetail과 예외 처리 실전 가이드 |
| 3424 | 2026. 08. 31. | Spring Data JPA N+1 조회 문제를 찾고 줄이는 실전 가이드 |
| 3392 | 2026. 08. 23. | Spring Boot 설정값을 안전하게 관리하는 방법: @ConfigurationProperties 검증과 환경별 구성 실전 가이드 |
| 3360 | 2026. 08. 15. | Spring Boot Actuator와 Micrometer로 장애 징후를 먼저 찾는 운영 모니터링 구성 |
| 3328 | 2026. 08. 06. | Spring Boot 예외 처리 표준화 가이드: ProblemDetail로 일관된 API 오류 응답 만들기 |
| 3296 | 2026. 07. 29. | Spring Boot 로그인 API 보안 설계: 세션 없이 JWT를 안전하게 검증하는 방법 |
| 3265 | 2026. 07. 21. | Spring Boot API 장애 원인 빨리 찾기: Actuator와 Micrometer 관측성 구성 가이드 |
| 3238 | 2026. 07. 13. | Spring Boot 안정적인 외부 API 연동: WebClient·Circuit Breaker·Retry를 함께 운영하는 실전 가이드 |
| 3182 | 2026. 07. 04. | Spring Boot 무중단 배포를 위한 Graceful Shutdown과 Readiness Probe 설정 |