@Transactional(readOnly = true) 꼭 붙여야 할까?

Haechan Kim·2024년 4월 9일
0

Spring

목록 보기
64/69

@Transactional

스프링은 트랜잭션 처리를 위해 선언적 트랜잭션을 사용한다.
선언적 트랜잭션은 설정 파일 또는 어노테이션 방식으로 트랜잭션에 관한 행위를 정의하는 것.

나의 경우 대부분의 프로젝트에서 어노테이션 방식으로 @Transactional을 사용했다.
@Transactional은 클래스 또는 메서드 단위로 선언 가능.

Spring AOP

AOP(관점 지향 프로그래밍)는 공통으로 사용하는 기능 분리해서 재사용하는 기법.
Spring AOP의 경우 런타임에 프록시 객체 생성해 공통 기능 삽입
프록시 객체 생성하고 그 안에 실제 객체 넣음
-> 실제 객체 통해 핵심 가능 수행.
-> 공통 기능은 프록시 객체가 처리.

동작 원리

트랜잭션은 Spring AOP를 통해 구현되어 있음.
클래스 or 메서드에 @Transactional 선언되면 트랜잭션 적용된 프록시 객체 생성.
프록시 객체는 @Transactional 포함된 메소드 호출 시 트랜잭션 시작하고 예외 없으면 커밋, 예외 발생 시 롤백 수행.

트랜잭션 처리가 필요한 CRUD의 CUD가 존재하는 메서드에 @Transactional 붙이면 됨.

@Transactional(readOnly = true)

조회 메서드에 @Transactional(readOnly = true)를 붙여야 성능이 좋다는 말에 별 생각 없이 계속해서 사용했다.
사용하지 않으면 어떻게 될까?

영속성 컨텍스트 생명주기

영속성 컨텍스트는 트랜잭션 시작시에 생성되고, 트랜잭션이 끝나면 종료된다.
즉 트랜잭션의 범위와 영속성 컨텍스트의 생존 범위가 같음.

그렇기 때문에 비지니스 로직 있는 서비스 계층에 @Transactional 붙이는 것!
서비스 계층에서 엔티티가 영속 상태 유지하지만, 컨트롤러나 뷰 계층에서는 트랜잭션이 이미 끝난 상태이기 때문에 엔티티가 준영속 상태가 됨.

Dirty Checking (변경 감지)

readOnly=true와 성능의 관계는 영속성 컨텍스트의 더티 체킹에 있다.
영속성 컨텍스트는 엔티티 조회 시 초기 상태를 스냅샷으로 저장함.
트랜잭션이 커밋될 때 초기 스냅샷과 엔티티의 상태 비교하고, 변경 내용을 쓰기 지연 저장소에 저장.

쓰기 지연 저장소(Write-Behind Store) : 변경된 엔티티 상태 임시 저장해, 트랜잭션 커밋될 때 한번에 DB에 반영

그 후 일괄적으로 저장소 쿼리를 flush라고 트랜잭션을 커밋하여, update를 명시하지 않아도 엔티티의 수정 이루어짐.

이 때 readOnly=true를 설정하면 JPA의 세션 플러시 모드가 MANUAL로 설정됨.
MANUAL 모드 : 트랜잭션 내에서 flush 명시적으로 호출해야 함 이뤄짐. 자동 x
따라서 트랜잭션 커밋 시 자동 flush 되지 않으므로, 조회용으로 가져온 엔티티의 예상치 못한 수정 막을 수 있음.
또한 JPA가 조회용 엔티티임을 인식하고 더티 체킹 위해 스냅샷 따로 보관하지 않기 때문에 메모리 절약되어 성능상 이점.

OSIV (Open Session In View)

OSIV 옵션은 영속성 컨텍스트의 생존 범위를 뷰까지 넓히는 것이다.
위에서 말했듯이 영속성 컨텍스트는 트랜잭션이 종료되면 닫히게 된다. 즉 서비스 계층에서 준영속 상태로 바뀌어 뷰 계층에서 사용할 수 없는데, OSIV 옵션을 true로 설정하면 API 응답이 끝날때까지 영속성 컨텍스트와 DB 커넥션을 유지할 수 있다.
디폴트는 true

조회 메서드에서

조회용 메서드에서 @Transactional 유무의 차이는 OSIV가 꺼져 있을 때 알 수 있다.
OSIV가 켜져 있으면 뷰(컨트롤러) 계층에서도 lazy loading이 가능하다.
하지만 OSIV가 false인 경우, 영속성 컨텍스트가 트랜잭션 범위를 벗어니는 순간, 엔티티는 엥속성 컨텍스트 관리 받지 않는 준영속 상태가 됨.
-> DB 커넥션 꺼지고, lazy loading 불가능.

따라서 OSIV 꺼져 있는 상황에서는 @Transactional 없으면 lazy loading 수행에 문제 생김. -> 조회임에도 사용하는게 좋다.

장단점

@Transactional 사용하면 트랜잭션 관리가 편하지만, 무분별하게 사용하면 메모리적 측면에서 좋지 않을 수 있고, 커넥션 오래 갖고 있어 커넥션 부족 문제 발생할 수 있음.

참고

https://hungseong.tistory.com/74
https://ssdragon.tistory.com/116
https://www.youtube.com/watch?v=PNnB8mN0v-o
https://hstory0208.tistory.com/entry/SpringJPA-OSIV-전략이란-언제-사용해야-할까

0개의 댓글