본문 바로가기

Back-End/Spring

Spring의 @Configuration

스프링을 공부하다 보면 가장 처음 마주치는 어노테이션 중 하나가 @Configuration 입니다.

처음엔 "설정 클래스인가 보다" 하고 넘어가기 쉬운데 이 어노테이션이 하는 일과 쓰는 이유를 정확히 이해하면 스프링 프레임워크에 대한 이해가 더 깊어질 수 있을 것 같아 글을 작성합니다.

 

이번 글에서는 @Configuration의 역할, @Component와의 차이점, 그리고 실제로 언제 왜 써야하는지를 예시와 함께 정리해보겠습니다.


@Configuration의 정체

@Configuration
public class AppConfig {
   @Bean
   public ObjectMapper objectMapper() {
        ObjectMapper mapper = objectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeatuer.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
   }
}

 

@Configuration이 붙은 클래스는 스프링이 설정 클래스로 인식합니다. 내부에 정의된 @Bean 메서들은 객체를 생성하고, 반환된 객체는 스프링 컨테이너에 빈으로 등록됩니다.

 

하지만 여기서 중요한 점은 Spring은 @Configuration이 붙은 클래스를 프록시 객체(CGLIB)로 감싸서 싱글톤을 보장해준 다는 것입니다. 즉 여러 개의 @Bean 메서드에서 동일한 빈을 참조하더라도 항상 같은 인스턴스를 주입하게 됩니다.

반면 @Component만 붙인 경우에는 프록시 적용이 되지 않기 떄문에 @Bean 메서드를 호출하면 객체가 매번 생성되어 싱클톤이 꺠질 수 있습니다.


그럼 이걸 왜 쓸까?

1. 외부 라이브러리 객체를  빈으로 등록하고 싶을 때

라이브러리에서 제공하는 클래스는 우리가 직접 @Component 를 붙일 수 없습니다. 이런 객체를 스프링 컨테이너에 등록하고 싶을 때 @Bean 을 사용해야 합니다.

@Configuration
public class RedisConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                 .setConnectTimeout(Duration.ofSeconds(5))
                 .setReadTimeout(Duration.ofSeconds(5))
                 .build();
    }
}
@Service
public class WeatherService {
    private final RestTemplate restTemplate;
    
    public WeatherService(RestTemplate restTemplate) {
       this.restTemplate = restTemplate;
    }
    
    public String getWeather(String city) {
       String url = "https://api.weather.com/wheater?city=" + city;
       return restTemplate.getForObject(url, String.class);
    }
}

 

2. 커스터마이징한 ObjectMapper를 등록하고 싶을 때

objectMapper를 그대로 쓰면 LocalDateTime 같은 타입이 timestamp로 출력되는데, 이걸 ISO 형식으로 바꾸고 싶다면?

 

@Configuration
public class AppConfig {
   @Bean
   public ObjectMapper objectMapper() {
        ObjectMapper mapper = objectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeatuer.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
   }
}
@Service
public class JsonService {
   
   private final ObjectMapper objectMapper;
   
   public JsonService(ObjectMapper mapper) {
       this.objectMapper = mapper;
   }
   
   public String toJson(Object obj) throws JsonProcessingExcpetion {
       return objectMapper.wirteValueAsString(obj);
   }
}

전체 흐름 요약 순서도

Application 시작
   ↓
@ComponentScan으로 HttpClientConfig 감지
   ↓
@Configuration 감지 -> restTemplate() 메서드 실행
   ↓
RestTemplate 객체 생성 및 커스텀 설정 적용
   ↓
Spring Container에 RestTemplate 빈으로 등록
   ↓
서비스 클래스에서 생성자 주입으로 사용

@Configuration은 단순히 설정 파일 이상의 역할을 합니다.

  • 객체 구성과 의존관계를 명확하게 설정하고 싶을 때
  • 외부 라이브러리 객체를 스프링 빈으로 등록할 때
  • 환경에 따라 다른 설정을 유연하게 적용하고 싶을 때