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

이재표·2023년 11월 19일
0

OSIV

최신 스프링 부트의 애플리케이션을 실행하면 WARN이 하나 뜨게 된다.
JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default....

해당 경고는 JPA가 데이터베이스 커넥션을 생명주기에 대한 경고이다. JPA의 영속성 컨텍스트는 데이터베이스 커넥션을 1대1로 사용을 해야하고, 트랜잭션(@Transactional) 안에서 데이터베이스를 시작할때 영속성 컨텍스트가 데이터베이스 커넥션을 가져온다. 이때 open session in view가 켜져 있으면, 트랜잭션이 끝나도 유저에게 응답이 나가기 전까지 데이터베이스 커넥션을 가지고 있습니다. 왜냐하면 트랜잭션이 끝나더라도 지연로딩을 컨트롤러단에서 실행하듯 커넥션이 필요한 시점이 트랜잭션 바깥에서 있을수 있기 때문입니다.

지연로딩은 영속성 컨텍스트가 살아있어야 가능하고, 영속성 컨텍스트는 기본적으로 db커넥션이 유지되어야 한다.

유용한 방법이라 생각되지만 해당 방식에는 치명적인 단점이 있습니다!
데이터베이스 커넥션을 너무 오랫동안 가지고 있기 때문에 실시간 트래픽이 중요한 애플리케이션에서 커넥션이 모잘라 장애로 이어질수 있는 점입니다. (eg. Api가 blocking이라도 걸린다면 blocking이 풀릴때까지 커넥션이 가지고 있게 됩니다.)

만약 OSIV를 끈다면, 트랜잭션이 끝나면 커밋을 다 날리고 영속성 컨텍스트를 닫아 커넥션을 반환하여 트랜잭션 바깥에서 지연로딩들을 사용할수 없게 됩니다.
물론 해당 방법도 치명적인 단점이 있는데, 지연로딩을 트랜잭션 안에서 해결해야한다. 예제에서는 컨트롤러에서 지연로딩을 해주곤 하였는데, 트랜잭션안에서 다 처리하고 완성된 dto를 반환해줘야한다.

실제 해당 옵션을 끄고 실행하면 org.hibernate.LazyInitializationException: could not initialize proxy [jpabook.jpashop.domain.Member#1] - no Session 다음과 같은 오류가 뜨게 된다. 왜냐하면 트랜잭션 바깥에서 지연로딩을 진행했기 때문이다.

해결법?

그렇다면 어떻게 해결하면 좋을까?
1. 옵션을 그냥 켜주는 방법
2. 트랜잭션 안에서 지연로딩을 다 하거나, 페치 조인을 사용하면 된다.
3. 계층을 분리해주는 방법

계층을 분리?

Command와 Query계층을 분리해준다. 핵심 비즈니스로직은 Command에서 처리해주고 Query에서 화면이나 API에 맞춘 서비스를 제공한다. 보통 Service단에서 트랜잭션을 유지하기 때문에 Query서비스에서 엔티티를 불러오는 주요비즈니스가 아닌 애플리케이션에서 직접 조작해야하는 경우 사용한다.

0개의 댓글