섭섭한 개발일지

[SpringBoot RestAPI] Controller에서 json(dto)과 multipart 동시 요청 본문

프로그래밍/Spring

[SpringBoot RestAPI] Controller에서 json(dto)과 multipart 동시 요청

Seop 2024. 2. 21. 14:43

jdk : 17

springboot : 3.2.1

 

사이드 프로젝트 개발 중 문의사항 등록 부분에서 데이터를 title, content, 이미지를 받아와야 하는 상황이 생겼다.

rest로 개발을 하는 중에 있어 postman을 통해 테스트를 해보던 중 미디어 타입의 문제가 발생했다.

 

초기 코드
// Controller
@PostMapping("/save")
@Operation(summary = "문의사항 등록")
public ResponseEntity<QuestionSaveResponse> saveQuestion(@RequestBody QuestionRequest questionRequest,
                                                         @AuthenticationPrincipal UserPrincipal user) {
    QuestionSaveResponse response = questionService.saveQuestion(questionRequest, user.getMember());
    return ResponseEntity.ok(response);
}


// DTO
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Schema(description = "문의사항 등록 요청 항목")
public class QuestionRequest {
    @Schema(description = "문의사항 제목")
    private String title;

    @Schema(description = "문의사항 내용")
    private String content;

    private List<MultipartFile> files;

    public static Question toEntity(QuestionRequest request, Member member) {
        return Question.builder()
                .title(request.getTitle())
                .content(request.getContent())
                .member(member)
                .build();
    }

    public static Question toEntity(QuestionRequest request) {
        return Question.builder()
                .title(request.getTitle())
                .content(request.getContent())
                .build();
    }
}

 

위와 같은 코드로 아래와 같은 에러가 발생했다.

 

postman

 

 

무슨 문제일까?

콘솔에서 에러메세지를 확인할 수 있었다.

exception

 

MediaType과 관련된 오류...

 

키워드를 통해 참고할 만한 글들을 봤다. (참고 글 : https://middleearth.tistory.com/35)

Multipart를 사용하면 @RequestBody가 아닌 @RequestPart를 통해서 받아야 한다는 것을 확인했다.

 

코드를 아래와 같이 수정했다.

 

1차 수정 코드
@PostMapping("/save/test")
@Operation(summary = "문의사항 등록")
public ResponseEntity<QuestionSaveResponse> saveQuestionTest(@RequestPart QuestionRequest questionRequest) {
    QuestionSaveResponse response = questionService.saveQuestion(questionRequest, questionRequest.getFiles());
    return ResponseEntity.ok(response);
}

 

 

postman

 

이번에 서버문제.. 

 

콘솔을 확인해보니

exception

 

questionRequest가 없단다

생각해 보니 당연히 없는 게 맞다.

 

RequestBody는 json type의 데이터를 java object로 파싱을 해주는데

지금 데이터는 json이 아닌 게 아닌가.. 

 

그럼 어떻게 수정을 해야 할까 하다가 서버사이드로 진행한 프로젝트와 같은 방식으로 dto 내부에 files를 두는 게 아닌 파라미터로 받는 선택을 했다.

 

 

2차 수정 코드
    @PostMapping("/save/test")
    @Operation(summary = "문의사항 등록")
    public ResponseEntity<QuestionSaveResponse> saveQuestionTest(@RequestBody QuestionRequest questionRequest,
                                                                 @RequestPart List<MultipartFile> files) {
        QuestionSaveResponse response = questionService.saveQuestion(questionRequest, files);
        return ResponseEntity.ok(response);
    }

 

그렇다면 데이터가 파싱되는 형태를 json으로 처리를 할 수 있으니 dto는 @RequestBody로 어노테이션을 변경했다.

 

테스트 해보자

postman

 

흠..... 왜그러는걸까.. 콘솔을 보니

exception

 

또 타입 오류다.. 이게 왜 안될까 고민을 하다 조금의 시간이 지난 뒤 깨달았다.

@RequestBody와 @RequestPart는 같이 사용할 수 없다는 것을.. (참고 게시글 : https://okky.kr/questions/1212782)

 

 

결론

@RequestPart를 사용하기 위해서는 dto 또한 @RequestBody가 아닌 @RequestPart를 통해서 받아와야 한다.

최종적으로 작성된 코드와 포스트맨은 아래와 같다.

 

최종 수정 코드
    @PostMapping("/save/test")
    @Operation(summary = "문의사항 등록")
    public ResponseEntity<QuestionSaveResponse> saveQuestionTest(@RequestPart QuestionRequest questionRequest,
                                                                 @RequestPart List<MultipartFile> files) {
        QuestionSaveResponse response = questionService.saveQuestion(questionRequest, files);
        return ResponseEntity.ok(response);
    }

 

postman

 

 

 

후기

 

코드가 나만 불편한 건지 모르겠다..

팀원분과 이야기를 나눴을 때는 이 방식으로 결국 가야겠다고 결론을 내리기는 했지만

 

뭔가 마음에 들지 않는다..

dto 안에서 files까지 파싱을 해서 처리를 한다고 하면 컨트롤러의 파라미가 깔끔해지고 @Schema 를 통해 명확하게 역할을 알 수 있는데

이건 처음 보는 사람이 한눈에 파악을 하기에는 어렵지 않을까? 라는 고민이 있다.

 

프로젝트를 진행하면서 다른 방법이 있는지 알아보는 걸로 하고 우선 마무리를 하자 

Comments