[spring] @Component vs @Configuration 차이점

2025. 10. 24. 20:36·공부일기../Spring

1. 기본 개념

1-1. @Component의 역할

@Component는 해당 클래스 자체를 스프링 빈으로 등록하는 어노테이션이다.

스프링이 컴포넌트 스캔을 할 때 이 어노테이션이 붙은 클래스를 자동으로 감지하고, 해당 클래스의 인스턴스를 생성하여 스프링 컨테이너에 빈으로 등록한다.

@Component
public class UserService {
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public void save(User user) {
        userRepository.save(user);
    }
}

위 코드에서 UserService 클래스는 @Component 어노테이션을 통해 빈으로 등록된다.

스프링은 리플렉션을 사용하여 클래스 정보를 읽고, 생성자를 찾아 필요한 의존성을 주입한 후 스프링 컨테이너에 등록한다.

 

 


1-2. @Configuration의 역할

@Configuration은 빈 설정을 담당하는 클래스임을 나타내는 어노테이션이다.

이 어노테이션이 붙은 클래스는 하나 이상의 @Bean 메서드를 포함하며, 개발자가 직접 객체를 생성하고 설정한 후 반환함으로써 빈으로 등록한다. @Configuration 클래스 자체도 @Component를 포함하고 있어 빈으로 등록된다.

@Configuration
public class AppConfig {
    
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
    
    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }
}

@Configuration을 사용하면 객체 생성 과정을 세밀하게 제어할 수 있다.

생성자에 특정 값을 전달하거나, 복잡한 초기화 로직을 수행하거나, 외부 라이브러리의 클래스를 빈으로 등록하는 등의 작업이 가능하다.

 

 


2. 빈 등록 방식의 차이

2-1. @Component의 자동 등록

@Component는 스프링의 컴포넌트 스캔 메커니즘을 통해 자동으로 빈이 등록된다.

스프링은 지정된 패키지를 스캔하여 @Component 어노테이션이 붙은 클래스를 찾고, 해당 클래스의 인스턴스를 생성한다.

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        // @ComponentScan이 기본적으로 활성화되어 있어
        // @Component가 붙은 클래스들이 자동으로 빈으로 등록됨
    }
}

이 방식은 개발자가 만든 클래스들을 빠르게 빈으로 등록할 때 유용하다.

별도의 설정 없이 어노테이션 하나만 붙이면 되므로 간편하다.

 

 


2-2. @Configuration의 수동 등록

@Configuration은 @Bean 메서드를 통해 수동으로 빈을 등록한다. 메서드 내부에서 직접 객체를 생성하고 필요한 설정을 적용한 후 반환한다.

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("user");
        config.setPassword("password");
        config.setMaximumPoolSize(10);
        config.setConnectionTimeout(30000);
        
        return new HikariDataSource(config);
    }
}

이 방식은 외부 라이브러리의 클래스를 빈으로 등록하거나, 복잡한 초기화 로직이 필요한 경우에 적합하다.

DataSource처럼 다양한 설정값을 코드로 직접 제어해야 하는 경우 @Configuration을 사용한다.

 

 


3. CGLIB 프록시와 싱글톤 보장

3-1. @Configuration의 동작 방식

@Configuration 어노테이션이 붙은 클래스는 일반 클래스와 다르게 동작한다.

스프링은 CGLIB 라이브러리를 사용하여 해당 클래스의 프록시 클래스를 생성한다.

 

이 프록시는 원본 클래스를 상속받은 서브클래스로, 빈의 싱글톤을 보장하는 역할을 한다.

@Configuration
public class AppConfig {
    
    @Bean
    public UserRepository userRepository() {
        System.out.println("UserRepository 생성");
        return new UserRepository();
    }
    
    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }
    
    @Bean
    public OrderService orderService() {
        return new OrderService(userRepository());
    }
}

위 코드에서 userRepository() 메서드가 userService()와 orderService()에서 각각 호출되지만, "UserRepository 생성" 메시지는 단 한 번만 출력된다.

CGLIB 프록시가 빈을 캐싱하여 같은 인스턴스를 반환하기 때문이다.

 

 


3-2. CGLIB 프록시의 동작 원리

CGLIB은 런타임에 바이트코드를 조작하여 동적으로 서브클래스를 생성한다. 스프링이 생성하는 프록시 클래스는 대략 다음과 같은 형태다.

// 스프링이 내부적으로 생성하는 프록시 (개념적 표현)
public class AppConfig$$EnhancerBySpringCGLIB extends AppConfig {
    
    private Map<String, Object> beanCache = new HashMap<>();
    
    @Override
    public UserRepository userRepository() {
        // 캐시에 이미 존재하면 캐시에서 반환
        if (beanCache.containsKey("userRepository")) {
            return (UserRepository) beanCache.get("userRepository");
        }
        
        // 없으면 부모 클래스의 메서드를 호출하여 생성
        UserRepository bean = super.userRepository();
        beanCache.put("userRepository", bean);
        return bean;
    }
}

프록시는 @Bean 메서드가 호출될 때마다 캐시를 확인하고, 이미 생성된 빈이 있으면 그것을 반환한다.

이것 덕분에 같은 빈을 여러 곳에서 의존하더라도 싱글톤이 보장된다.

 

 


