컴포넌트 스캔과 자동 의존관계 설정

스프링을 처음 실행시킬 때, 스프링 컨테이너라는 통이 하나 생긴다.

우리가 아무렇지 않게 클래스 위에 @Controller 이런식으로 쓰는 어노테이션들은 이 클래스 객체를 컨테이너에 생성해서 넣어주고 컨테이너에서 관리하겠다는 뜻으로 쓰는 것이다.

이것을 스프링 컨테이너에서 스프링 빈이 관리된다고 표현한다.

 

스프링이 관리를 하게 되면 이제부터는 다~ 스프링 컨테이너에 등록하고 컨테이너로부터 받아서 써야한다.

즉, 컨테이너안에서 모든 것을 해결해야한다.

 

저번 시간까지 service와 repository를 만들었는데 이제 뷰를 사용자에게 보여주기 위해 controller를 만들어야한다.

Controller, Service, Repository는 정형화된 패턴이다.

Controller를 통해서 외부의 요청을 받고

Service에서 비즈니스 로직을 만들고

Repository에서 데이터를 저장한다.

 

여기서 보면 Controller는 요청을 받아서 Service를 통해 회원가입을 하고, 조회를 할 것이다. 이 때, 의존관계가 있다고 표현한다. 의존관계가 있으므로 Controller와 Service를 연결해야하고 이를 위해 생성자 위에 @Autowired라는 어노테이션을 쓰면 자동으로 연결해준다.

자동으로 연결해준다는 의미는 아래 예시 코드를 통해 보여주도록 하겠다.

package hello.helloSpring.controller;

import hello.helloSpring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class MemberController {
    private final MemberService memberService;

    @Autowired // 스프링이 뜰 때, 컨테이너에서 관리하는 memberService 객체와 연결해준다.
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

스프링이 시작될 때, 스프링은 @Controller를 보고 MemberController가 컨트롤러 역할이구나 라고 생각하고 객체를 생성하여 컨테이너에 넣는다. (==빈을 생성한다.)

이 때, Controller는 Service를 통해 기능을 수행하므로 연관 관계가 있다. 이를 위해 @Autowired를 생성자 위에 써주면 이 Controller 객체를 생성할 때 생성자를 통해 컨테이너에 존재하는 MemberService를 넣어주면서 빈이 생성되는 것이다.

한 마디로 말하자면, 스프링은 @Controller를 보고 Controller 객체를 생성하고 그 때 생성자를 호출하면서 스프링 컨테이너에 있던 MemberService를 가져다가 넣어주는 것이다. 이해 완료?!!?

 

여기서 주의할 점은 저번 시간에 한 코드에서 위의 코드를 추가한다면 에러가 난다.

에러

왜냐하면 이전 코드의 MemberService 클래스는 컨테이너에 빈으로 등록되지 않았기 때문이다. (어노테이션 안썼음)

따라서 MemberService 클래스 위에 @Service라는 어노테이션을, 똑같은 방식으로 MemoryMemberRepository 클래스 위에 @Repository어노테이션을 쓰면 스프링이 시작될 때 컨테이너에 넣어서 관리가 된다.

위와 같은 @Controller, @Service, @Repository 를  컴포넌트 스캔이라고 하며

@Autowired를 자동 의존관계 설정이라고 한다.

 


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

어노테이션을 쓰지 않고 직접 코드로 등록하기 위해 @Controller를 제외한 @Service, @Repository, @Autowired를 다 지우고 시작한다.

 

1. hello.helloSpring 밑에 SpringConfig 클래스를 만들어준다.

구조

2. 코드를 작성해준다.

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을 통해 스프링에게 알리고 @Bean을 통해 빈을 등록하겠다고 하면 된다.

MemberService를 생성할때는 memberRepository가 필요한데(연관 관계) 이때 memberRepository()를 호출해주면 된다.

 

직접 코드를 작성하는 것의 장점

현재 어떤 db를 사용할 것인지 정해져있지 않은 상황이라고 가정이 되어있다. 나중에 db가 결정되면 저 Repository만 바꿔주면 되는 것이다. 즉, 다른 코드 손 볼 필요 없이 return new dbMemberRepository()와 같은 다른 레포지토리를 생성해주면 된다.

 

재밌다!