※ 본문은 김영한 선생님의 인프런 '스프링 핵심 원리 - 기본편' 강의를 듣고 정리한 내용임을 알립니다.
▶ @ComponentScan 사용 방법
- @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록
- @Autowired를 이용해서 의존관계를 자동 주입할 수 있음 (기본 조회 전략은 타입이 같은 빈을 찾아서 등록 / 생성자에 파라미터가 많아도 다 찾아서 주입)
- basePackages : 지정한 패키지를 탐색 시작 위치로 지정
- basePackageClasses : 지정한 클래스의 패키지를 탐색 시작 위치로 지정
→ 탐색의 default값은 @ComponentScan이 붙은 클래스의 패키지를 탐색 시작 위치로 지정
→ 권장하는 방법 : 설정 정보의 클래스의 위치를 프로젝트 최상단에 두는 것 (스프링 부트도 이 방법을 기본으로 함)
▶ ComponentScan 기본 대상
- @Component : 컴포넌트 스캔에서 사용
- @Controller : 스프링 MVC 컨트롤러로 인식
- @Service : 스프링 비즈니스 로직에서 사용
- @Repository : 스프링 데이터 접근 계층으로 인식 (데이터 계층의 예외를 스프링 예외로 변환)
- @Configuration : 스프링 설정 정보로 인식 (스프링 빈이 싱글톤을 유지하도록 추가 처리)
▶ @ComponentScan.Filter 옵션
- ANNOTATION : 기본값, 어노테이션 인식
- ASSIGNABLE_TYPE : 지정한 타입과 자식 타입 인식
- ASPECTJ : AspectJ 패턴 사용
- REGEX : 정규 표현식
- CUSTOM : TypeFilter라는 인터페이스를 구현해서 처리
▶ ComponentScan의 중복 등록과 충돌에 대해서
자동 빈 등록과 수동 빈 등록이 충돌할 수 있음
→ ConflictingBeanDefinitionException 예외 발생
→ 수동 빈이 우선권을 가지며, 오버라이딩 되었다는 로그를 남기게 됨
→ 그러려니 하고 넘어갈 수 있으나, 추후에 정말 잡기 어려운 버그를 만들 수 있음!
→ 최근 스프링 부트에서는 수동과 자동의 충돌 시 오류가 생기게 해놓았음 (굳이 하고 싶다면 application.properties를 수정해야 함)
▶ 다양한 의존관계 주입 방법
- 생성자 주입 : 생성자 호출시점에 딱 1번만 호출되는 것이 보장됨 / "불변, 필수" 의존관계에 사용됨 / 생성자가 딱 1개 있을 경우 @Autowired 생략 가능 / 가장 많이 쓰이는 방법
- 수정자(setter) 주입 : setter라 불리는 필드의 값을 변경하는 수정자 메소드를 통해서 의존관계를 주입 / "선택, 변경" 가능성이 있는 의존관계에 사용
- 필드 주입 : 코드가 간결하지만 외부에서 변경이 불가능해서 테스트가 까다로움 / DI 프레임워크가 없으면 아무것 도 할 수 없음 / 사용하지 말자! (테스트 코드나 @Configuration 클래스 정도에서만 사용)
- 일반 메소드 주입 : 한번에 여러 필드 주입 / 일반적으로 잘 사용하지 않음
=> 생성자 주입을 선택하라!
1. "불변"
- 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 변경할 일이 없다 / 오히려 변하면 안된다
- 수정자 주입의 경우 setXxx 메소드를 public으로 열어두어야 하며, 메소드를 열어두는 것은 좋은 설계 방법이 아님
2. "누락"
- 주입 데이터를 누락했을 때 '컴파일 오류'가 발생하면서 어떤 값을 필수로 주입해야 하는지 알 수 있다.
※ "컴파일 오류는 세상에서 가장 빠르고, 좋은 오류다!"
3. "final 키워드"
- final을 사용하여 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에서 막아준다.
▶ 옵션 처리
- 주입할 스프링 빈이 없어도 동작해야 할 때가 있음
- @Autowired만 사용할 경우 옵션의 기본값은 true이기 때문에 자동 주입 대상이 없으면 오류 발생
- @Autowired(required = false) : 자동 주입 대상이 없으면 호출하지 않음
- org.springframework.lang.@Nullable : 자동 주입 대상이 없으면 null 입력
- Optional<> : 자동 주입 대상이 없으면 Optional.empty 입력
▶ 롬복과 최신 트렌드
막상 개발을 해보면 대부분이 다 불변하는 요소
→ 생성자에 final 키워드를 사용하게 됨
→ 그런데 생성자도 만들어야 하고, 주입 받은 값을 대입하는 코드도 만들어야 함
→ 롬복의 등장 : @Getter, @Setter, @ToString 어노테이션 프로세싱으로 자동 생성 / @RequiredArgsConstructor를 통해 final이 붙은 필드를 모아서 생성자를 자동 생성 / 기능은 다 챙기면서 코드는 깔끔하게!
▶ 조회할 빈이 2개 이상일 때
1. @Autowired 필드명 매칭 : @Autowired는 타입 매칭을 시도하고 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭
2. @Qualifier끼리 매칭 → 없으면 빈 이름 매칭 → 그래도 없으면 NoSuchBeanDefinitionException 예외 발생
- 추가 구분자를 붙여주는 방법 (주입 시 추가적인 방법을 제공하는 것이지, 빈 이름을 변경하는 것은 아님)
- ex) @Qualifier("mainDiscountPolicy")
- 많이 헷갈릴 수 있으니 @Qualifier를 사용하는 것 끼리만 잘 묶어서 사용할 것!
3. @Primary 사용 : 우선순위를 정하는 방법 (생각보다 많이 사용됨)
=> @Primary, @Qualifier 활용
: 코드에서 메인 데이터베이스의 커넥션을 획득하는 스프링 빈이 있고 코드에서 특별한 기능으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 가정할 때, 메인 DB의 커넥션을 획득하는 스프링 빈은 @Primary를 적용해서 조회하고, 서브 DB 커넥션 빈을 획득할 때는 @Qualifier를 지정해서 명시적으로 획득하는 방식으로 하면 코드를 깔끔하게 유지할 수 있다.
※ @Primary는 기본값처럼 동작 / @Qualifier는 매우 상세하게 동작
4. Annotation 직접 만들기
- @Qualifier를 사용할 때처럼 문자를 계속 넣기보다는 편리하게 관리할 수 있게 됨
- 어노테이션에는 상속이라는 개념이 없기에 여러 어노테이션을 모아서 사용할 수 있게 해주는 스프링 기능
→ 하지만 스프링이 제공하는 기능을 뚜렷한 목적 없이 무분별하게 재정의하는 것은 유지보수에 혼란을 줄 수 있다.
5. 조회한 빈이 모두 필요할 때 (List, Map)
- 예를 들어 클라이언트가 할인의 종류(rate, fix)를 선택할 수 있다고 가정했을 때, 스프링은 전략 패턴을 간략하게 구현 가능
=> 자동 / 수동의 올바른 실무 운영 기준
: "편리한 자동기능을 기본으로 사용하자"
→ 스프링이 나오고 시간이 갈수록 자동을 선호하는 추세이며 자동 빈 등록으로도 OCP, DIP를 지킬 수 있다.
: 애플리케이션은 크게 업무 로직과 기술 지원 로직으로 분류됨
- 업무 로직 빈 : 보통 비즈니스 요구사항을 개발할 때 추가되거나 변경됨
- 기술 지원 빈 : 업무 로직을 지원하기 위한 하부 기술이나 공통 기술들
=> 업무 로직은 숫자도 매우 많고, 어느 정도 유사한 패턴이 존재하므로 자동기능을 적극적으로 사용하여 문제 발생지점 파악이 용이하도록
=> 기술 지원 로직은 애플리케이션 전반에 걸쳐 광범위하게 영향을 미치므로 적용이 잘 되고 있는지 아닌지 파악이 어려울 수 있으니 수동 빈 등록을 사용해서 명확하게 드러내는 것이 좋다.
=> 무엇보다도 스프링 자체를 잘 이해하고 스프링의 의도대로 잘 사용하는 것이 중요!
'JVM > Spring' 카테고리의 다른 글
싱글톤 빈과 프로토타입 스코프 (0) | 2021.03.25 |
---|---|
빈 생명주기 콜백 (0) | 2021.03.25 |
싱글톤 컨테이너 (0) | 2021.03.24 |
스프링 컨테이너와 스프링 빈 (0) | 2021.03.23 |
AppConfig와 IoC, DI에 관하여 (0) | 2021.03.22 |