스프링 빈 등록과 의존관계 설정 - 컴포넌트 스캔과 자동 의존관계 설정

장서연·2022년 1월 2일
0

스프링 입문

목록 보기
14/25

지금까지, Service 와 Repository, Member 객체를 만들었다.
Service 를 통해 멤버가 join할 수 있고, 리포지토리에 저장되고, 또 리포지토리에서 꺼내올 수 있고.. 이런 로직들을 만들었다. 테스트도 만들고...

이제는, 화면을 좀 붙이고 싶은데, 이를 위해서는 컨트롤러와 뷰 템플릿이 필요하겠다.

컨트롤러

멤버가 멤버서비스를 통해 회원가입하고, 멤버서비스를 통해 데이터를 조회할 수 있어야 한다. 이렇게 의존관계가 있게 해야 함!

스프링스럽게 가보자고!

먼저 멤버컨트롤러부터 만들어보자.

controller/MemberController.java

@Controller
public class MemberController {
    
}

이렇게 해 놓으면, 비록 지금은 아무것도 써넣은게 없지만, 어떤 일이 생기냐면
스프링이 처음에 뜰 때, 스프링 컨테이너라는 스프링 통이 생기는데, @Controller 이 애노테이션이 있으면, 그 클래스(지금은 MemberController)의 객체를 생성해서 넣어둔다. 그리고 스프링이 관리한다.

처음에 봤었던 HelloController 도 마찬가지이다. @Controller라는 애노테이션이 붙어있으니,
처음에 스프링이 뜰 때 HelloController 객체를 생성하여 스프링이 딱 들고 있다.

이것을 멋진 말로

"스프링컨테이너에서 스프링 빈이 관리된다"

라고 한다.

아래 그림을 보자. 녹색 땅콩 모양의 HelloController 스프링 빈이 보인다.

이제 MemberController 도 위 녹색 땅콩모양처럼 스프링 빈이 되어 관리될 것이다.

@Controller
public class MemberController {
	// MemberService 를 가져다 쓰게 해야함
	private final MemberService memberService = new MemberService();
    

}

MemberController 에서 MemberService 를 가져다 쓰게 해야한다.
그런데 스프링에서 관리하게 하려면, 스프링 컨테이너에 등록을 해아하고, 스프링 컨테이너로부터 받아서 쓰도록 바꿔야 한다.

위처럼 new 로 MemberService를 생성하면 어떤 문제가 있을까?

바로, MemberController 말고도 다른 여러 컨트롤러들이 MemberService를 가져다 쓸 상황이 올텐데, 얘는 여러개 생성되기보다는 하나로 생성되어 공유되어야 할 객체인데 이렇게 new 로 생성을 해버리면 안된다!

그래서, new 로 생성하는 것보다, 스프링 컨테이너에 등록해버리자. 스프링 컨테이너에 등록하면 딱 하나만 등록되기 때문이다. 등록하고쓰자!

@Controller
public class MemberController {
	private final MemberService memberService;
    
