본문 바로가기
Programming/하루 일기

@JsonNaming이 편리하네

by peter paak 2020. 6. 25.
728x90

기존 POJO에서 Jackson 바인딩 시, snake_case를 @JsonProperty로 필드마다 매번 변경하는 것이 번거로웠다. 뭔가 클래스 레벨에서 바로 바인딩 할 수 있는 옵션이 있을 것 같다 해서 찾아보니 stackoverflow@JsonNaming이라는 어노테이션에 관련된 내용이 있었다

한마디로 아래의 코드를

public class OAuthToken {

    @JsonProperty("access_token")
    private String accessToken;
    @JsonProperty("refresh_token")
    private String refreshToken;
    @JsonProperty("token_type")
    private String tokenType;
    private String name;
    @JsonProperty("expires_in")
    private int expiresIn;
    private String scope;
}

아래의 코드로 바꿀 수 있다.

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class OAuthToken {

    private String accessToken;
    private String refreshToken;
    private String tokenType;
    private String name;
    private int expiresIn;
    private String scope;
}

내부적으로 보면 대문자가 나오는 자리에 _와 소문자로 바꿔 넣어주는 간단한 구조를 가지고 있다

public static class SnakeCaseStrategy extends PropertyNamingStrategyBase
    {
        @Override
        public String translate(String input)
        {
            if (input == null) return input; // garbage in, garbage out
            int length = input.length();
            StringBuilder result = new StringBuilder(length * 2);
            int resultLength = 0;
            boolean wasPrevTranslated = false;
            for (int i = 0; i < length; i++)
            {
                char c = input.charAt(i);
                if (i > 0 || c != '_') // skip first starting underscore
                {
                    if (Character.isUpperCase(c))
                    {
                        if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
                        {
                            result.append('_');
                            resultLength++;
                        }
                        c = Character.toLowerCase(c);
                        wasPrevTranslated = true;
                    }
                    else
                    {
                        wasPrevTranslated = false;
                    }
                    result.append(c);
                    resultLength++;
                }
            }
            return resultLength > 0 ? result.toString() : input;
        }
    }

역시 조금만 찾아보면 다 나온다

@JsonCreator

방금 안 사실인데 생성자로 deserialize 해주려면 @JsonCreator를 써야한다. 공식문서에서 설명하는 것이 조금 모호해서 무슨 말인지 이해를 못했는데 기본적으로 Jackson은 getter/setter로 매핑을 하는 것 같다. getter/setter 방식으로 동작한다는 전제라면 내 예상이 맞을 것이다. 조금 있다가 찾아봐야겠다.

Marker annotation that can be used to define constructors and factory methods as one to use for instantiating new instances of the associated class.

잠깐 조사해 보니 역시 getter/setter 기준으로 매핑하는 방식인 것 같다. 역시 기본이 중요한 것 같다. 개념없이 아무리 많이 봐도 개념간의 연결이 안된다는 것을 또 한번 느낀다. 개발자로 홀로서기님의 블로그에 매우 자세하게 설명 되어있다. 나중에 간단하게 읽어봐야겠다.

결국 인터페이스 테스트인가??

테스트하다보니 테스트할 수 있는 것은 public 접근제어자 밖에 없는데 그럼 결국은 public한 인터페이스 테스트가 되는 거 아닌가?
private한 코드를 테스트를 하려면 결국은 그 private 메서드를 감싸고 있는 public 메서드를 테스트 함으로써 이루어 지는 것인가?
만약 public한 코드가 의존성이 많이 엮어있다면(물론 그렇게 되어서는 안되겠지만) 원하는 private 메소드만을 검증할 때는 힘들텐데 그럼 private 메소드를 따로 테스트해야 하는 것인가?

728x90