[Spring] PostConstruct를 사용한 테스트 환경 구축
간단한 웹 프로젝트를 진행하면서 테스트할 때마다 데이터베이스에 데이터를 추가해야 하는 번거로움을 겪었습니다. 이를 해결하기 위해 TestData 클래스를 만들어 스프링 빈으로 등록하고, init() 메서드에 @PostConstruct를 적용하여 스프링 컨테이너에 빈이 등록될 때 자동으로 테스트 데이터를 DB에 추가하도록 하였습니다.
그런데 Member, MemberService, MemberRepository 클래스를 구현한 결과를 살펴보니 MemberService에는 @Transactional을 걸어주었지만, MemberRepository에는 @Transactional을 걸어주지 않았습니다.
TestData 클래스에서는 MemberService와 MemberRepository를 모두 주입받아서 테스트용 데이터를 추가하는데, MemberService를 통해 엔티티를 저장하면 정상적으로 동작하지만, 직접 MemberRepository를 통해 엔티티를 저장하면 오류가 발생합니다.
이런 경우, MemberService의 @Transactional은 작동하지만, MemberRepository의 @Transactional은 작동하지 않는데, 그 이유는 무엇일까요?
MemberRepository에는 @Transactional이 없기 때문에 스프링 컨테이너가 해당 빈을 생성할 때 EntityManager를 주입해주지 않습니다. 그러나 MemberService는 @Transactional이 걸려있기 때문에 스프링이 해당 빈을 생성할 때 EntityManager를 주입해주고, 이를 통해 MemberRepository에 접근할 수 있습니다.
하지만 TestData의 init() 메서드에서는 스프링 빈이 생성될 때 스프링 AOP가 적용되는 시점과 @PostConstruct가 호출되는 시점이 다릅니다. 따라서 @Transactional이 적용된 MemberService를 통해 MemberRepository에 접근하는 경우는 정상적으로 동작하지만, init() 메서드에서 직접 MemberRepository에 접근하는 경우에는 EntityManager가 주입되지 않아 오류가 발생하는 것입니다.
이러한 문제를 해결하기 위해서는 다양한 방법이 있습니다. 예를 들어, 다른 스프링 빈을 호출해서 사용하거나, AOP를 사용하지 않고 트랜잭션을 직접 코딩하는 방법 등이 있습니다.
아직 작성중인 글이며 추가로 들어가야하는 내용이나 틀린점이 있다면 알려주시면 감사하겠습니다.