모 기업의 채용과제 해결 중 만난 문제이다..
클라이언트로부터 여러 개 첨부파일과 제목, 내용, 일자 등의 데이터를 받아서 저장해야 하는 요구사항을 처리하고 있었다.
이전에 비슷한 것을 기능을 구현해 본 적이 있어서 자신있게 구현에 들어가고 바로 테스트 코드 작성에 들어갔다. 분명 이상한게 없다고 생각했는데 2시간 동안 에러와 싸웠다 ..
요청을 받는 API는 아래와 같은 형태이다
@PostMapping
public ResponseEntity saveNotice(
@RequestPart("notice") SaveNoticeDto saveNotice, @RequestPart(name = "file", required = false) List<MultipartFile> files)
{
...
return new ResponseEntity(HttpStatus.CREATED);
}
public class SaveNoticeDto {
private String title;
private String content;
private LocalDateTime endTime;
}
MultipartFormData
형태를 받기 위해 @RequestPart
를 사용했다.
MultipartFormData
는 각 파트마다 이름과 Content-Type을 갖는 것으로 알고 있었다.
때문에 위 코드를 MockMvc
로 테스트 할 때 file
은 MockMultipartFile
로 만들어서 보내주고 SaveNoticeDto
에 필요한 값은 Dto로 만들고 Json으로 변환하여 MockPart
를 이용해서 보내줄 생각이었다.
@DisplayName("POST /notices 공지사항 등록 API 테스트")
@Test
void saveNoticeApiTest() throws Exception {
MockMultipartFile multipartFile1 = new MockMultipartFile("file", "test.txt", "text/plain", "test file".getBytes(StandardCharsets.UTF_8) );
MockMultipartFile multipartFile2 = new MockMultipartFile("file", "test2.txt", "text/plain", "test file2".getBytes(StandardCharsets.UTF_8) );
SaveNoticeDto noticeDto = new SaveNoticeDto("title1", "content1", LocalDateTime.now().plusDays(7));
mockMvc.perform(multipart("/notices")
.file(multipartFile1)
.file(multipartFile2)
.part(new MockPart("notice", noticeDtoJson.getBytes(StandardCharsets.UTF_8))).contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isCreated());
}
머리에서 그린 테스트를 코드로 그대로 옮긴 것이다.
실행결과는 실패 ..
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported]
처음보는 Content type에 대한 에러가 터졌고 그만할까 생각했다..
사실 이전에 다른 방식으로 위 기능을 동일하게 만든 적이 있다.
조금 다른 방식으로 구현한 것 관련 포스팅
코틀린으로 구현한 것이지만 내용은 거의 같다.
다른 점은 컨트롤러에서 MultipartFile
외에 다른 값을 받을 때 Dto가 아닌 String과 같은 단일값? 객체타입 로 받은 것이다.
똑같은 방식으로 문제를 해결할 수 있었지만 꼭.. Dto에 바인딩시키고 싶었다.
@DisplayName("POST /notices 공지사항 등록 API 테스트")
@Test
void saveNoticeApiTest() throws Exception {
MockMultipartFile multipartFile1 = new MockMultipartFile("file", "test.txt", "text/plain", "test file".getBytes(StandardCharsets.UTF_8) );
MockMultipartFile multipartFile2 = new MockMultipartFile("file", "test2.txt", "text/plain", "test file2".getBytes(StandardCharsets.UTF_8) );
SaveNoticeDto noticeDto = new SaveNoticeDto("title1", "content1", LocalDateTime.now().plusDays(7));
String noticeDtoJson = mapper.writeValueAsString(noticeDto);
MockMultipartFile notice = new MockMultipartFile("notice", "notice", "application/json", noticeDtoJson.getBytes(StandardCharsets.UTF_8));
mockMvc.perform(multipart("/notices")
.file(multipartFile1)
.file(multipartFile2)
.file(notice)
)
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
첨부파일을 위한 MultipartFile
뿐만 아니라 함께 넘어가는 값 또한 Dto로 만들고 Json으로 변한한 뒤 MultipartFile
로 만들어줬다.
덕분에 하나 배우고 갑니다! 감사해요