Spring/Dependency Injection

[Spring] @Lazy 사용해 의존성 주입 시 Bean 지연 초기화 설정하기

Dev.Cho 2024. 11. 19. 07:54
반응형

지연 초기화가 필요한 이유

@Component 어노테이션이나 @Bean 어노테이션을 통해 IOC Container에 Bean을 등록하면 스프링 애플리케이션 시작 시 등록된다.

 

예를 들어 다음과 같이 초기화 시 InitTestUseCase Initialized 를 출력하는 InitTestUseCase를 만들어보자.

@Component
class InitTestUseCase {
    init {
        println("InitTestUseCase initialized")
    }
}

 

그런 다음 Configuration 파일을 다음과 같이 만든 후

@ComponentScan
@Configuration
class InitConfiguration

 

다음과 같이 컨테이너를 초기화 하고, "After container initialized" 을 출력한다.

fun main(args: Array<String>) {
    val context = AnnotationConfigApplicationContext(InitConfiguration::class.java)
    println("After container initialized")
}

 

그러면 이 코드를 실행하면 다음과 같은 결과가 나오는 것을 볼 수 있다.

 

 

컨테이너가 초기화 될 때 이미 InitTestUseCase가 만들어져 컨테이너에 등록되는 것을 볼 수 있다.

 

하지만, 스프링 애플리케이션을 실행할 때 매번 모든 Bean을 등록하면 시작 시간이 느려지고, 시작 시 메모리에 모든 Bean이 올라가기 때문에 시작이 무거워진다.

 

이런 문제를 해결하기 위해 스프링은 Bean이 필요할 때 초기화되도록 하는 지연 초기화 기능을 제공한다.

 

@Lazy 사용해 Bean 지연 초기화 설정하기

지연 초기화 기능을 설정하는 방법은 간단하다. 단순히 @Component나 @Bean과 함께 @Lazy 어노테이션을 붙이면 된다. 예를 들어 위에서 설정한 InitTestUseCase는 다음과 같이 변경되면 지연 초기화가 적용된다.

@Lazy
@Component
class InitTestUseCase {
    init {
        println("InitTestUseCase initialized")
    }
}

 

이제 다시 컨테이너를 초기화하고 마지막 줄에 InitTestUseCase Bean을 가져오는 코드를 실행해보자.

fun main(args: Array<String>) {
    val context = AnnotationConfigApplicationContext(InitConfiguration::class.java)
    println("After container initialized")
    
    context.getBean(InitTestUseCase::class.java)
}

 

그러면 결과는 다음과 같이 출력된다.

 

 

컨테이너가 초기화 된 다음 InitTestUseCase가 getBean 함수가 불리는 시점에 초기화 되는 것을 볼 수 있다.

 

 

지연 초기화의 장단점

지연 초기화는 시작 시간 최적화외 메모리를 필요할 때 소비한다는 점에서는 좋지만, Spring 애플리케이션 시작 시 의존성에 오류가 있더라도 오류가 발생되지 않는 문제가 있다. 이후 런타임에 오류가 발생하게 되면 인스턴스가 제대로 만들어지지 않게 된다. 예를 들어 InitTestUseCase에 String을 주입한 다음 이 타입의 Bean을 컨테이너에 제공하지 않는 경우를 생각해보자.

@Lazy
@Component
class InitTestUseCase(
    private val string: String
) {
    init {
        println("InitTestUseCase initialized")
    }
}

 

그러면 앱 시작 시에는 이 코드를 실행하더라도 오류가 발생하지 않는다.

fun main(args: Array<String>) {
    val context = AnnotationConfigApplicationContext(InitConfiguration::class.java)
    println("After container initialized")
}

 

다음은 위 코드를 실행한 결과이다.

 

하지만 이후 이 InitTestUseCase Bean을 가져오려고 하면 런타임에 오류가 생긴다.

fun main(args: Array<String>) {
    val context = AnnotationConfigApplicationContext(InitConfiguration::class.java)
    println("After container initialized")

    context.getBean(InitTestUseCase::class.java)
}

 

 

다음은 이 코드를 실행한 결과이다.

 

즉, 지연 초기화는 메모리 최적화와 애플리케이션 시작 시간 최적화에는 좋지만, 애플리케이션 시작 시 오류가 발견되지 않을 수 있어 단점이 장점을 상쇄한다. 따라서 꼭 필요한 경우가 아니면 굳이 지연 초기화를 설정하지 않는 것이 좋을 수 있다.

 

전체 코드: GitHub
이 프로젝트가 도움이 되셨다면 저장소에 Star⭐️를 눌러주세요! Stargazers는 다음 페이지에서 확인할 수 있습니다.
반응형