빈 등록 및 의존 관계 설정, 수동 vs 자동 뭐가 더 나을까?

심현민·2025년 1월 7일
0

Spring

목록 보기
5/18

자동, 수동 의존 관계 주입을 배우면서 차이점이 정확히 뭐고, 어떤 상황에 쓰이는 지 이해가 부족한 것 같아서 추가로 정리하고자 해당 포스팅을 쓰게 되었다.

스프링 컨테이너에 빈을 등록하는 대표적인 방식은 두 가지가 있다.

  • 수동
  • 자동

여기서 말하는 수동이 뭐고, 자동이 뭔지 알아가보겠다.


수동 빈 등록

  • 스프링 컨테이너는 수동 빈 등록을 우선적으로 적용한다.

이는 조금만 생각하면 당연하다는 거를 알 수 있다.

우리가 어떤 변수를 선언한다고 해보자

int num;

그러면 처음엔 자동으로 의미없는 쓰레기값이 들어가게 될 것이다.

하지만, 값을 초기화하면 이후에 계속 그 값으로 고정된다.

비슷한 원리로 생각했을 때 스프링에서 수동 빈이 자동 빈보다 우선적으로 적용된다는 사실을 쉽게 이해할 수 있을 것이다.

코드로 수동 빈 등록을 알아보자

@Configuration
public class AppConfig {
    //@Bean을 붙힌 메서드는 전부 스프링 컨테이너에서 관리된다.
    @Bean
    public MemberService memberService() {
        System.out.println("AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    //역할 : 저장소
    //구현체 : 메모리 저장소
    @Bean
    public MemberRepository memberRepository() {
        System.out.println("AppConfig.memberRepository");
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        System.out.println("AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());
//        return null;
    }

    //역할과 구현 분리
    //역할 : 할인 정책
    //구현체 : 정액할인 정책
    @Bean
    public static DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }

}

구조가 간단해 보이지만, 모르는 애노테이션들이 많이 보인다.

하지만, 우리는 여기서 딱 두 가지만 기억하면 된다.

"@Configuration, @Bean"

먼저, @Configuration

  • 스프링 컨테이너에 의해 처리될 설정 클래스임을 나타낸다.

  • @Component를 기반으로 동작하며, 스프링이
    해당 클래스를 자동으로 감지하고 처리한다.

어려운 용어가 많은데 그냥 빈으로 등록할 메서드들을 가지고 있는 클래스라고 생각하면 된다.

그리고 여기서 @Component를 기반으로 동작한다는 것은 아래 과정을 통해 이해할 수 있다.

@ComponentScan vs @Component
1. 우선적으로 빈으로 등록할 클래스에 @Component 애노테이션 을 선언


2. 이후, @ComponentScan을 통해 @Component을 사용한 클래스를 스캔하고, 이를 빈으로 등록


@ComponentScan이 탐색하는 범위
@SpringBootApplication이 있는 클래스가 core.web 패키지에 위치할 때, core.web과 그 하위 패키지 모두를 포함해서 탐색한다.

이런 과정은 전부 @SpringBootApplication이 자동으로 수행해준다.
(@SpringApplication 안에 @ComponentScan이 있기 때문)

따라서, 우리는 스프링 애플리케이션을 실행하면 AppConfig를 빈으로 등록해서 사용할 수 있다.

다음으로, @Bean

  • AppConfig 내부에서는 우리가 빈으로 설정해서 사용할 메서드를 정의한다.
  • 이 때 사용하는 것이 바로@Bean애노테이션이다.

만약, 스프링 애플리케이션을 띄우지 않는다면?

테스트 환경에서 스프링 애플리케이션을 띄우지 않는 경우, ApplicationContext를 통해서 초기화해줄 수 있다.

ApplicationContext은 스프링의 핵심 코어로 빈 관리 및 설정 및 초기화, 의존성 주입 등 스프링과 관련된 핵심 로직을 수행한다.

스프링을 띄워주는 CoreApplication에서는 기본적으로 ApplicationContext를 초기화해주고, 빈 등록 및 의존 관계 설정을 모두 수행해준다.

하지만, 스프링을 띄우지 않는 테스트 환경에서는 이와 같은 작업을 수동으로 해주어야하기 때문에 아래와 같은 과정을 거치는 것이다.

@Test
...

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

MemberService memberService = ac.getBean("memberService", MemberService.class);

간단하게 정리하자면, 수동 빈 등록을 하기 위해서 거치는 과정은 다음과 같다.

  1. 설정 클래스를 만들고 (ex) AppConfig.class)
  2. 설정 클래스에@Configuration 애노테이션 붙히기
  3. 클래스 안에 빈으로 등록할 메서드 선언
  4. 선언한 메서드에 @Bean 애노테이션 붙히기

이후 스프링을 띄우냐 안 띄우냐에 따라서
ApplicationContext를 선언할 수도 있고 안 할 수도 있다.


이번엔 자동 빈 등록에 대해서 알아보겠다.

자동 빈 등록

수동 빈 등록 과정에서는 우리가 일일히 빈으로 등록할 메서드를 선언했어야 했다.

하지만, 자동 빈 등록은 이보다 더 편리하게 사용할 수 있다.

코드로 먼저 알아보자

MemoryMemberRepository.class

@Component
public class MemoryMemberRepository implements MemberRepository{
  ...
}

MemberServiceImpl.class

@Component
public class MemberServiceImpl implements MemberService{

