OSIV (Open Session in View)

Kkd·2025년 1월 4일
0

매일메일 개념정리

목록 보기
59/93

OSIV (Open Session in View)

OSIV(Open Session In View)HibernateJPA와 같은 ORM 프레임워크에서 Lazy Loading으로 데이터를 조회할 때 발생하는 LazyInitializationException 문제를 해결하기 위해 사용하는 패턴입니다. OSIV는 HTTP 요청이 완료될 때까지 Hibernate의 Session 또는 JPA의 EntityManager를 열어둠으로써 Lazy Loading이 가능하도록 지원합니다.

OSIV(open session in view) 는 영속성 컨텍스트를 뷰까지 열어둔다는 의미입니다. 영속성 컨텍스트가 살아있으면 엔티티는 영속 상태로 유지될 수 있어, 뷰에서도 지연 로딩을 사용할 수 있어요. OSIV의 핵심은 뷰에서도 지연 로딩이 가능하도록 하는 것입니다. 가장 단순한 구현은 클라이언트 요청이 들어올때 필터나 인터셉터에서 트랜잭션을 시작하는 방법인데요. 이를 트랜잭션 방식 OSIV라고 합니다. 하지만, 트랜잭션 방식 OSIV는 표현 계층에서도 엔티티를 수정할 수 있기 때문에 유지보수하기 어려운 코드를 만들 수 있습니다.


OSIV의 동작 원리

  1. 요청 시작

    • 사용자가 웹 요청을 보냅니다.
    • Spring에서는 DispatcherServlet에서 요청을 처리합니다.
  2. Session/EntityManager 열기

    • OSIV가 활성화된 상태라면 요청 시작 시점에 Hibernate의 Session 또는 JPA의 EntityManager가 열립니다.
  3. Controller/Service 계층 처리

    • Controller와 Service 계층에서 Lazy Loading이 필요한 데이터를 호출하더라도 Session/EntityManager가 열려 있으므로 데이터 초기화가 가능합니다.
  4. View 계층 처리

    • View 계층에서도 Lazy Loading을 통해 연관 데이터를 조회할 수 있습니다.
  5. Session/EntityManager 닫기

    • 요청 처리가 끝난 후 Session/EntityManager가 닫힙니다.

OSIV의 장점

  1. Lazy Loading 지원

    • View 렌더링 시점까지 Session/EntityManager가 열려 있으므로 Lazy Loading으로 연관 데이터를 쉽게 조회할 수 있습니다.
    • LazyInitializationException 방지.
  2. 구현 편의성

    • 별도의 DTO 변환이나 Fetch Join을 작성하지 않아도 됩니다.
    • 빠르게 개발을 진행할 수 있습니다.

OSIV의 단점

  1. 트랜잭션 범위 확장

    • OSIV는 트랜잭션이 끝난 이후에도 Session/EntityManager를 열어둡니다.
    • 이로 인해 데이터베이스 커넥션이 오래 유지될 수 있으며, 대규모 트래픽 환경에서는 성능 문제가 발생할 수 있습니다.
  2. 예측 불가능한 데이터 호출

    • View 계층에서 Lazy Loading이 실행되기 때문에 개발자가 데이터 호출 시점을 명확히 파악하기 어렵습니다.
    • 결과적으로 불필요한 쿼리가 실행될 가능성이 있습니다.
  3. 성능 저하

    • Lazy Loading 쿼리가 필요 이상으로 실행되면 성능이 저하될 수 있습니다.
    • 특히 N+1 문제와 같은 성능 이슈를 유발할 수 있습니다.

Spring Boot에서 OSIV 설정

  1. 기본 설정

    • Spring Boot는 OSIV를 기본적으로 활성화합니다.
    • 요청 시작 시점에 EntityManager를 열고, 요청 종료 시점에 닫습니다.
  2. OSIV 비활성화

    • application.properties에서 OSIV를 비활성화할 수 있습니다.
    spring.jpa.open-in-view=false
    • OSIV를 비활성화하면 트랜잭션 범위 내에서만 Lazy Loading이 가능합니다. View 계층에서 Lazy Loading을 사용하려면 Fetch Join, DTO 변환 등의 대체 방법을 사용해야 합니다.

OSIV가 비활성화된 경우 LazyInitializationException 방지 방법

  1. Fetch Join 사용

    • 필요한 연관 데이터를 즉시 로딩하여 한 번의 쿼리로 가져옵니다.
    @Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
    Optional<User> findUserWithOrders(@Param("id") Long id);
  2. DTO(Data Transfer Object) 활용

    • 필요한 데이터만 추출하여 전달합니다.
    public class UserDTO {
        private String name;
        private List<String> orders;
    
        public UserDTO(User user) {
            this.name = user.getName();
            this.orders = user.getOrders().stream()
                                .map(Order::getProduct)
                                .collect(Collectors.toList());
        }
    }
  3. 트랜잭션 범위 확장

    • 트랜잭션 내에서 Lazy Loading을 강제로 초기화합니다.
    @Transactional
    public User getUser(Long id) {
        User user = userRepository.findById(id).orElseThrow();
        user.getOrders().size(); // Lazy Loading 강제 초기화
        return user;
    }

OSIV의 활용 여부

  • OSIV 활성화 추천

    • 프로젝트 규모가 작고 트래픽이 적으며 개발 속도가 중요할 때.
    • Lazy Loading으로 연관 데이터를 조회하는 요구사항이 많을 때.
  • OSIV 비활성화 추천

    • 대규모 트래픽 처리 시스템에서 성능이 중요한 경우.
    • 명확한 데이터 호출과 예측 가능한 쿼리 동작이 필요할 때.

결론

OSIV는 Lazy Loading 문제를 간편하게 해결해주는 유용한 도구입니다. 그러나 성능과 유지보수 관점에서 신중히 고려해야 합니다. 프로젝트의 요구사항과 환경에 따라 OSIV를 적절히 활성화하거나 비활성화하고, 필요한 경우 Fetch Join, DTO 변환 등의 대체 방식을 활용해야 합니다.

추가 학습 자료

profile
🌱

0개의 댓글