
SpringBoot Application 실행하며 발생하는 Warn log
2024-10-24T21:32:47.997+09:00 WARN 5128 ---
[ main] JpaBaseConfiguration$JpaWebConfiguration :
- spring.jpa.open-in-view is enabled by default.
- Therefore, database queries may be performed during view rendering.
- Explicitly configure spring.jpa.open-in-view to disable this warning
- spring.jpa.open-in-view is enabled by default.
- OSIV 기능이 default로 켜져있습니다.
- Therefore, database queries may be performed during view rendering.
- Explicitly configure spring.jpa.open-in-view to disable this warning
- 명시적으로 OSIV 기능을 설정하여 WARNING을 없애세요
OSIV 기능 꺼보기
영속성 컨텍스트는 언제 만들어지고 없어질까요?
- 트랜잭션이 시작될 때 만들어져 트랜잭션이 커밋된 후 없어진다.

머쓱이의 문제
- 머쓱이는 게시글 단건 조회 api를 만들었다.
- 단건 조회는 PK로 Repository를 조회하는 간단한 api이다.
- 머쓱이는 Controller까지 Post(게시글) entity를 가져와 PostDto로 변환하였다.
- Post는 User와 다대일 연관관계 매핑이 되어있다.
- PostDto는 Post 정보와 User 정보를 가지고 있다.
Error 발생!
트랜잭션이 종료 되었는데, Lazy Loading인 객체를 참조하려 해서 Error 발생
- Service에서 트랜잭션 종료 되어 영속성 컨텍스트 사라짐
- Controller 단에서 프록시 객체인 user를 초기화하고 해당 user 객체의 속성을 가져오려 했지만 준영속 상태이기 때문에 예외 발생

에러 메세지
could not initialize proxy ~~
- 프록시를 초기화 할 수 없습니다.
no Session
- 영속성 컨텍스트가 없어요!
문제

필요성
- Transaction 밖에서도 영속성 컨텍스트가 계속 존재하여 데이터베이스 커넥션을 유지하고 영속성 컨텍스트를 사용할 수 있는 기능이 필요하다!
OSIV(Open Session In View)
- 영속성 컨텍스트를 요청이 들어올 때 미리 만들고 응답을 할 때까지 영속성 컨텍스트를 유지시켜주는 기능

Spring 제공 OSIV
- 하이버네이트에서는 OSIV란 용어를 사용하고 JPA에서는 OEIV라 하지만 관례상 OSIV라 부른다.
- 하이버네이트 OSIV 서블릿 필터
- 하이버네이트 OSIV 스프링 인터셉터
- JPA OEIV 서블릿 필터
- JPA OEIV 스프링 인터셉터
- 필터나 인터셉터에서 미리 영속성 컨텍스트를 만들어서 사용
application.yml에서의 OSIV 설정
JPA에서 제공해주는 Interceptor 사용
spring
jpa:
open-in-view : true
OSIV 기능을 키고 다시 실행
- 트랜잭션이 끝나도 영속성 컨텍스트를 종료하지 않는다.
- 인터셉터에서 영속성 컨텍스트를 종료한다.
머쓱이 문제 해결!
- Transaction 밖에서도 영속성 컨텍스트를 사용하여 id와 name을 LazyLoading 했다.
{
"postId" : 1,
"title" : "spring",
"content" : "osiv",
"userId" : 1,
"userName" : "이건우",
"createdAt" : "2023-01-24 05-21-18"
}

