본문 바로가기

🧑🏻‍💻 Dev/오류해결

[오류해결] No serializer found for class (InvalidDefinitionException)

🤔 문제 발생

Request 요청을 위한 DTO로 사용하기 위한 클래스를 생성한 후 해당 객체를 ObjectMapper의 writeValueAsString() 메서드에 넣어 serialize 해서 json 데이터로 mvc에 content에 담아 POST 요청을 하는 테스트를 진행했습니다.

 

# PlaceRequest.java 코드

@AllArgsConstructor
public class PlaceRequest {
    private PlaceType placeType;
    private String placeName;
    private String address;
    private String phoneNumber;
    private Integer capacity;
    private String memo;

    public static PlaceRequest of(
            PlaceType placeType,
            String placeName,
            String address,
            String phoneNumber,
            Integer capacity,
            String memo
    ) {
        return new PlaceRequest(placeType, placeName, address, phoneNumber, capacity, memo);
    }
}

위 코드의 static 메서드인 of()로 만든 객체를 ObjectMapper.writeValueAsString()에 넣어 serialize를 진행했고, 아래와 같은 오류가 발생했습니다.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class

 

 

 

🙆🏻‍♂️ 시도 / 해결

1. PlaceRequest 클래스에 빈 생성자가 존재하지 않으면 Serializer가 작동하지 않는다.

@NoArgsConstructor
@AllArgsConstructor
public class PlaceRequest {
    /* 코드생략 */
}

@NoArgsConstructor 애노테이션을 붙여 비어있는 생성자를 만들어줬습니다. 그리고 다시 실행했을 때의 결과는 동일한 예외가 발생되었습니다.

 

JackSon에서 Deserialize(역직렬화, json -> Object)를 할 때는 기본 생성자로 객체를 생성한 후 필드 값을 찾아서 바인딩해준다고 합니다. 하지만 우리가 하려는 과정은 Deserialize가 아닌 Serialize였기 때문에 사실상 지금은 @NoArgsConstructor가 아무런 효과가 없었습니다.

 

 

2. serialize를 하는 과정에서 PlaceRequest 클래스의 필드에 접근하게 되는데, 이때 public 접근제한자 또는 getter를 통해서 접근하게 된다.

여기서 2가지 방법을 시도해볼 수 있었습니다.

 

(1) 필드를 모두 public으로 바꿔보자

@AllArgsConstructor
public class PlaceRequest {
    public PlaceType placeType;
    public String placeName;
    public String address;
    public String phoneNumber;
    public Integer capacity;
    public String memo;
    
    /* 코드 생략 */
}

테스트를 돌려보니 통과하는 것을 볼 수 있습니다.

 

 

(2) 필드는 그대로 private로 두고, Getter를 만들어보자

@Getter
@AllArgsConstructor
public class PlaceRequest {
    private PlaceType placeType;
    private String placeName;
    private String address;
    private String phoneNumber;
    private Integer capacity;
    private String memo;
}

이 방법도 동일하게 테스트가 통과되는 것을 볼 수 있습니다. 저는 private 필드를 사용하기 위해서 두 번째 방법을 사용해서 코드를 작성했습니다.

 

 

 

💡 알게된 점

JackSon을 사용해서 Serialize(Object -> json)할 때는 필드를 public으로 지정하거나 private으로 해두었다면 JackSon이 해당 필드에 접근할 수 있도록 Getter를 만들어주어야 합니다.

 

반대로 Deserialize(json -> Object)를 할 때는 비어있는 기본 생성자(NoArgsConstructor)로 객체를 생성하기 때문에 반드시 기본 생성자를 클래스 안에 만들어줘야 합니다.

 

사실 해결 방법에는 @JsonProperty를 필드에 붙여주는 방법도 찾았던 거 같은데, 이 부분은 나중에 따로 시간을 내서 알아보고자 합니다.