02. 당신의 @Transactional의 readOnly 옵션 잘 쓰고 있나요?

시작하며
오늘은 Spring에서 마법의 어노테이션이라고 부르는 @Transactional의 readOnly 옵션에 대하여 이야기해보려고 한다.
엥? @Transactional에는 readOnly라는 옵션이 없는데?

엥? @Transactional에는 readOnly 옵션이 없는데 이게 무슨일이죠? 없는 걸로 이야기를 하려고 하는 건가?
우리는 이번 readOnly 이야기를 하기 전에 @Transactional은 2가지 있다는 것을 이야기하고 넘어가려고 한다.

서로 다른 패키지를 가진 2개의 어노테이션
이 2가지가 궁극적으로 하려고 하는 일은 동일하다. (실제 Spring AOP 상에서는 javax 패키지의 트랜젝션과 Spring 패키지의 트랜젝션도 모두 지원하고 있다.) Spring 패키지를 통해 제공되는 어노테이션이 더 많은 옵션을 제공하게 된다..
이 글에서는 readOnly가 하는 역할에 대해 이야기를 하려고 하니, 자세한 내용은 구글에서 찾아보도록 하자 (더 이야기하면 너무 삼천포로 많이 빠질 것 같아서..)
@Transactional의 readOnly는 어떻게 동작할까?

@Transactional의 readOnly가 어떻게 동작하는지 탐험을 떠나 봅시다
일단 readOnly 옵션에 대해 짚고 넘어 가보도록 하자
-
트랜젝션을 읽기 전용으로 설정 가능함.
-
개발자의 실수로 해당 트랜젝션 안에서 Write가 발생하는 것을 방지될 수 도 있고, 안될 수도 있다. (JDBC Driver에 따라 다르다.)
먼저, Spring 트랜젝션의 readOnly 옵션에 대한 API 문서를 먼저 확인해보도록 하자.
[
Transactional (Spring Framework 5.3.3 API) Defines zero (0) or more exception names (for exceptions which must be a subclass of Throwable), indicating which exception types must cause a transaction rollback. This can be a substring of a fully qualified class name, with no wildcard support at presen docs.spring.io
This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction but rather silently ignore the hint.
공식문서에서 해당 옵션은 이 옵션이** 읽기 전용 힌트를 제공한다.라고** 이야기를 한다.
이는 기본적으로 'Connection.setReadOnly(true)'를 호출하게 된다. 위에서 '될 수 도있고, 안 될 수도 있다'라고 이야기 한 이유도 이러한 이유다.
Oracle Driver의 경우는 예전부터 readOnly 옵션을 제공하였지만, MySQL Driver는 5.6.5 이상이 되어야 드디어 지원되었다고 한다.
그 외에 H2에서는 아직 지원을 하지 않는다고도 하고, JDBC 제조사 별로 다르게 적용된다는 점을 꼭 인지 해야 한다.
(관련된 참고 글 : wonwoo.ml/index.php/post/839)
Hibernate로 사용할 때는 어떻게 적용될까?
Hibernate는 readOnly 옵션이 설정된 경우는 Session의 Flush Mode를 'FlushMode.MANUAL' 모드로 설정한다.
이는 곧, '이 트랜젝션은 커밋 시 flush를 하지 않는다'는 것을 의미한다.

결국은 Hibernate는 Entity에 flush를 호출하지 않게 되고, 변경은 자연스럽게 무시되게 된다.
또한, flush가 호출되지 않고, Dirty Checking을 하지 않기 때문에 성능적으로도 이 점을 얻을 수 있다.
(Dirty Checking이 이루어지는 과정을 쉽게 설명한다고 하면, Entity와 Snapshot을 비교하게 되는데 이러한 과정이 생략되게 되면서 성능 상의 이점을 얻게 된다.)