API 요청 별 DTO 매핑 어노테이션 방식에 대한 것 을 정리하고자 기록한다.
주로 GET 방식으로 요청 할 때 queryString을 DTO로 변환해주는 @ModelAttribute 와 @RequestParam을 썼던 것 같고
POST방식에서는 @RequestBody 방식을 많이 썼다.
이번에 form-data 전송을 위한 MultipartFile데이터 처리에 대한 궁금이 생겨 기록 고고띵 한다.
일단! MultipartFile은 은 @RequestParam랑은 써봤던 것 같은데 @RequestPart 이번에 써보게 되면서 궁금해짐 👀
내 궁금증에 대한 요약을 해보자면 ❗️
✅ 결론 요약
✔ MultipartFile은 @RequestParam 또는 @RequestPart만 가능하다.
❌ @RequestBody MultipartFile는 절대 안 된다. (스프링 구조상 불가능)
🔍 왜 MultipartFile은 @RequestBody로 받을 수 없을까?
1) @RequestBody는 순수한 Body(JSON/XML/Raw data) 를 받아서 변환하는 구조를 가지며 이런 걸 HttpMessageConverter 로 변환해주는 방식이다. ➡️ 그런데 multipart/form-data는 메시지 컨버터가 아니라 MultipartResolver가 처리함.
2) multipart/form-data는 하나의 Body가 아니라 여러 파트의 조합 ➡️ form-data([key, value]) , form-data([key2,value2])
🔥 스프링이 Multipart를 처리하는 흐름
요청이 오면 → MultipartResolver가 multipart를 파싱
각 part를 Map 형태로 저장
file part는 → MultipartFile 객체로 매핑
그리고 컨트롤러 파라미터로 바인딩됨
즉, MultipartFile은 MultipartResolver가 생성한 객체이기 때문에
@RequestBody처럼 메시지 컨버터가 처리할 수 없음.
그리고 두번째로 파일 데이터 처리하면서 궁금했떤 RequestPart 랑 RequestParam 차이점 ‼️
🔥 핵심 요약 (한 문장 버전)
✔ @RequestParam → form-data의 “텍스트 필드” 또는 “파일” 같은 단순 파트에 사용
✔ @RequestPart → JSON + 파일처럼 “multipart/form-data 내부의 객체(JSON) 또는 파일 파트"를 명확히 구분해 받는 데 사용한다.
즉, 단순 텍스트 + 파일 → @RequestParam
JSON + 파일 → @RequestPart
@RequestParam으로 JSON 매핑 ? 가능은 하다.
근데 JSON 이 스트링 형식으로 변환되어서 요청이 오기 때문에 직접 역직렬화 작업을 진행해야 한다.
✅ 파일 업로드에서 @ModelAttribute를 쓸까 ?
✔ @ModelAttribute는 텍스트 필드 + 파일을 함께 DTO로 묶어서 받고 싶을 때 가능하다.
✔ JSON + 파일 조합에서는 절대 사용 못함 → 이건 반드시 @RequestPart.
✔ 추가로 @ModelAttribute는 GET 요청에서 쿼리 스트링(query string)을 DTO로 매핑할 때 가장 대표적으로 쓰는 방식이다.
요청시 매핑하고 싶은 DTO 예시를 들자면 이렇게 텍스트와+파일 조합의 form-data를 보내고 아래 예시와 같은 DTO로 @ModelAttribute가 변환한다.
/*DTO*/
public class UploadRequest {
private String title;
private String description;
private MultipartFile file;
// getter/setter
}
/*컨트롤러에서 요청 처리시 예시 */
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> upload(@ModelAttribute UploadRequest request) {
}
📌 GET에서 ModelAttribute가 좋은 이유
✔ DTO의 모든 필드에 자동으로 바인딩된다
스프링의 DataBinder가 필드명과 동일하게 파라미터를 매칭함.
✔ 기본값 처리, 타입 변환이 자동
int, boolean 같은 primitive 타입도 자동 변환.
✔ 파라미터 개수가 많아질수록 유지보수성 증가
필드 추가해도 컨트롤러 시그니처 변경할 필요 없음.
✔ 검증(Validation)도 쉽게 적용 가능
@Valid + @ModelAttribute 조합이 자연스럽게 동작.
🔥 스프링 웹 요청 매핑 어노테이션 완전 비교표
📌 RequestParam / RequestPart / ModelAttribute / RequestBody 전체 비교
| 구분 | @RequestParam | @RequestPart | @ModelAttribute | @RequestBody |
|---|---|---|---|---|
| 주 사용 용도 | 단순 파라미터(key=value), 단일 파일 | JSON + 파일 조합 (multipart 파트 구분) | 쿼리스트링, form-data(text+file) DTO 매핑 | JSON을 DTO로 받을 때 |
| 주로 사용하는 Content-Type | multipart/form-data, application/x-www-form-urlencoded, query-string | multipart/form-data | multipart/form-data, query-string, x-www-form-urlencoded | application/json |
| JSON 받을 수 있는가? | ❌ 직접 받을 수 없음 (String만 가능) | ✔ JSON → DTO 자동 매핑 | ❌ 불가능 | ✔ JSON → DTO 자동 매핑 |
| 파일(MultipartFile) 받을 수 있는가? | ✔ 가능 | ✔ 가능 | ✔ 가능 (DTO 필드로 가능) | ❌ 불가능 |
| 자동 객체 변환 | 기본 타입만 | JSON / 파일 자동 분리 | DTO 자동 바인딩 (Key-Value) | JSON → DTO 메시지 컨버터 |
| 언제 주로 쓰는가? | 텍스트 + 파일 간단 업로드 | JSON 메타데이터 + 파일 업로드 | GET 쿼리스트링 → DTO 매핑 / form-data DTO 매핑 | JSON POST/PUT 요청 |
| 클라이언트 FormData 요구 방식 | formData.append("file", file) | formData.append("meta", blob(JSON)) | key=value 구조 그대로 사용 | JSON body |
| 복잡한 구조 처리 | X | ✔ | X | ✔ |
| 멀티파트 구조 인식 | 부분적으로 | ✔ multipart 파트 단위로 분석 | ✔ 값 바인딩 형태 | X (multipart 자체는 못 파싱) |
| 스프링 내부 처리 방식 | WebDataBinder | MultipartResolver + HttpMessageConverter | WebDataBinder | HttpMessageConverter |
| GET 요청에 사용 | ✔ | 거의 안 씀 | ✔ (대표적) | ❌ |
🔍 각 어노테이션 핵심 요약 (1줄씩)
@RequestParam
→ 단순한 form-data / query parameter / 파일을 받을 때 가장 일반적인 방식.
@RequestPart
→ multipart/form-data 안에서 JSON + 파일 같은 복합 구조를 받을 때 필수.
@ModelAttribute
→ 여러 텍스트 필드를 DTO로 묶고 싶을 때 (GET/POST 모두), 파일도 DTO에 포함 가능. (쿼리 스트링으로 온 요청도 해당 어노테이션으로 매핑가능)
@RequestBody
→ JSON 요청 바디를 DTO로 변환할 때 유일하게 사용해야 하는 방식.