ArgumentResolver에서 반환한 엔티티에 대한 영속성 컨텍스트가 어째서 유지될까? feat. OSIV

yujamint·2023년 8월 5일
0
post-custom-banner

문제 상황

@Override
public Object resolveArgument(
	final MethodParameter parameter,
	final ModelAndViewContainer mavContainer, 
	final NativeWebRequest webRequest, 
	final WebDataBinderFactory binderFactory
)    throws Exception {
  return memberRepository.save(
          Member.builder()
                  .nickname("유민트")
                  .tier(2)
                  .githubId("yujamint")
                  .profileImageUrl("www.yujamint.site")
                  .introduction("자기소개입니다")
                  .build()
  );
}

위와 같이 ArgumentResolver에서 Member 엔티티를 영속화하며 컨트롤러에 전달하고 있다.

그리고 해당 엔티티는 Controller → Service로 전달된다. 그리고 서비스에서는 해당 엔티티의 정보를 수정하고, 수정된 정보를 DB에 반영하려 한다.(UPDATE)

내가 알기론 트랜잭션이 시작되는 시점에 영속성 컨텍스트가 DB 커넥션을 가져오고, 트랜잭션이 끝나는 시점에 DB 커넥션을 반납하고 영속성 컨텍스트도 반환된다.

그렇기 때문에 트랜잭션 내에서 동작하지 않는 ArgumentResolver가 반환한 Member 엔티티는 영속성 컨텍스트 내에서 관리되지 않으며, 변경 감지가 일어나지 않을 것이라고 예상했다.

하지만, 서비스 메서드 내에서 정상적으로 변경감지가 일어나는 것을 확인했다.

OSIV

Open Session In View - Session(EntityManager)을 View까지 열어두겠다.

즉, 원래 트랜잭션이 끝나는 시점에 닫혀야 되는 DB 커넥션, 영속성 컨텍스트를 더욱 오래 열어두겠다는 것이다.

OSIV

OSIV를 켜놓으면 위와 같이 요청이 들어오고 최종 Response가 나갈 때까지 영속성 컨텍스트, 커넥션이 살아 있는다.

OSIV의 장단점

OSIV를 켜놓았을 때의 장점

  • 영속성 컨텍스트를 오래 유지 → 지연 로딩이 가능해진다. 조회하는 시점에 연관된 모든 엔티티를 불러오지 않고 프록시 객체로 가지고 있다가, 해당 엔티티가 사용되는 시점에 실제 객체를 불러온다. 이는 OSIV를 통해 커넥션을 가지고 있을 때 더 자유롭게 활용할 수 있다. 만약 OSIV가 꺼져 있다면, 트랜잭션을 선언하는 서비스 내에서 모든 연관 엔티티를 로딩한 채로 컨트롤러에 반환해야 할 것이다.

단점

  • OSIV를 키지 않았을 때와 비교하면, 오랜 시간동안 커넥션 리소스를 사용하는 것이기 때문에 리소스 낭비가 발생할 수 있다.

결론

spring.jpa.open-in-view:true 와 같이 OSIV 여부를 설정할 수 있는데, 스프링의 기본 설정은 OSIV를 켜놓는 것이다.

그렇기 때문에 ArgumentResolver에서 가져온 Member 엔티티는 영속성 컨텍스트에 의해 관리될 수 있고, 변경 감지 또한 가능했던 것

+) 이렇듯, OSIV의 사용여부에 따라 ArgumentResolver에서 반환하는 엔티티는 영속 상태일 수도 있고, 준영속 상태일 수도 있다. 그렇기 때문에 이는 실수를 유발하기 매우 쉬운 형태이다. 그렇기 때문에 JPA를 사용한다면, ArgumentResolver에서 엔티티를 반환하지 않는 것이 권장된다.

  • 추가로, ArgumentResolver가 Repository를 의존하지 않게 된다.

+) 이 모든 것은 결국 Member의 정보를 수정하기 위한 과정에서 발생한 것이다. 하지만, 멤버 정보 수정은 애플리케이션 내에서 호출 빈도가 큰 비중을 차지하지 않는 기능이다. 그렇기 때문에 해당 기능에서의 더티 체킹만을 위해 OSIV를 켜놓는 것 보다는, 차라리 서비스 내에서 엔티티를 한 번 더 조회하는 것이 좋을 수도 있다.

  • 많이 호출되지 않는 기능의 쿼리를 한 번 줄이는 것은 사실상 성능 변화가 미미할 것이다.

References

https://tecoble.techcourse.co.kr/post/2020-11-03-osiv_with_interceptor/

https://kth990303.tistory.com/427 👍

profile
개발 기록
post-custom-banner

2개의 댓글

comment-user-thumbnail
2023년 8월 5일

개발자로서 배울 점이 많은 글이었습니다. 감사합니다.

1개의 답글