🍀 Spring
DI: Dependency Injection
제어의 역전
IoC: Inversion of Control정의
필요한 객체를 외부에서 결정하여 연결시키는 것
(= 객체 의존관계를 외부에서 넣어주는 것)
목적 및 특징
애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체 인스턴스를 생성하고 주입하여 실제 의존관계가 연결된다.
장점
- 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.
- 코드의 재사용성, 유연성이 높아진다
- 객체간 결합도가 낮아진다
- 유지보수가 쉬우며 테스트가 용이해진다
- 확장성을 가진다
단점
- 의존성 관계가 많아지면 관계가 잘 보이지 않는다.
- 역제어 구조로 코드를 쉽게 이해하기 어렵다.
- 코드 복잡도 상승
- 가독성 감소
- 코드 추적 불편
- 외부에서 조작하기 어려워, 테스트가 어렵다.
- 프레임워크에 대한 의존도 증가
예시
Spring Container: IoC Container주입방식
정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
@Autowired
가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.- 필드 주입
- Setter 주입
- 생성자 주입(권장)
- 순환 의존성 확인: 필드 주입으로는 순환 의존성을 파악하기 어렵다. 생성자 주입을 하게 되면 서버 기동 시 순환 의존성을 가지는 요소들을 파악할 수 있게 에러메시지를 표시하면서 서버가 가동이 되지 않는다.
- 불변성: 필드 주입은 final을 선언할 수 없지만 생성자 주입은 final을 선언함으로써 객체가 변하지 않도록 방지해준다.
- 단일 책임 원칙 위반 확인 가능
@RequiredArgsConstructor
어노테이션을 이용한 의존성 주입@RequiredArgsConstructor
라는 어노테이션을 붙이면 final 필드나@NonNull
이 붙은 필드에 대해 생성자를 생성해 준다. 주로 의존성 주입의 편의성을 위해서 사용된다.- 생성자가 오직 하나만 있고, 생성자의 파라미터 타입이 빈으로 등록 가능한 존재라면 이 빈은 @Autowired 어노테이션 없이도 의존성 주입이 가능하다.
질문
의존성 주입을 해야하는 이유
- 테스트가 용이해진다
- 코드 재사용율이 높아진다
- 객체 간의 의존성을 줄이거나 없앨 수 있다
- 객체 간의 별합도를 낮추면서 유연한 코드를 작성할 수 있다
@Autowired 필드 주입 VS @RequiredArgsConstructor final 주입 VS Setter 주입
@RequiredArgsConstructor final 주입
- 한번 의존성 주입을 받은 객체는 프로그램이 끝날 때까지 변하지 않는 특징을 가지므로 불변성을 표시해주는 것이 좋기 때문 → 객체의 불변성(Immutability) 보장
- final이 붙어있기 때문에 인스턴스가 생성될 때 1번만 참조되므로 코드 변이의 걱정은 사라진다.
- 순환 참조가 일어날 시, Exception이 발생하여 컴파일 중에 에러가 발생한다. 이를 통해 Test 단계에서 순환 참조를 파악하여 수정할 수 있다.
@Autowired 필드 주입
- 단일 책임의 원칙 위반 가능성
- 코드 변이의 가능성
- final 옵션을 사용할 수 없기에 코드가 변질될 가능성이 존재
- 불확실한 참조
- @Autowired는 타입(Type)이 같은 빈(Bean)이 발견되면 그냥 주입
- Type의 다른 객체가 여러 개일 때 문제가 발생
- 서로 다른 A타입의 객체가 2개 존재한다면 @Autowired는 Error
Setter 주입
- public으로 열려있기 때문에 다른 곳에서 해당 메서드를 통해 주입이 가능
- 즉, 호출하지 말아야 할 메서드를 호출하게 될 수도 있다.
생성자 주입 추천 이유
- final 사용 가능
- 불변
- 순환 참조 방지