3-3. proxyBeanMethods 옵션

@Configuration 어노테이션에는 proxyBeanMethods라는 옵션이 있다. 이 옵션을 false로 설정하면 CGLIB 프록시를 생성하지 않는다.

@Configuration(proxyBeanMethods = false)
public class AppConfig {
    
    @Bean
    public UserRepository userRepository() {
        System.out.println("UserRepository 생성");
        return new UserRepository();
    }
    
    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }
    
    @Bean
    public OrderService orderService() {
        return new OrderService(userRepository());
    }
}

이 경우 userRepository() 메서드를 직접 호출하면 매번 새로운 객체가 생성된다.

"UserRepository 생성" 메시지가 여러 번 출력되며, 싱글톤이 보장되지 않는다.

이 옵션은 빈 간의 의존성이 없고 성능 최적화가 필요한 경우에 사용한다.

 

 


4. 활용

4-1. @Component 사용 사례

@Component는 주로 개발자가 직접 작성한 비즈니스 로직을 담당하는 클래스에 사용한다.

Service, Repository, Controller 등의 역할을 하는 클래스들이 이에 해당한다. (타고들어가보면 @Component이 붙어있다!)

@Service  // @Component의 특화 버전
public class UserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    public UserService(UserRepository userRepository, 
                      EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    public void registerUser(User user) {
        userRepository.save(user);
        emailService.sendWelcomeEmail(user);
    }
}

이러한 클래스들은 생성자를 통한 의존성 주입만으로 충분하며, 복잡한 초기화 로직이 필요하지 않다.

 

 

 


4-2. @Configuration 사용 

@Configuration은 외부 라이브러리의 객체를 빈으로 등록하거나 복잡한 설정이 필요한 경우에 사용한다.

@Configuration
public class SecurityConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests()
            .requestMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/dashboard");
            
        return http.build();
    }
}

SecurityFilterChain처럼 외부 라이브러리의 클래스를 빈으로 등록하면서 다양한 설정을 적용해야 하는 경우 @Configuration이 적합하다.

 

 


5. 정리

@Component와 @Configuration은 스프링 빈을 등록하는 두 가지 방식이다.

@Component는 클래스 자체를 빈으로 등록하는 간단한 방식이고, @Configuration은 @Bean 메서드를 통해 객체 생성 과정을 세밀하게 제어할 수 있는 방식이다.

 

@Configuration의 가장 중요한 특징은 CGLIB 프록시를 통한 싱글톤 보장이다.

프록시는 빈을 캐싱하여 같은 객체를 반환함으로써 싱글톤을 보장한다. 이 프록시 메커니즘은 스프링 AOP의 기반이 되는 기술이기도 하다.

 

실무에서는 자신이 작성한 클래스는 @Component를 사용하고, 외부 라이브러리나 복잡한 설정이 필요한 객체는 @Configuration을 사용하는 것이 일반적이다. 두 방식을 적절히 조합하여 사용하면 스프링의 빈 관리 기능을 효과적으로 활용할 수 있다.

'공부일기.. > Spring' 카테고리의 다른 글

[spring] final 키워드와 @RequiredArgsConstructor의 원리  (0) 2025.10.22
[spring] 스프링 컨테이너와 스프링 빈  (1) 2025.10.15
[spring] 언제! 리팩토링해야 할까? 실전 가이드-유지보수성 문제 (파트 3/3)  (0) 2025.10.05
[spring] 언제! 리팩토링해야 할까? 실전 가이드- 코드 품질 문제 (2/3)  (0) 2025.10.04
[spring] 언제! 리팩토링해야 할까? 실전 가이드- 설계원칙 위반 (1/3)  (0) 2025.10.03
'공부일기../Spring' 카테고리의 다른 글
  • [spring] final 키워드와 @RequiredArgsConstructor의 원리
  • [spring] 스프링 컨테이너와 스프링 빈
  • [spring] 언제! 리팩토링해야 할까? 실전 가이드-유지보수성 문제 (파트 3/3)
  • [spring] 언제! 리팩토링해야 할까? 실전 가이드- 코드 품질 문제 (2/3)
s0-0mzzang
s0-0mzzang
공부한것을 기록합니다...
  • s0-0mzzang
    승민이의..개발일기..🐰
    s0-0mzzang
  • 전체
    오늘
    어제
    • 전체~ (108)
      • 마음가짐..! (10)
      • 공부일기.. (76)
        • weekly-log (6)
        • Spring (19)
        • Java (18)
        • DataBase (10)
        • git (2)
        • JPA (6)
        • kafka (1)
        • Backend Architecture (3)
        • Troubleshooting (삽질..ㅋ) (2)
        • Cloud (1)
        • Docker (2)
        • 알고리즘 (1)
        • 리액트 (2)
        • Infra (3)
      • 하루일기.. (22)
        • 그림일기 (8)
        • 생각일기 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    Paging
    ERD
    BufferedReader
    React
    MySQL
    자바
    spring boot
    TDD
    StringTokenizer
    JPA
    항해99
    항해플러스
    스프링부트
    SpringBoot
    인프라 기초
    다짐
    리팩토링
    spring
    ADC 환경
    swagger
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
s0-0mzzang
[spring] @Component vs @Configuration 차이점
상단으로

티스토리툴바