모르지 않다는 것은 아는것과 다르다.

Spring Jpa

OSIV

채마스 2022. 2. 27. 00:55

OSIV 란?

  • OSIV(Open Session In View)는 영속성 컨텍스트를 뷰까지 열어두는 기능이다. 영속성 컨텍스트가 유지되면 엔티티도 영속 상태로 유지된다. 뷰까지 영속성 컨텍스트가 살아있다면 뷰에서도 지연 로딩을 사용할 수가 있다.




OSIV ON

  • spring.jpa.open-in-view : true -> 기본값이다.
  • 애플리케이션 시작 시점에 warn 로그를 남긴다.
  • OSIV 전략은 트랜잭션 시작처럼 최초 데이터베이스 커넥션 시작 시점부터 API 응답이 끝날 때 까지 영속성 컨텍스트와 데이터베이스 커넥션을 유지한다.
  • 동작 과정
      1. 클라이언트의 요청이 들어오면 서블릿 필터나, 스프링 인터셉터에서 영속성 컨텍스트를 생성한다. 단 이 시점에서 트랜잭션은 시작하지 않는다.
      2. 서비스 계층에서 @Transeactional 로 트랜잭션을 시작할 때 1번에서 미리 생성해둔 영속성 컨텍스트를 찾아와서 트랜잭션을 시작한다.
      3. 서비스 계층이 끝나면 트랜잭션을 커밋하고 영속성 컨텍스트를 플러시한다. 이 시점에 트랜잭션은 끝내지만 영속성 컨텍스트는 종료되지 않는다.
      4. 컨트롤러와 뷰까지 영속성 컨텍스트가 유지되므로 조회한 엔티티는 영속 상태를 유지한다.
      5. 서블릿 필터나, 스프링 인터셉터로 요청이 돌아오면 영속성 컨텍스트를 종료한다. 이때 플러시를 호출하지 않고 바로 종료한다.
  • 그 덕분에 View Template이나 API 컨트롤러에서 지 연 로딩이 가능했던 것이다.
  • 지연 로딩은 영속성 컨텍스트가 살아있어야 가능하고, 영속성 컨텍스트는 기본적으로 데이터베이스 커넥션 을 유지한다. 이것 자체가 큰 장점이다.
  • 그런데 이 전략은 너무 오랜시간동안 데이터베이스 커넥션 리소스를 사용하기 때문에, 실시간 트래픽이 중 요한 애플리케이션에서는 커넥션이 모자랄 수 있다.
  • 컨트롤러에서 외부 API를 호출하면 외부 API 대기 시간 만큼 커넥션 리소스를 반환하지 못하 고, 유지해야 한다.




OSIV OFF

  • spring.jpa.open-in-view: false : OSIV 가 종료된다.
  • OSIV를 끄면 트랜잭션을 종료할 때 영속성 컨텍스트를 닫고, 데이터베이스 커넥션도 반환한다. 따라서 커 넥션 리소스를 낭비하지 않는다.
  • OSIV를 끄면 모든 지연로딩을 트랜잭션 안에서 처리해야 한다. 따라서 지금까지 작성한 많은 지연 로딩 코 드를 트랜잭션 안으로 넣어야 하는 단점이 있다. 그리고 view template에서 지연로딩이 동작하지 않는다.
  • 결론적으로 트랜잭션이 끝나기 전에 지연 로딩을 강제로 호출해 두어야 한다.
  • 문제의 상황을 예를 들어보면 아래와 같다.
    • 주문 상세 내역을 가져오는 컨트롤러가 있다고 가정하자.
  @GetMapping("/api/v2/orders")
      public Result memberV2() {
          List<Order> orderList = orderService.findOrders(); // 영속성 컨텍스트 초기화

          orderList.forEach(order ->{
              order.getMember().getName() // 이시점에서 문제발생
          })

          return new Result();
      }
  • OSIV OFF 상황에서 위의 경우 orderList가 반환되는 순간 영속성 컨텍스트가 초기화된다.
  • 당연히 컨텍스트안에 있는 Member 프록시 객체도 사라졌을 것이다.
  • 결과적으로 order.getMember().getName() 여기서 프록시가 영속성컨텍스트에 없기때문에 예외가 터진다.
  • 물론 orderList 를 불러올때 fetch join으로 애초에 전부다 select 해오면 문제를 해결할 순 있다.




결론

  • 고객 서비스의 실시간 API는 OSIV를 끄고, ADMIN 처럼 커넥션을 많이 사용하지 않는 곳에 서는 OSIV를 켠다.
  • 대용량의 트래픽을 필요로 하는 경우 OSIV 를 끌 수밖에 없다.
    • 이 경우 관심사를 정확히 분리하면 오류없이 구현할 수 있다.
    • OrderService
      • OrderService: 핵심 비즈니스 로직
      • OrderQueryService: 화면이나 API에 맞춘 서비스
      • 이렇게 비즈니스 로직을 처리하는 클래스와 같은 상태의 영속성 컨텍스트안에서 처리되야할 기능들을 묶어서 처리해야한다.
    • 이렇게 관심사를 분리해서 개발하는 습관은 유지보수할 때에도 큰 이점을 가져간다.



REFERENCES

  • 김영한님의 스프링 부트와 JPA 활용2

'Spring Jpa' 카테고리의 다른 글

조회 성능 최적화  (0) 2022.02.27
값타입  (0) 2022.02.27
Spring Data Jpa  (0) 2022.02.27
JPA 란  (0) 2022.02.27
영속성 전이 (CASCADE)  (0) 2022.02.27