    private final MemberRepository memberRepository;

    @Autowired //ac.getBean(MemberRepository.class)
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
...
}

모르는 애노테이션이 많이 등장하는데 자동 의존 관계도 딱 두 가지만 알면 된다.
"@Component, @Autowired"

@Component는 어떤 애노테이션인지 알 것 같은데 그러면 @Autowired는 뭘까?

@Autowired :

  • 스프링 프레임워크에서 지원하는 애노테이션으로 의존성 주입을 스프링 빈들 간 자동으로 주입해주는 것이다.

여기서 의존성 주입은 의존관계를 설정해주는 것인데

코드로 이해해보자면

@Autowired
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

다음과 같은 코드에서 현재 인터페이스인 memberRepository의 구현체로 어떠한 것도 선언되어 있지 않다.

이게 가능한 이유는
@Autowired 애노테이션이 붙은 메서드에 한해서
memberRepository 하나로 초기화해주더라도 컨테이너에 등록된 빈을 탐색해서 적절한 구현체를 넣어주기 때문이다.

이는 예전에 포스팅으로 올렸던
스프링 Controller 및 Mapping애노테이션 이해하기 에서 분석했던
list 메서드로 쉽게 이해할 수 있다.

@GetMapping("/members")
    public String list(Model model) {
        List<Member> members = memberService.findMembers();

        model.addAttribute("members", members);
        return "members/memberList";
    }

해당 메서드에서 파라미터로 넘어오는 Model model을 스프링이 참조값으로 넘겨줬듯이 MemberServiceImpl에서도 마찬가지로 스프링이 파라미터로 MemberRepository 객체를 넘겨주게 된다.
이 때 넘겨주는 객체는 스프링 컨테이너에 등록된 빈 객체를 탐색해서 전달해준다.

실제로 스프링 컨테이너에 등록된 빈들은 다음과 같이 객체가 생성된 채로 등록되어 있기 때문에 빈 객체를 넘겨줄 수 있는 것이다.

스프링 애플리케이션을 띄우지 않는다면?
자동 빈은 설정 클래스가 필요 없지만, 스프링 애플리케이션을 띄우지 않는다면, @ComponentScan을 대신 해주는 설정 클래스가 존재해야 한다.

따라서, 스프링을 띄우지 않는 테스트 환경에서 테스트를 진행하기 위해 아래와 같은 클래스를 선언해주었다.

AutoAppConfig.class

@ComponentScan
public class AutoAppConfig {
}

이제 테스트 환경에서 이 설정 클래스를 가지고

 ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

다음과 같이 선언해서 자동 빈 등록을 진행할 수 있는 것이다.

수동 vs 자동 뭐가 더 좋나?

  • 사실 수동이 더 뛰어나고 자동이 더 뛰어나고 그런건 없다.
  • 상황에 맞게 적절하게 조합해서 사용하는 것이 유지보수 관점에서 제일 좋다.

이에 대해 애플리케이션 설계 관점으로 바라보았을 때 두 가지로 나뉘는데

업무 로직 관점

업무 로직 관점에서는 에러가 발생했을 때 어디서 발생했는 지 쉽게 찾을 수 있다.

왜냐하면, 기본적인 서비스나 레포지토리 등 일반적인 형태로 사용되는 경우 에러가 명확하기 때문이다.

따라서, 자동 빈 등록을 통해 간결하게 작성하는 게 효율적이다.

기술 지원 로직 관점

반면에, 기술 지원 로직 관점에서는
오류가 발생했을 때 어디서 발생했는 지 찾기가 매우 어렵다.

기술 지원 빈같은 경우 업무 로직과 비교해서 그 수가 매우 적은데 반해 애플리케이션에 광범위한 영향을 미치기 때문이다.

따라서, 이 경우 수동 빈 등록으로 해주는 게 관리하는 데에 용이하다.

정리

보통 실무에서는 상황에 따라 적절하게 조합해서 사용하는 게 일반적인 형태이다.

수동 빈 등록은 설정을 더 세밀하게 할 수 있고, 자동 빈 등록은 코드가 간결하고 쉽다.
따라서, 각각의 특성에 맞게 적절하게 조합해주는 게 유지보수하기에도 편하다.

profile
혼자 성장하는 것보다 함께 성장하는 것을 선호합니다.

0개의 댓글