이젠 컴포넌트 스캔에서 스프링 빈을 하나 하나 등록하는 방식으로 바꿔보겠습니다.

컴포넌트 스캔으로 자동으로 인식하는 방법도 있고, 직접 설정 파일에 등록할 수도 있습니다.

자바 코드로 직접 스프링 빈 등록하기

회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행하겠습니다. (제거하고 실행하면 MemberService가 스프링 빈에 등록이 안되어있으니 에러가 터집니다.)

SpringConfig 클래스를 만듭니다.

Untitled

package hello.hellospring;

import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

이렇게 하면 스프링이 뜰 때 @Configuration 읽고 스프링 빈에 등록하란 뜻이라고 스프링이 인식합니다.

그래서 memberService()의 로직을 호출해서 MemberService를 스프링 빈에 등록합니다.

그런데 생성자에서 MemberRepository를 넣어줘야합니다.

그래서 MemberRepository를 @Bean으로 만들어주고 MemberService의 파라미터로 memberRepository를 넣어줍니다.

그러면 MemberService와 MemberRepository를 둘다 스프링 빈에 등록을 하고 스프링 빈에 등록되어 있는 memberRepository를 MemberService에 넣어줍니다.

이렇게 아래의 그림이 완성 됩니다.

Untitled

MemberService와 MemberRepository를 스프링 올라올 때 @Bean 을 보고 스프링 빈을 컨테이너에 올리고 MemberService는 memberRepository()를 호출했으니 스프링 빈에 등록된 MemberRepository를 딱 넣어줍니다.

그리고 컨트롤러는 그냥 두면 어차피 스프링이 관리하는 것이기 때문에 컴포넌트 스캔으로 올라갑니다. 컴포넌트 스캔이기 때문에 @Autowired로 해주면 됩니다. 그럼 빈으로 등록한 memberService를 넣어줍니다.

그럼 실행을 해보겠습니다.

Untitled

당연히 컴포넌트 스캔으로 하는게 편할겁니다.

하지만 각각 장단점이 존재합니다.

<aside> ✏️ [참고]

과거에는 자바 코드로 설정하지 않고 XML이라는 문서로 설정을 했습니다. 지금은 거의 XML을 실무에서 사용하지 않습니다.

</aside>

<aside> ✏️ [참고]

DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있습니다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장합니다.

지금까지 했던 방식을 생성자 방식이라고 합니다.

필드 주입, setter 주입, 생성자 주입 자세히

필드 주입은 바꿀 수 있는 방법이 없어서 안좋습니다.

필드 주입 단점

setter 주입은 MemberController를 호출 했을 때 setter가 public으로 열려 있어야합니다.

잘못 바꾸면 문제가 생길 수 있습니다. 그리고 보통 한번 세팅이 되면 바꿀일이 잘 없습니다.

setter 주입 단점

</aside>

<aside> ✏️ [참고]

실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용합니다.

그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록합니다.

MemberRepository를 만들었는데 아직 데이터 저장소가 결정되지 않았다는 가상의 시나리오가 있었습니다. 그래서 일단 Memory로 만들고 나중에 교체하기로 했습니다.

그렇기에 MemoryRepository 인터페이스를 쓰고 MemoryMemberRepository 구현체를 만들었습니다.

나중에 MemoryMemberRepository를 다른 것으로 바꿔치기 할 것 입니다. 이 때 기존의 운영중인 코드를 하나도 손대지 않고 바꾸기 위해서 인터페이스를 구현했습니다.

즉, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다는 의미는 설정 파일만 바꾸면 된다는 의미입니다.

자세한 내용은 스프링 핵심 원리 - 기본편을 참고

</aside>