DB 커넥션 유지하고 영속성 컨텍스트를 사용할 수 있으니 무조건 사용해야 하는 것 아냐?!
주의사항
- Transaction 밖에서도 조회를 위한 쿼리가 나가게 되어 성능을 따질 때 확인해야 할 부분이 넓어진다.
- DB 커넥션 시작 시점부터 응답이 나갈 때까지 데이터베이스 커넥션을 유지하여 커넥션이 부족할 수 있다.
OSIV의 대안
- Transaction 밖에서 조회를 하지 않기 위해 어떻게 해야 할까?
- Transaction 안에서 미리 조회 해야한다.
강제 초기화
- 머쓱이가 Entity -> Dto 변환을 트랜잭션 안에서 처리하였다면 에러가 나지 않았을 것이다.
- 혹은 fetchJoin 같은거로 해서 LazyLoading 없이도 가능
- Lazy Loading을 트랜잭션 안에서 하여 초기화 작업을 트랜잭션 안에서 끝낸다.
OSIV 장점
- 지연 로딩을 적극적으로 활용할 수 있다.
- 지연 로딩을 사용할 수 있어 Service에 초기화를 위한 코드를 작성하지 않아도 된다.
OSIV의 단점
- 응답이 나갈 때까지 데이터베이스 커넥션을 유지하여 커넥션이 부족할 수 있다.
- Transaction 밖에서도 조회를 위한 쿼리가 나가게 되어 성능을 따질 때 확인해야 할 부분이 넓어진다.
OSIV를 사용하는 것이 좋을까?
- 정답은 X, 유연히 판단하여 적용할 수 있는 개발자가 되자!
OSIV를 명시적으로 설정하여 WARN 로그를 없애자!
추가 내용
OSIV를 끄지 않으면 @Transaction을 구분 못한다
한 요청 내에서 여러 트랜잭션을 구분하기 어렵다
- OSIV가 활성화되면, 요청의 생애주기 동안 영속성 컨텍스트가 유지되기 때문에 여러 트랜잭션을 명확하게 구분하기가 어렵습니다.
Interceptor에서 영속성 컨텍스트를 생성
- 요청이 시작될 때 Interceptor에서 영속성 컨텍스트가 생성되고, 요청이 끝날 때 clear됩니다.
- 이때 commit과 clear는 별개로 처리됩니다.
- 트랜잭션이 커밋된 후에도 영속성 컨텍스트는 유지되며, 요청의 마지막 단계인 View를 반환할 때까지 활성화됩니다.
@Transactional(readOnly = true)
- 더티 체킹을 위한 스냅샷을 저장하지 않으므로 성능이 향상될 수 있습니다. 이는 OSIV와 관계없이 적용됩니다.
OSIV를 끄면 Transaction 사용 시 주의해야 한다
트랜잭션 범위를 벗어나면 해당 Entity는 준영속 상태가 되어 영속성 컨텍스트에서 관리하지 않는다.
- 트랜잭션이 종료되면 해당 엔티티는 영속성 컨텍스트에서 관리되지 않게 됩니다.
Lazy Loading 사용 시 Exception 발생
- 해당 엔티티의 속성으로 가진 참조 변수에 저장된 Proxy 객체를 통해 Lazy Loading을 시도할 경우, 영속성 컨텍스트가 닫혀 있으므로 LazyInitializationException이 발생합니다.
무분별하게 @Transactional을 사용
- 트랜잭션이 길어지면 DB 커넥션이 오랫동안 유지되므로, 커넥션 풀이 소진될 수 있습니다.
- 특히 실시간 서비스에서는 이로 인해 성능 저하가 발생할 수 있습니다.
- 따라서 필요한 범위 내에서만 트랜잭션을 유지해야 합니다.
@Transactional(readOnly = true) 옵션
- OSIV를 끄더라도 @Transactional(readOnly = true) 옵션은 여전히 더티 체킹을 위한 스냅샷을 저장하지 않는 특성이 적용됩니다.
- 데이터베이스에 대한 읽기 전용 작업에 최적화된 설정이기 때문에, 성능 향상에 기여할 수 있습니다.
장점
Data 일관성 보장 가능
- 트랜잭션 사용의 목적은 DB의 데이터의
일관성과 무결성
을 보장하기 위해 사용하는데 목적
- 읽기 전용으로 설정하면 실수로 데이터를 수정해서 일관성을 위반할 가능성이 낮아진다.
- 조회 메서드에서도 일관성을 위반할 우려가 있어 항상 주의를 해야한다.
가독성 향상
@Transactional(readOnly=true)
설정된 메서드가 DB에서 데이터를 읽기만 한다는 것을 명확하게 확인할 수 있어 가독성이 향상된다.
성능 최적화
- 해당 메서드가 읽기만 한다는 것을 DB에 알려줌으로써 쿼리 및 캐싱을 최적화 가능
- JPA의 변경감지(Dirty Checking)를 위한 스냅샷을 저장 하지 않아 성능 향상 기대
- 영속성 컨텍스트는 Entity 조회 시 초기 상태에 대한 snapshot 저장
- 트랜잭션 Commit 될 때, 초기 상태의 정보를 가지는 snapshot과 Entity의 상태를 비교하여 변경된 내용에 대해 update query 생성해 쓰기 지연 저장소에 저장
- flush 후 Commit 함으로써 update 메서드를 명시적으로 사용하지 않아도 Entity 수정됨
readOnly = true
를 설정하게 되면 JPA의 세션 플러시 모드를 MANUAL로 설정
- 트랜잭션 내에서 사용자가 수동으로 flush를 호출하지 않으면 flush가 자동으로 수행 안됨
Replication 시 readOnly = true 설정 -> Slave DB에서 데이터 가져오도록 동작
- Replication 목적에 맞게 트래픽 분산을 온전히 적용할 수 있는 추가적 이점 존재