강의

멘토링

로드맵

Inflearn brand logo image

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

병훈님의 프로필 이미지
병훈

작성한 질문수

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

주문 조회 V1: 엔티티 직접 노출

order.getOrderItems() 시 필드 접근 안했는데 select문이 나가는 이유

작성

·

50

·

수정됨

0

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)

[질문 내용]
안녕하세요.

강의 자료에
orderItems.stream().forEach(o -> o.getItem()).getName()); // LAZY 강제 초기화

라고 적혀있어서 그 위의 코드인
List<OrderItem> orderItems = order.getItems()에서는 DB에 SELECT 쿼리를 보내지 않는구나라고 이해했습니다.

근데 확인해보니 아래 코드를 PostMan으로 테스트를 해보았더니 orderItems : null 이지만

SELECT절을 통해 데이터를 조회한 결과를 보였습니다.

또한, order.getOrderItems()를 하지 않아도 orderItem을 조회하는 SELECT절이 나가더라구요 ..

@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1() {
    List<Order> all = orderRepository.findAllByString(new OrderSearch());
    for (Order order : all) {
        order.getMember().getName(); // LAZY 강제 초기화 => 예외 발생 안함 ! why? Open Session in View(OSIV) 때문에
        order.getDelivery().getAddress(); // LAZY 강제 초기화 => 예외 발생 안함 ! why? Open Session in View(OSIV) 때문에
        order.getOrderItems(); // ?
    } // 원래는 LazyInitializationException이 발생하는게 정상이지만 OSIV로 인해 발생하지 않음
    return all;
}
image.png

 

다른 질문을 참고하였을 때 David님께서 아래와 같은 답변을 해주셨습니다.

안녕하세요. Jeongmin Lee님:), 공식 서포터즈 Taewon David Hwang입니다.

지연로딩(Lazy Loading)은 엔티티의 애트리뷰트에 접근할 때 데이터를 가져옵니다.

그러나 하이버네이트에서 지정한 기본타입에 해당되는 것들은 기본적으로 지연로딩을 허용하지 않습니다.

따라서 OrderItem의 애트리뷰트인 name, price, stockQuantty 중 하나라도 접근하게 되면 기본타입에 해당하는 name, price, stockQuantity의 데이터를 모두 불러오게 됩니다.

David 선생님 의견에 따르면 엔티티의 에트리뷰트에 접근해야 데이터를 가져오는거 같은데 왜 위 사진같이 DB에 SELECT절을 호출 해 데이터를 가져오는건가요?

답변 2

1

병훈님의 프로필 이미지
병훈
질문자

아 !! 이해 완료했습니다 !!

저와 같은 궁금증을 가지고 있으실 분들은 위해 아래와 같이 정리하였습니다.

order.getOrderItems().getXxx() 및 order.getOrderItems()를 호출하지도 않았는데 DB에 orderItem SELECT 절이 나가는 이유는 ?

일단, Jackson이 JSON으로 직렬화 할 때 "public getXxx()" 메서드들을 전부 뒤져서 값을 꺼내옵니다. @Getter도 포함입니다 !

근데 orderItems, member, delivery는 LAZY 설정이 되어 있기 때문에 SELECT 절이 나가지 않는게 정상입니다.

하지만, 아래 코드를 보시면 Order 클래스에 getTotalPrice()에 반복문을 통해 orderItem을 순회하고 있습니다. 이로 인해 OrderItem을 조회하기 위해 SELECT절을 보내는겁니다.

public int getTotalPrice() {
    int totalPrice = 0;
    for (OrderItem orderItem : orderItems) {
        totalPrice += orderItem.getTotalPrice();
    }
    return totalPrice;
}

따라서 위 메서드에 @JsonIgnore을 설정 할 시 Jackson은 해당 메서드를 무시하게 되므로 SELECT절이 나가지 않습니다. !!

 

getTotalPrice()를 @JsonIgnore 설정을 하지 않아서 SELECT절이 나갔음에도 불구하고 JSON 응답 값 이 "orderItems" : null 인 이유는?


먼저 Jackson이 기본적으로 다음과 같은 순서로 처리됩니다.

getter 메서드 중 프로퍼티 메서드들을 먼저 처리하고 다음 getXxx로 명시되어 있는 메서드들을 처리합니다.

따라서, getOrderItems()를 만났는데 LAZY 상태니까 null로 직렬화를 합니다.

그 다음에야 getTotalPrice()를 호출하면서 OrderItems 컬렉션을 초기화해서 SQL이 나가는 겁니다.

0

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

질문해 주신 내용은 JPA의 지연 로딩(Lazy Loading)과 관련된 쿼리 실행에 관한 것입니다. order.getOrderItems() 메소드를 호출했을 때, 실제로 DB에서 SELECT 쿼리를 실행하여 데이터를 가져오는 이유에 대해 설명드리겠습니다.

JPA에서 기본적으로 연관 관계는 지연 로딩(Lazy Loading)으로 설정됩니다. 이는 연관된 엔티티를 실제로 사용할 때까지 DB에서 데이터를 조회하지 않는 기능입니다. 그러나 특정 상황에서는 예상치 못한 시점에 쿼리가 실행될 수 있습니다.

질문에서 언급하신 order.getOrderItems() 호출은 실제로 데이터를 접근할 때 발생하며, 이는 OSIV(Open Session in View)라는 개념과 밀접한 관련이 있습니다. OSIV는 트랜잭션 범위를 요청-응답 전반으로 확장하여, 서비스 계층을 넘어 컨트롤러나 뷰까지 영속성 컨텍스트를 열어주는 방식입니다. 따라서 엔티티를 사용하는 동안에는 지연 로딩이 가능해집니다. 실제로 엔티티의 데이터에 접근하지 않더라도, 프레임워크 레벨에서 객체가 직렬화 또는 변환 되는 과정에서, 혹은 JSON으로 변환하는 과정에서 필드를 접근할 수 있습니다. 이 과정에서 지연 로딩이 발생하여 SELECT 쿼리가 실행될 수 있습니다.

이미지가 보여주는 SQL SELECT 쿼리는 이렇게 ‘프록시 초기화’ 과정에서 발생할 수 있는 예입니다. 즉, order.getOrderItems() 자체는 SQL을 실행하지 않지만, 이후의 프로세스(예: 응답 생성)를 통해 쿼리가 실행되고, 해당 프록시가 초기화될 수 있습니다.

기존의 유사한 질문과 답변을 확인해 보시기 바랍니다:
- OrderItems까지 같이 조회되는 이유가 궁금합니다!
- Hibernate5Module가 기본상태일때

해당 링크에는 OSIV의 동작 원리와 지연 로딩의 특성에 대한 상세한 설명이 포함되어 있습니다. 강의를 들으시면서 이 개념들을 정리해 두시면 좋을 것 같습니다.

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

병훈님의 프로필 이미지
병훈
질문자

근데 왜 반환할 때 orderItems : null

즉, null로 반환하는거에요 ?

병훈님의 프로필 이미지
병훈

작성한 질문수

질문하기