결론부터 말하자면, 엔터티에서 양방향 관계일 때, Lombok의 @Data와 @toString은 StackOverFlow 문제를 일으킬 수 있기 때문에 사용에 조심해야한다는 것이다.
문제
FareEntity라는 객체를 담은 List를 Map<FareEntity 키, FareEntity> 형태의 Map으로 변경하고자 했다.
변경하는 부분에서 아래와 같이 StackOVerFlow 에러가 발생하였다.
java.lang.StackOverflowError: null
at java.base/java.lang.String.coder(String.java:3258) ~[na:na]
at java.base/java.lang.String.getBytes(String.java:3191) ~[na:na]
at java.base/java.lang.AbstractStringBuilder.putStringAt(AbstractStringBuilder.java:1664) ~[na:na]
at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:536) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:174) ~[na:na]
at java.base/java.lang.StringBuilder.<init>(StringBuilder.java:125) ~[na:na]
at com.tradlinx.zimgo.forwarder.entity.CnsltnCostItemEntity.toString(CnsltnCostItemEntity.java:11) ~[classes/:na]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na]
at com.tradlinx.zimgo.forwarder.entity.FareEntity.toString(FareEntity.java:16) ~[classes/:na]
... 생략 ...
문제 원인
Map<FareEntity 키, FareEntity>의 FareEntity 키를 만들 때도 toString() 메서드를 사용하지 않았는데, 에러 메세지에는 FareEntity.toString, CnsltnCostItemEntity.toString 등 toString() 메서드가 존재했다.
그래서 에러의 toString()을 클릭해보니, Lombok의 @Data 어노테이션을 가리켰다.
참고로, Lombok의 @Data에는 아래의 Lombok 기능이 포함되어 있다.
- @ToString
- @EqualsAndHashCode
- @Getter : 모든 필드
- @Setter : 정적 필드가 아닌 모든 필드
- @RequiredArgsConstructor
그래도, Map<FareEntity 키, FareEntity>를 만드는 동안 toString()을 사용하지 않았기 때문에, 왜 @Data에서 에러가 발생하는 지 알 수 없었다. 디버깅을 위해 FareCostEntity의 toString() 메서드를 직접 오버라이드하였다.
메서드 호출 스택을 살펴보니, Map<FareEntity 키, FareEntity>을 만들 때, duplicateKeyException이 발생했다.
duplicateKeyException은 변수로 받은 k, u, v를 출력한다. 결과적으로 Map<FareEntity 키, FareEntity>의 v(value)에 해당되는 FareEntity의 toString() 메서드를 호출하게 된다.
FareEntity에도 @Data 어노테이션이 존재하였고, FareCostEntity와 양방향 관계로 되어있었다.
Lombok의 @ToString은 해당 엔티티가 가지고 있는 모든 속성들이 작성된다.
또한, 양방향이기 때문에 'FareEntity의 toString() 호출 -> FareCostEntity의 toString() 호출 -> FareCostEntity 내 FareEntity의 toString() 호출 -> ....' 식으로 순환하면서 toString() 메서드를 무한으로 호출하게 되는 것이다.
문제 해결
우선, Map을 만들 때, DuplicateKeyException이 발생했으므로, Key가 중복된 원인을 제거하였다.
StackOverFlow 에러가 다시는 나지 않도록, @Data를 사용하지 않고, @Getter, @Setter, @RequiredArgsConstructor 등 필요로 하는 Lombok 어노테이션만 사용하는 것으로 변경하였다.
참고 자료
https://lkhlkh23.tistory.com/m/159
https://kwonnam.pe.kr/wiki/java/lombok/pitfall
'트러블슈팅' 카테고리의 다른 글
JPA로 엔터티의 테이블 CREATE 시, expected "identifier" 에러 (0) | 2022.08.11 |
---|---|
iFrame에 서버로부터 받은 pdf 파일이 나오지 않는 문제 (0) | 2022.08.11 |
JAVA SSL 인증서 문제 - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException (0) | 2022.08.09 |
BooleanExpression의 NullPointException에러 (0) | 2022.05.04 |
Mac에서 올린 파일이 Window에서 다운 받을 때, 파일명의 자음 모음이 분리되는 현상 (0) | 2022.05.02 |