평화로운 우테코 6시 레벨5 미션!
DB를 분리하고 복제 지연을 해결하는 미션입니다
원활한 배경 설명을 위해 깃헙 링크 첨부합니다
미션을 진행하며 리뷰이에게 리뷰를 남기는 와중에 뜻밖의 사실을 알게 되었어요

사실 저도 궁금해서 여쭤봤는데...
슬쩍 여쭤보고 지식만 쏙쏙 흡수하려고 했는데 딱 걸렸어요
짐작가는 게 있다면

JPA에서는 대부분의 것들을 지원해 주기 때문에 당연히 이런 부분까지 지원해 줄 수도 있겠다 추측하였지만 공식 문서를 뒤져보니 광활한 공식 문서에서 찾지 못하였어요
그래서 넘겼다가 여유가 생겼을 때 작성해 봅니다
사실 원리는 간단해요
저는 아래 코드와 같이 JpaRepository를 extends하여 사용하고 있습니다
public interface CouponRepository extends JpaRepository<Coupon, Long>
이 JpaRepository를 타고타고 들어가면 SimpleJpaRepository가 있어요

여기 옵션을 보시면 @Transactional에 ReadOnly 옵션이 있는게 보이시나요?
해당 class에 이미 readOnly 옵션이 걸려 있어서 Service단에 Transaction을 걸지 않아도 DB가 분리되어 나갑니다
(물론 Service 단에 Transactional을 거냐마냐는 여러 논의가 있지만 해당 글에서 다루지는 않습니다)

위 사진과 같이 delete 혹은 save 같은 WRITE DB로 가야 할 쿼리들은 따로 @Transactional을 걸어 주어 readOnly = false 옵션이 적용됩니다

find 함수에는 따로 Transactional을 걸지 않아 readOnly = true 옵션이 적용되고요
그렇다면 여기서 하나의 의문이 생깁니다
❓ Transaction의 전파를 공부할 때에
@Transactional의 기본 전파 레벨은Required라고 배웠어요 즉, 상위 트랜잭션에 묶이게 됩니다 하지만 클래스 단위로readOnly = true옵션을 적용하고 함수 단위로readOnly = false옵션을 적용했을 때에는 왜 함수의 트랜잭션 먼저 적용이 되는가?
이건 공식 문서를 참고하면 알 수 있는 답이에요

바로 클래스 레벨과 메서드 레벨에 @Transactional이 둘 다 걸려 있을 경우 메서드 레벨에 걸려 있는 것이 우선적으로 적용되기 때문입니다
해당 구현은 TransactionAspectSupport의 invokeWithinTransaction 메서드 부분을 보면 알 수 있습니다
// 트랜잭션 경계 내에서 메서드를 호출하는 메서드
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable {
// 메서드와 클래스의 트랜잭션 속성 가져오기
TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// 트랜잭션을 설정할 트랜잭션 관리자 선택
PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 트랜잭션 식별을 위한 메서드 이름 가져오기
String joinpointIdentification = methodIdentification(method, targetClass);
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// 타겟 메서드를 실제로 호출
retVal = invocation.proceedWithInvocation();
} catch (Throwable ex) {
// 예외 발생 시 트랜잭션 처리 완료
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
// 트랜잭션 정보 정리
cleanupTransactionInfo(txInfo);
}
// 성공적으로 메서드가 완료된 경우 트랜잭션 커밋
commitTransactionAfterReturning(txInfo);
return retVal;
}
위 부분에서 메서드와 클래스의 트랜잭션 속성 가져오기 코드를 살펴보면 메서드와 클래스 중 우선 적용할 트랜잭션을 찾음을 알 수 있어요!
해당 글을 작성하는 데 도움을 준 우아한테크코스 6기 BE 폴라에게 감사드리며 글을 마무리합니다 😊
폴라 샤라웃 뭐예요