Develop/TIL(Today I Learned)

Spring - 클래스 초기화 방법 비교 (PostConstruct, EventListener)

IJY 2023. 3. 6. 15:37

예전에 Spring 프로젝트에서 Spring 기동 시 Bean으로 등록하는 클래스의 초기화 방법을 포스팅한 적이 있다.

이때는 Bean으로 등록하는 클래스가 정상적으로 생성되어 의존성 주입(DI)까지 끝난 후 초기화가 되도록 하는 방법으로 @PostConstrcut 어노테이션을 사용한 초기화 방법을 주 내용으로 기록하였었다.

이후 @EventListener(ApplicationReadyEvent.class)를 사용하여 초기화하는 방식도 알게 되어 두 방법의 차이점에 대해 기록하고자 한다.

 

@PostConstruct

해당 어노테이션을 사용한 초기화 동작은 언급했듯 DI까지 다 끝나고 정상적으로 클래스가 생성된 이후 수행된다.

즉, 클래스가 생성된 직후 해당 어노테이션을 적용한 초기화 메서드가 실행된다고 보면 된다.

그렇기에 일반적인 초기화 과정은 해당 어노테이션을 사용하여 초기화 과정을 수행하면 문제없이 잘 동작을 하게 된다.

하지만 AOP가 적용된 상태에서 동작해야 하는 초기화 작업을 해당 어노테이션으로 해결하려고 한다면 안 되는 것을 확인할 수 있다.

이는 Proxy와 연관된 내용이긴 하지만 간단하게 말하자면 AOP가 적용된 클래스는 해당 원본 클래스를 그대로 사용하는 것이 아니라 Spring에서 원본 클래스를 상속받아 Proxy 클래스를 만든 후, 그렇게 만들어진 Proxy 클래스를 사용하기 때문이다.

따라서 Spring이 만들어낸 Proxy 클래스에서 초기화 메서드를 수행해야만 원하는 동작이 수행이 될 텐데 원본 클래스의 생성 시점에 원본 클래스에서 초기화 메서드가 동작을 하게 되면서 문제가 발생하는 것이다.

이를 해결하기 위한 방법이 @EventListener(ApplicationReadyEvent.class) 어노테이션을 사용하는 방법이다.

 

@EventListener(ApplicationReadyEvent.class)

사용 방법은 위 @PostConstruct와 동일하게 초기화 메서드에 해당 어노테이션을 적용하면 끝이다.

해당 방법으로 Proxy 클래스 생성 후 해당 클래스에 초기화가 가능한 이유는 어노테이션의 이름을 보면 알 수 있다.

어노테이션 이름 그대로 특정 이벤트 발생 시점에 메서드를 실행하게 되는데, ApplicationReadyEvent의 경우 Spring이 모든 초기화가 종료된 시점에 발생하기 때문이다.

Spring의 모든 초기화가 종료된 시점이라는 말은 Proxy 클래스의 생성도 전부 마친 상태라는 의미이기에 이때의 초기화 메서드 요청은 Proxy 객체에 전달되어 실행된다.

그럼 여기서 Proxy 클래스가 정상적으로 생성된 후의 초기화 요청은 왜 Proxy 객체에 전달이 되어 실행이 되는가?에 대한 의문이 생길 수도 있는데.. 이는 Spring의 동작에 대한 공부를 좀 한다면 알 수 있다.

간단하게 설명하자면 클래스들이 Spring bean으로 등록된 후 해당 객체들에 대한 요청(메서드 콜 등...)은 bean에 등록된 객체에 하게 되기 때문이다.

 

정리

정리를 해보자면 AOP 등 Proxy 클래스가 생성된 후 초기화 동작이 필요한 경우라면 @EventListener(ApplicationReadyEvetn.class) 어노테이션을 사용하면 되고, 그냥 원본 클래스 그대로 사용하는 경우에는 @PostConstruct 어노테이션을 사용하면 된다.

참고로 Proxy 클래스를 만들어 사용하는 경우는 AOP 적용 클래스, JPA 사용 시 @Transactional 어노테이션 적용 등의 경우들이 있으므로 해당 클래스가 Proxy 클래스를 만들어 동작하는 상황인지 잘 판단하여 사용하면 된다.