본문 바로가기
Java/Spring입문

Spring 입문(8) 스프링 빈과 의존관계-컴포넌트 스캔(@애너테이션)

by 이쟝 2023. 5. 3.
인프런의 김영한님의 스프링입문 강의를 듣고 정리한 내용입니다.

*스프링 빈을 등록하고, 의존관계 설정하기*

이제 화면에 보이게 하려면 Controller와 View 템플릿이 필요하다.

그러려면 회원 컨트롤러가 회원서비스(MemberService)와 회원레포지토리(MemberRepository)를 사용할 수 있게 해야 한다. 이것을 의존관계를 준비한다고 한다! 

의존관계(회원 컨트롤러가 회원 서비스를 의존한다): 회원 컨트롤러가 회원 서비스를 통해서 회원가입을 하고 회원 서비스를 통해서 데이터를 조회할 수 있어야 한다. 

*회원 컨트롤러에 의존관계 추가하기*

1. controller 프로젝터(폴더)에 MemberController.java 클래스 생성

src > main> java > hello > hellospring > controller > MemberController.java
package hello.hellospring.controller;

import org.springframework.stereotype.Controller;

@Controller
public class MemberController {

}
- 이렇게 하면 스프링이 처음 실행될 때, 스프링 컨테이너가 생기는데 그곳에 @Controller 어노테이션이 붙으면  이 MemberController를 객체로 생성해서 스프링에 넣어두고 스프링이 관리를 한다. 
-> "이것을 스프링 컨테이너에서 스프링 빈이 관리 된다"고 표현한다.

 

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
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }
}
- 생성자에 `@Autowired`가 있으면 객체 생성 시점에 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존 관계를 외부에서 넣어주는 것DI(Dependency Injection), 의존성 주입이라고 한다. 
- 이전 테스트에서는 개발자가 직접 주입한 것이고, 여기서는 @Autowired에 의해 스프링이 주입해준다.

- 스프링이 실행될 때, 이 MemberController가 생성이 되면서 생성자를 호출하게 되는데, 생성자에 @Autowired가 있어서 스프링이 생성자에 있는 MemberService를 스프링 컨테이너에서 MemberService를 가져다 연결시켜준다. 

참고: 생성자가 1개만 있으면 `@Autowired`는 생략할 수 있다. 

@Controller
public class MemberController{
    private final MemberService memberService = new MemberService();
}

 

- 이렇게 new키워드로 생성해서 사용할 수도 있지만, 스프링이 관리하게 되면 이제 스프링 컨테이너에 등록하고, 스프링 컨테이너로부터 받아서 쓰도록 바꿔야 한다. 

- new 키워드로 생성해서 사용하게 되면 MemberController 이외에도 다른 여러 Controller들이 이 memberService를 가져다 사용할 수 있기 때문에 그때마다 new로 객체를 여러 개 생성하면 복잡해진다. 

- 또한 MemberService에도 구현된 기능이 별로 없어서 굳이 여러 개의 객체로 만들기 보다는 하나를 만들고 공유하는 게 좋다. 

-> 그래서 new로 객체를 생성하는 것보다는 스프링 컨테이너에 하나만 등록을 하고 사용하면 된다. 

 

* 오류 발생! *

Consider defining a bean of type 'hello.hellospring.service.MemberService' in your configuration.
MemberService가 스프링 빈으로 등록되어 있지 않은 것이다!!!

MemberService는 그냥 순수한 자바 클래스이기 때문에 스프링이 인식을 하지 못한다.

그래서 @Service 애너테이션을 MemberService에 작성한다.
-> 스프링이 실행될때 MemberService의 @Service 애너테이션을 보고 스프링이 스프링 컨테이너에다가 MemberService를 등록해준다. 

 

참고로 helloController는 스프링이 제공하는 컨트롤러이기 때문에 스트링 빈으로 자동 등록된다. 
`@Controller`가 있으면 자동 등록됨

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

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

1. 컴포넌트 스캔과 자동 의존관계 설정하기

*회원 서비스 스프링 빈 등록*

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
- 스프링이 실행될 때, 이 MemberService가 생성이 되면서 생성자를 호출하게 되는데, 생성자에 @Autowired가 있어서 스프링이 생성자에 있는 MemberRepository를 스프링 컨테이너에서 MemberRepository
(MemoryMemberRepository)를 가져다 연결시켜준다. 

*회원 레포지토리 스프링 빈 등록

: Repository를 연결시켜 주기 위해 회원레포지토리의 구현체에 가서 @Repository 추가하기*

@Repository
public class MemoryMemberRepository implements MemberRepository{

 

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

@Component 이 애너테이션이 있으면 스프링 빈으로 자동 등록된다.
@Controller 컨트롤러가 스프링 빈으로 자동 등록되는 이유도 컴포넌트 스캔 때문이다.
@Conponent를 포함하는 다음 애너테이션도 스프링 빈으로 자동 등록된다. (@Controller, @Service, @Repository)
이렇게 애너테이션을 달아서 스프링 빈을 등록하는 방식 => 컴포넌트 스캔방식

*스프링 빈 등록 이미지*

`memberService`와 `memberRepository`가 스프링 컨테이너에 스프링 빈으로 등록되었다. 

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


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

- 기본적으로 HelloSpringApplication이 있는 패키지 안에서만 스프링이  하위 폴더에서부터 스프링 빈으로 컴포넌트 스캔을 하기 때문에 패키지가 아닌 곳에서는 스프링 빈으로 컴포넌트 스캔을 하지 않는다.