티스토리 뷰

728x90

회사 깃 내용중 OSIV 수정 했다는 로그를 보게 되었고 OSIV가 무엇인지 궁금해하다가

강의를 듣게 되어 정리해보았습니다. 

 

1. OSIV란?


  1. Open Session In View: 하이버네이트
  2. Open EntityManager In View: JPA
    (관례상 OSIV라 한다.)

 

2.  OSIV를 적용하기 전 알고 있으면 좋을 내용


[영속성 컨텍스트]

OSIV를 이해하기 전에 사실 JPA의 영속성 콘텍스트에 대해 알고 있어야 한다.

 

OSIV에 대해 JPA를 이해하고 난 뒤 온 사람도 있을 거고, JPA를 모르고 온 사람도 있을 것이다.

영속성 콘텍스트 = 내가 어떤 엔티티를 선언하고 사용하는 데 있어 "프로그램이 종료될 때까지 엔티티를 영구 저장하는 환경"이라고 간략하게 이해하고  영속성 콘텍스트에 대한 자세한 내용은 아래 링크를 참고하자

https://study-easy-coding.tistory.com/105?category=1069339

 

 

3.  true/false  OSIV의  적용  


[OSIV 적용 상태의 코드]

OSIV default "true"입니다.

spring.jpa.open-in-veiw = "true"

 

아래는 Controller 로직의 일부 입니다. 

http://localhost:8080/api/v3/orders
@GetMapping("/api/v3/orders")
    public List<OrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithItem();
        List<OrderDto> collect = orders.stream()
                .map(o -> new OrderDto(o))
                .collect(Collectors.toList());
        return collect;
    }

ordersV3메소드를 OSIV를 "false"로 선언하려면 application.yml에 아래처럼하면 된다.

spring.jpa.open-in-veiw = "false"

 

order.getMember(). getName();  해당 부분에서 오류가 터진다.

[오류가 터지는 이유]

해당 이미지는 영속성 컨텍스트의 생존 범위에 대한 이미지이다. 

(영속성 컨텍스트를 모르겠으면 아래 글 참고)

https://study-easy-coding.tistory.com/105?category=1069339

OSIV false로 선언하게 되어 끊어버리면 

Controller에서 영속성 콘텍스트가 없기 때문에 , 즉 엔티티를 모르기 때문에 발생한 오류가 발생한다. 

 

[오류 해결방법]

1. OSIV를 켠다.

2. 트랜잭션 안에서 다 지연 로딩을 밀어 넣는다.

3. 쿼리용 서비스 로직을 만든다.

 

1번 OSIV를 켠다.

우리는 OSIV를 끈 상태에서 문제를 해결하기 위함으로 제외

 

2. 트랜잭션 안에서 다 지연로딩을 밀어 넣는다.

지연 로딩을 트랜잭션안에서 적용해야하는데 작성한 많은 지연로딩 코드를  트랜잭션 안으로 밀어 넣어야 한다.

또한 view template지연 로딩이 동작하지 않는다. 결론적으로 트랜잭션이  끝나기 전에 지연 로딩을 강제로 호출해 두어야 한다.

또한 코드가 복잡해진다.

 

3. 쿼리용 서비스 로직을 만든다.

해당 내용으로 적용해보겠다.

4.   쿼리용 서비스 로직 만들기.


Controller의 내용을 Service로 옮기기

오류가 터졌던 Contoller를 Service로 옮기고 @Transational(ReadOnly=true)를 이용하여 영속성 상태를 선언할 것이다.

@GetMapping("/api/v3/orders")
public List<OrderDto> ordersV3() {
    List<Order> orders = orderRepository.findAllWithItem();
    List<OrderDto> collect = orders.stream()
            .map(o -> new OrderDto(o))
            .collect(Collectors.toList());
    return collect;
}

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ 서비스로 옮기기

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class OrderQueryService {
	
    private final OrderRespository orderRepository;
    
    public List<OrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithItem();
        List<OrderApiController.OrderDto> collect = orders.stream()
                .map(o -> new OrderApiController.OrderDto(o))
                .collect(Collectors.toList());
        return collect;
    }

@Transactional을 선언해주고, 기존에 Controller에 주입되었던 DTO를 선언해주면 트랜잭션 안에서 동작하게 된다. 

 

 

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ Controller의 return 값을 service참조로 수정 

@GetMapping("/api/v3/orders")
public List<OrderDto> ordersV3() {
    return OrderQueryService.orderV3();
}

5.   OSIV 장단점


OSIV선언은  아래 방식처럼 하면 된다. 

[장점]

spring.jpa.open-in-veiw = "true"

1. osiv전략은 트랜잭션 시작처럼 최초 데이터베이스 커넥션 시작 시점부터 API응답이 끝날 때까지 영속성 
콘텍스트와 데이터베이스 커넥션을 유지한다.
2. 엔티티를 적극 활용하여 레이지 로딩 같은 기술을 controller나 view에서 중복도 줄이고, 코드 유지보수성을 높일 수 있다.
이 때문에 view나 api 컨트롤러에서 지연 로딩이 가능했다.
3. 지연 로딩은 영속성 콘텍스트가 살아있어야 가능하고, 영속성 콘텍스트는 기본적으로 데이터베이스 커넥션을 유지한다.

-> 이것 자체가 큰 장점이지만 단점이 존재한다.

[단점]

spring.jpa.open-in-veiw = "false"

1. 너무 오랜 시간 동안 데이터베이스 커넥션 리소스를 사용하기 때문에 실시간 트래픽이 중요한 애플리케이션에서는 커넥션이 부족할 수 있다. -> "오류 발생" 

2. OSIV를 끄면, 트랜잭션을 종료할 때 영속성 콘텍스트를 닫고, 데이터베이스 콘텍스트도,
반환한다. 따라서 커넥션 리소스를 낭비하지 않는다. 
3. OSIV를 끄면, 모든 지연 로딩을 트랜잭션 안에서 처리해야 한다.
따라서 지금까지 작성한 많은 지연 로딩 코드를 트랜잭션 안으로 넣어야 하는 단점이 발생한다. 
4. 결론적으로 트랜잭션이 끝나기 전에 지연 로딩을 강제로 호출해야 한다. 

 

 

 

 

내용이 잘못되었거나, 부족한 내용이 있다면 피드백 바랍니다. 

감사합니다.

 

728x90
댓글
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday