728x90
반응형
SMALL

이번 포스팅에는 OSIV가 무엇인지, 이게 어떤 성능에 영향을 주는지 알아보는 시간을 가져보자!

OSIV(Open Session In View)라는 이 녀석은, 스프링을 사용하면 이런 설정값으로 표현된다.

spring.jpa.open-in-view

근데, 이 값이 기본으로 `true`이다. 즉, 아무것도 설정하지 않으면 이 설정이 켜져있는건데 이게 켜져있으면 데이터베이스 커넥션이 반납되는 시점이 크게 달라진다. 

 

자, 다음 코드를 보자.

package cwchoiit.shoppingmall.service;

import cwchoiit.shoppingmall.domain.Member;
import cwchoiit.shoppingmall.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {

    private final MemberRepository memberRepository;

    @Transactional
    public Long signup(Member member) {
        validateDuplicateMember(member);

        memberRepository.save(member);
        return member.getId();
    }
}
  • 여기보면 signup() 이라는 메서드가 하나 있고, @Transactional 애노테이션이 달려있다.
  • JPA는 @Transactional 애노테이션이 붙어있는 메서드가 실행하는 시점에 커넥션을 받아온다. 그리고 일반적으로 @Transactional 애노테이션이 달린 메서드가 끝나면 커넥션을 반납하게 된다.
  • 그런데, 이 OSIV가 켜져있으면 메서드가 끝나는 시점에 커넥션을 반환하지 않는다. 
  • 그럼 언제 반환하지? 자, 이 signup()을 호출한 컨트롤러로 넘어가보자.
@PostMapping("/members/new")
public String create(@Validated MemberForm memberForm, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return "members/createMemberForm";
    }

    Address address = new Address(memberForm.getCity(), memberForm.getStreet(), memberForm.getZipCode());

    Member member = Member.builder()
            .name(memberForm.getName())
            .address(address)
            .build();

    memberService.signup(member);
    return "redirect:/";
}
  • 이 컨트롤러에서 signup()을 호출하는데, 호출하고 다시 돌아오는 시점에도, 커넥션은 반환되지 않는다. 그리고 사용자에게 최종적으로 화면이 뿌려지고 완전히 응답이 끝나면! 그때 커넥션을 반납한다.
  • 왜 그러냐면, 만약, 데이터베이스를 통해 데이터를 받아왔는데 이 데이터 중 프록시를 초기화해야 하는 데이터가 있을수도 있기 때문에 그때까지 영속성 컨텍스트를 살려두어야만 초기화가 가능하기 때문이다.
  • 프록시 초기화는 반드시 영속성 컨텍스트가 관리하고 있는 엔티티여야만 가능하다!

그럼, 결국 OSIV가 켜져있으면, API라면 호출한 곳으로부터 응답이 완전히 다 나가고 나서 커넥션이 반납되고, API가 아니라 뷰를 반환하는 경우라면 템플릿이 완전히 다 만들어지고 사용자에게 화면이 뿌려지는 시점에 커넥션이 반납된다.

 

그러니까, OSIV는 장단점이 있다. 컨트롤러나 뷰 레이어에서도 지연로딩을 처리할 수도 있다는 장점이 있지만 단점은 커넥션을 너무 오랫동안 유지하고 있다는 점이다. 그래서 자칫 잘못하면 커넥션이 반납이 너무 느려져서 말라버릴 수도 있다. 그럼 도대체 이 옵션은 켜야하나 꺼야하나? 끄면 모든 지연로딩을 트랜잭션 안에서 처리해야 하는 단점이 있지만, 키면 커넥션을 너무 오랫동안 가지고 있다는 단점이 있다. 

 

 

내가 생각하는 좋은 방법은..

  • OSIV 옵션을 끈다.
  • 모든 지연로딩을 페치 조인이나, 중간 레이어를 두어서 컨트롤러나 뷰에서 처리할 필요가 없도록 데이터를 받아온다.

그래서, 커넥션을 너무 오랫동안 유지하고 있지 않도록 하는게 가장 좋은 방법인것 같다. 어차피 지연로딩을 초기화해야 하는 경우라면 페치 조인을 사용해서 처음부터 모든 데이터를 받아오도록 하면 될 것이고, 바로 지연로딩이 초기화가 필요한 게 아니라 특정 순간에만 필요한 경우 중간 레이어 하나를 만들어서 쿼리와 커맨드를 분리해서 한번 더 트랜잭션의 도움을 받으면 될 것 같다. 

 

근데 이게 뭐 딱 정해진 정답이 있는게 아니라 상황에 따라 잘 활용하면 될 것 같다. 예를 들어 이렇게 하는 것이다.

  • 고객 서비스의 실시간 APIOSIV를 꺼서 최대한 커넥션 반납을 빠르게 하고, ADMIN 화면과 같은 커넥션을 많이 사용하지 않는 곳에서는 OSIV를 키는 방식으로 유연하게 적용한다.

 

 

728x90
반응형
LIST

+ Recent posts