프로젝트 진행 중 상품에 대한 update메서드 실행 중 Exception이 발생하면 부모 트랜잭션에서 catch로 잡아 로그 테이블에 데이터를 적재하고 싶은 경우가 있었다.
부모 트랜잭션에서 Entity를 select하고 다른 서비스의 @Transactional(propagation = Propagation.REQUIRES_NEW) 설정된 메서드를 호출하도록 하며, 이 메서드의 호출부를 try catch 문으로 잡았다.
일부러 Exception을 발생시켜 테스트 해 본 결과 로그테이블에 데이터가 정상적으로 적재되는 것을 확인했는데,
트랜잭션으로 묶여 롤백되기를 기대했던 상품 데이터가 그대로 저장되어 있는 것을 확인했다.
문제에 대한 정확한 원인을 파악한건지는 모르겠으나...
select한 Entity를 새 트랜잭션에 그대로 전달했기 때문에 엔티티 매니저 메모리 상에서 변경값이 그대로 유지되었고, 새 자식 트랜잭션에서는 롤백이 되었으나 부모 트랜잭션이 커밋될 시점에 해당 메모리가 dirty 상태기 때문에(자식 트랜잭션에서 필드 값은 변경해 둔 상태기 때문에) 반영한 것으로 추측했다.
위에서 추측한 것을 기반으로, try catch로 잡고 싶어 새 트랜잭션을 생성하려 한다면 메서드 파라미터에 Entity를 그대로 전달하는 것이 아니라 조회 가능한 유일 식별값을 전달하는게 맞다고 판단하여 id만 전달한 뒤 자식 트랜잭션에서 select해서 사용하도록 변경했다.
부모 트랜잭션에서는 로그 데이터에 대한 저장만 할 뿐 상품, Entity에 대한 수정(dirty) 자체가 이루어지지 않도록 변경한 것이다.
이후에 기대한 대로 결과가 잘 나오는 것을 확인했다.
Entity를 그대로 전달하면 비용 면에서 유리할 것이라고 판단했으나 결과적으로 결합도를 높이는 안 좋은 방식이였던 것 같다. 심지어 1차 캐시를 이용하면 메모리에서 다시 가져올텐데...
결과적으로 큰 이득은 못 보고 부작용만 많은 구조를 만들었던 것 같다.
항상 결합도는 낮게. 응집도는 높게. 유념해야 하겠다.