본문 바로가기

공부/개념 정리

[Spring] Bean의 쓰임과 수동 등록에 대해

스프링에서 Bean이란 스프링 IoC 컨테이너에서 관리하는 자바 객체를 말한다.

 

[스프링 IoC 컨테이너]

제어의 역전을 하기 위해 DI방식으로 기능을 구현한다. 이때 인자로서 객체를 넣어주기 위해 객체를 생성해야 하는데 Bean으로 등록을 한다면, Spring에서 자체적으로 객체를 생성하고, 관리해서 사용 하는 것. 

이때 등록된 Bean들을 모아 놓는 곳이 스프링 IoC 컨테이너이다.

자바에서는 new 키워드를 통해 객체를 생성하지만, Bean을 이용하여 관리한다면 어노테이션을 이용하여 Bean을 등록한다.

@Component 키워드를 클래스 구현부에 사용하면 빈으로 등록 할 수 있다.

@Component // Bean으로 등록
public class MemoService { ... }

 

Bean을 사용하는 방법으로는 @Autowired을 이용하여 사용.

@Component
public class MemoService {
		
    @Autowired	// DI를 위해 객체를 생성해줄 필요 없이 Bean으로 등록
    private MemoRepository memoRepository;
		
		// ...
}

 

@Component
public class MemoService {

    private final MemoRepository memoRepository;

    @Autowired	// @Autowired 어노테이션이 달려 있을 때 인자로 Bean 객체가 들어오면 생성이 필요 없다.
    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
		
		// ...
}

이런식으로 클래스의 필드에 @Autowired를 등록해주면 나중에 메서드인자로서 사용할 때 따로 생성을 해주지 않아도 된다.

@Companent 어노테이션은 Bean을 자동으로 스프링 IoC 컨테이너에 등록하는 방법이다. 그리고 Bean은 수동으로도 등록 할 수 있다.

 

[Bean을 수동 등록 하는 방법]

@Configuration // 수동으로 빈을 등록한 클래스라는 선언
public class PasswordConfig {

    @Bean	//빈을 수동으로 등록
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

 

수동으로 빈으로 등록하고자하는 필드값을 갖는 클래스에는 @Configuration 어노테이션을 달아준다. 그리고 수동으로 Bean을 달아줄 필드값에 @Bean 어노테이션을 달아주면 된다.

 

그렇다면 수동 Bean 등록은 언제 사용할까?

 

1. 기술 지원 Bean에 대해 등록

 

[기술 지원 Bean]

애플리케이션 로직은 업무 로직과 기술 지원 로직으로 나눌 수 있다.

1. 업무 로직 Bean : 3Layered-Architecture 패턴의 Controller, Service, Repository가 업무 로직 Bean이다. 

업무 로직 Bean의 특징은 양이 매우 많으며, 사용패턴이 유사하다. 따라서 자동 등록 기능을 통해서 사용해도 문제가 발생했을 때 명확하게 파악하기가 쉽다

2. 기술 지원 Bean : 기술적인 문제나 공통 관심사를 처리할 때 주로 사용. DB연결, 공통 로그 처리와 같은 업무 로직을 지원하는 하부 기술들이 해당한다. 기술 지원 Bean은 업무 로직 Bean에 비해 수가 매우 적으며, 어플리케이션에 광범위하게 영향을 미친다. 업무 로직 빈과 달리 문제가 생겼을 때 파악하기 어려운 경우가 많다. 따라서 수동 Bean 등록을 하여 문제들이 명확하게 드러날 수 있도록 하는 것.

 

의문점> 왜 수동Bean으로 등록하면 문제가 명확하게 보이는지?

 

[다형성을 활용하는 Bean]

인터페이스를 구현한 클래스를 Bean으로 등록할 때 하나의 인터페이스에 2개 이상의 구현체 클래스가 존재할 수 있다.

이때 다형성을 이용하여 인터페이스 객체를 선언하고, 생성할 때 내가 원하는 Bean으로 생성하여 주입할 수 있게 된다.

이를 통해 IoC를 활용한 코드를 구현할 수 있게 된다. 

public interface Food {
    void eat();
}

@Component
public class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}

@Component
public class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}

Food 인터페이스를 구현한 Chicken 클래스와 Pizza 클래스가 있다. 이때

@SpringBootTest
public class BeanTest {

    @Autowired
    Food food;
    
}

테스트 코드에 이렇게 테스트를 하면 Food food; 코드에 오류가 난다. 그 이유는 @Autowired되어 있어서 Bean으로 등록이 되어야 하는데, Food 인터페이스를 구현한 클래스가 두개이기 때문에 어떤 클래스로 생성할지 모르기 때문(지정해줘!).

@SpringBootTest
public class BeanTest {

    @Autowired
    Food pizza;	// 변수명을 pizza로 해서 Bean으로 등록(Bean으로 등록될 땐 클래스가 소문자로 바뀌어서?)
    
    @Autowired
    Food chicken;	//변수명을 chicken으로 해서 Bean으로 등록
    
}

이렇게 간단하게 선언할 때 해당하는 Bean을 지정해서 등록할 수 있다.

@Component
@Primary	// @Primary 어노테이션을 지정하면 Bean으로 등록할 때 첫번째 우선순위로 지정된다.
public class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}

@SpringBootTest
public class BeanTest {
    @Autowired
    Food food;	//@Primary 어노테이션이 지정된 Chicken으로 생성됨!
}

@Primary 어노테이션은 Bean의 우선순위를 정해준다. @Autowired를 통해 객체를 생성할 때 위의 예시같이 Bean타입을 정해주지 않았을 경우 @Primay 어노테이션이 지정된 클래스로 자동으로 주입해준다.

 

@Component		
@Qualifier("pizza")		
public class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}

@SpringBootTest
public class BeanTest {

    @Autowired
    @Qualifier("pizza")
    Food food;

    @Test
    @DisplayName("Primary 와 Qualifier 우선순위 확인")
    void test1() {
        // 현재 Chicken 은 Primary 가 적용된 상태
        // Pizza는 Qualifier 가 추가된 상태입니다.
        food.eat();
    }
}

@Qualifier 어노테이션을 사용하여 주입을 할 때에는 사용할 클래스에 @Qualifier("선언할 이름")을 지정해주고,

내가 등록된 Bean을 사용할 자리에 @Qualifier("선언한 이름")을 입력해주면 해당 클래스로 주입을 해주게 된다.

이때 food는 Chicken 클래스에는 @Primary가 선언되었음에도 Pizza클래스의 객체로 주입된다. 이를 통해 @Primary보다 지정된 @Qualifier가 더 높은 우선순위를 갖음을 알 수 있다.