Spring 입문 Chapter 4-1. 스프링 빈과 의존관계 : 컴포넌트 스캔과 자동 의존관계 설정
이 글은 인프런에 있는 김영한님의 "스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술" 강의를 듣고 정리한 필기입니다.
⛅️ 이전에 구현했던 내용들과 이번에 할 내용
이전까지 MemberRepository와 MemberService를 만들었다.
이를 통해
1. 멤버 객체를 만들기
2. 서비스를 통해서 멤버 가입
3. 리포지트리에 멤버 정보를 저장하고 꺼내는 기능
로직을 만들고 테스트 코드를 작성했다.
회원 가입하고 그 결과를 html을 통해 화면에 띄우려고 하는데, 그러러면 Controller와 View template가 필요하다.
MemberController를 만들어서 사용해야 하는데, MemberController가 MemberService를 통해서 회원 가입을 하고, 데이터 조회를 해야 한다.
이런 것을 "MemberController가 MemberService를 의존한다" 라고 표현하는데, 그 작업을 진행해보자.
⛅️ MemberController 생성
우선 Java - Controller 패키지 내에 MemberController 클래스를 생성하고, @Controller 어노테이션을 붙여준다.
@Controller
public class MemberController {
}
@Controller 어노테이션을 적으면 무슨 일이 일어날까?
spring이 실행될 때, spring container가 생긴다. 그 때, @Controller이 적혀있다면 spring container에 해당 객체를 넣어두고, spring이 관리를 한다.
이전에 했던 예제를 다시 살펴보자.
helloController 클래스를 만들었을 때에도 @Controller 어노테이션을 달아줬다.
spring이 시작할 때, @Controller 어노테이션을 보고 spring이 helloController 객체를 생성한 후 관리를 한다.
이후 Controller가 필요할 때 container에서 지정된 Controller를 사용한다.
이를 "spring container에서 spring bean이 관리된다" 라고 표현한다.
마찬가지로 MemberController도 @Controller 어노테이션을 적음으로써 container에 저장되고, spring이 관리를 하게 된다.
⛅️ Autowired을 이용하여 MemberController와 MemberService 연결하기
다음으로 MemberController가 MemberService를 사용해야 하는데, 이전처럼 new 키워드를 이용하여 MemberService 객체를 생성하여 사용할 수도 있다.
하지만 MemberService를 spring container에 등록을 해서 spring이 관리를 하도록 해야한다. spring container에 등록하면 하나의 객체만 등록이 된다. (singleton 패턴)
이렇게 해야 다른 Controller에서도 MemberService를 가져와 사용할 수 있을 것이다.
(회원 관련 함수들은 여러 군데에서 사용되기 때문!)
MemberService를 spring container에 연결하는 방법을 알아보자.
1. MemberController 클래스에서 memberService 클릭 - Alt+Enter - Add Constructor Parameter를 선택한다.
2. 생성자에 @Autowired 어노테이션을 붙여준다.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
이렇게 하면 spring이 MemberService를 spring container에 연결을 시켜 줄 것이다.
하지만 이 코드를 실행해보면 오류가 뜨는 것을 볼 수 있다.
“'Practice1.practicespring.service.MemberService' that could not be found.”
MemberService를 찾을 수 없다는 뜻이다.
@Autowired 어노테이션이 있다면 spring container에서 해당 객체를 찾은 후 연결시켜준다. 그러려면 MemberService가 spring container에 스프링 빈으로 등록이 되어 있어야 할 것이다.
하지만 MemberService는 순수한 java class이기 때문에 spring bean으로 등록되어 있지 않다.
따라서 spring이 MemberService를 알 수 있는 방법이 없기 때문에 오류가 난 것이다.
⛅️ 어노테이션을 통해 Spring Container에 등록하기
@Service와 @Repository 어노테이션이 있는데, 이를 이용하여 spring container에 등록하자.
먼저 MemberService 클래스에 가서 @Service 어노테이션을 붙여준다.
그리고 MemoryMemberRepository에는 @Repository 어노테이션을 붙여준다.
여기까지 하면 MemberController, MemberService, MemoryMemberRepository가 spring container에 등록될 것이다.
MemberService의 생성자를 살펴보면, MemberRepository를 할당받는데 @Autowired를 통해 spring container에 등록된 MemoryMemberRepository 객체를 넣어주도록 하자.
MemberController, MemberService, MemoryMemberRepository가 모두 연결될 것이다.
이렇게 해놓으면 spring이 controller, service, repository를 spring container에 등록하고, Autowired로 객체를 받아야 할 때 꺼내 올 것이다.
이후에 코드를 실행해보면 오류 없이 잘 돌아가는 것을 볼 수 있다.
Controller, Service, Repository는 정형화 되어 있는 패턴이다.
Controller를 통해서 외부 요청을 받고, Service에서 비지니스 로직을 만들고, Repository에서 데이터를 저장하는 패턴이다.
[MemberController]
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
[MemberService]
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
[MemoryMemberRepository]
@Repository
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
...
}
⛅️ 컴포넌트 스캔
Spring bean을 등록하는 두 가지 방법이 있다.
첫 번째로는 컴포넌트 스캔이고, 두 번째 방법은 자동 의존관계 설정이다.
이번시간처럼 @Controller, @Service, @Repository, @Autowired와 같은 어노테이션을 이용하는 방법을 "컴포넌트 스캔 방식"이라고 한다.
@Controller나 @Service 어노테이션 내부 코드를 보면 @Component 어노테이션이 있는 것을 알 수 있는데,
spring이 올라올 때 Component와 관련된 어노테이션이 있으면, 해당 객체를 모두 하나씩 생성해서 spring container에 등록을 하고, @Autowired를 통해 객체들을 연결해준다.
이런 과정을 통해 MemberController, MemberService, MemoryMemberRepository가 서로 연결될 수 있는 것이다.
[컴포넌트 스캔 원리]
- @Component 어노테이션이 있으면 spring bean으로 자동 등록된다.
- @Controller 컨트롤러가 spring bean으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.
- 컴포넌트 스캔 시, 객체는 기본적으로 싱글톤(singleton)으로 등록하고, 하나의 객체를 공유한다.
- 컴포넌트 스캔은 기본적으로 main 메서드가 있는 package의 하위 package들만을 스캔하고 등록한다.
[@Component를 포함하는 다음 어노테이션도 spring bean으로 자동 등록된다.]
- @Controller
- @Service
- @Repository