37DAYS) Spring Framework 핵심 개념(2) - DI (Dependency Injection)

nacSeo (낙서)·2022년 12월 9일
0

◉ 학습목표

1. Java 기반 컨테이너 설정에 대해 익히고, Component Scan에 대해 이해할 수 있다.
2. 다양한 의존관계 주입 방법과 DI를 종합적으로 실습하며 익힐 수 있다.
  1. DI

⦿ 학습내용

☞ 자바 기반 설정 가장 중요한 애너테이션 2가지

✔︎ @Configuration : 클래스 레벨에 사용
✔︎ @Bean : 메서드 레벨에 사용
✔︎ 메서드가 Spring 컨테이너에서 관리할 새 객체를 인스턴스화, 구성 및 초기화한다는 것을 나타낼 때 사용
✔︎ 외부 라이브러리에 사용

☞ AnnotationConfigApplicationContext를 사용하여 스프링 컨테이너를 인스턴스화하는 방법

✔︎ 애너테이션을 이용해 Config 클래스 설정하는 방법

  • ApplicationContext 구현은 아래와 같은 애너테이션이 달린 클래스로 파라미터를 전달 받음
    • @Configuration 클래스
    • @Component 클래스
    • JSR-330 메타데이터
  • @Configuration : 클래스 자체가 Bean 정의로 등록되고 클래스 내 선언된 모든 @Bean 메서드도 Bean 정의에 등록
  • @Component, JSR-330 : 빈 정의로 등록, 필요한 경우 해당 클래스 내에서 @Autowired 또는 @Injection과 같은 DI 데이터가 사용되는 것으로 가정

☞ Java 코드에서 애너테이션을 사용해 Spring 컨테이너를 구성하는 방법

✔︎ @Import 애너테이션 사용
✔︎ 추가한 @Bean 애너테이션에서 의존성 주입

☞ Component Scan

✔︎ 설정 정보없이 자동으로 스프링 빈을 등록하는 스프링 기능
✔︎ @Component가 붙은 모든 클래스를 스프링 빈으로 등록해주기 때문에 설정 정보에 붙여주면 됨
✔︎ 의존 관계도 자동으로 주입하는 @Autowired 기능 제공
✔︎ @ComponentScan : @ComponentScan이 등록된 곳에서 @Component를 가져오기 위해 사용
✔︎ @Autowired : 생성자 의존성 주입에 필요한 설정 정보 대신 의존관계 자동 주입
✔︎ 기본 범위 : 동일 및 하위 패키지

☞ basePackages

✔︎ 탐색할 패키지의 시작 위치를 지정하고, 해당 패키지부터 하위 패키지 모두 탐색
✔︎ @ComponentScan()의 매개변수로 basePackages=""를 줄 수 있음
✔︎ 지정하지 않으면, @ComponentScan()이 붙은 설정 정보 클래스의 패키지가 시작 위치가 됨

☞ 컴포넌트 스캔 기본 대상

✔︎ @Component : 컴포넌트 스캔에 사용
✔︎ @Controller & @RestController : 스프링 MVC 및 REST 전용 컨트롤러에서 사용
✔︎ @Service : 스프링 비즈니스 로직에서 사용

  • 특별한 처리 ❌
  • 핵심 비즈니스 로직 위치를 알려줌으로써 비즈니스 계층을 인식하는데 도움을 줌

✔︎ @Repository : 스프링 데이터 접근 계층에서 사용

  • 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환

✔︎ @Configuration : 스프링 설정 정보에서 사용

  • 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리

☞ 필터

✔︎ includeFilter : 컴포넌트 스캔 대상을 추가로 지정
✔︎ excludeFilter : 컴포넌트 스캔에서 제외할 대상을 지정
✔︎ FilterType 옵션

  • ANNOTATION : 기본값으로써 애너테이션으로 인식해서 동작
  • ASSIGNABLE_TYPE : 지정한 타입과 자식 타입을 인식해서 동작
  • ASPECTJ : AspectJ 패턴 사용
  • REGEX : 정규 표현식 나타냄
  • CUSTOM : TypeFilter라는 인터페이스를 구현해서 처리

☞ 다양한 의존관계 주입 방법

✔︎ 생성자 주입

  • 생성자를 통해 의존 관계를 주입 받는 방법
  • 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입
  • 특징
    • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장
    • 불변과 필수 의존 관계에 사용
    • 생성자가 1개만 존재하는 경우 @Autowired 생략해도 자동 주입
    • NullPointerException 방지
    • 주입받을 필드를 final로 선언 가능
  • 예제
@Component
public class CoffeeService {
	private final MemberRepository memberRepository;
    private final CoffeeRepository coffeeRepository;
    
