티스토리 뷰

Java

DTO 의 Deserialize(역직렬화): 3부

Nickolodeon 2022. 11. 30. 21:38

Property based creator 가 생성되는 이유

저번 포스팅까지 @NoArgsConstructor 가 Request body 의 DTO 에 포함되지도 않았는데 JSON 데이터가 Java 객체로 올바르게 변환될 수 있는 이유가 Property based creator 덕분이라는 사실을 발견했다.

오늘 포스팅에서는 Property based creator 가 언제 생성되고 언제 생성되지 않는지에 대해 알아본다.

 

지난 시간까지 디버그한 부분

 

먼저, Jackson 에서 PropertyBasedCreator 를 생성하는 부분이 어디인지를 확인하기 위해서 디버깅을 계속 step into 를 통해 들어가보았다. 너무 길어서 캡처는 중간부분을 생략하고 처음 진입과 마지막 부분만 남기겠다.

결국 찾아냈다. _propertyBasedCreator 를 생성하는 곳을.

결국 BeanDesereializerBase 라는 클래스의 생성자에서 _propertyBasedCreator 가 만들어지고 null 로 초기화됨을 알 수 있었다. 이제 남은 일은 이게 언제 다른 값을 할당받는지 찾는 일이다. 분명 이 시점까지는 null 인 변수가 언제부턴가 값을 할당받는다.

Add toWatches 를 설정해두었다.

우선 위와 같이 _propertyBasedCreator 가 언제 값을 할당받는지 디버깅을 하는 도중 발견하기 위해 Add to Watches 를 활용하였다. 아래와 같이 맨 위에 고정되어서 계속 값이 어떻게 달라지는지 관찰할 수 있다.

맨 위에서 _propertyBasedCreator 의 값이 어떻게 달라지는지 볼 수 있게 해 놓았다.

 

이후 디버깅을 지속해 보았다. 중간 과정을 생략하고, 계속 step into 로 들어가보면 다음 줄에 도달한다. 드디어 _propertyBasedCreator 에 무언가가 할당되고 있는 곳을 발견했다. 

무언가 _propertyBasedCreator 에 할당되고 있다.

여기에서는 creatorProps 가 null 이 아닐 때 할당이 된다. creatorProps 가 어디서 만들어지는지를 확인해보기 위해 클래스 상단으로 가보니 찾을 수 있었다. 그 라인을 중단점으로 다시 디버깅을 시작했다.

creatorProps 에 값이 할당되는 부분

하지만 이 곳에는 도달하지 않았다. 메서드 자체에 중단점을 찍어보니, if 문에 들어가려고 시도한다.

Step into 로 한 번 더 들어가보니, 이 메서드는 _withArgsCreator 라는 boolean 변수의 값을 리턴한다.

그리고 다음과 같은 설명을 디버거에서 해주고 있었다.

UserLoginRequest 의 생성자를 찾았고, 2개의 매개 변수가 있어서 _withArgsCreator 의 조건을 만족했다.

if 문을 만족해서 실행된 getFromObjectArguments 는 _constructorArguments 를 반환했다. 번역하면 생성자의 매개변수라는 말이다. 결국 creatorProps 는 UserLoginRequest 생성자의 매개변수들의 존재 여부로 값을 갖는지 null인지가 결정된다.

즉, 내 프로젝트에서는 @NoArgsConstructor 가 없어도 2개의 매개 변수를 가진 생성자를 컴파일러가 인식하고 creatorProps 를 null 이 아닌 값으로 채우며, 결국 _propertyBasedCreator 에도 무언가 값을 할당할 수 있었던 것이다.

위와 같이, 계속 Step into 로 타고 들어가면 SettalbleBeanProperty 라는 메서드로 매개변수 password 와 userName 이 bean property 로 잘 등록되는 것을 볼 수 있었다.

@JsonProperty annotation 은 없지만 #1 parameter 를 인식한다.
UserLoginRequest DTO 에 @JsonProperty(value="password") 을 붙인 이후에 값이 달라졌다.
정확히 null 이었던 _propertyBasedCreator 에 값이 할당되는 부분은 여기이다.

_propertyBasedCreator 에 값이 할당되는 부분 위에 주석으로 CreatorProperty 인스턴스들이 resolve 된 것을 확인했으므로 creator 를 만들 수 있다고 되어 있다.

PropertyBasedCreator.construct 에서는 이런 일이 벌어진다.

그렇다면 언제는 매개변수가 존재한다는 것을 인식해서 @NoArgsConstructor 없이도 객체를 잘 생성하고 역직렬화가 진행되는데, 왜 언제는 매개변수의 존재 여부를 인식하지 못하는 것일까?

 

그 차이를 정말 어이없는 이유로 발견할 수 있었다.

 

