본문 바로가기

Development/Spring

[Spring] OpenSessionInViewFilter

Spring Boot에서의 Session

Spring Boot에서 Session 은 서버와 클라이언트 간의 통신 상태를 유지하는 데 사용됩니다. Session은 사용자 정보, 로그인 상태, 장바구니 내용 등과 같은 데이터를 저장하는 데 사용할 수 있습니다.

세션 작동 방식

Spring Boot는 기본적으로 Tomcat 서블릿 컨테이너를 사용하며, Tomcat은 HTTP Session을 사용하여 Session을 관리합니다. HTTP Session은 클라이언트 컴퓨터에 저장되는 쿠키를 사용하여 식별됩니다.

세션 생성:

  • 클라이언트가 서버에 처음 요청을 보내면 서버는 새 HTTP 세션을 생성하고 클라이언트 컴퓨터에 쿠키를 보냅니다.
  • 쿠키에는 세션 ID가 포함되어 있으며, 클라이언트는 이후 요청에 쿠키를 포함하여 서버에 보냅니다.
  • 서버는 쿠키에 있는 세션 ID를 사용하여 클라이언트와 연결된 세션을 찾습니다.

세션 유지:

  • 세션은 지정된 시간 동안 유지됩니다. 세션 유효기간이 만료되면 서버는 세션을 삭제합니다.
  • 세션 유효기간을 늘리려면 클라이언트가 서버에 요청을 보내는 동안 세션을 활성화해야 합니다.

세션 종료:

  • 클라이언트가 브라우저를 닫거나 로그아웃하면 서버는 세션을 종료합니다.
  • 서버는 세션 유효기간이 만료되면 세션을 종료합니다.

Spring Boot에서 세션 사용

Spring Boot는 세션을 사용하기 위한 다양한 기능을 제공합니다.

  • HttpSession 인터페이스: 세션에 저장된 데이터를 추가, 삭제, 검색하는 데 사용됩니다.
  • @SessionAttribute 어노테이션: 세션에 저장된 속성을 나타내는 데 사용됩니다.
  • @SessionScoped 어노테이션: 세션 범위 빈을 정의하는 데 사용됩니다.

OpenSessionInViewFilter (OSIV)

OpenSessionInViewFilter는 Spring Web MVC 애플리케이션에서 하나의 HTTP 요청 동안 Hibernate 세션을 열고 유지하는 데 사용되는 필터입니다.

OSIV 설정

  • SpringBoot 2.0부터 OSIV는 기본적으로 True입니다.
  • resources/application.properties에서 spring.jpa.open-in-view=false를 추가하면 false로 설정할 수 있습니다.

장점

  • 코드 간소화: 컨트롤러 코드에서 Hibernate 세션 관리 코드를 제거할 수 있습니다.
  • 세션 관리 통합: 모든 HTTP 요청에 대해 Hibernate 세션 관리가 일관되게 수행됩니다.
  • 지연 로딩 지원: 엔티티의 연관 속성을 지연 로딩할 수 있습니다.

단점

  • 메모리 누수 가능성: 세션을 명시적으로 닫지 않으면 메모리 누수가 발생할 수 있습니다.
  • 성능 저하: 모든 요청에 대해 Hibernate 세션을 열고 닫는 데 오버헤드가 발생할 수 있습니다.

작동 방식

  1. Request Starts:

새 Hibernate session이 자동으로 열립니다.
transaction도 내재적으로 이 Session과 함께 시작합니다.
2. Service Method Execution:
서비스 레이어에서 DB와 통신하며, 로직을 수행합니다.
3. View Rendering(구현에 따라 없을 수도 있습니다):
컨트롤러 레이어에서 엔티티 접근을 할 때 지연 로딩을 하더라도 Session은 항상 열려있기 때문에 문제가 발생하지 않습니다.
4. Request Ends:
요청이 끝나면 session이 닫히고, transaction은 commit이 되어 DB에 반영됩니다.

OSIV가 False일 경우

