글에서 나오는 모든 코드와 사진들은 김영한님의 인프런 스프링 입문 강의에서 가져온 것임을 미리 알립니다.
스프링 빈과 의존관계
컴포넌트 스캔과 자동 의존관계 설정
컨트롤러가 서비스를 통해서 기능을 동작하는 것을 의존관계가 있다고 표현한다. ( 컨트롤러가 서비스를 의존한다. )
MemberController 만들어주기
스프링은 @Controller라는 어노테이션을 보고 스프링이 작동할 때 해당 컨트롤러 객체를 생성해서 가지고 있는다.
=> "스프링 컨테이너에서 스프링 빈이 관리된다" 라고 표현한다.
객체를 선언할 때 다음과 보통 같이 new를 사용해서 사용한다.
private final MemberService memberService = new MemberService();
그러나 스프링에서는 모두 스프링 컨테이너에 등록을 하고, 사용할 때는 컨테이너로부터 받아서 사용하도록 수정해야한다.
왜? 만약 new를 사용해서 구현한다면 여러개의 컨트롤러가 매번 서비스 객체를 생성해서 사용하게 된다. 즉, 여러개의 서비스 객체가 사용된다. 그러나 서비스 객체는 하나만 있으면 충분하기 때문에 모든 컨트롤러에서 사용하는 서비스 객체는 동일하도록 수정해야한다.
모든 컨트롤러에서 사용하는 서비스 객체가 같게 하기 위해서 스프링 컨테이너에 서비스객체를 등록해주면 된다.
@Controller
public class MemberController {
private MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
여기서 @Autowired를 사용하는데 생성자에 이 어노테이션이 있으면 인자로 받은 값을 스프링 컨테이너에 있는 memberService에 연결을 시켜준다.
그러나 위의 코드만 작성해주면 에러가 발생한다. 왜냐면 memberService 클래스는 현재 순수한 자바 클래스이고 스프링이 인식할 수 있는 방법이 없다.
그래서 서비스 클래스위에 @Service 어노테이션을 사용해서 스프링이 인식할 수 있도록 해줘야한다. Repository 클래스도 동일하게 @Repository 어노테이션을 붙여준다.
컨트롤러 - 서비스 - 리포지토리 구조를 보면 다음과 같다.
순서대로 보면
MemberController의 생성자에 @Autowired가 되어있으므로 생성이 될 때 스프링 컨테이너에 있는 memberService 객체를 클래스 변수인 memberService안에 주입을 해준다.
이러한 구조를 DI(의존관계를 주입) 라고 한다. DI에는 3가지 방법이 있는데 아래에서 추가로 설명할 예정이다.
@Controller
public class MemberController {
private MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
다음으로 MemberService에 가서도 똑같이 생성자에 @Autowired 어노테이션을 추가해주고 같은 방식으로 자동으로 스프링 컨테이너에 등록되어있는 memberRepository를 가져와서 등록해준다.
//다음과 같이 외부에서 객체를 생성해서 넣어주는 것을 Denpendency Injection(DI)라고 함.
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
정리해보면 @Controller, @Service, @Repository 등의 어노테이션을 사용해서 스프링 컨테이너에 등록하고,
@Autowired 어노테이션을 사용해서 등록된 객체를 가져와서 사용할 수 있다.
스프링 빈을 등록하는 방법에는 2가지가 있다. 두가지 모두 알아야한다.
1. 컴포넌트 스캔과 자동 의존관계 설정
: 방금 위에서 작업했던 일이 컴포넌트 스캔 방식이다. 우리가 쓴 코드는 Controller, Service, Repository인데 왠 컴포넌트? 라고 생각할 수 있는데 각 어노테이션을 ctrl를 누른채 클릭해보면 @Component가 적혀있는 것을 볼 수 있다.
컴포넌트 스캔의 범위는 스프링 프로젝트의 메인 함수가 있는 HelloSpringApplication으로 가보면 위에 패키지가 적혀있다.
package hello.hellospring;
이 패키지를 기준으로 이 패키지와 동일 선, 또는 하위에 있는 파일들만 스캔에서 컴포넌트를 등록한다.
그리고 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본적으로 싱글톤으로 등록한다. 설정을 통해서 싱글톤이 아니도록 설정해줄 수도 있지만 대부분은 싱글톤을 사용한다.
2. 자바코드로 직접 스프링 빈 등록하기
다음 목차에서 계속..
자바 코드로 직접 스프링 빈 등록하기
컴포넌트 스캔이 아닌 자바 코드를 작성해서 수동으로 빈을 등록하는 방법이다. 위에서 작성한 @Service, @Repository, @AutoWired 어노테이션을 제거해준다.
hello.hellospring 패키지 안에 SpringConfig라는 클래스를 만들어준다.
그리고 클래스 위에는 @Configuration 어노테이션을 작성해주고, @Bean 어노테이션을 사용해서 Bean을 등록해줄 수 있다.
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
위와 같이 작성해주면 MemberService와 MemberRepository를 빈으로 등록한다.
두 개의 방법에는 각 장단점이 있는데 설명하기 전 DI의 3가지 방법에 대해서 작성해보려고 한다.
1. 생성자 주입
private MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
위에서 계속 작성했던 방식으로 생성자를 통해서 주입이 된다.
2. 필드 주입
@Autowired
private MemberService memberService;
필드 주입은 바로 가져와서 사용하기 때문에 중간에 바꿔치기 할 수 있는 방법이 없다.
3. setter 주입
private MemberService memberService;
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
빈은 조립시점에 한번 세팅하고 그 후에는 이제 값이 변경되지 않는 것이 좋은데, public 메소드이기 때문에 한번 세팅한 이후에도 계속해서 값을 변경할 수 있다는 단점이 있다.
결론은 생성자 주입이 제일 좋다!
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화되지 않거나, 상황에 따라 구현 클래스를 변경해야하면 설정을 통해 스프링 빈으로 등록한다.
예를 들면 아까 DB가 정해지지 않았고, 추후에 변경한다는 가상의 시나리오가 있었는데 그 상황에서 직접 자바코드로 빈을 등록하면 매우 편리하게 코드를 작성할 수 있다.
이전까지는 MemoryMemberRepository 를 사용했는데 만약 DBRepository 를 사용하고 싶다면
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
이 부분을
@Bean
public MemberRepository memberRepository(){
return new DBRepository();
}
로 바꿔주기만 하면 된다.
'개발 공부 > Spring' 카테고리의 다른 글
[SpringBoot] 스프링부트 프로젝트 세팅하기 (0) | 2023.01.25 |
---|---|
[Spring] 인프런 스프링 입문 강의 정리 #5 (0) | 2022.06.26 |
[Spring] 인프런 스프링 입문 강의 정리 #3 (0) | 2022.06.23 |
[Spring] JAVA에서 Spring으로 변환 정리 (개인 공부 정리용) (0) | 2022.04.22 |
[Spring] 인프런 스프링 입문 강의 정리 #2 (0) | 2022.04.17 |