Spring/공부

[Spring/공부] Custom Interceptor

오잎 클로버 2023. 3. 8. 13:35
728x90

토이 프로젝트를 진행하다 Spring Security 없이 토큰을 통한 인가를 해야 했다.

이때, 음악(Music)과 회원(User)이라는 엔티티가 다대일(양방향)이고, 회원에서 굳이 음악을 가져올 일이 없는 경우가
종종 있어 지연로딩으로 가져오려 하는 도중 LazyInitializationException 이 발생하여 이에 대해 기록하고자 한다.

 

Interceptor란?

Spring MVC에서 요청이 컨트롤러로 위임되기 전, 위임된 후, 뷰 처리 단계에서 특정 로직을 할 수 있는 스프링에서
제공하는 장치

쉽게 설명하자면, "요청에 대한 작업 전/후로 가로채어 특정 로직을 수행하는 역할" 정도로 설명할 수 있다.

HandlerInterceptor 인터페이스를 직접 구현하여, 인터셉터를 직접 구현할 수 있다.

 

LazyInitializationException이란?

JPA에서 관리하는 세션(정확히는 persistence context)이 종료된 후, 관계가 설정된 엔티티를 참조하려고 할 때 발생한다.

Lazy(지연로딩)으로 엔티티를 가져올 때, persistence context가 종료되었다면 발생하는 예외이다.

대표적인 해결 방법으로는 @Transactional, fetch-join, @EntityGraph 가 있다.

 

문제 해결 시도

우선 Spring Security가 없는 관계로 Interceptor를 사용해야 한다. (물론 dependency를 추가해 주면 가능하지만, Filter 외의 인터셉터 방식으로 시도해 보고자 시도했다.)

Spring Boot 설정 중 OSIV가 기본적으로 켜져 있으므로, 영속성 컨텍스트가 레이어를 넘어 동일하다고 생각하여,
  코드를 작성했으나, 안타깝게도 다음과 같은 이유로 실패했다.

LazyInitializationException 이 인터페이스에서 발생한 이유

Spring Boot에서 OSIV가 켜져 있는 경우, OpenSessionInViewInterceptor라는 클래스가 자동으로 등록된다.

하지만, 커스텀 인터셉터가 선 작업 후, OpenSessionInViewInterceptor가 영속성 컨텍스트를 공유하게 된다.

따라서 영속성 컨텍스트가 공유되지 않아 예외가 발생한 것이었다.

문제 해결

OpenSessionInViewInterceptor를 직접 빈으로 등록한다.

기존 방식은 HandlerInterceptor -> OpenEntityManagerInViewInterceptor -> Controller -> ...

에서 OpenSessionInViewFilter -> HandlerInterceptor -> OpenEntityManagerInViewInterceptor  -> Controller -> ...

으로 변경된다. 위와 같이 변경될 경우, OpenSessionInViewFilter에서 영속성 컨텍스트가 열려 문제가 해결된다.

(물론 위 경우에는 OSIV를 켜져 있기에 동작한다.)

@Bean
public FilterRegistrationBean<OpenEntityManagerInViewFilter> registrationBean() {
    FilterRegistrationBean<OpenEntityManagerInViewFilter> registrationBean = new FilterRegistrationBean<OpenEntityManagerInViewFilter>();
    OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
    registrationBean.setFilter(filter);
    registrationBean.setOrder(5);
    return registrationBean;
}

 

 

이상입니다.

 

참고