Service를 리펙토링 하던 중 No EntityManager with actual transaction available for current thread - cannot reliably process 'merge' call 에러를 발생했고 해당 에러는 @Transactional과 관계 있음이 확인됐습니다.
우선 해당 에러가 발생한 코드는 다음과 같습니다.
entityManager.merge(existingSummonerInfo);
확인해보니 클래스, 메서드 어디에도 @Transactional을 선언하지 않았더군요. 그래서 메서드에 선언해보고 재시도 했지만, 동일한 에러가 발생했습니다. 왜 그럴까요?
에러는 일반적으로 EntityManager가 현재 스레드에 대한 활성 트랜잭션을 찾지 못할 때 발생합니다. 이는 EntityManager의 merge와 같은 연산이 트랜잭션 내에서 실행되어야 하는데, 실행 시점에 유효한 트랜잭션이 없다는 것을 의미합니다.
우선 해당 메서드는 여러 차례에 걸쳐 호출되는데
현재 service 내의 클래스에는 @Transactional이 설정되어 있지 않고 메서드에는 오직 callSummonerInfo 만이 설정되어 있습니다.
여기서 문제는 join 메서드에서 callSummonerInfo 메서드를 호출할 때, join 메서드 자체가 트랜잭션으로 관리되지 않기 때문에 발생합니다. callSummonerInfo는 @Transactional 어노테이션이 적용되어 있어, 해당 메서드 호출 시 새로운 트랜잭션이 시작됩니다. 그러나 join 메서드에서 수행되는 다른 JPA 연산들(예: 회원 저장)은 트랜잭션 관리 범위 밖에서 실행되고 있습니다.
callSummonerInfo에서 merge를 호출하려고 할 때, 이미 해당 메서드에 의해 시작된 트랜잭션의 컨텍스트 내에서 실행되어야 합니다.그러나 join 메서드에 트랜잭션이 없으면 callSummonerInfo 내에서 시작된 트랜잭션이 join 메서드의 데이터 변경과 분리되어 관리됩니다. 이는 EntityManager가 트랜잭션 관리의 일관성을 잃게 만듭니다.
현재 상황으로 봤을 땐 join에서 작업이 시작되기 때문에 join에만 트렌젝션이 활성화 되어 있으면 동작이 됩니다.
JPA를 잘 쓰려면 작업 단위의 일관성을 유지하는게 중요하다. service 단의 클래스에 @Transacional을 선언해 놓고 추가적인 옵션이 필요한 메서드에는 추가 선언을 하는게 안전하다.
.