JsonProperty with Lombok getter

TunaHG·2024년 7월 20일
0

Trouble Shooting

목록 보기
1/1

문제상황

일할 때, 프론트 담당자에게 연락이 왔다.
똑같은 의미의 응답값이 중복되서 응답되고 있다는 것
이런 데이터가 오고 있다는 것이였다.

{
  "xvalue": "2022-05-19T10:00:00",
  "yvalueR": 3.0,
  "yvalueL": 20.0,
  "xValue": "2022-05-19T10:00:00",
  "yValueR": 3.0,
  "yValueL": 20.0
}

그래서 해당 응답 객체를 살펴보니 아래와 같이 선언되어 있었다.

@Getter
@Builder
public static class Value {
    @JsonProperty("xValue")
    private final LocalDateTime xValue;
    @JsonProperty("yValueR")
    private final Float yValueR;
    @JsonProperty("yValueL")
    private final Float yValueL;
}

뭐가 문제지? 하고 서칭을 좀 해보니 Lombok의 @Getter@JsonProperty를 같이 사용해서 발생하는 문제였다!

Lombok camel case issue

Lombok을 사용하여 getter/setter를 생성하는 경우 변수명이 위 xValue와 같이 두 번째 단어가 대문자라면, getXValue(), setXValue()와 같이 생성된다.
그리고 해당 getXValue(), setXValue()를 사용하여 변수명을 추정할 때 이어진 대문자를 모두 소문자로 변경한 값으로 추정하기 때문에 xvalue라는 변수명으로 추정한다.

아마 초기에 위 문제를 해결하기 위해 @JsonProperty("xValue") 코드를 추가한 것이라고 생각한다

Jackson

Object와 JSON 간의 Serialize, Deserialize를 담당하는 라이브러리이다.
(위 진행했던 프로젝트에서는 별도의 설정 변경 없이 그대로 사용했다)

Jackson 라이브러리의 기본 네이밍 전략은 lowerCamelCase이다.
jackson-databind 라이브러리 내의 PropertyNamingStrategies.class를 살펴보면 다음과 같은 코드가 보인다

public static class LowerCamelCaseStrategy extends NamingBase {
    private static final long serialVersionUID = 2L;

    public LowerCamelCaseStrategy() {
    }

    public String translate(String input) {
        return input;
    }
}

translate(String input)을 살펴보면, input 그대로 return 하고 있기 때문에 해당 lowerCamelCase 형식이 default 설정값이라는 것을 확인할 수 있다.

Object to JSON으로 Deserialize를 진행할 때는 getter 메소드를 통해서 역직렬화를 진행한다.
(이게 Jackson을 사용할 때 getter()를 신경써야 하는 이유다)

다만 처음의 문제상황은 @JsonProperty를 Lombok의 @Getter와 같이 사용하면서 중복된 응답값이 발생했다.
변수에 선언한 @JsonProperty에서 선언한 값으로 Deserialize를 진행하는데 Lombok으로 생성된 getter()에서도 Deseiralize를 진행한다.
@JsonProperty("xValue")로 인해서 xValue 응답값이 생성되고, Lombok의 @Getter로 생성된 getXValue()에서도 xvalue 응답값이 생성된다.
그래서 최종적으로 xValue, xvalue 응답값이 생성되며 중복된 응답값이 발생한 것이다.

해결방안

문제 상황을 파악했으니 이를 해결하기 위해 어떻게 하는게 좋을지를 찾아봤다.

1. Lombok의 @Getter 제거

응답 객체에 @Getter를 선언한 이유는 jackson 라이브러리가 getter()로 Deserialize를 진행하기 때문이다. @JsonProperty가 선언되어 있다면 @JsonProperty가 Deserialize를 진행하기 때문에 getter()가 존재하지 않아도 정상적으로 동작한다.

Lombok의 @Getter가 xValue를 getXValue()를 자동 생성하고 있기 때문에 응답값이 xvalue로 나오고 있다. getter()가 필요하다면, @Getter를 사용하지 않고 getxValue()로 직접 선언해주면 xValue로 응답값이 나온다.

위 프로젝트에서는 해당 객체를 테스트코드에서 응답값 자체 검증을 진행하지 않고 있어서 getter()를 사용하지 않고 있었다. 그래서 Lombok의 @Getter를 제거해도 다른 문제가 발생하지 않았다.

2. getter() 직접 선언

getxValue()와 같이 getter()를 직접 선언해 준다면, getter를 사용할 수도 있고 @JsonProperty도 필요없어진다.
다만, 다른 객체들은 @Getter가 선언되어 있는데 해당 객체에서만 사용하지 않아서 추후 리팩토링할 때 까먹고 수정할 수도 있고 인텔리제이 IDEA에서 lombok이 설정되어있으면 getter()대신 @Getter를 사용하라고 추천하기도 하니 그런 외압들을 받아들여서 수정하는 것을 방지하고자 주석으로 설명을 추가할 필요가 있다.

참고 링크

profile
나태해지지 말자

0개의 댓글