Spring @Configuration vs @Component 어노테이션 차이에 대해 알아보기

Spring @Configuration vs @Component 어노테이션 차이에 대해 알아보기

July 9, 2024

Spring에서 의존성(Bean 클래스라고도 한다)을 등록 하는 방식은 크게 2가지가 있다. 바로 아래에서 설명할 @Configuration@Component이다.

Bean은 이전 포스팅에서 설명한 것과 같이 Spring IoC Container에서 관리하는 객체를 말한다.

@Configuration@Component는 바로 그 Bean을 등록하는 방식이다. 이 둘의 차이점에 대해 알아보자.

@Component 사용하기

@Component는 Spring IoC Container에 Bean을 등록하는 방식 중 하나이다.

아래 3가지의 컴포넌트를 등록하는 방식을 보자

Computer.java
@Component
public class Computer {
	MainBoard mainBoard;
	CPU cpu;

	public Computer(MainBoard mainboard, CPU cpu) {
		this.cpu = cpu;
		this.mainBoard = mainBoard;
	}

	void printComputerInfo() {
		System.out.println("CPU Name:" + cpu.name);
		System.out.println("CPU Manufacturer:" + cpu.manufacturer);
		System.out.println("Main board Name:" + mainBoard.name);
		System.out.println("Main board Manufacturer:" + mainBoard.manufacturer);
	}
}
MainBoard.java
@Component
public class MainBoard {
	String name; // 제품명
	String manufacturer; // 제조사

	// 생성자 함수
	public MainBoard() {
		this.name = "B760M Pro RS D5";
		this.manufacturer = "ASRock";
	}
}
CPU.java
@Component
class CPU {
	String name; // 제품명
	String manufacturer; // 제조사

	// 생성자 함수
	public CPU() {
		this.name = "Ryzen7 PRO 8700GE";
		this.manufacturer = "AMD";
	}
}

이렇게 하면 IoC 컨테이너에 각각 Computer, MainBoard, CPU 클래스가 각각 Bean으로 등록된다.

해당 컴포넌트들은 어플리케이션 실행 시점에 의존성이 등록되며, 3개의 클래스가 각각 의존성을 주입 받는 것을 도표로 그리면 다음과 같다.

image

@Configuration@Bean 사용하기

@Configuration은 Spring IoC Container에 Bean을 등록하는 방식 중 하나이다.

@Component 로 선언한 경우 멤버 변수로 선언된 name, manufacturer 등을 미리 초기화 했어야 했다. 만약 String 타입의 변수를 초기화 하지 않았다면 아래와 같은 에러가 발생한다.

Field name in com.example.demo.product.domain.MainBoard required a bean of type 'java.lang.String' that could not be found.

물론 String 타입의 Bean을 등록하면 에러가 발생하지 않겠지만, String 자체는 너무 광범위하기에 Bean으로 등록해서 사용하기에 적합하지 않다.

그럼 멤버변수를 무조건 사전에 초기화 해야 사용할 수 있을까?

그렇진 않다.

@Configuration을 사용하면 생성자 메서드에서 매개변수들을 유지하면서도 Bean을 등록할 수 있다.

아래 3가지 설명한 클래스는 이전에 정의했던 @Component를 빼고, 생성자에 각 클래스의 매개변수를 받도록 하여 초기화한다.

Computer.java
public class Computer {
	MainBoard mainBoard;
	CPU cpu;

	public Computer(MainBoard mainboard, CPU cpu) {
		this.cpu = cpu;
		this.mainBoard = mainBoard;
	}

	void printComputerInfo() {
		System.out.println("CPU Name:" + cpu.name);
		System.out.println("CPU Manufacturer:" + cpu.manufacturer);
		System.out.println("Main board Name:" + mainBoard.name);
		System.out.println("Main board Manufacturer:" + mainBoard.manufacturer);
	}
}
MainBoard.java
public class MainBoard {
	String name; // 제품명
	String manufacturer; // 제조사

	// 생성자 함수
	public MainBoard(String name, String manufacturer) {
		this.name = name;
		this.manufacturer = manufacturer;
	}
}
CPU.java
class CPU {
	String name; // 제품명
	String manufacturer; // 제조사

	// 생성자 함수
	public CPU(String name, String manufacturer) {
		this.name = name;
		this.manufacturer = manufacturer;
	}
}

이제 ComputerConfig 라는 클래스를 생성하여 @Configuration을 선언하고, @Bean을 통해 각 클래스의 Bean을 등록한다.

ComputerConfig.java
@Configuration
public class ComputerConfig {
    @Bean
    public Computer computer(MainBoard mainBoard, CPU cpu) {
        return new Computer(mainBoard, cpu);
    }

    @Bean
    public MainBoard mainBoard() {
        return new MainBoard("B760M Pro RS D5", "ASRock");
    }

    @Bean
    public CPU cpu() {
        return new CPU("Ryzen7 PRO 8700GE", "AMD");
    }
}

이렇게 @Configuration을 사용하면 생성자 메서드에서 매개변수들을 유지하면서도 Bean을 등록할 수 있다.

이처럼 Bean 을 등록하는 방식의 차이이지 사실 기술상 큰 차이는 없다.

각각은 언제 쓰이나?

@Component는 자체적으로 구현한 컴포넌트에 대해 많이 사용되는 편이다. 예를 들어 @Controller, @Repository, @Serivce 등이 사용자가 구현하는 대표적인 컴포넌트의 예이다.

@Configuration 이름처럼 외부 클래스의 설정을 Bean으로 등록하고자 할 때 사용한다.

예를 들어, @Configuration을 사용하여 DataSource를 Bean으로 등록하면, application.yaml 등을 사용하지 않더라도 DataSource를 Bean으로 등록할 수 있다. (사실 application.yaml 도 yaml을 읽어 Bean으로 등록하는 방식을 따르고 있으며, 이를 Spring 에서는 AutoConfiguration이라고 한다.)

DataSourceConfig.java
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/test")
                .username("root")
                .password("password")
                .build();
    }
}