팀프로젝트중에 service 부분을 개발하면서 @Transactional에 관해서 공부가 필요해서 이렇게 정리해본다.


트랜젝션 동작 순서

Transactional(readOnly = true)는 왜 사용할까?

Spring에서 @Transactional은 하나의 트랜잭션 단위에서 DB 작업을 안정적으로 처리하기 위한 핵심 어노테이션이다.
그 중에서도 @Transactional(readOnly = true)읽기 전용 조회 성능을 최적화하기 위해 사용된다.

단순히 “쓰기 기능이 없어서 붙인다” 정도로 끝나는 것이 아니라,
JPA/Hibernate 내부 동작이 어떻게 달라지는지 이해해야 왜 필요한지 명확해진다.

효과: readOnly = true가 단순히 “쓰기 없음”뿐만 아니라 성능 최적화에 쓰인다는 것을 보여줌


1. 읽기 전용 모드가 적용되면 Hibernate의 동작이 달라진다

readOnly = true가 설정되면 Hibernate는 해당 트랜잭션을 쓰기 트랜잭션이 아닌 조회 트랜잭션으로 인식한다.

그 결과 다음과 같은 최적화가 이루어진다.

1) Dirty Checking(변경 감지) 비활성화

일반적인 트랜잭션에서는 엔티티가 조회되면 Hibernate는 이를 영속성 컨텍스트에 저장하고,
트랜잭션 종료 시 값이 변경되었는지 비교(Dirty Checking) 한 뒤 변경 시점에 UPDATE 쿼리를 자동 실행한다.

하지만 조회 전용에서 Dirty Checking은 불필요한 CPU 연산비용이다.

readOnly = true 효과

  • Hibernate가 Dirty Checking 수행 X
  • 엔티티 변경 여부를 추적하지 않으므로 불필요한 계산 감소
  • 퍼포먼스 개선

한 마디로: “조회만 할 거니까 변경 체크는 하지 말자”
→ 조회 성능이 더 빨라진다.

2) Flush가 발생하지 않음

일반 트랜잭션은 COMMIT 시점에 변경사항 반영을 위해 Flush를 발생시키지만
읽기 전용 트랜잭션에서는 Flush 과정 자체를 생략한다.

Flush 비활성화 효과

  • DB와의 불필요한 동기화를 수행하지 않음
  • 트랜잭션 종료 시 추가 쿼리 발생 X
  • 전체적인 수행 비용 절감

3) 데이터베이스 레벨에서의 최적화

일부 DB는 read-only 세션을 최적화하여

  • Lock을 줄이거나
  • 캐시 최적화
  • 불필요한 리소스 사용 감소

등의 효과가 있다.

(DB별로 동작은 상이하지만, 최소한 불필요한 쓰기 락을 줄여 효율적이다.)


2. “조회 엔티티는 변경하지 않겠다”는 의도를 명확하게 표현

@Transactional(readOnly = true)를 붙이면 코드를 읽는 사람에게 의도를 명확히 전달할 수 있다.

  • 이 함수에서는 조회만 수행한다
  • 데이터 변경이 일어나지 않도록 설계되었다
  • 엔티티를 수정하려 해도 실제 DB 반영이 일어나지 않는다(변경해도 무효)

즉, 코드의 가독성과 유지보수성도 향상된다.

Lazy Loading 예시


3. 단순한 조회 API에 사용하면 성능이 생각보다 확실히 좋아진다

특히 성능이 중요한 서비스에서 다음과 같은 메서드에 사용하면 효과가 크다.

  • 목록 조회
  • 상세 조회
  • 검색 기능
  • 단일 조회 검증 로직

비즈니스에서 가장 많이 호출되는 형태의 API가 대부분 조회 API이기 때문에
readOnly 적용은 전체 트래픽 최적화에 매우 효과적이다.

예시 코드


4. 언제 사용하면 안 되는가?

readOnly = true는 변경 감지를 비활성화하기 때문에 아래 작업에서는 사용하면 안 된다.

  • 엔티티 값을 수정하는 서비스
  • save(), update() 등 쓰기 작업 포함
  • Lazy Loading 후 엔티티 수정 상황

조회 전용이 아닌 서비스에서 사용하면
변경이 DB에 반영되지 않는 심각한 오류가 발생할 수 있다.

사용하면 안 되는 코드 예시


핵심 효과 정리

동작일반 트랜잭션readOnly 트랜잭션
Dirty CheckingO
FlushO
DB 쓰기 락 가능성O감소
엔티티 변경 반영O
조회 성능기본향상

결론

@Transactional(readOnly = true)는 단순한 설정이 아니라
조회 성능 최적화를 위한 Spring/JPA의 강력한 기능이다.

✔ 변경 감지 끔
✔ Flush 끔
✔ 불필요한 리소스 최소화
✔ DB 접근 최적화
✔ 코드 의도 명확화

즉, “읽기 API에는 반드시 readOnly를 붙이는 것이 좋은 습관이다.”


🔹 오늘의 나는 무엇을 잘했는지

오늘은 @Transactional(readOnly = true)의 개념과 내부 동작을 정리하면서, Hibernate Dirty Checking과 Flush 동작이 실제로 어떻게 바뀌는지까지 이해했다.
처음에는 단순히 “조회 전용 트랜잭션”이라고만 알고 있었는데, 내부 최적화 구조까지 확인하며 왜 readOnly가 성능에 도움이 되는지 명확하게 체감할 수 있었다.
작은 예시 코드도 직접 만들어보면서 실제 서비스에서 적용하는 방식까지 연습했다.

🔹 오늘 배운 것 (학습한 내용)

  • @Transactional(readOnly = true)는 단순히 쓰기 금지가 아니라 Hibernate 성능 최적화 기능이라는 것
  • Dirty Checking과 Flush가 어떻게 비활성화되는지 이해
  • DB 레벨에서 read-only 세션이 Lock과 리소스를 줄이는 효과가 있다는 점
  • Lazy Loading된 엔티티도 조회 전용 트랜잭션에서 안전하게 읽을 수 있음
  • 조회 API에는 readOnly를 적용하면 실제 트래픽에서 성능 향상 효과가 있음

0개의 댓글