회원가입을 하고, 결과물을 html로 뿌려주는 등 웹 어플리케이션을 잘 만들기 위해서 MVC 모델이 당연히 필요하다.
Controller는 MemberService를 통해서 회원가입을 하고, MemberService를 통해서 DB를 조회할 수 있어야 한다. 해당 상황을 의존관계에 있다고 표현한다.
각 클래스마다 다음과 같이 작성하여 대신할 수도 있다.
@Controller
public class MemberController {
private final MemberService memberService = new MemberService();
}
하지만 이러한 코드는 효율적이지 못하다.
다른 Controller가 MemberService를 가져다 쓸 수 있는데, 굳이 매번 controller에게 해당 클래스를 인스턴스화할 필요가 없다는 뜻이다.
즉 한번만 스프링 컨테이너에게 단 한번만 해당 객체를 등록하면 해결이 가능하다.
이게 스프링의 장점이다.
스프링 Container가 작동할 때, Controller 생성자를 호출한다. 이 때, 생성자에 Autowired Annotation이 붙게되면 해당 객체를 스프링 컨테이너에서 가져와서 연결을 시켜준다.
하지만
해당 Autowired 어노테이션으로 연결하기 위해선 전제가 필요하다.
당연한 것이, 해당 클래스가 스프링 컨테이너에 등록이 되어야하는 것 아니겠는가.
먼저 해당 코드는 다음과 같다.
package Goat.CouponCheck.controller;
import Goat.CouponCheck.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 어노테이션의 전제가 빠졌기 때문이다.
MemberService 클래스는 전적으로 자바 클래스일 뿐, 스프링은 해당 클래스가 자신이 관리해야할 객체인지 알 턱이 없다.
따라서 해당 클래스에도 어노테이션이 필요하다. - @Service
해당 어노테이션을 이용하여 스프링은 스프링 컨테이너에 어노테이션이 적용된 클래스를 등록한다.
Repository에 해당하는 클래스도 @Repository로 어노테이션을 달아서, 스프링이 해당 클래스들을 관리하게 만들자. (다만 interface가 아닌 구현체에 등록할것 !)
어노테이션을 다 달아주면, Autowired가 적용되어서, 스프링 컨테이너에서 어노테이션된 클래스와 상위 클래스를 연결시켜준다. (Dependency Injection)
사실은 @Repository @Service 안나눠도, @Component라고 퉁쳐도 된다.
그래서 해당 작업을 컴포넌트 스캔이라고 한다.
이때 컴포넌트 스캔 대상은, 해당 디렉토리 위치 혹은 하위폴더까지만 스캔한다
해당 작업을 실행하기 전에 앞서 이전에 적었던 Component Annotation에 해당하는 것들은 지우고 시작한다. (다만 Controller는 지우지 않아야 겠다.)
해당 작업을 하기 위해서 SpringConfig 라는 클래스를 만들었다.
바로 코드부터 보자.
package Goat.CouponCheck;
import Goat.CouponCheck.repository.MemoryRepository;
import Goat.CouponCheck.repository.Repository;
import Goat.CouponCheck.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(repository());
}
@Bean
public Repository repository(){
return new MemoryRepository();
}
}
보면 크게 어려운 것은 없다.
MemberService 클래스에서 Injection 방식으로 생성자를 만들었으므로, repository를 파라미터로 가져오는 것은 당연하다.
해당 코드는 Bean 어노테이션으로 두가지 생성자를 스프링 빈에 등록시킨다. 그리고 MemberService는 repository를 파라미터로 인자를 받았기 때문에 Autowired와 비슷한 양상으로 연결이 된다. (생성자 주입)
하지만 Controller는 어쩔수 없이 어노테이션을 사용해야겠다. 따라서 Controller 측에서 autowired는 필요함.
정형화 되지 않거나, 상황에 따라 구현 클래스를 바꿔치기 해야할 때 사용한다.
현재 상황에서는 DB가 선정되지 않아서, repository 구현체를 따로 만드는 상황이기 때문에 직접 등록하는 편이 좋다.