@Configuration

초코칩·2024년 4월 19일
1

spring

목록 보기
7/10
post-thumbnail

@Configuration이란?

@Configuration 어노테이션은 스프링에게 해당 클래스가 애플리케이션의 구성(configuration)을 정의하는 클래스임을 나타낸다. 해당 클래스 내부에는 Bean 정의를 나타내는 @Bean 어노테이션이 적용된 메서드가 존재한다.

@Configuration
public class AppConfig {

    @Bean
    public Choco getChoco() {
        return new Choco();
    }

    @Bean
    public Chocochip getChocochip() {
        return new Chocochip(getChoco());
    }
}

이때 @Bean을 메서드 사용하여 수동으로 등록한다. Bean을 등록해줄 때에는 메소드 이름으로 Bean 이름이 결정된다.

언제 사용할까?

@Configuration을 활용한 수동 Bean 등록의 경우, 다음 두 가지 경우에 대해 사용하는 것이 좋다.

  1. 인프라 로직 관련 Bean
  2. 비즈니스 로직 중 다형성을 적극 활용하는 Bean

인프라 로직 관련 Bean에 사용하자.

애플리케이션의 로직은 비즈니즈 로직과 인프라 로직으로 나눌 수 있다.

  • 비즈니즈 로직 관련 Bean: 웹을 지원하는 Controller, 핵심 비즈니스 로직이 있는 Service, 데이터 계층의 로직을 처리하는 Repository 등과 같은 Bean을 비즈니즈 로직 Bean으로 볼 수 있다. 이러한 Bean들은 양이 많으며 비즈니스 요구사항들을 개발할 때 추후 추가되거나 변경된다.
  • 인프라 로직 관련 Bean: 기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다. 데이터베이스 연결, 공통 로그 처리와 같은 업무 로직을 지원하기 위한 하부 기술이나 공통적인 기술들이다.

비즈니즈 로직 관련 Bean은 Component Scan 기능을 적극 활용하는 것이 좋다. 문제가 발생해도, 어떤 곳에서 문제가 발생했는지 명확하게 파악하기 쉽기 때문이다.

하지만 인프라 로직 Bean은 비즈니즈 로직 Bean에 비해 그 수가 매우 적으면서도 애플리케이션 전반에 걸쳐 광범위하게 영향을 미친다.

그러나 문제가 발생 했을 때 문제의 위치를 파악하기 어렵고, 애플리케이션에 적용이 잘 되고 있는지 아닌지 파악하기 어려운 경우가 많다. 따라서 가급적 @Configuration을 활용한 수동 Bean 등록을 사용하여 문제들이 명확하게 드러날 수 있도록 하는 것이 좋다.

즉, 애플리케이션에 광범위하게 영향을 미치는 인프라 로직 관련 객체는 수동 Bean으로 등록하여 설정 정보에 명확히 나타나게 하는 것이 유지 보수하기에 좋다.

비즈니스 로직 중 다형성을 적극 활용하는 Bean에 활용하자.

@Configuration을 활용한 수동 Bean 등록과 Component Scan의 경우 모두 최대한 인터페이스의 구현 Bean들을 따로 모아 특정 패키지에 모아두어, Bean의 이름을 쉽게 파악할 수 있도록 할 수 있다.

다음과 같이 Piece의 종류 중 하나인 King과 Queen 클래스가 있다.

public interface Piece {
    void move();
}

public class King implements Piece {
    @Override
    public void move() {
        System.out.println("King Moved");
    }
}

public class Queen implements Piece{
    @Override
    public void move() {
        System.out.println("Queen Moved");
    }
}

List, Map을 통해 같은 자동적으로 Piece타입 내 모든 Bean들을 주입 받아, 클라이언트의 요청에 따라 동적으로 구현 객체를 선택하도록 만든 PieceService 에 대하여, @Configuration을 통해 별도의 설정 정보 클래스인 PieceConfig 클래스를 만들어 수동으로 Bean을 등록하는 방식으로 변경하면 다음과 같다.

@Configuration
public class PieceConfig {

   @Bean
   public Piece king() {
       return new King();
   }

   @Bean
   public Piece queen() {
       return new Queen();
   }
}

위와 같이 Piece의 구현 Bean들만 따로 모아 특정 패키지에 모아두면, PieceService가 의존 관계 자동 주입으로 Map<String, Piece>에 주입을 받는 상황에서, 어떤 Bean들이 조회되어 주입되는지, 각 Bean들의 이름이 무엇인지 쉽게 파악할 수 있게 된다.

@Configuration vs @Component

결론부터 말하자면 등록된 클래스가 proxy로 등록되는지 아닌지의 차이이다.

아래 클래스에 @Configuration@Component를 적용시켜보자.

public class AppConfig {

    @Bean
    public Choco getChoco() {
        return new Choco();
    }

    @Bean
    public Chocochip getChocochip() {
        return new Chocochip(getChoco());
    }
}

@Configuration를 적용했을 때에는 AppConfig 클래스에 CGLIB 프록시가 등록된 것이다.

반면에 @Component를 적용했을 때에는 실제 AppConfig 객체가 Bean으로 등록된다.

왜 Proxy로 등록될까?

그럼 왜 @Configuration을 적용한 클래스는 Proxy로 등록되는 것일까? 이는 완전한 싱글톤을 보장하기 위함이다. @Configuration을 적용하는 경우는 수동으로 불가피하게 Bean으로 등록해야 하는 상황일 것이다. 예를 들어 개발자가 직접 제어가 불가능한 라이브러리를 Bean으로 등록할 때 불가피하게 사용하는 경우가 있을 것이다.

이러한 상황에서 실수로 Bean을 생성하는 라이브러리의 메소드를 여러 번 호출하였다면 불필요하게 여러 개의 Bean이 생성이 된다. 스프링은 이러한 문제를 방지하고자 @Configuration이 있는 클래스를 객체로 생성할 때 CGLib 라이브러리를 사용해 프록시 패턴을 적용한다.

그래서 @Bean이 있는 메소드를 여러 번 호출하여도 항상 동일한 객체를 반환하여 싱글톤을 보장한다.

Ref

profile
초코칩처럼 달콤한 코드를 짜자

0개의 댓글