Spring Controller에서 MultipartFile, Dto를 함께 요청하기

라모스·2022년 7월 26일
6

삽질.log

목록 보기
9/12

문제 상황

게시글에 이미지를 첨부하여 작성하는 기능을 구현하고자 Controller에서 @RequestBody로 Dto를 받고, @RequestPart로 이미지 파일을 받고자 하는 상황이었다. Postman에서 해당 API를 테스트하는데 다음과 같은 오류가 발생하였다.

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported

해결책

이 경우, dto와 file 모두 @RequestPart로 받고, 해당 애노테이션에서 value값을 정확하게 지정해야 한다.

애노테이션 value값을 지정하지 않았을 경우 컨트롤러가 다음과 같이 읽지 못하는 경우가 발생한다.

Required request part 'dto' is not present

코드를 수정하면 다음과 같다.

@PostMapping("/boards")
public ResponseEntity<ResultResponse> enroll(@RequestPart(value = "dto") EnrollRequest dto,
                                             @RequestPart(value = "files") List<MultipartFile> files,
                                             @AuthenticationPrincipal AuthMember authMember) {
    BoardResponse responseDto = boardService.create(dto, files, authMember);
    ResultResponse result = ResultResponse.of(ResultCode.ENROLL_SUCCESS, responseDto);
    return new ResponseEntity<>(result, HttpStatus.valueOf(result.getStatus()));
}

이 후 Postman에서 form-data로 다음과 같이 지정하여 요청하면 정상적으로 API 응답 결과가 반환된다. (세부적인 사항도 정확하게 지정해야 한다.)

// 임시적으로 file을 실제 파일명과 서버에 저장하는 파일명을 구분하지 않고 지정한 상태다.

Controller Unit Test

@Test
@DisplayName("게시글 등록 성공")
@WithMockAuthUser(id = 1L, email = "test@test.com", role = Role.ROLE_ADMIN)
void postSuccess() throws Exception {
    //given
    Map<String, String> input = new HashMap<>();
    input.put("title", "게시글 제목 테스트");
    input.put("contents", "게시글 본문 테스트");

    BoardResponse board = givenBoard();

    MockMultipartFile files = new MockMultipartFile(
            "files",
            "imagefile.png",
            "image/png",
            "<<png data>>".getBytes());
    String contents = mapper.writeValueAsString(input);

    given(boardService.create(any(EnrollRequest.class), anyList(), any(AuthMember.class))).willReturn(board);

    //andExpect
    mockMvc.perform(
            multipart("/boards")
                    .file(files)
                    .file(new MockMultipartFile("dto", "", "application/json", contents.getBytes(StandardCharsets.UTF_8)))
                    .contentType("multipart/form-data")
                    .accept(MediaType.APPLICATION_JSON)
                    .characterEncoding("UTF-8"))
            .andExpect(status().is2xxSuccessful())
            .andDo(print());
}

WebMvcTest를 통해 해당 메소드를 단위 테스트하는 과정에서 다음과 같은 오류가 있었지만, 위 코드와 같이 해결하였다.

  • Content type 'application/octet-stream' not supported

테스트 결과 모두 성공하였다.

References

profile
Step by step goes a long way.

0개의 댓글