개요
- 서비스를 운영하다 보면 간헐적으로 요청 오는 정보(ex. 코드로 처리하면 편리한 경우)
- 작업들 또는 암호화 / 복호화 같은 애플리케이션 내부에서만 처리할 수 있는 작업(ex. 유저의 복호화된 전화번호 정보가 담긴 엑셀파일이 필요하다)
- 해당 유저 혹은 작업에 대한 복합적인 여러 정보들을 쉽게 확인할 수 있도록 하는 작업(ex. 여러 테이블들에 있는 정보들이 필요하다)
- 위의 운영 작업들을 효율적으로 처리하기 위해 ApplicationRunner와 CommandLineRunner를 활용한 방법들을 소개합니다.
ApplicationRunner와 CommandLineRunner 인터페이스란?
- 스프링부트 애플리케이션을 시작할 때 어떤 동작을 수행하도록 지원하는 인터페이스이며 Runner 인터페이스를 상속받고 있습니다.
- 스프링부트 애플리케이션이 시작할때 필요한 초기화 작업을 처리하기 위해 사용됩니다.
- ApplicationRunner와 CommandLineRunner의 차이점은 run 메서드의 매개변수 타입이 다릅니다.
- ApplicationRunner는 ApplicationArgument를 사용하고 CommandLineRunner는 단순한 String 배열을 받습니다.
interface Runner {
}
class CustomCommandLineRunner : CommandLineRunner {
override fun run(vararg args: String?) {
TODO("Not yet implemented")
}
}
class CustomApplicationRunner : ApplicationRunner {
override fun run(args: ApplicationArguments?) {
TODO("Not yet implemented")
}
}
- Runner 인터페이스는 스프링부트 애플리케이션이 run을 마무리할 때쯤에 callRunners()를 호출하며 실행됩니다.
public ConfigurableApplicationContext run(String... args) {
...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments); // 이부분
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
...
return context;
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> {
if (runner instanceof ApplicationRunner applicationRunner) {
callRunner(applicationRunner, args);
}
if (runner instanceof CommandLineRunner commandLineRunner) {
callRunner(commandLineRunner, args);
}
});
}
CommandLineRunner 인터페이스 활용
@Component
class CustomCommandLineRunner(
private val userExcelExporter: UserExcelExporter
) : CommandLineRunner {
override fun run(vararg args: String?) {
if (args.isEmpty()) return
if (args[0].equals("createUserExcel")) {
userExcelExporter.export(args)
}
}
}
- CommandLineRunner 인터페이스를 구현하게 되면 run 메서드를 override 하여야 합니다.
- 원하는 작업의 네이밍, 파라미터 등의 컨벤션을 정하고 코드를 작성하면 됩니다.
- 실행은 스프링부트 애플리케이션을 실행하는 것과 동일하게 실행하면 됩니다.
java -jar RunnerApplication.jar createUserExcel 20230203
- spring-boot-starter-web 라이브러리를 사용하는 경우 application.yml에 tomcat 서버로 실행되지 않도록 해주어야 합니다.
spring:
main:
web-application-type: none
결론
- 기존 스프링을 사용하는 것처럼 사용할 수 있습니다.
- 멀티 모듈을 활용하면 기존 엔티티나 쿼리 등을 활용할 수 있습니다.
- 운영을 하다보면 비정기적으로 요청하는 작업들 혹은 정기적으로 요청하는 작업이어도 개발자를 거쳐야 하는 작업들에 사용하기 편리합니다.
- 엑셀 파일을 읽어 DB에 데이터를 적재해줘야하는 경우나 DB에 있는 데이터를 엑셀로 출력해야 하는 경우 사용하기 용이합니다.