본문 바로가기
Computer Science/개념 | 이론

[JPA] OSIV (Open Session In View)

by Baest 2025. 7. 18.

1. 계기

SpringBoot Application을 실행 후 아래와 같은 로그를 보게 되었다.

그와 동시에 로그 레벨이 warning이라 내용을 살펴보았고, 그 중 spring.jpa.open-in-view 와 관련된 정리를 해보려고 한다.

PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)

	
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

 

아래 위주로 내용을 정리해 보려고 한다.

  • spring.jpa.open-in-view is enabled by default 경고의 의미는 무엇이며
  • 왜 Spring Boot에서 이 경고를 보여주는지
  • 그리고 실제 운영에서 겪을 수 있는 문제들은 어떤 것인지

 

2. OSIV

 

우선 OSIV가 어떤 것인지 알아볼 필요가 있는데, 기본적으로는 개념과 동작방식 그리고 장단점 정도를 알고 있어야할 것이다.

그와 별개로 JPA에서는 OEIV라고 부르고, Hibernate에서는 OSIV로 부른다고 한다. 하지만 관례상 모두 OSIV로 불리고 있다고 하니 참고하면 좋을 것 같다.

 

본격적인 OSIV의 개념 설명에 앞서 개념을 더 잘 이해하기 위한 몇가지를 알아둘 필요가 있다.

 

영속성 컨텍스트, 지연 로딩 에 대해 간략히 설명해 보려고 한다.

 

영속성 컨텍스트 (Persistence Context)

엔티티를 관리하는 메모리 공간
즉, JPA가 엔티티의 상태를 추적하고 관리하는 1차 캐시

 

// 1. 동일성 보장
val user1 = entityManager.find(User::class.java, 1L)
val user2 = entityManager.find(User::class.java, 1L)
// user1 === user2 → true (같은 인스턴스)

// 2. 변경 감지 (Dirty Checking)
@Transactional
fun updateUser() {
    val user = userRepository.findById(1L).get()
    user.name = "변경된 이름"  // save() 호출 없이도 자동 업데이트
}

 

생명주기

  • 트랜잭션 시작 : 영속성 컨텍스트 생성
  • 트랜잭션 종료 : 영속성 컨텍스트 종료
  • OSIV 활성화 시 : HTTP 요청 전체 동안 유지

 

지연 로딩 (Lazy Loading)

필요할 때만 데이터를 가져오는 전략
즉, 연관된 엔티티를 실제 사용 시점에 DB에서 조회

 

기본적으로 아래와 같이 동작한다.

@Entity
class User {
    @OneToMany(fetch = FetchType.LAZY)  // 기본값
    var orders: List<Order> = mutableListOf()
}

// 사용 예시
val user = userRepository.findById(1L).get()  // User만 조회
println(user.name)  // OK

println(user.orders.size)  // 이 시점에 Order 조회 쿼리 실행

 

 

여러 블로그에도 나와 있듯 주로 발생되는 Exception은 LazyInitializationException 인데, 아래와 같이 영속성 컨텍스트가 종료된 이후 발생된다.

// LazyInitializationException 발생 케이스
@Transactional
fun getUser(): User {
    return userRepository.findById(1L).get()
}  // 여기서 영속성 컨텍스트 종료


// Controller
fun controllerMethod() {
    val user = userService.getUser()
    // 영속성 컨텍스트가 없어서 지연 로딩 실패
    println(user.orders.size)  // Exception 발생
}

 

 

OSIV 활성화를 통해 위와 같은 이슈를 해결할 수 있다.

// OSIV 활성화 시
@Controller
class UserController {
    fun getUser(): String {
        val user = userService.getUser()
        // 컨트롤러에서도 지연 로딩 가능
        user.orders.forEach { println(it.product) }
        return "user-page"
    }
}

// OSIV 비활성화 시
@Controller
class UserController {
    fun getUser(): String {
        val user = userService.getUser()
        // 지연 로딩 불가능
        // user.orders 접근 시 LazyInitializationException
        return "user-page"
    }
}

 

 

OSIV를 사용하지 않는 경우에는 EntityManager와 Transaction은 같은 라이프 사이클을 가지게 된다.

Transaction이 종료되면서 영속성 컨텍스트도 같이 종료하기 때문이다.

 

OSIV를 사용하는 경우에는 Transaction은 @Transactional을 선언한 객체/메소드까지만 라이프 사이클이 유지되고 영속성 컨텍스트는 View 영역까지 살아있다.

 

 

위 두가지 개념을 통해 OSIV가 필요한 배경을 다시 정리하면,

  • 영속성 컨텍스트는 엔티티 관리의 핵심이고, 그 생명주기가 중요하다.
  • 지연 로딩은 편리하지만 영속성 컨텍스트가 살아있을 때만 동작한다.
  • OSIV는 위 두 개념을 연결하는 다리 역할을 한다.

 

3. 장단점

장점

  • 편리한 Lazy Loading (View에서 엔티티 접근 가능)
  • 코드 작성의 단순함
  • N+1 문제 회피 가능

단점

 

4. 결론

팀 상황에 맞는 선택

  • 트래픽이 많은 서비스: OSIV 비활성화 권장
  • 프로토타입이나 소규모 서비스: 활성화 고려 가능

점진적 마이그레이션 전략

 

 

 

 

'Computer Science > 개념 | 이론' 카테고리의 다른 글

Hikari CP  (3) 2025.06.08
Forward Proxy VS Reverse Proxy  (0) 2025.02.26