
그동안 스프링의 핵심 기능들에 대해 공부한 내용들을 쭉 복습하며 정리한 글입니다.
DI(Dependency Injection)은 말 그대로 의존성 주입이다.
의존성(Dependency)이란, A 클래스가 B 클래스를 참조하고 있다면(즉 A 클래스 내부에서 B 클래스를 사용, 접근하고 있다면) A 클래스는 B 클래스에 의존하고 있다고 한다.
이런 의존성을 A 클래스 내부에서 직접 정할 수도 있지만(아래 예시처럼 new 연산자를 이용해서)
// A 클래스의 내부입니다.
// 클래스B는 '인터페이스'라는 이름의 인터페이스를 구현한 클래스입니다.
인터페이스 example = new 클래스B();
A 클래스가 의존할 구체 클래스를 직접 선택하지 않고 밖에서 의존성을 넣어줄 수도 있다. 이것이 DI이다.
private 인터페이스 example;
// 생성자를 통한 의존성 주입.
// A 클래스는 example에 클래스B가 들어올지, 클래스 C가 들어올지 알 수 없다.
클래스A(인터페이스 example) {
this.example = example;
}
DI를 해주는 방법에는
크게 이런 방법들이 있다.
그렇다면 IoC란 뭘까.
스프링 프레임워크가 제공하는 DI 기능을 IoC라고 한다.
즉, DI를 구현하는 방법들 중 하나가 스프링을 이용한 IoC인 것이다.
스프링 프레임워크가 제공하는 IoC는
- Configuration 파일을 통해 수동으로 의존성을 주입하는 방법
- 컴포넌트 스캔과
@Autowired를 통해서 의존성을 자동 주입하는 방법
등이 있는데, 요즘 주로 쓰이는 것은 자동 주입이다.
의존관계 자동 주입에는 크게 아래 세가지 방법이 있다.
@Autowired
필드타입 필드명;
이렇게 필드 위에 바로 @Autowired를 붙여서 의존성을 주입하는 것이다.
이 방법의 장점은
이 방법의 단점은
뒤에서 한번 더 언급하겠지만, 필드 주입은 🚨안티 패턴🚨이니 쓰지 말자...
(애플리케이션과 관련없는 테스트코드에서는 쓰기도 한다. -> 그렇다면 @SpringBootTest 달고 테스트해야 되고 순수 자바로 단위 테스트는 못하겠지..?)
메소드 위에 @Autowired 어노테이션을 붙여서 의존성을 주입받을 수 있다.
@Autowired
public void init(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
setter 주입이라는 주입 방법도 있는데, 그냥 setter 위에다 @Autowired 붙인 것이므로 메소드 주입과 똑같다.
이 방법의 장점은
이 방법의 단점은
NullPointerException 가능순환 참조란 A 클래스와 B 클래스가 서로를 참조하고 있는 상황을 의미한다.
아래에서도 말하겠지만, 생성자 주입은 순환참조가 발생했을 때 앱 구동을 할 수 없도록 친절하게 Exception을 던져준다고 한다.
하지만 메소드 주입과 필드 주입은 Exception을 던져주지 않는다.
생성자 주입은 생성자 위에 @Autowired 어노테이션을 달아서 자동 주입하는 방법이다.
@Autowired
public OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
public OrderServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
이 방법의 장점은
NullPointerException을 막을 수 있다.final 키워드를 사용할 수 있다. -> 런타임 중에 의존성 바뀌지 X. 안전보통 의존성을 주입받을 필드에 final 을 붙여놓고 롬복 라이브러리의 @RequiredArgsConstructor와 함께 사용하면 가장 좋은 듯 하다.
그리고 스프링 프레임워크를 쓸 때, 클래스 내에 생성자가 딱 한개이면 @Autowired를 붙이지 않아도 자동으로 생성자 주입을 해준다는 특징이 있다.
-> 무적권 생성자 주입을 쓰세요.