회사에서 프로젝트 도중 이미지를 받아 업로드를 했어야 했다.
문제는 이미지와 dto(application/json) 을 동시에 받아서 처리를 해야 한다는 것이었다.
처음에는 먼저 application/json 을 받고 저장 후 response로 PK 값을 다시 프론트에서 보내면 다시 프론트에서 PK 값과 이미지를 백엔드로 던져주는 방식으로 진행했다.
그러나 api를 두번 전달하는 것은 비효율적이라 생각했다.
백엔드에서 동시에 받는 방법은 의외로 간단하다
@RequestPart
로 각각 MultipartFile과 DTO를 받으면 된다.
@PostMapping
public ResponseEntity<ResponseDto<?>> createEdgeDevice(@RequestPart(value = "request") ARequestDto request,
@RequestPart(value = "file", required = false) MultipartFile file) {
int aId = AService.createA(request, file);
return ResponseEntity.ok()
.body(ResponseDto.builder()
.code("200")
.message("SUCCESS")
.data(aId)
.build());
}
💡
@RequestPart
“multipart/form-data” 요청의 일부를 메소드 인수와 연결하는 데 사용할 수 있는 어노테이션이다. MultipartFile 형식의 파라미터가 들어오면 MultipartFileResolver를 통해 역직렬화를 하게 되고, 만약 메소드 파라미터 타입이 “multipart/form-data” 외의 다른 타입이라면 Request Headers의 Content-type를 고려하여 HttpMessageConverter를 거치게 된다고 한다. 이는@RequestBody
와 유사한 방식이다.
문제는 프론트엔드에서 텍스트와 이미지를 동시에 보내야 했는데 Headers 에 Content-type는 하나만 설정이 가능하다는 것이었다.
다행히 http에서는 Content-type을 multipart으로 설정하면 두가지 이상의 타입을 사용할 수 있다.
const files = this.fileList; //file 목록
const data = {
name: "name",
grade: 3
}
const formData = new FormData();
const json = JSON.stringify(data);
const blob = new Blob([json], { type: 'application/json' });
formData.append("data", blob);
for(let i = 0; i<files.length; i++) {
formData.append("multipart", files[i]);
}
try {
const res = await.axios.post('url', formData, {
headers: {
'Content-Type': 'multipart/form-data'
{
});
} catch (error) {
console.error("error:", error);
}
위와 같이 텍스트는 Blob에 넣고 이미지와 함께 formData에 넣어 content-type를 multipart/form-data로 설정해주니 성공하였다.