이 글은 김영한 님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
- 컴포넌트 스캔과 자동 의존관계 설정
- 자바 코드로 직접 스프링 빈 등록하기
컴포넌트 스캔과 의존관계 설정
회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 준비한다.
회원 컨트롤러에 의존관계 추가
- @Controller
- 스프링 컨테이너에 해당 클래스 객체를 생성해 넣어둔다.
- 해당 객체는 스프링이 관리한다.
- 이를 스프링 컨테이너에서 스프링 빈이 관리된다고 표현한다.
위 이미지처럼 MemberService 객체를 new로 생성해 사용할 수 있다.
하지만, 여러 Controller들이 MemberService를 사용하게 될 것인데 굳이 여러 개의 인스턴스를 생성할 필요가 없다.
하나의 인스턴스를 공유해야 한다.
따라서 스프링 컨테이너에 하나의 인스턴스를 등록하고 스프링 컨테이너로부터 받아 쓰도록 해야한다.
- @Autowired
- 스프링 컨테이너로부터 필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입한다.
- 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라 한다.
- 생성자, setter, 필드 세 가지 경우에 @Autowired 어노테이션을 사용할 수 있다.
- 기본값이 true이기 때문에 의존성 주입할 대상을 찾지 못하면 애플리케이션 구동에 실패한다.
- 스프링 컨테이너로부터 필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입한다.
실행 결과를 살표보면 MemberService를 찾을 수 없다는 문구가 출력된다.
memberService가 스프링 빈으로 등록되어 있지 않다.
참고 : helloController는 스프링이 제공하는 컨트롤러여서 스프링 빈으로 자동 등록된다.
@Controller가 있으면 자동 등록됨
MemberService 클래스는 아무런 어노테이션도 등록되어 있지 않은 순수 자바 클래스이기에 spring이 알 수 있는 방법이 없다.
이 문제를 해결하기 위해 다음 어노테이션을 추가해야 한다.
- @Service
- 비지니스 로직을 수행하는 클래스임을 나타낸다.
- @Repository
- DataBase에 접근하는 메서드를 가지고 있는 클래스임을 나타낸다.
현재 어노테이션을 살펴보면
Controller 클래스에서는 @Controller
Repository 클래스에서는 @Repository
Service 클래스에서는 @Service
각 구조의 이름과 동일한 이름의 어노테이션을 작성한다.
이것은 매우 정형화된 패턴이다.
Controller를 통해 외부 요청(request)을 받고
Service에서 비지니스 로직을 생성하고
Repository에서 데이터를 저장한다.
spring가 Controller, Repository, Service 를 모두 알게 되었다.
이제 세 가지를 연결시켜 주어야 한다.
위에서 연결시켜 줄 때 @Autowired를 사용하면 된다 하였다.
- 현재 MemberService는 MemberRepository가 필요한 상태다.
- spring이 MemberService를 생성할 때 @Service 어노테이션을 확인하고 스프링 컨테이너에 등록한다.
- 생성자를 호출한다.
- @Autowired 어노테이션을 확인하고 스프링 컨테이너에 존재하는 MemberRepository를 주입시킨다.
-> MemberRepository의 구현체 MemoryMemberRepository를 주입시킨다.
이전 실행과 달리 정상적으로 결과가 출력되는 것을 확인할 수 있다.
스프링 빈을 등록하는 2가지 방법
- 컴포넌트 스캔과 자동 의존관계 설정
- 자바 코드로 직접 스프링 빈 등록하기
컴포넌트 스캔과 자동 의존관계 설정
우리가 위에서 수행한 @Service, @Repository, @Controller가 컴포넌트 스캔 방식에 해당한다.
컴포넌트 스캔 원리
- @Component 어노테이션이 있으면 스프링 빈으로 자동 등록된다.
- @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유 또한 컴포넌트 스캔 때문이다.
- @Component를 포함하는 다음 어노테이션도 스프링 빈으로 자동 등록된다.
- @Controller
- @Service
- @Repository
모두 @Component 애노테이션을 포함하기 때문에 컴포넌트 스캔이라 한다.
spring은 @Component 애노테이션과 관련된 클래스를 스캔하고 해당 클래스의 객체를 생성해 스프링 컨테이너에 등록한다.
그리고 @Autowired는 이미지에 나타나는 연관관계를 연결시켜준다. (자동 의존관계)
Q. 다음 이미지처럼 새로운 demo package 내부에 Demo 자바 파일을 생성하고 @Service 어노테이션을 작성하면 스프링 빈으로 등록이 될까?
A. 결론은 안된다!!
왜냐하면 현재 우리가 실행시키고 있는 파일은 hello.hellospring package 아래 HelloSpringApplication 자바 파일이기 때문이다.
hello.hellospring package를 포함해 하위 패키지들은 spring이 모두 스캔해 자바 빈에 등록해 준다.
demo 패키지는 다른 패키지이므로 spring이 스캔하지 않기 때문에 등록되지 않는다.
참고
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다.(유일하게 하나만 등록해서 공유한다.) 따라서 같은 스프링 빈이면 모두 동일한 인스턴스다.
설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 싱글톤을 사용한다.
자바 코드로 직접 스프링 빈 등록하기
- 회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제고하고 진행한다.
SpringConfig 파일 생성
- @Configuration
- 클래스에 적용하고 @Bean을 해당 클래스의 메서드에 적용하면 @Autowired로 빈을 부를 수 있다.
- @Bean
- 스프링 컨테이너에 스프링 빈을 등록하도록 하는 어노테이션
@Bean VS. @Component
@Bean : 개발자가 직접 제어 불가능한 외부 라이브러리 등을 만들때 사용
@Component : 개발자가 직접 생성한 클래스를 빈으로 등록하기 위해 사용
참고: XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
참고: DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 존재한다.
의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권한다.
setter 주입 방식의 문제점은 memberService를 주입하는 메서드가 public이라는 것이다.
이는 개발 중간에 실수로 memberService를 변경될 수 있음을 의미한다.
하지만, 생성자 주입 방식을 이용하면 MemberController 객체를 생성할 때 memberService가 주입되고 그 이후로 변경되지 않는다는 장점이 존재한다.
이때, 클래스 내부에 다른 객체를 참조하는 방식을 조립 방식이라 한다.
참고: 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다.
그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
현재 우리는 다음과 같은 요구사항을 가진다.
"아직 데이터 저장소가 선정되지 않음(가상의 시나리오)"가 존재한다.
우선 메모리로 생성하고 나중에 교체하기로 한 상태다.
그래서 MemberRepository라는 인터페이스를 생성하고 MemoryMemberRepository를 구현체로 사용하는 중이다.
향후 우리는 메모리 리포지토리를 다른 리포지토리로 변경할 예정이다.
이때, 기존 코드를 하나도 고치지 않고 변경할 수 있는 방법이 존재한다.
위 이미지처럼 MemoryMemberRepository를 DbMemoryRepository로만 변경해주면 된다.
이것이 자바 코드로 직접 스프링 빈 등록하기의 장점이다.
만약 컴포넌트 스캔 방식을 사용하면 여러 코드를 변경해야 한다.
주의: @Autowired를 통한 DI는 helloController, memberService등과 같이 스프링이 관리하는 객체에서만 동작한다.
스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
여기서는 향후 메모리 리포지토리를 다른 리포지토리로 변경할 예정이므로, 컴포넌트 스캔 방식 대신 자바 코드로 스프링 빈을 설정한다.
'스프링 > 스프링 입문' 카테고리의 다른 글
스프링 DB 접근 기술 #1 (0) | 2022.03.02 |
---|---|
회원 관리 예제 - 웹 MVC 개발 (0) | 2022.03.01 |
회원 관리 예제 - 백엔드 개발 (0) | 2022.02.28 |
스프링 웹 개발 기초 (0) | 2022.02.27 |
빌드하고 실행하기 (0) | 2022.02.27 |