MyThing 프로젝트를 진행중에 파일업로드를 진행하는 일이 있었다. 테스트 진행하는것은 생각하지 않고 multipartForm데이터로 통신하는것임에도 patchMapping으로 설정하였다.
(회원정보를 수정하는 API라서 한번에 사진까지 변경할 수 있게 로직을 작성하였다.)
하지만 MockMvc에서 Multipart 요청은 HTTP 메소드가 POST로 강제되었다.. 내가 작성한 API는 아무리봐도 수정하는 것이지만, 결국 나는 patch에서 post로 변경하였고 그렇게 테스트를 통과하게 되었다.
그래서 테스트 하는 방법은?
코드를 먼저 확인하자.
~~~~~
final String fileName = "testFile.png";
final String contentType = "image/png";
final String filePath = "src/main/resources/" + fileName;
FileInputStream fileInputStream = new FileInputStream(filePath);
MockMultipartFile mockMultipartFile = new MockMultipartFile(
"multipartFile",
fileName,
contentType,
fileInputStream
);
given(userService.uploadImageAndEditUserProfile(any(), any(), any(), any(), any())).willReturn(responseImageURl);
//when
ResultActions perform = mockMvc.perform(
multipart("/users/profiles")
.file(mockMultipartFile)
.part(new MockPart("userId", "1".getBytes(StandardCharsets.UTF_8)))
.part(new MockPart("name", "백시온".getBytes(StandardCharsets.UTF_8)))
.part(new MockPart("infoMessage", "상태메세지 입니다.".getBytes(StandardCharsets.UTF_8)))
.part(new MockPart("birthDay", "1999-04-08".getBytes(StandardCharsets.UTF_8)))
.contentType(MediaType.MULTIPART_FORM_DATA)
);
~~~~~
앞뒤로 많은 로직이 존재하지만, MockMultipartFile과 관련없는 로직은 제거하였다.
파일을 생성한뒤 mockMultipartFile을 생성한다. mockMultipartFile 생성자의 첫번째 인자는 내가 설정한 통신할때 multipartForm데이터에서 설정한 키값명이다.
위의 코드에서 가장 중요한 점은 테스트 이미지 파일이 내가 지정해준 filePath에 존재해야 한다는것이다. 이부분에서 어려운이 많았다. 그저 가짜 url만을 집어넣어줘도 가능한것이라 생각했지만, 결론은 실제 파일이 존재해야 모킹멀티파트파일 객체가 생성된다.
많은 도움을 얻어가길 바란다.
API를 작성후 파일업로드와 데이터 요청을 한번에 보내는것이 옳은 것인가에 대해서 궁금해서 찾아보았다. 테스트 코드 작성하는법에 많은 도움을 얻은 망나니 개발자님의 사이트에 또 도움을 얻을 수 있었다.
앞서 설명한대로 저는 개발을 진행하다가 데이터와 파일을 함께 보내는 API를 설계하였습니다. 그런데 문제는 데이터와 파일을 함께 보내기는 API가 바람직하지 않다는 점입니다.
HTTP 스펙의 주요 저자이자 REST 아키텍처 스타일의 창시자인 Roy T. Fielding은 이러한 상황을 두고 다음과 같이 코멘트하였습니다.
나와 같은 상황이다.
Roy T. Fielding이 하고자하는 얘기는 데이터와 파일(서로 다른 포맷의 리소스)이 혼재된 패키지 데이터는 데이터 형식이 명확하지 못한데, 그러한 이유는 서로 다른 리소스를 한번에 처리하기 때문이라는 것입니다. 그렇기 때문에 명확한 Zip과 같은 형태로 데이터를 묶는 것이 더 좋다는 것인데, 그것보다는 아무래도 서로 다른 리소스인 데이터와 이미지의 API를 각각 따로 만드는 것이 더욱 좋은 설계일 것입니다. 그러므로 우리도 이러한 작업을 해야하는 경우에 서로 다른 포맷의 리소스가 혼재된 경우에는 분리해서 API를 개발하는 것이 좋을 것 같습니다.
이글에서 많은 도움을 얻었다. 글에서 정확하게 설명하진 않았지만, 내 개인적인 생각은 서로다른 리소스를 한번에 처리하는것은 서버에 부담이 가고, 유지보수가 좋은 설계가 아닐것이다. 라고 결론을 내렸다. 하지만, 한가지 의문이 더 생기고 말았다.
그러면 카카오톡은 프로필을 수정할 때 프로필 이미지와 이름, 상태메세지등을 한번에 수정 가능한데?
이미지를 수정하려고 올리기만해도 서버에 저장되는 것인가? -> 그렇다면 너무 많은 사진을 불필요하게 저장하는것 같은데? 라는 생각을 하였다. 정확한것은 실무에 가서 확인이 가능하겠지만, 지금도 예측가능한 상황은 실무에서는 언제나 예외라는것이 존재한다는 점이다. 아마 카카오도 나와같은 방식으로 API를 만들었을거라고 생각한다.