인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

하춘화마타타님의 프로필 이미지
하춘화마타타

작성한 질문수

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화

간단한 주문 조회 V1: 엔티티를 직접 노출

jsonignore와 지연로딩?

해결된 질문

작성

·

427

·

수정됨

3

[섹션3 - 간단한 주문조회 V1 엔티티를 직접 노출] 강의에서

[6:09] 처음에 api를 호출했을때는 양방향 연관관계 때문에 무한루프가 발생하였는데요, 이때는 지연로딩에 의한 이슈가 없었는데

[11:07] JsonIgnore를 추가하여 순환 참조가 되는 상황을 제거했을때는 왜 지연로딩에 의한 이슈가 생기는 건가요??

잘 동작하던 프록시객체가 JsonIgnore가 생겼다고 에러가 나는 상황이 잘 이해가 안되어 질문을 올립니다

답변 2

7

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 하춘화마타타님

이 문제는 JSON 라이브러리와 하이버네이트 프록시가 복잡하게 얽혀서 발생하는 문제입니다.

Order와 프록시가 적용된 Member는 다음과 같이 만들어집니다.

 

Order
- id
- member

 

Member(Proxy)
- id
- List orders
- hibernateLazyInitializer 속성이 프록시 때문에 추가되어 있음(이것은 프록시가 만든 속성)


여기서 Member를 LAZY로 지정해서, 프록시로 만드는 경우 프록시가 자체적인 처리를 위해 hibernateLazyInitializer라는 속성을 추가합니다. 그런데 이 속성을 JSON이 처리할 수 없기 때문에 이 속성을 JSON으로 만드는 순간 예외가 터지는 것이지요.

그렇다면 Member.orders에 @JsonIgnore를 걸고 걸지 않고에 따라서 왜 결과가 달라지는 것일까요?

1. Member.orders에 @JsonIgnore가 없는 경우

Order
- id
- member

 

Member(Proxy)
- id
- List orders
- hibernateLazyInitializer List orders 속성 때문에 이 속성이 호출되지 못함

 

Member.orders에 @JsonIgnore가 없는 경우 JSON 라이브러리는 Order -> Member를 호출하고 또 Member.orders를 호출하는 순간 다시 Member의 내용을 생성합니다. 이 과정에서 Member에 있는 id, orders는 호출되지만 orders를 호출하는 순간 다시 Order의 속성을 호출하기 때문에 결과적으로 Member.hibernateLazyInitializer는 호출되지 않습니다. 계속 무한 반복 되는데, Member.orders에서 다시 Order로 넘어가는 것이지요.

JSON은 일정 이상의 문자가 버퍼에 차면 자동으로 출력합니다. 따라서 무한 루프 과정에서 출력된 결과를 볼 수 있습니다.

 

2. Member.orders에 @JsonIgnore가 있는 경우

Order
- id
- member

 

Member(Proxy)
- id
- List orders(@JsonIgnore)
- hibernateLazyInitializer 호출됨

Member.orders에 @JsonIgnore 속성이 있는 경우에는 JSON 라이브러리가 Member.id는 호출하고, Member.orders는 무시합니다. 그리고 바로 hibernateLazyInitializer를 호출합니다. 이 순간 예외가 발생합니다.

정리하면 Member.orders에 @JsonIgnore가 없는 경우 hibernateLazyInitializer가 호출되었기 때문에 예외가 발생했습니다.

하지만 첫 번째 경우도 무한 루프에 빠지는 문제가 발생하고, 두 번째 경우도 예외가 터지기 때문에 둘 다 문제가 되는 케이스 입니다.

따라서 이 부분에 대해서 이해가 어렵다면, 너무 자세히 이해하려는 것 보다는 무한 루프가 되는 케이스와, 프록시를 호출하는 케이스를 모두 조심하는 것으로 이해하시는 정도면 충분합니다.

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

하춘화마타타님의 질문에 대해 관련된 자료를 찾아보았습니다. 우선, JsonIgnore를 추가함으로써 발생하는 지연로딩 이슈의 원인에 대해 설명 드리겠습니다.

API 호출 시 지연 로딩(LAZY Loading)과 즉시 로딩(EAGER Loading)의 처리 방식 차이로 인해 발생하는 문제입니다. 지연 로딩은 실제로 엔티티의 속성 값이 필요할 때까지 로드를 지연시키는 방식입니다. 반면, 즉시 로딩 방식은 연관된 엔티티를 한 번에 모두 로드하는 방식입니다.

강의에서 언급한 상황에서 순환 참조가 발생한 이유는 엔티티 간 연관 관계가 있을 때, 서로를 참조함으로써 발생합니다. JSON으로 변환하려 할 때 서로 참조하는 구조가 반복되어 무한 루프에 빠지게 되는 것입니다.

JsonIgnore를 추가하면, JSON 변환 과정에서 해당 필드를 무시하게 됩니다. 순환 참조 문제는 해결되지만, 이 경우 Jackson 라이브러리가 지연 로딩된 프록시 객체를 초기화하지 않고 그대로 직렬화하려고 시도하게 됩니다. 지연 로딩된 프록시 객체를 직렬화하는 과정에서 Hibernate 세션이 이미 종료되었거나 접근할 수 없는 상태일 때, 지연 로딩 이슈(정확히는 could not initialize proxy - no Session 같은 에러)가 발생합니다.

이러한 상황에 대한 자세한 설명과 다양한 해결방안은 다음 링크에서 확인하실 수 있습니다:

특히, 프록시 객체가 직렬화되는 과정에서 발생할 수 있는 문제를 해결하기 위한 다양한 방법들이 논의되어 있습니다. 이러한 내용은 하춘화마타타님의 질문에 깊이 있는 이해를 돕는 데 유용할 것입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

하춘화마타타님의 프로필 이미지
하춘화마타타

작성한 질문수

질문하기