Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.io.File` from Object value (token `JsonToken.START_OBJECT`) 오류 해결

2024. 7. 2. 16:34트러블슈팅

반응형

js에서 ajax로 api를 호출할 때 json 형식으로 보내고자 했다. 그러나 api 호출 시 오류가 발생하였다.

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.io.File from Object value (token JsonToken.START_OBJECT)

무언가 직렬화할 때 나는 문제인것같았다.

아래는 내 코드이다.

const config = files[0];

const param = {
            name: $("#nameVal option:selected").text(),
            config: config,
            rateVal : $('#rateVal').val(),
            cntVal: $('#cntVal').val(),
            skillVal: $('#skillVal').val()
        }
        
$.ajax({
            url: '/api/example', // API 엔드포인트 URL
            type: 'POST', // 요청 방식 (GET, POST 등)
            data: param, // 전송할 데이터
            contentType: false, // 전송할 데이터 타입
            dataType: false, // 수신받을 데이터 타입
            success: function(response) {
                // 요청이 성공했을 때 실행할 코드
                console.log('Success:', response);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                // 요청이 실패했을 때 실행할 코드
                console.error('Error:', textStatus, errorThrown);
            }
        });
@PostMapping("/api/example")
public ResponseDTO exampleApi(@RequestBody RequestDTO request) {
    return null;
}
@Getter
@Setter
public class RequestDTO{
    private String nameVal;
    private File config;
    private Long rateVal;
    private Long cntVal;
    private Long skillVal;

}

해당 오류는 서버가 JSON 객체에서 java.io.File 타입을 직렬화/역직렬화할 수 없기 때문에 발생한다. 파일을 포함한 데이터를 API에 전송하려면, 일반적으로 multipart/form-data 형식을 사용한다. 이 형식을 사용하면 파일과 JSON 데이터를 함께 전송할 수 있다.

이 오류를 완전히 이해하기 위해서는 @RequestBody, @ReqestParam, @RequestPart, @ModelAttribute의 차이점과 각각의 쓰임을 이해하고 있어야한다.

@RequestBody
application/json을 주고받을때 주로 사용함. multipart/form-data이 포함되는 경우는 사용 불가.

@RequestPart
@RequestBody + multipart/form-data인 경우에 사용. 

@RequestParam
1개의 HTTP 파라미터를 받을 때 사용. multipart/form-data을 받아야 되는 경우에 사용 가능. 기본 설정으로 필요 여부가 필수로 되어있음.

@ModelAttribute
주로 폼 데이터를 객체에 바인딩할 때 사용. 파일 업로드보다는 텍스트 필드와 같은 단순한 폼 데이터를 처리할 때 사용.

처음에 나는 json 형식과 파일을 함께 받기 위해서 @RequestPart를 사용하려고 하였다. 하지만 그렇게하려면, MultipartFile 데이터와 나머지 파라미터들을 FormData로 넘겨줘야하는데 이렇게되면 api에서 파라미터를 두가지로 받아줘야한다. 근데 나는 하나의 DTO안에 file과 나머지 데이터들을 담고싶었다. 

아래는 @ModelAttribute를 사용한 내 코드이다.

@PostMapping("/api/example")
public ResponseDTO exampleApi(@ModelAttribute RequestDTO request) {
    return null;
}

이렇게 해놓으니 코드는 한결 단순해지긴했으나.. @RequestPart를 쓰는거랑 큰 차이가 없는것같았다.

@RequestPart와 @ModelAttribute의 차이점을 간략하게 정리해보았다.

  • @RequestPart:
    • 주로 Multipart 요청에서 사용.
    • 파일 업로드와 함께 JSON 또는 다른 데이터를 처리할 때 사용.
    • 명확하게 파일과 JSON 데이터를 분리하여 처리할 수 있음.
  • @ModelAttribute:
    • 주로 폼 데이터를 객체에 바인딩할 때 사용.
    • 파일 업로드보다는 텍스트 필드와 같은 단순한 폼 데이터를 처리할 때 사용.
    • HTML 폼 데이터와 유사한 구조를 처리할 때 유용.

어떤 상황에 무엇을 사용할지:

  • 파일 업로드가 포함된 요청: @RequestPart가 더 적합하다. 파일과 JSON 데이터를 분리하여 명확하게 처리할 수 있다.
  • 단순 폼 데이터 처리: @ModelAttribute가 더 적합하다. 여러 폼 필드를 객체로 바인딩할 때 유용하다.

 

주요 차이점은 @RequestPart는 명확하게 파일과 JSON 데이터를 분리하여 처리한다는 점인데, 이때 json 파일만 따로 파싱해서 사용을 할때가 있다던지 파일에 대한 다른 처리를 한다던지 로직을 구현할 때, 하나의 DTO에 담겨있는거보단 분리를 해주는게 효율적이라는 것이다! 

그래서 결론은 나는 단순한 폼 데이터를 처리하는게 아니고 파일을 다루며, json 파라미터와 분리하고 싶기때문에 @RequestPart를 사용할 것이다!

 

아래는 다시 수정한 나의 코드이다.

let formData = new FormData();
formData.append('config', config);
formData.append('data', new Blob([JSON.stringify(param)], { type: 'application/json' }));

$.ajax({
    url: '/api/exampleApi', // API 엔드포인트 URL
    type: 'POST', // 요청 방식 (GET, POST 등)
    data: formData, // 전송할 데이터
    contentType: false, // 전송할 데이터 타입
    dataType: false,
    processData: false,
    success: function(response) {
        // 요청이 성공했을 때 실행할 코드
        console.log('Success:', response);
    },
    error: function(jqXHR, textStatus, errorThrown) {
        // 요청이 실패했을 때 실행할 코드
        console.error('Error:', textStatus, errorThrown);
    }
});
@PostMapping("/api/example")
public ResponseDTO exampleApi(@RequestPart("config") MultipartFile file, @RequestPart("data") RequestDTO data) {
    return null;
}

 

반응형