OSIV(OpenSessionInViewFilter)가 False인 경우, Spring Data JPA는 하나의 HTTP 요청 동안 하나의 Hibernate 세션을 사용합니다. 세션의 생명 주기는 Transaction과 밀접해집니다.

  1. 요청이 들어옴: 아직 세션이 존재하지 않습니다.
  2. Transaction Begins: 서비스 레이어에서 메서드에 @Transactional이 있다면, Transaction이 시작됩니다. 이 때, Hibernate는 Session을 엽니다.
  3. Database Operations: 비즈니스 로직에 의해 데이터베이스와 통신합니다.
  4. Transaction Commit: Transaction이 끝나면 Commit이 이루어지고, Session은 즉시 종료됩니다.

OpenSessionInViewFilter와 Transaction의 관계

  • OpenSessionInViewFilter세션 관리를 담당하며 Transaction데이터베이스 작업을 담당합니다.
  • OpenSessionInViewFilterTransaction 외부에서 사용될 수 있지만 Transaction 내에서 사용하는 것이 좋습니다.

지연 로딩 사용 예시

@Controller
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    @Transactional // OSIV = True라면 @Transactional을 적용하지 않아도 LazyInitializationException이 발생하지 않음
    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id) {
        Product product = productRepository.getById(id);
        return product;
    }

}

위 코드에 getProduct Product product = productRepository.getById(id);에서 getById는 프록시 객체를 리턴하여 지연 로딩을 사용합니다. 이 때 OpenSessionInViewFilter가 False이고, getProduct 메서드에 @Transactional이 없다면 외부 메서드에서 product에 접근할 때 LazyInitializationException이 발생합니다.

그 이유는 다음과 같습니다.

  1. Spring 프록시 차단: 처음으로 Spring 프록시가 호출을 가로채 현재 트랜잭션을 가져오거나 존재하지 않으면 새 트랜잭션을 생성합니다.
  2. 구현체 위임: 그런 다음 메서드 호출을 실제 구현체로 위임합니다.
  3. 트랜잭션 커밋 및 세션 닫기: 마지막으로 프록시는 트랜잭션을 커밋하고 결과적으로 기본 Session을 닫습니다. 왜냐하면 우리는 서비스 계층에서만 해당 Session이 필요하기 때문입니다.
  4. 따라서 getProduct 메서드 구현에서 product를 사용할 수 없습니다. 만약 이 객체에 접근하면 LazyInitializationException이 발생합니다.

OpenSessionInViewFilter가 True라면 세션을 유지하여 @Transactional이 없어도 Product 엔티티를 즉시 로딩하고 LazyInitializationException가 발생하지 않습니다.

OSIV의 성능상의 위험

1. 혼합 I/O 문제

  • 사용자 조회 메서드에 @Transactional 어노테이션을 사용하면 데이터베이스 I/O와 원격 서비스 호출이 하나의 트랜잭션 내에서 함께 실행됩니다.
  • 원격 서비스가 느리게 응답하는 경우 데이터베이스 연결이 차단되어 다른 요청들이 대기하게 됩니다.
  • 혼합 I/O는 성능 저하를 야기하므로 피해야 합니다.

2. 연결 풀 고갈

  • OSIV가 활성화된 경우 각 요청에 대해 세션이 열려있습니다.
  • 데이터베이스 쿼리가 실행되면 세션이 연결되고 요청이 끝날 때까지 연결 상태를 유지합니다.
  • 심지어 @Transactional 어노테이션을 사용하지 않아도 OSIV는 Connection pool 을 고갈시킬 수 있습니다.
  • 느린 원격 서비스가 원인이지만 데이터베이스 Connection pool 고갈이라는 증상으로 인해 진단이 어렵습니다.

3. 불필요한 쿼리

  • 세션이 열려 있는 동안 필요하지 않은 속성에 접근하면 추가 쿼리가 발생할 수 있습니다.
  • 최악의 경우 n+1 문제가 발생할 수 있습니다.
  • 세션은 자동 커밋 모드로 작동하여 데이터베이스에 부하를 줄 수 있습니다.

참고: