1. 시작하며
웹 애플리케이션을 개발할 때, JSON 데이터와 파일을 함께 전송해야 하는 경우가 있습니다.
예를 들어, 게시글 작성 시 게시글 정보(JSON)와 첨부파일(MultipartFile)을 함께 API에 전달해야 하는 경우를 생각해 볼 수 있습니다.
보통 API에서 @RequestBody
를 사용하여 JSON 데이터를 받지만, multipart/form-data 요청에서는 @RequestBody
를 사용할 수 없습니다.
이러한 경우, Spring의 @RequestPart
어노테이션을 활용하면 JSON과 파일을 동시에 전송할 수 있습니다.
이 글에서는
@RequestBody
와@RequestPart
의 차이점@RequestPart
를 활용하여 게시글 정보(JSON) + 첨부파일을 한 번의 요청으로 처리하는 방법- 프론트엔드에서
multipart/form-data
로 API를 호출하는 방법
위 내용을 정리하고 실전 예제 코드를 통해 이해를 돕겠습니다.
2. @RequestBody
와 @RequestPart
비교
2.1 @RequestBody
란?
@RequestBody
는 HTTP 요청 본문(body) 데이터를 자바 객체로 변환하는 역할을 합니다.
Spring에서는 HttpMessageConverter를 이용하여 요청 데이터를 처리하며, 일반적으로 application/json
타입의 데이터를 처리합니다.
1) @RequestBody
의 특징
application/json
타입의 요청 데이터를 받아 자바 객체로 변환- 파일을 포함한
multipart/form-data
요청을 처리할 수 없음 @RequestBody
를 사용할 경우 Content-Type을 반드시 명시해야 함 (application/json
)- 단순 JSON 형식의 요청 데이터를 DTO로 변환하여 사용할 때 주로 활용
- API에서 클라이언트가 보낸 JSON 데이터를 검증하고 비즈니스 로직에서 활용할 때 유용
즉, 파일을 포함하여 데이터를 전송해야 하는 경우에는 @RequestBody
를 사용할 수 없으며, JSON 데이터를 DTO로 변환하여 처리할 때 적합합니다.
2.2 @RequestPart
란?
@RequestPart
는 multipart/form-data 요청을 처리할 때 사용됩니다.
@RequestPart
를 사용하면 파일과 JSON 데이터를 함께 전송할 수 있습니다.
1) @RequestPart
의 특징
- JSON 데이터 + 파일을 함께 처리 가능
- Content-Type이
multipart/form-data
여야 함 MultipartFile
을 함께 받을 수 있음@RequestBody
와 다르게 JSON과 파일을 함께 전송할 수 있도록 지원
3. JSON + 파일 전송 시 문제점
3.1 @RequestBody
의 한계
@PostMapping("/boards")
public ResponseEntity<Long> createBoard(
@RequestBody BoardCreateRequest boardCreateRequest // JSON 데이터 받기
) {
long boardId = boardUseCase.createBoard(boardCreateRequest);
return ResponseEntity.ok(boardId);
}
위와 같이 @RequestBody
를 사용하면, JSON 데이터는 받을 수 있지만 파일은 받을 수 없습니다. (즉, 게시글 생성 정보만 받을 수 있습니다.)
이는 @RequestBody
가 application/json
형식의 데이터만 처리하도록 동작하기 때문입니다.
multipart/form-data
요청에서는 @RequestBody
를 사용할 수 없으므로, 다른 방법이 필요합니다.
4. @RequestPart
를 활용한 JSON + MultipartFile 전송
4.1 백엔드에서 JSON + MultipartFile 데이터 요청 받아 처리하기
@RequestPart
를 사용한 Spring 컨트롤러 예제 코드
@PostMapping(
value = "/boards",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE
)
public ResponseEntity<Long> createBoardWithFiles(
@RequestPart("board") BoardCreateRequest boardCreateRequest, // JSON 데이터 받기
@RequestPart(value = "files", required = false) List<MultipartFile> files // 파일 받기 (선택적)
) {
long boardId = boardUseCase.createBoardWithFiles(boardCreateRequest, files);
return ResponseEntity.ok(boardId);
}
해당 코드에서 @RequestPart
동작 방식은 다음과 같습니다.
@RequestPart("board")
→ JSON 데이터(BoardCreateRequest
)를multipart/form-data
에서 추출하여 역직렬화@RequestPart(value = "files", required = false)
→ MultipartFile 리스트를 받아 파일을 처리consumes = MediaType.MULTIPART_FORM_DATA_VALUE
→ multipart 요청을 처리하도록 설정
이제 API 요청 시 JSON 데이터와 파일을 동시에 전송할 수 있습니다.
4.2 프론트엔드에서 multipart/form-data
요청 보내기
FormData
를 활용하여 JSON과 파일을 함께 전송하는 React 예제 코드
function createBoardWithFiles(request: BoardCreateRequest, files?: File[]) {
const formData = new FormData();
// JSON 데이터 추가 (Blob으로 변환)
const jsonData = new Blob([JSON.stringify(request)], { type: 'application/json' });
formData.append('board', jsonData);
// 파일 추가
if (files && files.length > 0) {
files.forEach(file => {
formData.append('files', file);
});
}
// API 호출
return api.post('/boards', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
}
해당 코드의 요청 흐름은 다음과 같습니다.
- JSON 데이터를
Blob
으로 변환하여 FormData에 추가 files
배열을formData.append('files', file)
을 통해 추가multipart/form-data
형식으로 API에 전송
이제 프론트엔드에서 게시글 데이터 + 파일을 함께 서버로 전송할 수 있습니다.
4.3 @RequestPart
와 consumes
속성의 관계
Spring에서는 @RequestPart
를 활용하여 JSON 데이터와 파일을 함께 처리할 수 있습니다.
이때, @PostMapping
또는 @RequestMapping
의 consumes
속성을 적절히 설정하는 것이 중요합니다.
1) consumes
속성이란?
consumes
속성은 해당 API가 처리할 수 있는 요청의 Content-Type을 명시하는 역할을 합니다.
즉, 클라이언트가 전송하는 요청의 MIME 타입이 consumes
속성에 지정된 값과 일치해야 합니다.
Spring에서 사용할 수 있는 consumes
속성에는 여러 미디어 타입이 있으며, 대표적인 예는 다음과 같습니다.
2) consumes
속성에 지정할 수 있는 미디어 타입 (MIME 타입)
MIME 타입 | 설명 |
---|---|
MediaType.APPLICATION_JSON_VALUE (application/json ) |
JSON 데이터 처리 |
MediaType.APPLICATION_XML_VALUE (application/xml ) |
XML 데이터 처리 |
MediaType.APPLICATION_FORM_URLENCODED_VALUE (application/x-www-form-urlencoded ) |
URL 인코딩된 폼 데이터 처리 (HTML 폼 제출 시 사용) |
MediaType.MULTIPART_FORM_DATA_VALUE (multipart/form-data ) |
파일 업로드 처리 (JSON + 파일 전송 가능) |
MediaType.ALL_VALUE (*/* ) |
모든 콘텐츠 타입 허용 |
3) multipart/form-data
요청에서 JSON을 받을 수 있는 이유 (@RequestPart
에서MediaType.APPLICATION_JSON_VALUE
을 consumes
에 추가할 필요가 없는 이유)
multipart/form-data
요청을 처리할 때는 JSON 데이터가 multipart의 일부(part)로 포함되기 때문에 별도로 application/json
을 consumes
에 명시할 필요가 없습니다.
즉, MediaType.MULTIPART_FORM_DATA_VALUE
만 지정하면 JSON 데이터도 함께 처리할 수 있습니다. Spring이 multipart 내부의 JSON을 자동 변환하여 처리해 주기 때문입니다.
5. @RequestBody
vs @RequestPart
비교
어노테이션 | 사용 목적 | 지원하는 Content-Type | JSON + 파일 동시 전송 가능 여부 |
---|---|---|---|
@RequestBody |
JSON 요청 본문을 객체로 변환 | application/json |
❌ (파일 포함 불가) |
@RequestPart |
multipart/form-data 내 JSON 및 파일 처리 |
multipart/form-data |
✅ (파일 포함 가능) |
6. 결론
@RequestBody
는application/json
요청만 처리 가능하며, 파일을 함께 전송할 수 없음@RequestPart
를 사용하면 JSON + 파일을 동시에 전송 가능- 프론트엔드에서는
FormData
를 활용하여 JSON을Blob
으로 변환 후 전송
이번 글에서는 Spring에서 @RequestPart
를 활용하여 JSON과 파일을 동시에 처리하는 방법을 정리했습니다.
@RequestPart
를 사용하면 게시글과 첨부파일을 하나의 API 요청으로 처리할 수 있어 데이터 정합성을 보장할 수 있습니다.
REST API 설계 시, 리소스 중심의 URL을 유지하고, HTTP 메서드를 적절히 사용하여 multipart/form-data
요청을 처리하는 것이 중요합니다.
또한, 프론트엔드에서는 FormData
를 활용하여 JSON을 Blob
으로 변환하는 방식도 고려해야 합니다.
위와 같은 방법으로 설계를 변경하는 것이 바람직합니다. 이번 문제 해결 과정을 통해 HTTP 표준 준수와 RESTful 설계의 중요성을 다시 한 번 깨달을 수 있었습니다.
7. 참고
'BackEnd > Spring & JPA' 카테고리의 다른 글
[Spring Data JPA] JPA 엔티티 설계 시 생성자 접근 제한을 PROTECTED로 설정하는 이유 (0) | 2025.01.21 |
---|---|
[Spring] @ResponseBody VS ResponseEntity<T> (0) | 2025.01.06 |
[Spring] @Controller VS @RestController (0) | 2025.01.06 |
[Spring] 스프링 개념 및 동작 원리 정리 (0) | 2025.01.05 |