본문 바로가기

Development/Diary

[개발 일기]JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. (OSIV Anti Pattern)

스프링을 다시 공부하려고 새 프로젝트를 만들어서 바로 실행만 했더니, 이런 경고가 발생했습니다.

WARN 6264 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning

 

뤼튼(GPT 4)에게 물어봤는데 답변은 이러했습니다.

 

이 경고 메시지는 Spring Boot에서 'spring.jpa.open-in-view' 설정이 기본적으로 활성화되어 있음을 알리는 것입니다.

이 설정이 활성화되면, 뷰 렌더링 동안에 데이터베이스 쿼리가 수행될 수 있습니다.

즉, HTTP 요청이 완전히 처리될 때까지 데이터베이스 연결이 열려있는 상태를 유지하게 됩니다.

이 설정은 어플리케이션의 성능에 영향을 미칠 수 있습니다.

왜냐하면, 뷰 렌더링이 오래 걸리는 경우 데이터베이스 연결이 불필요하게 오래 열려있게 되기 때문입니다.

이 설정이 활성화되면, 뷰 렌더링 동안에 발생할 수 있는 데이터베이스 에러를 처리하는 것이 더 어려워질 수 있습니다.

 

따라서, 이 경고 메시지를 해결하려면 'spring.jpa.open-in-view' 설정을 명시적으로 비활성화해야 합니다.

이를 위해 application.properties 파일에 다음 설정을 추가하면 됩니다:

'spring.jpa.open-in-view=false'

이렇게 하면, 뷰 렌더링 동안에 데이터베이스 쿼리가 수행되지 않도록 할 수 있습니다. 이는 데이터베이스 연결을 보다 효율적으로 관리하고, 어플리케이션의 성능을 향상시키는 데 도움이 될 수 있습니다.

 

사실인가 확인해보았습니다. (AI의 답변은 진짜인지 검증하는 편..)

 

https://stackoverflow.com/questions/30549489/what-is-this-spring-jpa-open-in-view-true-property-in-spring-boot

 

What is this spring.jpa.open-in-view=true property in Spring Boot?

I saw spring.jpa.open-in-view=true property in Spring Boot documentation for JPA configuration. Is the true default value for this property if it's not provided at all?; What does this really do? ...

stackoverflow.com

OSIV (Open Session in View)

OSIV(Open Session in View)는 Hibernate에서 사용하는 패턴 중 하나로, 데이터베이스 연결(Session)을 HTTP 요청의 시작부터 끝까지 열어두는 방식을 말합니다.

이 패턴을 사용하면, 뷰 렌더링 동안에 필요한 데이터를 게으르게(Lazy) 로딩할 수 있게 됩니다.

즉, Hibernate 세션이 HTTP 요청의 생명주기와 동일하게 유지되며, 이는 웹 애플리케이션에서 JPA 엔티티를 뷰 템플릿에서 직접 접근할 수 있게 해줍니다.

 

그러나 이 방식은 여러 가지 문제점을 가지고 있어 '안티 패턴'으로 간주되기도 합니다. 

OSIV Anti Pattern

게시물(Post)의 목록을 얻는 요청이 들어왔다고 가정합니다.

  1. OpenSessionInViewFilter는 기본 SessionFactory의 openSession 메서드를 호출하고 새로운 세션을 얻습니다.
  2. 세션은 TransactionSynchronizationManager에 바인딩됩니다.
  3. OpenSessionInViewFilter는 javax.servlet.FilterChain 객체 참조의 doFilter를 호출하고 요청이 추가로 처리됩니다.
  4. DispatcherServlet이 호출되고, HTTP 요청을 기본 PostController로 라우팅합니다.
  5. PostController는 Post 엔티티의 리스트를 얻기 위해 PostService를 호출합니다.
  6. PostService는 새로운 트랜잭션을 열고, HibernateTransactionManager는 OpenSessionInViewFilter에 의해 열린 동일한 세션을 재사용합니다.
  7. PostDAO는 모든 Lazy 연관 관계를 초기화하지 않고 Post 엔티티의 리스트를 가져옵니다.
  8. PostService는 기본 트랜잭션을 커밋하지만, 세션은 외부에서 열렸기 때문에 닫히지 않습니다.
  9. DispatcherServlet는 UI를 렌더링을 시작하며, 차례로 Lazy 연관 관계를 탐색하고 그들의 초기화를 트리거합니다.
  10. OpenSessionInViewFilter는 세션을 닫을 수 있고, 기본 데이터베이스 연결도 해제됩니다.