    @Autowired
    public OrderServiceImpl (MemberRepository memberRepository, CoffeeRepository coffeeRepository) {
    	this.memberRepository = memberRepository;
        this.coffeeRepository = coffeeRepository;
    }
}

✔︎ 수정자 주입 (setter 주입)

  • setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존 관계를 주입하는 방법
  • 특징
    • 선택과 변경 가능성 있는 의존 관계에 사용
    • set 필드명 메서드를 생성하여 의존 관계를 주입
    • @Autowired를 입력하지 않으면 실행 ❌
    • @Component가 실행하는 클래스를 스프링 빈으로 등록
    • 스프링 빈으로 등록한 다음 의존 관계를 주입 (@Autowired에 있는 것들을 자동 주입)
  • 예제
@Component
public class CoffeeService {
	private final MemberRepository memberRepository;
    private final CoffeeRepository coffeeRepository;
    
    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
    	this.memberRepository = memberRepository;
    }
    
    @Autowired
    public void setCoffeeRepository(CoffeeRepository coffeeRepository) {
    	this.coffeeRepository = coffeeRepository;
    }
}

✔︎ 필드 주입

  • 필드에 @Autowired를 붙여 바로 주입하는 방법
  • 특징
    • 코드가 간결해 많이 사용했었지만, 외부에서 변경이 불가해 테스트하기 힘들다는 단점 존재
    • DI 프레임워크가 없으면 ❌
    • 실제 코드와 상관없는 특정 테스트를 하고 싶을 때 사용
    • 결국 setter가 필요해서 수정자 주입을 사용하는 게 더 편리
  • 예제
@Component
public class CoffeeService {
	@Autowired
    private MemberRepository memberRepository;
    @Autowired
    private CoffeeRepository coffeeRepository;
}

✔︎ 일반 메서드 주입

  • 일반 메서드를 사용해 주입하는 방법
  • 특징
    • 한 번에 여러 필드를 주입 받을 수 있음
    • 일반적으로 사용 ❌

☞ 옵션 처리

✔︎ 주입할 스프링 빈이 없을 때 동작해야하는 경우
✔︎ @Autowired만 사용하는 경우 required 옵션 기본 값인 true가 사용되어, 자동 주입 대상이 없으면 오류가 발생할 수 있음
✔︎ 스프링 빈을 옵셔널하게 해둔 상태에서 등록되지 않고, 기본 로직으로 동작하는 경우
✔︎ 자동 주입 대상 옵션 처리 방법

  • Autowired(required=false) : 자동 주입할 대상이 없으면, 수정자 메서드 자체가 호출되지 않게 함
  • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면, null이 입력됨
  • Optional<> : 자동 주입할 대상이 없으면, Optional.empty이 입력됨

☞ 생성자 주입 사용 이유

✔︎ 불변
✔︎ 누락 방지 (생성자 주입 시 컴파일 오류 발생)
✔︎ final 키워드 사용 가능
✔︎ 순환 참조 방지 (생성자 주입 시 BeanCurrentlyInCreationException 발생)

☞ 생성자 주입 장점

✔︎ 의존관계 설정이 되지 않으면, 객체 생성 불가

  • 컴파일 타임에 인지 가능
  • NPE 에러 방지 가능

✔︎ 의존성 주입이 필요한 필드를 final로 선언 가능
✔︎ 순환 참조 감지 기능
✔︎ 테스트 코드 작성 용이
✔︎ 수정자 주입이 필요한 경우도 있으나 옵션이 필요할 때만 선택

◉ 느낀 점

☞ 반복해서 읽어도 이해가 어려운 부분들이 많았으나, 직접 인텔리제이를 통해 예시 코드를 작성해가고, 블로깅 회고를 통해 다시 한번 내 손으로 직접 입력해보면서 깨달은 부분들이 많았다. 역시 직접 해봐야 이해가 편하다..!!
Spring은 이번 부트캠프 과정에서 핵심적인 부분이기에 기존 유어클래스 내용들 이외에도 여러 문서나 정보들을 구글링해보며 추가 학습을 하는 것이 중요할 것 같다. 아직 Spring 도입부인데 벌써부터 이렇게 어렵다니 😂 그치만,, 개념적인 이해가 오히려 직접 코드를 짜고 프로젝트를 만드는 것보다 어렵다는 말도 있었다! 쫄지말고 프로젝트 전까지 내가 할 수있는 최선을 다해보자 :)

◉ 내일의 키워드

・ AOP (Aspect Oriented Programming)
profile
백엔드 개발자 김창하입니다 🙇‍♂️

0개의 댓글