SW 개발/Spring

Spring Boot, 콘솔 애플리케이션을 만드는 노하우 정리

지단로보트 2021. 12. 27. 22:36

개요

  • Spring Boot 생태계에서 콘솔 애플리케이션을 만드는 방법은 다양하지만, 내가 선호하는 방법을 정리하였다. (대부분은 팀 내부에서 사용하는 유틸리티 성격의 툴링이 목적이다.)

build.gradle.kts

  • 웹 애플리케이션이 아닌 한 번 실행되면 준비된 기능을 실행하고 종료되는 애플리케이션을 만들 것이기 때문에 spring-boot-starter이면 충분하다.
dependencies {
    implementation("org.springframework.boot:spring-boot-starter")
}

@SpringBootApplication 클래스 작성

  • 커맨드라인 인자를 다루면 좋겠지만 팀 내에서만 사용할 것이기에 운영체제 환경 변수 주입을 더 선호한다.
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class FooApplication

fun main(args: Array<String>) {

    runApplication<FooApplication>(*args)
}

@EventListener 클래스 작성

  • Spring Boot의 부트 스트래핑이 완료되고, 애플리케이션이 완전히 실행 준비 상태가 되는 시점에 발동되는 ApplicationReadyEvent 이벤트에 개입하면 아래와 같이 원하는 서비스 로직을 실행시킬 수 있다.
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component

@Component
class FooRunner(
    val fooService: FooService
) {
    @EventListener(ApplicationReadyEvent::class)
    fun run() {

        fooService.runSomething()
    }
}

@EnableAsync 클래스 작성

  • 쉘 스크립트를 두고 굳이 JVM 언어를 사용한 커맨드라인 툴을 만드는 것의 장점은 바로 멀티 쓰레드의 사용성에 있다. 운영체제 환경 변수를 통해 사용할 쓰레드를 설정하고 아래와 같이 쓰레드 풀을 생성할 수 있다.
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.task.TaskExecutor
import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

@Configuration
@EnableAsync
class AsyncConfig(
    @Value("\${tools.number-of-threads-to-use}") val numberOfThreadsToUse: Int,
) {
    @Bean
    fun taskExecutor(): TaskExecutor {

        val taskExecutor = ThreadPoolTaskExecutor()
        taskExecutor.corePoolSize = numberOfThreadsToUse
        taskExecutor.setQueueCapacity(0)
        taskExecutor.maxPoolSize = numberOfThreadsToUse

        return taskExecutor
    }
}

@Async 클래스 작성

  • 앞서 비동기 쓰레드 풀을 활성화했기 때문에 @Async를 이용하여 여러 개의 쓰레드로 분기하여 실행할 수 있다. 이를 통해 머신 자원의 사용을 극대화하여 원하는 작업을 실행할 수 있다.
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service

@Service
class FooAsyncService {

    @Async("taskExecutor")
    fun doSomethingAsAsync() {
          // 실행 로직 작성
    }
}

참고 글