애플리케이션을 운영하다 보면 애플리케이션이 실행되는 시점에 단 한 번만 실행되어야 하는 로직을 정의할 필요가 있는 경우가 많습니다.
예를 들어, 초기 설정값을 미리 로딩하는 Warm up 과정이나 환경 설정을 기반으로 하는 로직이 이에 해당합니다.
그렇다면 이처럼 최초 1회 실행되어야 하는 로직은 어디에 정의해야 할까요?
그 방법을 네 가지로 나누어 상세히 소개합니다.
1. Bean 생명주기를 이용하는 방법
스프링 부트에서 빈(Bean)의 생명주기를 활용하여 최초 실행 로직을 정의할 수 있습니다.
가장 많이 사용되는 방법으로 생성자(Constructor)와 @PostConstruct 어노테이션을 사용하는 방법이 있습니다.
1.1 생성자(Constructor) 이용
@Slf4j
@Component
public class MyBean {
private final RequiredBean requiredBean;
@Value("${hello.world}")
private String helloWorld;
@Autowired
public MyBean(RequiredBean requiredBean) {
this.requiredBean = requiredBean;
log.info("[constructor] helloWorld: {}", helloWorld); // null
}
}
스프링 애플리케이션이 시작되면, ApplicationContext 초기화 과정에서 필요한 Bean을 생성하고 등록합니다.
이때 생성자 안에 특정 로직을 정의하여 최초 1회 실행 로직을 수행할 수 있습니다.
하지만 생성자를 이용할 때에는 아래 사항에 주의해야 합니다:
- 생성자 호출 시점에는 아직 ApplicationContext 초기화가 완전히 이루어지지 않았으므로, 환경변수가주입되지 않아 helloWorld와 같은 값이 null일 수 있습니다.
- Bean의 기본 스코프는 싱글톤(singleton)이지만, 다른 스코프(예: 프로토타입(prototype))로 설정된 경우 생성자가 여러 번 호출될 수 있습니다. 따라서 반드시 싱글톤으로 관리해야 할 필요가 있습니다.
1.2 @PostConstruct 이용
@Slf4j
@Component
public class MyBean {
@Value("${hello.world}")
private String helloWorld;
@PostConstruct
void init() {
log.info("[@PostConstruct] helloWorld: {}", helloWorld); // real value
}
}
@PostConstruct는 ApplicationContext 초기화가 완료된 후 호출됩니다.
따라서, 모든 데이터가 주입된 상태에서 안전하게 최초 로직을 실행할 수 있습니다.
다만, 프로토타입 스코프인 경우 여러 번 실행될 수 있음을 유의하세요.
이 방식은 로직이 비교적 간단하고 특정 Bean에 직접적으로 관련된 초기화 작업일 때 유용합니다.
예를 들어, 외부 API와의 연결 설정 또는 특정 Bean의 초기화 작업을 수행할 때 적합합니다.
2. Runner를 이용하는 방법
스프링 부트에서는 애플리케이션 실행 시 단 한 번 호출되는 Runner 인터페이스를 이용할 수 있습니다.
대표적으로 CommandLineRunner와 ApplicationRunner가 있습니다.
2.1 CommandLineRunner 이용
@Slf4j
@Component
public class MyRunner implements CommandLineRunner {
@Value("${hello.world}")
private String helloWorld;
@Override
public void run(String... args) throws Exception {
log.info("[CommandLineRunner] helloWorld: {}", helloWorld); // real value
}
}
CommandLineRunner는 프로그램 실행 시점에 커맨드라인 인자를 받을 수 있으며, 한 번만 호출됩니다.
모든 Bean과 환경변수가 주입된 후 실행되므로 초기화 과정에서 발생할 수 있는 문제를 피할 수 있습니다.
CommandLineRunner는 애플리케이션의 시작과 동시에 간단한 초기화 작업을 수행하거나 커맨드라인에서
전달받은 파라미터를 기반으로 하는 초기 설정에 적합합니다.
2.2 ApplicationRunner 이용
@Slf4j
@Component
public class MyRunner implements ApplicationRunner {
@Value("${hello.world}")
private String helloWorld;
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("[ApplicationRunner] helloWorld: {}", helloWorld);
}
}
ApplicationRunner는 CommandLineRunner와 매우 유사하지만, 인자들이 ApplicationArguments로 래핑되어 제공됩니다. 이를 통해 명확하고 편리하게 인자를 다룰 수 있습니다.
ApplicationRunner를 사용하면 전달된 인자를 보다 유연하게 처리할 수 있으며, 특정 옵션 값이 존재하는지
확인하거나 인자를 손쉽게 필터링할 수 있습니다.
3. Event를 이용하는 방법
스프링 애플리케이션이 실행되는 과정에서 다양한 이벤트(Event)가 발생하며, 이 이벤트를 구독하는
리스너를 통해 최초 실행 로직을 처리할 수 있습니다.
3.1 ApplicationStartedEvent 이용
@Slf4j
@Component
public class MyEventListener {
@EventListener
public void listen(ApplicationStartedEvent event) {
log.info("[ApplicationStartedEvent] call: {}", event);
}
}
ApplicationStartedEvent는 ApplicationContext 초기화가 완료된 후 발생하며, 모든 Bean과 환경변수를 사용할 수 있습니다.
이 이벤트는 애플리케이션이 시작되었지만 아직 모든 준비가 완료되지 않은 시점에 필요한 초기화를 수행할 때 적합합니다.
3.2 ApplicationReadyEvent 이용
@Slf4j
@Component
public class MyEventListener {
@EventListener
public void listen(ApplicationReadyEvent event) {
log.info("[ApplicationReadyEvent] call: {}", event);
}
}
ApplicationReadyEvent는 Runner가 모두 호출된 후 발생하는 이벤트로, 애플리케이션이 완전히 준비된 후 실행됩니다. 이 이벤트는 모든 초기화 작업이 완료된 후에 최종적으로 수행되어야 하는 작업을 처리하는 데 유용합니다.
예를 들어, 외부 서비스와의 통신을 시작하거나 데이터베이스에 초기 데이터를 넣는 등의 작업이 이에 해당할 수 있습니다.
이벤트 기반 초기화는 특정 시점에 정확히 로직을 실행하고 싶을 때 매우 유용합니다.
특히 대규모 애플리케이션에서는 이벤트 기반으로 작업을 나누어 처리함으로써 코드의 유연성과 유지보수성을 높일 수 있습니다.
4. @Bean 어노테이션을 활용한 초기화 방법
또 다른 초기화 방법으로는 @Bean 어노테이션을 사용하여 특정 메서드를 초기화 시점에 호출하는 방법이 있습니다.
4.1 @Bean(initMethod) 이용
@Configuration
public class MyConfiguration {
@Bean(initMethod = "init")
public MyService myService() {
return new MyService();
}
}
public class MyService {
public void init() {
System.out.println("MyService 초기화 로직 실행");
}
}
@Bean 어노테이션의 initMethod 속성을 사용하면, 빈이 초기화된 후 호출될 메서드를 지정할 수 있습니다.
이 방식은 XML 기반 설정과 유사한 방식으로 Java 기반의 설정에서도 초기화를 관리할 수 있어, 코드의
가독성을 높이고 관리 편의성을 제공합니다.
호출 순서 요약
- 생성자(Constructor)
- @PostConstruct
- Runner (CommandLineRunner, ApplicationRunner)
- Event (ApplicationStartedEvent, ApplicationReadyEvent)
- @Bean(initMethod)
이 순서대로 호출되므로, 각 방법의 특성을 이해하고 적절한 방법을 선택해야 합니다.
언제 무엇을 사용할까?
- Bean에 종속된 부가 로직이라면 @PostConstruct를 추천합니다.
- Bean과 관계없는 초기화 로직이라면 Runner나 EventListener를 사용하세요.
- 비동기로 호출해야 한다면 @Async와 함께 사용하는 것도 좋은 방법입니다.
- 초기화 메서드를 명시적으로 지정하고 싶다면 @Bean(initMethod)를 고려하세요.
이렇게 각각의 방법을 적절히 사용하면, 스프링 부트 애플리케이션에서 효율적으로 최초 실행 로직을 관리할 수 있습니다.
'개발일지 > 스프링' 카테고리의 다른 글
스프링 부트에서 데이터 그룹화하기: groupBy() 활용법 (0) | 2024.11.01 |
---|---|
Faker 라이브러리 사용하는 방법 (1) | 2024.10.31 |
Gradle 의존성 설정의 이해 - api와 implementation의 차이 (0) | 2024.10.29 |
자바 스프링부트 웹 개발에서 중요한 디자인 패턴과 활용 (0) | 2024.10.29 |
MyBatis 사용법 [gradle 사용] (0) | 2024.03.27 |
댓글