[Spring] OSIV로 알아보는 Spring Transaction 헤짚기

반응형

Spring boot에는 spring.jpa.open-in-view라고 하는 옵션이 있습니다. 이 옵션은 JPA의 OSIV 기능을 ON/OFF 할 수 있는 옵션인데요. 

 

이 옵션이 무엇인지 알아보도록 하겠습니다.

 

 

 

OSIV

먼저 이 옵션은 JPA의 OSIV 기능의 사용 유무를 나타내는데, OSIV란, 영속성 컨텍스트를 View까지 열어두는 기능입니다. 영속성 컨텍스트가 무엇인지 잘 모르시겠다면 아래 글을 참고해보세요.

 

2020.06.23 - [Programming/Spring] - [Spring] JPA의 영속성 컨텍스트와 생명주기

 

[Spring] JPA의 영속성 컨텍스트와 생명주기

Spring Framework를 이용하여 웹 애플리케이션이나 서버 애플리케이션을 개발할 때 사용하는 DB 라이브러리가 있습니다. 보통 JDBC 드라이버를 이용하여 DB Connection을 수행하고, 여러 개의 커넥션이 연

blog.neonkid.xyz

OSIV는 영속성 컨텍스트가 계속 유지되면 Entity 객체도 영속 상태로 유지되는 기능으로써 이 기능은 차후 관계형 데이터 객체를 지연 로딩할 때 사용합니다.

 

OSIV는 Open Session In View의 약자로 여기서 Session은 Hibernate 인터페이스의 Session을 말한다. JPA에서는 EntityManager를 사용하기 때문에 OSIV(Open Session In View)가 아닌 OEIV(Open EntityManager In View)로 부르는 것이 맞지만 통상 편하게 OSIV로 통합해서 사용한다.

 

 

 

OSIV 동작 원리

그렇다면 Spring Framework 위에서 OSIV는 어떻게 동작할까요? Spring이 제공하는 OSIV에는 두 가지 사용방법이 있습니다.

 

 

아시다시피 영속성 컨텍스트는 사용자가 요청하는 시점에서 생성되지만 데이터를 읽고 쓰는 DB 트랜잭션은 비즈니스 계층에서만 사용할 수 있도록 트랜잭션이 발생합니다.

 

Spring JPA의 OSIV 옵션을 true로 주게 되면 위와 같은 그림으로 동작합니다. 이 원리를 요약하면 아래와 같습니다.

 

  • Browser으로부터 요청이 들어오면 Servlet Filter나 Spring Interceptor에서 영속성 컨텍스트(EntityManager)를 생성합니다. (하지만 우리는 이전 글에서 다뤘듯, 이 부분에서 Transaction을 시작하는 것은 아닙니다.)
  • Transaction AOP 혹은 begin을 이용해서 트랜잭션을 시작할 땐 위에서 생성한 영속성 컨텍스트를 이용해 트랜잭션을 시작합니다. (Spring boot의 경우는 이미 정의된 autoconfigure를 이용합니다)
  • 위 그림에서는 Service 클래스에서 Transaction AOP를 이용한 경우이므로 Service 로직이 완료되면 트랜잭션을 커밋하고, 트랜잭션은 종료되지만 영속성 컨텍스트는 아직 살아있습니다. (즉, DB와 커넥션이 아직 종료된 상태가 아닙니다.)
  • Controller와 View까지 영속성 컨텍스트가 유지되기 때문에 이 때 사용한 Entity는 계속 영속 상태를 유지하게 됩니다.
  • Servlet Filter 혹은 Spring Interceptor로 로직이 다시 회귀된 경우, 그 때 영속성 컨텍스트를 종료합니다.
    (이 때, flush 메서드가 아닌 close 메서드가 호출됩니다.)

 

여기서 봐야할 점은 Transaction AOP를 벗어난 Controller 레이어에서부터 Entity는 수정 불가능한 상태가 되는데, 이 때부터는 트랜잭션이 이미 끝난 상태이기 때문입니다. 하지만 Entity 객체를 수정/삭제하는 작업이 아닌 조회를 할 때는 아무런 상관이 없습니다.

 

따라서 Entity 객체를 렌더링하는 과정에서 Lazy Loading이 발생해도 조회만 하기 때문에 Controller 레이어에서 수행 가능합니다.

 

  • JPA를 이용해 Entity 객체를 수정/삭제하는 경우에는 반드시 트랜잭션 범위 내에서 수행해야 한다.
  • 트랜잭션 범위 밖에서는 Entity 조회 외에는 불가능하다.

 

그렇다면 이건 어떨까? 만약 트랜잭션 범위 밖인 Controller, View 내에서 Entity를 수정한다면? 우리는 이전 글에서 영속성 컨텍스트는 변경 감지(Dirty Checking) 기능을 가지고 있으므로 데이터 수정이 바로 되어야 하지만 트랜잭션 밖에서는 그렇게 되지 않습니다.

 

  • 영속성 컨텍스트의 변경 내용을 DB에 반영하려면 영속성 컨텍스트를 flush 해야하는데, Spring이 제공하는 OSIV는 요청이 긑나면 flush를 호출하지 않고, close 메서드를 호출하므로 Entity 객체의 변경 내용을 반영하지 않고 종료함.
  • 강제로 DI를 이용해 Spring 내에서 사용하는 EntityManager를 사용해 Controller나 View에서 flush 메서드를 호출해도 이를 예외 처리로 막아버리는 코드를 삽입해서 이 역시 안됨.

 

 

