문제 제기
레거시 프로젝트 리뉴얼 과정에서 프론트 코드가 UPPER SNAKE CASE인 것을 발견했다. ex) POST_ID
dto를 해당 컨벤션에 맞췄지만, 첫 단어가 소문자로 변환되는 문제가 있었고, lombok을 써도 해결되지 않았다.
CAEML CASE를 써왔기에 이런 컨벤션 문제가 당황스러웠지만, 앞으로 레거시 프로젝트를 다루면 이런 상황들을 접하게 될 것이라 생각해 정리했다.
해결 방안 1. JsonProperty 사용
lombok의 @Getter를 쓰지 않고 직접 작성했다.
개인적으로 이 방법이 가장 깔끔했지만 다음 문제가 있었다.
- 각 getter마다 추가해야 한다.
- 해당 코드를 추가할 곳이 매우 많다.
- 오타가 자주 발생한다. 생각보다 JsonProperty 하고 변수명 바꿔쓰는 경우가 많았다.
import com.fasterxml.jackson.annotation.JsonProperty;
public class TestDto {
private Long postId;
public TestDto(Long postId) {
this.postId = postId;
}
@JsonProperty("POST_ID") // 해결에 사용된 코드
public Long getPostId() {
return postId;
}
}
해결 방안 2. jackson-databind 업데이트
가장 간단한 방법이지만 우리는 적용하지 못했다.
최신 jackson은 UPPER SNAKE CASE를 처리하지만 우리 프로젝트가 지원하는 범위 내에선 불가능했다.
톰캣 서버단에서 에러가 났고, 이걸 해결하기엔 산으로 가는 느낌이었다.
그래서 상위 버전의 PropertyNamingStrategies
클래스 코드를 다운받아 직접 패키지에 넣어봤는데 신기하게 이 클래스만 가져오는 것은 문제가 없었다.
하지만, 에러가 나는 버전의 코드를 가져오는 것은 좀 찝찝했기 때문에 다른 방법을 찾기로 했다.
아무튼 코드는 다음과 같다.
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import java.io.Serializable;
/**
* 현재 버전의 jackson databind는 프론트의 UPPER SNAKE CASE에 맞출 수 없다.
* 상위 버전의 코드를 다운받은 코드
* jackson 업데이트나 통신 과정에서 문제가 발생하면 이 클래스가 문제일 수 있다.
*/
public abstract class CustomPropertyNamingStrategies implements Serializable {
private static final long serialVersionUID = 2L;
/*
/**********************************************************************
/* Static instances that may be referenced
/**********************************************************************
*/
public static final PropertyNamingStrategy LOWER_CAMEL_CASE = CustomPropertyNamingStrategies.LowerCamelCaseStrategy.INSTANCE;
public static final PropertyNamingStrategy UPPER_CAMEL_CASE = CustomPropertyNamingStrategies.UpperCamelCaseStrategy.INSTANCE;
public static final PropertyNamingStrategy SNAKE_CASE = CustomPropertyNamingStrategies.SnakeCaseStrategy.INSTANCE;
// 우리에게 필요한 기능
public static final PropertyNamingStrategy UPPER_SNAKE_CASE = CustomPropertyNamingStrategies.UpperSnakeCaseStrategy.INSTANCE;
public static final PropertyNamingStrategy LOWER_CASE = CustomPropertyNamingStrategies.LowerCaseStrategy.INSTANCE;
public static final PropertyNamingStrategy KEBAB_CASE = CustomPropertyNamingStrategies.KebabCaseStrategy.INSTANCE;
public static final PropertyNamingStrategy LOWER_DOT_CASE = CustomPropertyNamingStrategies.LowerDotCaseStrategy.INSTANCE;
// ...
}
Dto 클래스엔 다음과 같이 적용했다.
@JsonNaming(CustomPropertyNamingStrategies.UpperSnakeCaseStrategy.class)
public class TestDto {
private Long postId;
public TestDto(Long postId) {
this.postId = postId;
}
public Long getPostId() {
return postId;
}
}
해결 방안 3. @JsonAutoDetect
jackson docs들을 찾아본 결과 @JsonAutoDetect
기능을 통해 해결할 수 있었다.
CAMEL CASE를 쓰지 못해 마음에 들진 않았지만, 다른 방법들에 비해 그나마 이게 낫다 생각해 적용했다.
import com.fasterxml.jackson.annotation.JsonAutoDetect;
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE)
public class TestDto {
private Long POST_ID;
public TestDto(Long postId) {
this.POST_ID = postId;
}
public Long getPOST_ID() {
return POST_ID;
}
}
왜 3번을 채택했는지?
프론트 기술 부채가 심했기 때문에 일단은 여기에 맞추고 하나하나 완성될 때마다 CAMEL CASE로 바꾸는 것이 낫다고 생각했다.
기존 Controller에선 DTO를 반환하지 않고, JSON 그대로 반환했다. 즉, 타입이 String 이어서 Dto를 새로 만들어야 했다.
1번 방법대로 DTO 만들다 오타 이슈가 생기면 그것도 피곤했기 때문에, 보기 안 좋더라도 3번으로 했다 돌아가는 것이 낫다고 생각했다.
마치면서
프로젝트를 유지 보수하기보단, 새로 만드는 것에 익숙하다 보니 이런 문제들이 좀 당황스러웠다.
내가 처음 개발했을 때 버전보다 낮아, 알던 지식으로 해결하는 게 어려웠고, 나온 해결책도 이렇게 해도 되나.. 란 고민을 많이 했다.
그래도 이 문제를 통해 당연하게 사용해왔던 것들을 분석하는 기회가 됐다. 비슷한 문제가 발생하면 이때의 경험이 큰 도움이 될 것이다.
Reference
JsonAutoDetect (Jackson-annotations 2.9.0 API)
Class annotation that can be used to define which kinds of Methods are to be detected by auto-detection, and with what minimum access level. Auto-detection means using name conventions and/or signature templates to find methods to use for data binding. For
fasterxml.github.io
https://stackoverflow.com/questions/26744885/jackson-objectmapper-upper-lower-case-issues
Jackson ObjectMapper upper/lower case issues
When I serialize/deserialize any object, all field names are converted to lower case. Is there any configuration to set that makes Jackson keep the field names exactly as they are? Both for seriali...
stackoverflow.com
'개발 > 자바' 카테고리의 다른 글
오류 요약해서 남기기 (0) | 2024.09.20 |
---|---|
[Java] 무지성으로 final 쓰지 않기 (0) | 2024.01.05 |
자바가 엔진단에서 어떻게 동작할까 (0) | 2023.12.30 |
JPA 연관 관계에서 set과 list 차이 (2) | 2022.04.14 |
Serialization (+ JPA) (0) | 2022.04.09 |