티스토리 뷰
Property based creator 가 생성되는 이유
저번 포스팅까지 @NoArgsConstructor 가 Request body 의 DTO 에 포함되지도 않았는데 JSON 데이터가 Java 객체로 올바르게 변환될 수 있는 이유가 Property based creator 덕분이라는 사실을 발견했다.
오늘 포스팅에서는 Property based creator 가 언제 생성되고 언제 생성되지 않는지에 대해 알아본다.
먼저, Jackson 에서 PropertyBasedCreator 를 생성하는 부분이 어디인지를 확인하기 위해서 디버깅을 계속 step into 를 통해 들어가보았다. 너무 길어서 캡처는 중간부분을 생략하고 처음 진입과 마지막 부분만 남기겠다.
결국 BeanDesereializerBase 라는 클래스의 생성자에서 _propertyBasedCreator 가 만들어지고 null 로 초기화됨을 알 수 있었다. 이제 남은 일은 이게 언제 다른 값을 할당받는지 찾는 일이다. 분명 이 시점까지는 null 인 변수가 언제부턴가 값을 할당받는다.
우선 위와 같이 _propertyBasedCreator 가 언제 값을 할당받는지 디버깅을 하는 도중 발견하기 위해 Add to Watches 를 활용하였다. 아래와 같이 맨 위에 고정되어서 계속 값이 어떻게 달라지는지 관찰할 수 있다.
이후 디버깅을 지속해 보았다. 중간 과정을 생략하고, 계속 step into 로 들어가보면 다음 줄에 도달한다. 드디어 _propertyBasedCreator 에 무언가가 할당되고 있는 곳을 발견했다.
여기에서는 creatorProps 가 null 이 아닐 때 할당이 된다. creatorProps 가 어디서 만들어지는지를 확인해보기 위해 클래스 상단으로 가보니 찾을 수 있었다. 그 라인을 중단점으로 다시 디버깅을 시작했다.
하지만 이 곳에는 도달하지 않았다. 메서드 자체에 중단점을 찍어보니, if 문에 들어가려고 시도한다.
Step into 로 한 번 더 들어가보니, 이 메서드는 _withArgsCreator 라는 boolean 변수의 값을 리턴한다.
그리고 다음과 같은 설명을 디버거에서 해주고 있었다.
if 문을 만족해서 실행된 getFromObjectArguments 는 _constructorArguments 를 반환했다. 번역하면 생성자의 매개변수라는 말이다. 결국 creatorProps 는 UserLoginRequest 생성자의 매개변수들의 존재 여부로 값을 갖는지 null인지가 결정된다.
즉, 내 프로젝트에서는 @NoArgsConstructor 가 없어도 2개의 매개 변수를 가진 생성자를 컴파일러가 인식하고 creatorProps 를 null 이 아닌 값으로 채우며, 결국 _propertyBasedCreator 에도 무언가 값을 할당할 수 있었던 것이다.
위와 같이, 계속 Step into 로 타고 들어가면 SettalbleBeanProperty 라는 메서드로 매개변수 password 와 userName 이 bean property 로 잘 등록되는 것을 볼 수 있었다.
_propertyBasedCreator 에 값이 할당되는 부분 위에 주석으로 CreatorProperty 인스턴스들이 resolve 된 것을 확인했으므로 creator 를 만들 수 있다고 되어 있다.
그렇다면 언제는 매개변수가 존재한다는 것을 인식해서 @NoArgsConstructor 없이도 객체를 잘 생성하고 역직렬화가 진행되는데, 왜 언제는 매개변수의 존재 여부를 인식하지 못하는 것일까?
그 차이를 정말 어이없는 이유로 발견할 수 있었다.
디버깅을 하느라 계속 프로그램을 실행하는 것이 시간이 많이 걸려 순간 Intellij 빌드로 바꾸었는데 (살짝 더 빠름), 바꾸고 나서 디버깅을 다시 했는데 _propertyBasedCreator 가 null 이 나왔다. 그리고 애초에 위의 과정에서 본 클래스들과 메서드들에 도달도 하지 않는 것을 발견했다. 게다가 다른 것을 아무것도 바꾸지 않았으니 빌드의 문제가 맞는 것 같다.
이후의 과정은 디버깅을 해도 모두 상세하게 이해할 수가 없었다.
확실한 것은 canCreateFromObjectWith() 가 실행되었을 때 _withArgsCreator 가 없다는 것이 발견되었고, 이는 빌드 시에 false 가 나오게끔 설정이 되어있다는 의미이다.
구글링 결과, Gradle 빌드와 Intellij 의 빌드는 컴파일러 플래그에서 차이가 있다는 것을 알 수 있었다.
Gradle 과 Intellij 빌드의 차이
기억을 더듬어 보니, 이미 빌드와 관련 있다는 사실을 알고 있었다. 전날 Stack Overflow 의 글에서 읽은 것이다. 내용인즉슨, JSON 데이터가 POJO 로 바인딩되려면 @JsonProperty 어노테이션을 붙여야 하는데, 컴파일러의 -parameters 옵션을 붙여서 클래스 파일에 매개 변수들의 이름이 포함되어 있을 때에는 선택 사항이고, 그렇지 않을 때는 필수사항이라는 것이다. 100퍼센트 Intellij 에서 빌드 시 컴파일 시에 플래그로 -parameters 를 사용하지 않고, Gradle 은 사용할 것이라는 짐작을 할 수 있었다.
나와 비슷한 궁금증이 있던 블로그 글쓴이는 자바 공식 문서에 -parameters 의 역할이 설명되어 있다는 사실을 알려주었다. (공식 문서를 읽었어야 한다.) -parameters 가 있으면 이름을 통한 추론이 가능하다고 한다. 나의 경우에는 매개 변수에 @JsonProperty 를 붙이지 않고 변수 이름으로 어떻게 JSON 으로부터 바인딩해야 하는지를 이름을 통해 알 수 있게 된다. 그래서 디폴트 생성자가 필요가 없는 것이다.
남은 할 일은 Intellij 에서 빌드 시에 컴파일러 플래그에 -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
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
'Java' 카테고리의 다른 글
[Java] 원시 타입과 참조형 타입: 배열 생성 방법에서의 차이 (0) | 2022.11.30 |
---|---|
DTO 의 Deserialize(역직렬화): 2부 (0) | 2022.11.30 |
DTO 의 Deserialize(역직렬화): 1부 (0) | 2022.11.29 |
[Java] enum type Class (0) | 2022.11.18 |
- Total
- Today
- Yesterday
- Firebase
- Jackson
- JOIN FETCH
- gitlab
- json web token
- docker
- 코테
- Spring Boot
- 알고리즘
- 가상 서버
- @RequestBody
- 도커
- LazyInitializationException
- N+1
- 깃랩
- 프로그래머스
- 실시간데이터
- FCM
- 기지국 설치
- DTO
- 지연 로딩
- JPQL
- spring
- 인증/인가
- ci/cd
- Java Data Types
- 역직렬화
- google cloud
- JPA
- DeSerialization
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |