안녕하세요. 성조입니다.
이번 포스팅은 스프링을 처음 시작할 때 자주 접하게 되는 DI(Dependency Injection)에 대해서 정리하는 시간을 가져보려 합니다.
올바르지 못한 지식 전달이 존재할 수 있습니다.
올바르지 못한 지식 전달의 경우 댓글로 피드백 주시면 감사드리겠습니다!
DI(Dependency Injection)이란?
DI는 클래스 사이의 의존 관계를 Spring과 같은 컨테이너가 책임지는 하나의 디자인 패턴을 말한다.
DI는 클래스의 의존성을 클래스 내부에서 정의하지 않고, 객체 간의 의존성을 외부(클래스의 생성자나 프로퍼티를 통한다.)에서 주입하여 코드의 유연성, 재사용성을 향상하고, 객체지향 프로그래밍에서의 고수준의 모듈이 저수준의 모듈에 의존하지 않도록 하여, 모듈 간의 결합도를 낮춰. 유지보수와 테스트 용이성을 높이는 핵심 기술이다.
DI의 작동 원리는 IoC, IoC 컨테이너 포스팅에서 다뤄볼 예정이다.
Spring Framework에서 의존성 주입 설명
1-1. Constructor-based Dependency Injection
생성자를 통해 의존성을 주입하는 방식이다.
생성자 기반 의존성 방법을 선택할 때는 필수적인 의존성을 주입할 때 주로 사용된다.
생성자 인자로 주입되는 의존성은 변경될 수 없으므로(즉, 'final'이 가능하므로), 객체의 불변성을 보장한다.
1-2. 예제 코드
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Car {
private Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
this.engine.run();
}
}
Car 클래스는 Engine 클래스에 의존하고 있는 예시다.
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
Car 클래스의 생성자에 Autowired 어노테이션이 붙어 있다. 이 부분을 통해서 IoC 컨테이너는 Engine 타입의 Bean을 찾고, Car 클래스에 의존성을 주입해 준다. 이렇게 Car는 외부에서 Engine 인스턴스를 받아, drive 메서드를 실행할 때 engine.run()을 호출할 수 있게 만들어 준다.
2-1. Setter-based Dependency Injection
setter 메서드를 통해 의존성을 주입하는 방식이다.
세터 기반 의존성을 주입할 때는 선택적인 의존성을 주입할 때 주로 사용된다.
세터 메서드를 통해 주입되는 의존성은 나중에 변경될 수 있다.
2-2. 예제 코드
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Car {
private Engine engine;
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
public void drive() {
this.engine.run();
}
}
@Autowired 어노테이션이 setEngine 메서드에 붙어 있기 때문에 IoC 컨테이너는 Engine 타입의 Bean을 찾아 setEngine 메서드를 통해 Car에 의존성을 주입해 준다.
3-1. Field-based Dependency Injection
직접 필드에 주입하는 방식이다.
필드 기반 의존성 주입할 때는 간결한 코드를 원할 때 종종 사용된다. 하지만 테스트와 디버깅이 어려워질 수 있는 부분을 주의해야 한다.
3-2. 예제 코드
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Car {
@Autowired
private Engine engine;
public void drive() {
this.engine.run();
}
}
@Autowired 어노테이션이 engine 필드에 붙어 있으므로, Spring IoC 컨테이너는 Engine 타입의 Bean을 찾아 Car에 직접 주입해 준다.
DI의 장점과 단점
[장점]
1. 코드의 결합도 감소
DI는 객체가 직접 의존성을 생성하지 않게 해 주고, 외부에서 주입하기 때문에 객체는 필요한 의존성만 알면 되므로, 특정 구현에 대해 알 필요가 없어진다. 이를 통해서 객체 간의 결합도가 감소되고, 코드의 유지 보수성 측면이 향상된다.
2. 테스트 용이성 증가
객체가 의존성을 직접 생성하지 않고, DI를 통해서 외부에서 주입받기 때문에 Mock 객체를 쉽게 주입할 수 있다. 이를 통해서 테스트가 간편해진다. 특히 단위 테스트에서 유리함을 갖는다.
3. 코드 재사용성 향상
객체가 의존성을 주입만 받을 뿐. 만들지 않기 때문에 독립성이 높아지고, 모듈화가 좋아지기 때문에 재사용성이 높아진다.
[단점]
1. 복잡도
DI를 과도하게 사용하면 복잡도가 증가하는 문제가 생길 수 있다.
2. 러닝커브
Spring 생태계는 매우 넓고 거대한데 처음 접하면 조금 어려움을 느낄 수 있다. 그런데 'DI' 개념까지 처음 접하게 되면 초반에 학습 곡선이 더욱 높아질 수 있다.
오타나 궁금한 부분이 있다면 언제든지 댓글 남겨주시면 감사드리겠습니다.
다음 포스팅 때 뵙겠습니다.
- 참고 -
https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85
'Java ☕ > Spring' 카테고리의 다른 글
[Spring] IoC (Inversion of Control Container)란? (With IoC 컨테이너) (0) | 2023.05.27 |
---|