여러가지 이야기

[Spring] 예상치 못한 JSON 필드 추가? - Lombok과 Jackson 직렬화 문제 해결 본문

study/Spring

[Spring] 예상치 못한 JSON 필드 추가? - Lombok과 Jackson 직렬화 문제 해결

jimddong 2025. 2. 16. 12:39

응답 통일을 위해 DTO, BaseCode, 성공 시 응답 처리 등을 코드로 작성했었다.

 

* 💭 응답 통일을 해야하는 이유

더보기

내 생각: 데이터가 전달될 때 일정한 형식으로 오고 가야 CRUD 기능 내 메소드가 처리될 수 있고, 프론트단에서도 일정한 형식으로 (json 등) 제대로 정보를 받고 다시 보낼 데이터의 형식도 맞추는 등 쉽게 처리할 수 있다. 꼭 응답 통일 코드 작성을 잊지 말자!

응답의 형식을 담을 BaseResponse 코드를 대략 아래와 같이 짰었다.

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class BaseResponse<T> {

    @JsonProperty("isSuccess")
    private final boolean isSuccess;
    private final String code;
    private final String message;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private T result;

    public static <T> BaseResponse<T> onSuccess(T result) {
        return new BaseResponse<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), result);
    }
	.
    .
    .
}

 

보다시피 내가 json으로 변환 시키고 싶은 자바 필드는 isSuccess, code, message, result 이다.

 

근데 TestController를 실행 시켜 onSuccess의 결과를 localhost:8080에서 확인 했을 때 다음과 같은 결과가 나왔다.

{ "isSuccess": true, "code": "COMMON200", "message": "성공입니다.", "result": "성공 응답 테스트 - 성공!", 
"success": true }

 

내가 추가시키지도 않은 success라는 값이 추가된 것!

 

이렇게 Java 객체를 JSON으로 변환할 때 예상과 달리 나올 때를 JSON 직렬화 문제라고 한다.

(참고로 반대로 JSON에서 Java 객체로 변환시키는 것은 역직렬화라고 한다.)

 

왜 이런 문제가 발생했을까?


Lombok의 @GetterJackson의 원리

문제를 해결하려면 내가 어노테이션으로 붙인 @Getter와 Jackson에 대해서 생각해봐야한다.

Lombok@Getter는 자동으로 각 필드에 대한 getter 메서드자동 생성해준다.
(ex. 내 코드에는 code, message라는 필드가 있기에 getCode, getMessage 등이 되겠다.)

 

그런데 @Getter가 boolean 타입의 필드를 가지고 getter 메서드를 만들 때는 getXXX()가 아니라 isXXX()를 이름으로 한 메서드를 생성시킨다. 내 코드의 경우, isSuccess가 boolean 타입이었기에 다음과 같이 자동으로 getter가 만들어졌을 것이다.

public boolean isSuccess() { return isSuccess; }

 

이제 자바에서 json 변환할 때 getter 메서드를 탐색하여 json 필드를 결정해주는 도구, Jackson이 사용된다.

Jackson은 getter 메서드 이름을 json키로 사용하기에 is 제외 success를 필드로 간주해 json으로 자동 변환한다.

따라서 json 형식 응답을 확인 했을 때, 우리가 원하는 isSuccess뿐만 아니라 success도 추가로  생기는 위와 같은 문제가 생긴 것이다.

* 참고로 Jackson은 자바 객체 -> json 변환 시 이용되는 도구일뿐, 단순 데이터 타입 json과는 다르다!

 

해결

@JsonIgnore는  Jackson이 해당 필드를 자동 변환하지 않도록(직렬화에서 제외) 막을 수 있는 어노테이션이다.

Lombok이 @Getter로 자동 생성 시켜주는 isSuccess() 대신, 직접 명시적으로 getter 메서드를 적어주고 그 위에 @JsonIgnore 어노테이션을 붙여주자.

@JsonIgnore
public boolean isSuccess() { return isSuccess; }

 

public static <T> BaseResponse<T> onSuccess(T result) {
        return new BaseResponse<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), result);
    }
    
// 필드 값을 초기화하는 것

 

이렇게 하면 isSuccess 값은 우리가 직접 정의한 getter를 통해 가져오고, 나머지(code, message, result)는 Lombok이 자동 생성한 getter를 통해 JSON으로 변환된다.

 


처음에는 '뭐지? 갑자기 난데 없는 success가 어디서 생긴거지? success라는 걸 내가 어딘가 넣었었나' 해서 당황스러웠다. 하지만 문제 해결을 위해 찾아보다 내가 무심코 쓰고 있던 어노테이션이 이런 원리를 하고, 거기서 json 변환에 Jackson이라는 도구가 이용된다는 사실을 알게 되었다.

 

역시 각 도구들이 어떤 기능을 하는지 원리를 알아야함을 뼈저리게 깨달았다. 앞으로도 궁금하거나 잘 모르는 어노테이션, 도구가 등장하면 꼭 원리를 익히고 사용해야겠다고 다짐했다!