Jackson과 @Getter를 이용한 JSON 직렬화시 필드 이름이 바뀌는 에러

2023. 11. 1. 16:59Project 해축갤/에러 해결

728x90

Java에서 데이터를 표현하는 클래스는 종종 필드와 해당 필드에 접근하기 위한

getter, setter 메서드를 포함하고 있습니다.

 

Lombok의 @Getter 어노테이션을 사용하면, 이러한 getter 메서드를 자동으로 생성할 수 있죠.

그런데 Jackson 라이브러리를 이용해서 이러한 클래스를 JSON으로 직렬화할 때,

일부 메서드의 네이밍이 JSON의 키로 변환될 때 직렬화 하고자 한 객체의 필드 네이밍과는 다른 경우가 있습니다.

 

특히, boolean 타입의 필드의 경우, getter 메소드의 이름이 is 시작하는 경우가 많습니다.

그럼 Jackson 이러한 메소드 이름을 어떻게 처리하고 JSON 키로 변환할까요?

예제 코드(이자 실제 저의 코드 ㅠ)

@Getter
@NoArgsConstructor
@Builder
public class PostDbDTO {

    @NotNull
    private Long id;

    @NotBlank
    @Size(min = 1, max = 50)
    private String title;

    @NotBlank
    @Size(min = 1, max = 1000)
    private String content;

    @NotBlank
    @Size(min = 2, max = 50)
    private String writer;

    @NotNull
    private boolean isDeleted;

    ...
}

여기서 주목할 부분은 isDeleted라는 boolean 타입의 필드입니다.

Lombok의 @Getter를 사용하면, 이 필드에 대한 getter 메서드는

isDeleted()가 아니라 getDeleted()로 생성됩니다.

 

이는 Lombok 이 버그가 아니라, boolean 필드의 getter 생성 규칙 때문에 그렇습니다.

 

Java Beans 명명 규약에 따르면,

boolean 타입의 프로퍼티에 대한 getter 메서드는 is 접두사를 사용합니다.

즉, isDeleted라는 boolean 타입의 필드는 isDeleted()라는 이름의 getter 메서드를 가지게 됩니다.

A default getter simply returns the field,
and is named getFoo if the field is called foo (or isFoo if the field's type is boolean). -> primitive 타입인 게 중요

[출처] : https://projectlombok.org/features/GetterSetter

하지만 boolean isDeleted 라는 필드의 `is` 접두사를 또 한번 붙이면

isIsDeleted...? 라는 건 정말 이상하죠.

그래서 lombok 은 isDeleted라는 boolean 필드의 경우 isDeleted라는 is-getter 메서드를 생성하게 됩니다.

Jackson 의 동작 방식

Jackson 라이브러리는 많이들 Java 객체 직렬화를 하기 위한 라이브러리로써 많은 Java 개발자들에게 사랑받고 있습니다.

이러한 Jackson 라이브러리는 Java 객체를 JSON 직렬화할 때에 필드의 이름에서 따오는 것이 아닌

getter 메서드의 이름을 기반으로 JSON 의 키 이름을 결정하게 됩니다.

 

1.1 getter 처리

밑 코드는 Jackson 의 findNameForRegularGetter 메서드의 일부입니다.

이 메서드는주로 'get' 접두사를 가진 메서드를 처리합니다.

if ((_getterPrefix != null) && name.startsWith(_getterPrefix)) {

	if ("getCallbacks".equals(name)) {
		if (_isCglibGetCallbacks(am)) {
                    return null;
		}
	} else if ("getMetaClass".equals(name)) {
		if (_isGroovyMetaClassGetter(am)) {
			return null;
		}
	}
	return _stdBeanNaming
		? stdManglePropertyName(name, _getterPrefix.length())
		: legacyManglePropertyName(name, _getterPrefix.length());
	}
return null;

위 코드에서는 메서드의 이름이 'get' 으로 시작하는 지 확인하고,

다음에 'get' 접두사를 제외한 나머지 이름을 return 합니다.

1.2 is-Getter 처리

밑 코드는 findNameForIsGetter 라는 메서드입니다.

'is' 접두사를 가진 boolean 타입의 getter 메서드를 처리합니다.

if (_isGetterPrefix != null) {
     if (_isGettersNonBoolean || _booleanType(am.getType())) {
         if (name.startsWith(_isGetterPrefix)) {
             return _stdBeanNaming
                     ? stdManglePropertyName(name, _isGetterPrefix.length())
                     : legacyManglePropertyName(name, _isGetterPrefix.length());
         }
     }
 }

위 코드에서는 메서드의 이름이 'is' 으로 시작하는 지 확인하고,

다음에 'is' 접두사를 제외한 나머지 이름을 return 합니다.

 

결론

즉, boolean(primitive) 타입의 필드를 lombok 은

무조건 getter 의 이름을 isDeleted() 이런 식으로 붙이게 됩니다.

 

그리고 jackson 은 이러한 getter 에서의 이름 중 prefix 를 제외한 이름을

직렬화한 JSON 의 key 값으로 할당한다는 것입니다!

 

따라서, 필드의 이름 isDeleted를 그대로 JSON 의 key 값에 할당하고 싶다면

Boolean(Reference Type) 으로 바꾸시면 됩니다!

728x90