서비스 계층은 데이터베이스 트랜잭션을 열고 닫지만, 이후에는 명시적인 트랜잭션이 진행되지 않습니다.

이 때문에 UI 렌더링 단계에서 발행되는 추가적인 문장마다 자동 커밋 모드에서 실행됩니다. 자동 커밋은 각 트랜잭션 끝에서 커밋을 발행하므로, 이는 디스크에 트랜잭션 로그 플러시를 트리거할 수 있습니다. 이로 인해 데이터베이스 서버에 부담을 주게 됩니다.

 

하나의 최적화 방법은 연결을 읽기 전용으로 표시하는 것이며, 이는 데이터베이스 서버가 트랜잭션 로그에 쓰는 것을 피하게 해줍니다.

서비스 계층과 UI 렌더링 프로세스 모두에서 문장이 생성되므로, 더 이상 관심사의 분리가 이루어지지 않습니다. 생성되는 문장 수를 확인하는 통합 테스트를 작성하려면 모든 계층(웹, 서비스, DAO)을 거쳐야 하며, 애플리케이션을 웹 컨테이너에 배포해야 합니다.

메모리 내 데이터베이스(예: HSQLDB)와 경량 웹서버(예: Jetty)를 사용하더라도, 이러한 통합 테스트는 계층을 분리하고 백엔드 통합 테스트가 데이터베이스를 사용하면서 프론트엔드 통합 테스트가 서비스 계층을 모두 모킹하는 것보다 느리게 실행될 것입니다.

 

UI 계층은 연관 관계를 탐색하는 것에 제한되며, 이는 차례로 N+1 쿼리 문제를 트리거할 수 있습니다.

Hibernate는 연관 관계를 배치로 가져오기 위해 @BatchSize와 FetchMode.SUBSELECT를 제공하지만, 이러한 주석은 기본 패치 계획에 영향을 미쳐 모든 비즈니스 유스케이스에 적용됩니다.

이 때문에 데이터 액세스 계층 쿼리가 훨씬 적합합니다. 왜냐하면 현재 Usecase의 데이터 패치 요구사항에 맞게 맞춤화할 수 있기 때문입니다.

 

마지막으로, UI 렌더링 단계 동안 데이터베이스 연결이 계속 유지되므로, 연결 임대 시간이 늘어나고 데이터베이스 connection pool에 대한 병목으로 인해 전체 트랜잭션 처리량이 제한됩니다.

연결이 유지될수록, 다른 동시 요청은 풀에서 연결을 얻기 위해 더 많이 기다려야 합니다.

 

요약하자면 뷰 렌더링 동안에 발생하는 모든 데이터베이스 쿼리는 별도의 트랜잭션 없이 자동 커밋 모드에서 수행되고, 이는 데이터베이스에 부담을 줄 수 있고, 또한 서비스 계층과 뷰 계층간의 역할 분리를 방해하며, 데이터베이스 연결을 오랫동안 유지하게 되어 리소스를 비효율적으로 사용하게 됩니다.

 

Springboot에서는 OSIV 설정이 default 값으로 enable이 적용되기 때문에, 성능과 확장성 측면에서 이를 비활성화 하려면

application.properties 파일에 아래를 추가하도록 권장합니다.

spring.jpa.open-in-view=false