@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 엔티티는 영속성 컨텍스트 내에서 관리되지 않으며, 변경 감지가 일어나지 않을 것이라고 예상했다.
하지만, 서비스 메서드 내에서 정상적으로 변경감지가 일어나는 것을 확인했다.
Open Session In View - Session(EntityManager)을 View까지 열어두겠다.
즉, 원래 트랜잭션이 끝나는 시점에 닫혀야 되는 DB 커넥션, 영속성 컨텍스트를 더욱 오래 열어두겠다는 것이다.
OSIV를 켜놓으면 위와 같이 요청이 들어오고 최종 Response가 나갈 때까지 영속성 컨텍스트, 커넥션이 살아 있는다.
OSIV를 켜놓았을 때의 장점
단점
spring.jpa.open-in-view:true
와 같이 OSIV 여부를 설정할 수 있는데, 스프링의 기본 설정은 OSIV를 켜놓는 것이다.
그렇기 때문에 ArgumentResolver에서 가져온 Member 엔티티는 영속성 컨텍스트에 의해 관리될 수 있고, 변경 감지 또한 가능했던 것
+) 이렇듯, OSIV의 사용여부에 따라 ArgumentResolver에서 반환하는 엔티티는 영속 상태일 수도 있고, 준영속 상태일 수도 있다. 그렇기 때문에 이는 실수를 유발하기 매우 쉬운 형태이다. 그렇기 때문에 JPA를 사용한다면, ArgumentResolver에서 엔티티를 반환하지 않는 것이 권장된다.
+) 이 모든 것은 결국 Member의 정보를 수정하기 위한 과정에서 발생한 것이다. 하지만, 멤버 정보 수정은 애플리케이션 내에서 호출 빈도가 큰 비중을 차지하지 않는 기능이다. 그렇기 때문에 해당 기능에서의 더티 체킹만을 위해 OSIV를 켜놓는 것 보다는, 차라리 서비스 내에서 엔티티를 한 번 더 조회하는 것이 좋을 수도 있다.
References
https://tecoble.techcourse.co.kr/post/2020-11-03-osiv_with_interceptor/
개발자로서 배울 점이 많은 글이었습니다. 감사합니다.