디버깅을 하느라 계속 프로그램을 실행하는 것이 시간이 많이 걸려 순간 Intellij 빌드로 바꾸었는데 (살짝 더 빠름), 바꾸고 나서 디버깅을 다시 했는데 _propertyBasedCreator 가 null 이 나왔다. 그리고 애초에 위의 과정에서 본 클래스들과 메서드들에 도달도 하지 않는 것을 발견했다. 게다가 다른 것을 아무것도 바꾸지 않았으니 빌드의 문제가 맞는 것 같다.

 

인텔리제이로 빌드 시에는 여기서 Run to Cursor 를 반복해 누르면,
다음과 같이 Gradle 로 빌드했을 때와 다르게 if 문으로 걸러져서 creatorProps 에 null 이 할당됨을 볼 수 있다.
소리지르고 싶었다.

이후의 과정은 디버깅을 해도 모두 상세하게 이해할 수가 없었다.

확실한 것은 canCreateFromObjectWith() 가 실행되었을 때 _withArgsCreator 가 없다는 것이 발견되었고, 이는 빌드 시에 false 가 나오게끔 설정이 되어있다는 의미이다.

 

구글링 결과, Gradle 빌드와 Intellij 의 빌드는 컴파일러 플래그에서 차이가 있다는 것을 알 수 있었다.

Gradle 과 Intellij 빌드의 차이

Jackson 이 JSON 을 POJO 로 바인딩하는 방법에 대해서 설명한 글의 일부분을 발췌했다.

기억을 더듬어 보니, 이미 빌드와 관련 있다는 사실을 알고 있었다. 전날 Stack Overflow 의 글에서 읽은 것이다. 내용인즉슨, JSON 데이터가 POJO 로 바인딩되려면 @JsonProperty 어노테이션을 붙여야 하는데, 컴파일러의 -parameters 옵션을 붙여서 클래스 파일에 매개 변수들의 이름이 포함되어 있을 때에는 선택 사항이고, 그렇지 않을 때는 필수사항이라는 것이다. 100퍼센트 Intellij 에서 빌드 시 컴파일 시에 플래그로 -parameters 를 사용하지 않고, Gradle 은 사용할 것이라는 짐작을 할 수 있었다.

출처에 밝힌 티스토리 블로그에서 읽었다.

나와 비슷한 궁금증이 있던 블로그 글쓴이는 자바 공식 문서에 -parameters 의 역할이 설명되어 있다는 사실을 알려주었다. (공식 문서를 읽었어야 한다.) -parameters 가 있으면 이름을 통한 추론이 가능하다고 한다. 나의 경우에는 매개 변수에 @JsonProperty 를 붙이지 않고 변수 이름으로 어떻게 JSON 으로부터 바인딩해야 하는지를 이름을 통해 알 수 있게 된다. 그래서 디폴트 생성자가 필요가 없는 것이다.

 

남은 할 일은 Intellij 에서 빌드 시에 컴파일러 플래그에 -parameters 를 붙여주면 정상 동작하는지 확인하는 일이었다.

 

출처에서 밝힌 Stack Overflow 의 답변에서 어디에 -parameters 를 적어야 할지 알 수 있었다.

 

하지만, 정상 동작하지 않았다.

그래서 Gradle 의 빌드 과정이 어떤 다른 점이 있는지 알아보기 위해, Gradle 빌드 에 대해 공부해보기로 했다.

 

결론

돌아왔다고 생각이 들긴 했지만, 문제 해결 과정에서 디버깅 방법과 같이 실무에서 유용한 스킬을 배울 수 있었다.

빌드 도구의 차이인 것을 알았을 때는 매우 후련하고 즐거웠다.

 

다음 포스트에서 Gradle 빌드에 어떤 점이 Jackson 으로 하여금 어노테이션 없이 매개 변수를 인식하게 해 주는지에 대해 포스팅하도록 하겠다.

 

출처

https://haenny.tistory.com/394

 

자바의 빌드 시스템, Gradle Build와 IntelliJ Build

컴파일과 빌드 요즘에는 IDE가 워낙 좋아져 컴파일과 빌드의 차이를 모르는 경우가 많다.(=나) 왜냐하면 대부분 툴에서 그 둘을 동시에 Run 하나로 퉁치기 때문이다. 컴파일 : 소스의 문법을 분석

haenny.tistory.com

https://stackoverflow.com/questions/39217830/how-to-use-parameters-javac-option-in-intellij

 

How to use -parameters javac option in intellij?

I have this all working in eclipse and am trying to do so in intellij now. I opened settings {command and comma} then went to Build, Execution and Deployment and clicked Compiler and in Shared Build

stackoverflow.com

https://stackoverflow.com/questions/64080983/when-does-jackson-require-no-arg-constructor-for-deserialization

 

When does Jackson require no-arg constructor for deserialization?

In my spring boot project, I noticed a strange Jackson behavior. I searched over internet, found out what to do, but haven't found out why. UserDto: @Setter @Getter @AllArgsConstructor public class

stackoverflow.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함