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

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

kongminoo님의 프로필 이미지
kongminoo

작성한 질문수

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

주문 조회 V2: 엔티티를 DTO로 변환

트랜잭션 질문

작성

·

64

0

public class OrderApiController {

@GetMapping("/api/v1/orders")
public List<Order> orderV1() {
    List<Order> all = orderRepository.findAllByString(new OrderSearch());

    for (Order order : all) {
        order.getMember().getName();
        order.getDelivery().getAddress();
        List<OrderItem> orderItems = order.getOrderItems();
        orderItems.forEach(o -> o.getItem().getName());
    }

    return all;
}
}
public class OrderRepository{
public List<Order> findAllByString(OrderSearch orderSearch) {

        String jpql = "select o from Order o join o.member m";
        boolean isFirstCondition = true;

    //주문 상태 검색
    if (orderSearch.getOrderStatus() != null) {
        if (isFirstCondition) {
            jpql += " where";
            isFirstCondition = false;
        } else {
            jpql += " and";
        }
        jpql += " o.status = :status";
    }
...
  1. 트랜잭션을 따로 붙히지 않았는데 상관없는건가요?

  2. List<Order> all = orderRepository.findAllByString(new OrderSearch()); 이 때 연관관계매핑되어 있는 객체들은 지연로딩에 의해 프록시로 존재하고

     

    for (Order order : all) { order.getMember().getName(); order.getDelivery().getAddress(); List<OrderItem> orderItems = order.getOrderItems(); orderItems.forEach(o -> o.getItem().getName()); } 이 때 강제호출 되어서 쿼리가 나가는 게 맞나요?

  3. orderRepository.findAllByString에서 repository에서 작업이 마쳐서 트랜잭션이 커밋되어서 영속성 컨텍스트에는 아무값이 없을 거 같은데 컨트롤러에서 리턴하기 전까지 하나의 트랜잭션 안에서 작동이 이뤄지는 건가요?

  4. 트랜잭션이 언제 시작하고 언제 종료되는 지 궁금합니다

답변 2

0

안녕하세요. kongminoo님, 공식 서포터즈 코즈위버입니다.

  1. 영속성 컨텍스트에서 데이터를 수정하기 위해서는 반드시 트랜잭션 내에서 수정해야 한다는 제약이 있습니다. 그러나 단순히 조회만 하는 것이라면 문제 없습니다.

  2. 관계가 모두 지연로딩으로 설정되어있다면 말씀하신 내용이 맞습니다.

  3. 현재 명시적으로 @Transactional 을 선언하지 않았기에 트랜잭션이 발생하지 않습니다. Repository의 경우 SpringDataJPA 프레임워크를 사용한다면 자동으로 트랜잭션이 실행되나 그런 경우가 아니라면 별도의 트랜잭션이 실행되지 않습니다.

  4. 일반적으로 Service 클래스의 상단에 @Transactional 을 사용하게 되는데요 이 경우 서비스 메서드에 진입하기 전 트랜잭션을 시작하고, 서비스 메서드에서 벗어난 후 트랜잭션을 종료(커밋) 하게 됩니다.

감사합니다.

0

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

kongminoo님께서 질문하신 내용에 대해 설명해드리겠습니다. 우선 JPA와 스프링에서 트랜잭션이 어떻게 작동하는지에 대한 이해가 필요합니다.

  1. 트랜잭션 설정: 코드 상에서 @Transactional을 사용하지 않더라도, 스프링 부트 애플리케이션에서는 OSIV(Open Session in View)라는 설정이 기본적으로 활성화되어 있습니다. 이 설정은 뷰 렌더링까지 트랜잭션이 유지되도록 하여 지연 로딩(Lazy Loading)이 정상적으로 작동할 수 있게 합니다. @Transactional 없이도 데이터베이스 읽기 작업이 가능한 이유는 이 때문입니다.

  2. 지연 로딩: List<Order> all = orderRepository.findAllByString(new OrderSearch());에서 불러온 엔티티들은 지연 로딩 설정에 의해 프록시로 존재하게 됩니다. 따라서 실제 데이터를 필요로 하는 시점에서(예: order.getMember().getName() 호출 시)에 지연 로딩 쿼리가 실행됩니다.

  3. 트랜잭션의 커밋과 영속성 컨텍스트: orderRepository.findAllByString()에서 데이터를 불러온 후, 트랜잭션이 없다면 영속성 컨텍스트는 유지되지 않습니다. 하지만 OSIV 설정 덕분에 뷰 렌더링까지 트랜잭션 범위가 유지됩니다. 결국, 데이터는 컨트롤러에서 리턴하기 전까지는 사용 가능합니다.

  4. 트랜잭션의 시작과 종료: 기본적으로 스프링 트랜잭션은 서비스 계층에서 시작하는 것이 일반적입니다. 하지만 OSIV 설정으로 인해 HTTP 요청이 시작될 때 트랜잭션이 열리고, 응답이 완료될 때까지 유지되다가 종료됩니다.

관련하여 더 깊이 있는 이해가 필요하시다면, 아래 링크들을 참조해 주세요:

이 게시물들은 JPA와 스프링 부트에서 트랜잭션의 작동 방식과 연관된 내용이므로 도움이 되실 것입니다.

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

kongminoo님의 프로필 이미지
kongminoo

작성한 질문수

질문하기