OSIV를 사용하지 않는 경우

아마 눈치를 채신 분들도 계시겠지만 OSIV를 사용하지 않도록 설정한 경우에는 아래와 같이 동작합니다.

 

옵션에서 설명한 그대로 View에서 영속성 컨텍스트를 종료하기 때문에 이 때부터 Entity 객체는 준영속 상태로 전환됩니다. 이 때부터는 트랜잭션 밖에서 Child Entity를 로딩하는 등의 행위가 일절 불가능합니다.

 

 

 

OSIV를 사용해야 하는 경우와 그렇지 않은 경우

그렇다면 OSIV는 언제 쓰는 것이 좋을까? 

 

  • View에서 Entity 객체를 사용해 렌더링 해야하는 경우. (Spring MVC)
  • Entity 객체 내 연관 객체를 View에서 제어해야하는 경우
  • 같은 영속성 컨텍스트 내에서 여러 트랜잭션을 공유해야 하는 경우.

 

반대로 사용하면 오히려 독이되는 경우도 있습니다.

 

  • Controller와 Service 로직이 1:1 매핑인 경우
  • Service 레이어에서 트랜잭션이 종료되는 경우 
  • Controller에서 Transaction 외에 별도의 처리 시간이 긴 로직이 있는 경우 (예: 외부 API 호출)

 

Spring MVC를 사용하는 경우에는 보통 Entity 객체를 Service Layer 위층까지 끌어올려서 사용하거나 여러 트랜잭션을 공유해서 사용하는 경우도 있을 것입니다. 그럴 때가 아니라면 OSIV의 활성화는 오히려 DB 커넥션을 오래 사용하게 할 뿐 권장하는 옵션은 아닙니다.

 

그렇지만 이런 경우가 있을 수 있습니다. 영속성 컨텍스트에서 Lazy Loading(지연 로딩)은 View에 렌더링 하는 것 뿐 아니라 REST API 애플리케이션을 개발할 때 Child Entity를 로딩할 때도 사용합니다. 이런 경우에는 View에서 로딩하지 않고 Service Layer에서 가급적이면 로딩을 끝낼 수 있도록 구현하는 것이 좋습니다.

 

 

 

CQRS

API가 많아지고 비즈니스 로직이 커지면 OSIV 옵션 없이 비즈니스 레이어에서 지연 로딩을 주는 것이 복잡도를 늘리는 원인이 될 수 있습니다. 이럴 때는 Command와 Query를 분리하는 CQRS를 사용하여 문제를 해결해 볼 수 있습니다.

 

  • OrderService (영속)
  • OrderQueryService (조회)

 

Spring MVC와 같은 View까지 담당하는 애플리케이션의 경우에는 화면에 맞춰 Query 성능 최적화가 필요한데, 이래서 사실 MyBatis나 iBatis를 쓰는 경우도 있습니다. 핵심 비즈니스 로직과 분리하기 위해 OSIV를 사용하기도 하는 가장 큰 경우이지만 보편적으로 큰 애플리케이션이 아닌 이상 비즈니스 로직에 큰 영향을 주지는 않습니다.

 

그럼에도 불구하고, 정말 크고 복잡한 애플리케이션을 개발할 때는 이 둘의 관심사를 분명하게 분리해야 차후 유지보수가 편해집니다. 조회와 영속에 대한 비즈니스 로직을 분리하고 커넥션을 가능한 많이 재활용해 사용함으로써 대용량 트래픽 처리에 조금은 유연하게 해주는 것이죠.

 

2022.02.21 - [Programming/Spring] - [Spring boot] Axon Framework로 시작하는 CQRS 기초

 

[Spring boot] Axon Framework로 시작하는 CQRS 기초

많이 미루어진 Axon Framework에 대해 알아보도록 하겠습니다. 이 포스트를 다루기 전에 아주 오랜 시간 전, MSA의 트랜잭션 이야기 중 이벤트 소싱과 CQRS에 대해 다뤄본 적이 있습니다. 해당 내용을

blog.neonkid.xyz

CQRS를 구현할 수 있는 방법에는 여러 가지가 있습니다. 현재 제가 작성한 글 중에서는 Axon 미들웨어를 사용해 Command와 Query를 분리하는 방법이 있는데, 관심있다면 참고해보시면 좋을 것 같습니다.

 

 

 

마치며...

여기까지 간단히 OSIV에 대해 알아봤습니다. 정리해보면 아래와 같습니다.

 

  • OSIV는 Spring에서 ORM의 영속성 컨텍스트를 View에서까지 사용할 수 있도록 옵션.
  • 하지만 트랜잭션 밖에서는 Entity의 지연 로딩 등 조회만 가능.
  • 서로 각기 독립된 트랜잭션에서 트랜잭션 공유 가능

 

만약, 여러분들 중 애플리케이션에서 Controller에 영속 로직 호출 후 외부 API를 호출하는 등의 행위를 하고 있다면 최적화를 위해 OSIV 옵션에 대해 다시 한 번 생각하실 필요가 있습니다. 

 

 

반응형

Tistory Comments 0