    @Autowired
    public MemberController(MemberService memberService){ //생성자로!
    	this.memverService = memberService;
    }

MemberController는 어쨌든 스프링이 뜰 때 생성자를 호출해서 객체화된다. 생성자 위에 @Autowired가 붙어있으면, memberService가 스프링 컨테이너에 있는 것으로 연결시켜준다.

여기까지 하고 실행시켜보면, 다음과 같은 에러가 발생한다.

com.hello.hellospring.controller.MemberController required a bean of type 'com.hello.hellospring.service.MemberService' that could not be found.

MemberService를 찾을 수 없다고 한다.
@Autowired 는 스프링 컨테이너에서 memberService를 가져오는데, 현재 memberService가 스프링빈으로 등록되어있지 않기 때문에 에러가 발생했다.

현재 상황으로는, HelloController는 스프링 컨테이너에 등록되어있지만, memberService는 스프링빈으로 등록하지 않았다.

@Autowired가 있어서, memberService를 컨테이너에서 찾아보려했지만 등록하지 않아 당연히 찾을 수 없음.

MemberService.java 클래스를 보면 아래처럼 되어있다.

순수한 자바클래스...
스프링이 얘를 알 수 있는 방법이 없다.

이 MemberService 를 스프링이 알게 하려면,

@Service 애노테이션을 붙여주면 스프링이 컨테이너에 등록해준다

그리고, 구현체인 MemoryMemberRepository.java에 가서도,

@Repository 를 붙여주자.

Controller, Service, Repository 굉장히 정형화된 패턴이다. 컨트롤러를 통해서 외부 요청을 받고, 서비스에서 비즈니스 로직을 만들고, 리포지토리에서 데이터를 저장하고...

이렇게 설정을 해놓으면, 스프링이 뜰 때 컨트롤러와 서비스, 리포지토리를 쫙 가지고 올라온다.

MemberController가 생성이 될 때, @Autowired가 붙어있으므로 스프링 빈에 있는 memberService를 가지고 와서 뾱 하고 넣어줌. 이것이 바로 Dependency Injection이다.
의존관계주입, 즉 뭔가 밖에서 넣어준 것! 스프링이 뾱하고 넣어주는 것이다.


MemberService 도 마찬가지이다. MemberService의 경우 memberRepository가 필요하기 때문에,

@Service가 붙은 MemberService를 본 스프링은, 생성자를 통해 생성할 것이고, 매개변수에 memberRepository가 있고 또 @Autowired가 붙어있으므로 컨테이너를 뒤져서 그곳에 있는 memberRepository를 넣어줄 것이다.

현재는 MemoryMemberRepository로 구현체가 되어있는데, 이 구현체를 서비스에 뾱 하고 주입을 해줄 것이다.

이렇게 의존관계를 잘 설정해주고 다시 메인메서드를 실행시켜보면, 스프링이 컨테이너를 잘 만들어서 오류 없이 잘 뜬 것을 확인할 수 있다.

현재 MemberController관련해서 어떤 기능도 추가한 것이 없다. 그냥 연결만 했다. 연결은 성공~
MemberController.java


@Controller
public class MemberController {
    private MemberService memberService;
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }
}

스프링 빈을 등록하는 2가지 방법

  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록하기

방금 @Controller, @Service, @Repository 한 것 이게 바로 컴포넌트스캔 방식이다. 왜 컴포넌트 스캔이냐면, 각 애노테이션을 들어가보면 모두 @Component가 다 내장되어있기 때문.

스프링이 처음 시작될 때, @Component라는 어노테이션이 붙어있으면 클래스의 경우 모두 객체화하여 스프링 컨테이너에 등록을 한다.

@Autowired는 아래처럼 선을 연결하는, 즉 연관관계를 설정해주는 역할을 한다.

그래서, MemberController가 MemberService를 쓸 수 있게 해주고, MemberService가 memberRepository를 쓸 수 있게 되는 것이다.

이것이 바로 컴포넌트 스캔과 자동의존관계 설정 방법!

정리

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

  • @Component애노테이션이 있으면 스프링 빈으로 자동 등록된다
  • @Controller 가 스프링 빈으로 자동등록된 이유도 컴포넌트 스캔 때문이다
  • @Component를 포함하는 애노테이션( @Controller,@Service,@Repository )도 스프링빈으로 자동등록된다

스프링을 쓰면, 웬만한건 다 스프링빈으로 등록해서 써야한다. 그렇게 해야 얻는 이점들이 많음

  • 생성자에 @Autowired 를 사용하면 객체 생성시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다. 생성자가 1개만 있으면 @Autowired는 생략할 수 있다

참고

현재, hello/hellospring/HelloSpringApplication.java에 메인메서드가 있고, 여기서 프로젝트를 실행시키고 있다. 근데, hello/hellospring 와 동일하거나, 하위가 아닌 demo 같은데에 @Component한 것들은 스프링빈으로 컴포넌트 스캔하지 않는다.

물론 뭐 따로 설정해주면 되긴하지만 기본적으로는 컴포넌트 스캔에 포함되지 않는다.

@SpringBootApplication을 까보면, @ComponentScan 이 있다!

스프링은 스프링 컨테이너에 스프링빈을 등록할 때, 기본으로 싱글톤으로 등록한다.(유일하게 하나만 등록해서 공유) 따라서 같은 스프링빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.

0개의 댓글