🔌 의존성 주입(Dependency Injection)
1. '의존성 주입(DI)'이란?
객체가 사용할 객체를 스스로 만들지 않고,
필요한 객체를 외부에서 받아오는
외부에서 주입 받는 디자인 패턴!
우리가 택배 기사라고 했을 때,
(기존 방식) 직접 박스를 만들고, 물건을 넣고, 포장하고, 배송하고
(의존 객체를 생성하는 방식) 박스(=의존 객체)는 회사에서 준비(주입)해줄게. 넌 배달만 해
라는 차이!
직접 코드로 이해해보자.
[기존 방식]
public class CarService {
private Engine engine;
public CarService() {
// CarService가 직접 엔진을 생성함 (강한 결합)
this.engine = new teslaEngine();
}
public void startCar() {
engine.start();
}
}
자동차 서비스(CarService)가 엔진(Engine)을 사용한다고 할 때, 기존 방식은 직접 객체를 생성한다.
직접 객체를 생성하는 방식의 문제점은
1. 테스트가 어려움 : 테스트 시 객체가 항상 생성됨
2. 유연성 부족 : 다른 종류의 엔진으로 쉽게 교체할 수 없음
3. 코드 재사용성 저하 : 엔진을 변경할 경우 CarService도 수정해야 함
[의존성 주입]
public class CarService {
private final Engine engine;
// 의존성 주입(DI) : 생성자를 통한
public CarService(Engine engine) {
this.engine = engine;
}
public void startCar() {
engine.start();
}
}
의존성 주입을 사용하면 어느 엔진, 객체가 주입되는지 상관 없다
즉 향후 테스트 시에 Mock 객체를 넣을 수 있다!
결합도가 낮아지고,유연성이 높아지고,유지보수가 쉬워진다!

2. 의존성 주입의 종류
2-1. 생성자 주입(Constructor Injection)
- 가장 권장되는 방식
- 객체 생성 시점에 모든 의존성을 주입받음
- 생성자 호출 시점에 딱 한번만 호출되는 것이 보장됨
- 불변성이 보장(final)되며, 의존성이 필수일 때 사용
public class CarService {
private final Engine engine;
private final Fuel fuel;
// 생성자 주입
public CarService(Engine engine, Fuel fuel) {
this.engine = engine;
this.fuel = fuel;
}
}
2-2. 세터 주입(Setter Injection)
- 객체 생성 이후 별도로 주입
- 세터 메소드를 통해 의존성의 주입함
- 주입이 선택적일 때 유용
- 테스트에서 부분적인 주입이 가능하다
public class CarService {
private Engine engine;
// 세터 주입
public void setEngine(Engine engine) {
this.engine = engine;
}
}
2-3. 필드 주입(Field Injection)
- 필드에 직접 주입하는 방식
- 외부 변경이 불가능해 테스트하기 어려움
- 의존성이 명확하지 않고, final 사용이 불가
public class CarService {
@Autowired
private Engine engine;
}
2-4. 일반 메서드 주입(Method Injection)
- 생성자나 세터 외 일반 메서드에 @Autowired 사용
- 의존성 외 추가 초기화 작업에 사용되나 거의 사용되지 않음
- Spring에서는 생성자 주입을 기본으로 권장
public class CarService {
private Engine engine;
@Autowired
public void init(Engine engine) {
this.engine = engine;
}
}
3. 의존성 주입의 장점
- 테스트 시 Mock 객체를 쉽게 주입할 수 있어 단위 테스트가 간편하다
- 코드 유연성 : 시스템의 동작을 쉽게 변경할 수 있다
- 로직 분리 : 객체는 비즈니스 로직만, 의존성 관리는 DI 컨테이너(Spring 등)에 맡김
- 생명 주기 관리 : Spring과 같은 프레임워크는 객체의 생성과 소멸을 효율적으로 관리할 수 있다
4. Spring에서의 의존성 주입
- IoC 컨테이너(Spring 컨테이너)가 객체를 대신 생성하고, 의존 객체를 자동으로 주입한다
- 개발자는 객체를 직접 만들 필요 없이, 주입 받아서 사용하면 된다
- 컴포넌트 스캔을 통해 자동으로 Bean 등록 가능(Bean과 관련된 글은 다음 포스트에서!)
- 컴포넌트 클래스에 @Component, @Service, @Repository 등을 붙여 자동으로 Bean으로 등록하고,
Bean으로 등록된 객체를 필요로 하는 곳에 @Autowired 혹은 생성자를 통해 주입받아 사용한다!
Engine, Light를 생성자 주입으로 의존성을 주입하는 CarService 클래스를 만들어보자
@Component
public class Engine {
public void drive() {
System.out.println("Engine On");
}
}
@Component
public class Light {
public void lightOn {
System.out.println("Light On");
}
}
@Service class CarService {
private final Engine engine;
private final Light light;
@Autowired // 생성자 주입
public CarService(Engine engine, Light light) {
this.engine = engine;
this.light = light;
}
public void startCar() {
engine.drive();
light.lightOn();
}
}
Engine과 Light는 각각 @Component 어노테이션을 통해 Spring 컨테이너에 Bean으로 등록되고,
CarService는 @Service로 등록되며 생성자를 통해 두 컴포넌트(Bean)을 자동 주입 받는다
Spring4.3 이후 생성자가 1개일 때는 @Autowired 생략이 가능하다
의존성 주입의 핵심은!
객체는 비즈니스 로직에만 집중하고, 의존은 외부에서 주입하는 것
Spring 에서는 컨테이너가 객체 생성과 주입을 대신해주며,
우리는 비즈니스 로직에 집중할 수 있다는 것이다!
다음에는 제어의 역전과 Bean, 생명주기에 대한 글로 찾아오겠다!
'프로그래밍 > Spring' 카테고리의 다른 글
[Spring] 제어의 역전(IoC) + Bean (1) | 2025.03.24 |
---|