Spring/Dependency Injection

[Spring] @Component와 @Bean의 차이는 무엇이고 언제 사용해야 할까?

Dev.Cho 2024. 11. 17. 07:07

@Component를 사용하지 못하고 @Bean을 사용해야 하는 경우

@Component 어노테이션은 클래스 위에 붙는다. 즉, 내가 클래스를 만들 수 있을 때만 사용할 수 있다. 예를 들어 외부 라이브러리의 클래스를 Bean으로 만들 경우 @Component를 사용할 수는 없다.

 

예를 들어 A메신저에서 라이브러리를 제공하는데, 이 라이브러리에는 A메신저의 채팅방에 알림을 보내기 위한 AChatService 라는 클래스를 제공한다고 해보자.

// 다른 라이브러리의 클래스라서 수정할 수 없다고 가정
class AChatService() {
    fun sendMessage(message: String) {
        println("Message sent: $message")
    }
}

 

이런 경우 우리는 AChatService 클래스는 수정할 수가 없기 때문에 위에 @Component를 사용할 수 없다.

 

따라서 이런 경우에는 @Bean 어노테이션을 통해 Bean을 설정해야 한다. 예를 들어 다음과 같이 Bean을 설정하면 된다.

@ComponentScan
@Configuration
class ChatContainerConfiguration {
    @Bean
    fun aChatService(): AChatService {
        return AChatService()
    }
}

 

 

@Component와 @Bean의 차이

@Component와 @Bean은 비슷한 역할을 하지만, 내부적으로는 약간의 차이가 있다.

 

1. 사용할 수 있는 대상의 차이

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component

 

@Bean은 Target이 ElementType.METHOD나 ElementType.ANNOTATION_TYPE이다. 즉, 주로 함수에 사용된다. 

@Bean
fun inMemoryUserRepository(): UserRepository {
    return InMemoryUserRepository()
}

 

반면, @Component는 ElementType.TYPE 이다. 이 TYPE이라는 값은 클래스나, 인터페이스, enum, record에 붙을 수 있다.

public enum ElementType {
    /** Class, interface (including annotation interface), enum, or record
     * declaration */
    TYPE,
    ..
}

*Java쪽 코드이기 때문에 record가 들어갔는데, record는 코틀린의 data class와 비슷하다.

 

이 때문에 주로 다음과 같이 클래스 위에 붙어 사용된다.

@Component
class InMemoryUserRepository(): UserRepository {
    override fun createUser(id: String, user: String) {
        println("User $user created with id: $id in InMemoryUserRepository")
    }
}

 

2. 코드 작성 차이

@Bean은 의존성을 설정하기 위한 코드를 직접 작성해야 한다. 즉, 클래스 파일 따로 @Bean 따로 작성해야 한다.

@Configuration
class UserContainerConfiguration {
    @Primary
    @Bean
    fun inMemoryUserRepository(): UserRepository {
        return InMemoryUserRepository()
    }

    @Qualifier("dbUserRepository")
    @Bean
    fun dbUserRepository(): UserRepository {
        return DbUserRepository()
    }

    @Bean
    fun userCreator(@Qualifier("dbUserRepository") repository: UserRepository): UserCreator {
        return UserCreator(repository)
    }
}

 

하지만 @Component는 단순히 클래스 위에 붙임으로써 사용될 수 있고, 스프링에서 모든 설정을 알아서 해준다.

@Component
class UserCreator
@Component
class InMemoryUserRepository(): UserRepository

 

이 차이는 별거 아닌 것 같아 보이지만, 실제 개발을 할 때는 의존성이 매우 복잡해서 @Bean을 사용하게 되면 작성해야 할 코드가 많아지고 의존성이 변경될 때마다 직접 설정을 하나하나 해줘야 하기 때문에 매우 불편하다. 물론 @Bean을 사용했을 때 의존성을 한 파일 내부에서 볼 수 있는 것은 장점이지만, 불편함이 장점보다 크다.

 

이 때문에 개인적으로 외부 라이브러리에 대한 의존성 설정을 하는 것이 아니라면, @Component 를 사용하는 것이 좋다고 생각한다. 물론 가장 우선 순위는 팀 컨벤션으로 팀 컨벤션이 @Bean을 사용하는 것이라면 @Bean을 사용하는 것이 맞고, @Component를 사용하는 것이 컨벤션이라면 @Component를 사용해야 하며 외부 라이브러리를 쓰는 것 같은 불가피한 상황을 제외하고는 둘을 혼용하는 것은 권장되지 않는다.

 

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