프로그래밍/CS
IoC, DI (without Spring)
hwangsehee
2025. 4. 1. 21:29
IoC , DI 의 필요성
SOLID 원칙에서 DIP 원칙(의존관계 역전 원칙)을 살펴보자
추상화에 의존해야지, 구현체에 의존하면 안된다. (역할과 구현이 분리가 되어야함)
class Service {
void doSomething() {
System.out.println("Service 동작");
}
}
class Client {
private Service service = new Service(); // 직접 객체 생성
void execute() {
service.doSomething();
}
}
❎ Client가 Service를 의존한다. → DIP 원칙 위배
(Service 변경 시 Client 클래스도 변경되어야하기 때문)
DIP를 위해서 IoC를 사용하면 편리하고, IoC를 구현하는 방법 중 하나가 DI이다.
- Service 클래스 → 인터페이스 변경 , Service 인터페이스를 구현하는 구현체 DefaultService 생성
// 1. 인터페이스 정의
interface Service {
void doSomething();
}
// 2. 인터페이스 구현체 (기본 서비스)
class DefaultService implements Service {
public void doSomething() {
System.out.println("Default Service 동작");
}
}
// 3. 클라이언트 클래스 - 인터페이스에 의존
class Client {
private Service service;
// 의존성 주입 (Dependency Injection)
public Client(Service service) {
this.service = service;
}
void execute() {
service.doSomething();
}
}
//4. 외부에서 객체 주입
public static void main(String[] args) {
Client client = new Client(new DefaultService()); //객체 주입
client.doSomething(); // "Default Service 동작" 출력
}
☑️ Client 클래스는 더이상 구현체를 의존 하지않고 추상체를 의존한다.
☑️ Client는 구현체가 뭔지 몰라도 된다. 인터페이스만 알면 됨!
☑️ 이는 외부에서 구현체를 결정할 수 있게 해준다. (IoC 적용!)
IoC , DI 정의
IoC : Inversion of Control 제어의 역전
코드의 흐름을 제어하는 주체가 바뀌는 것.
- 제어권이 뒤바뀐다. (개발자 → 프레임워크)
- 스프링만의 고유 특징이 아니고 프레임워크의 특징
- 프로그램 흐름을 직접 제어하는것이 아니라 외부에서 관리하는 것을 제어의 역전이라고 한다.
// 전략 인터페이스
interface Strategy {
void execute();
}
// 전략 A
class StrategyA implements Strategy {
public void execute() {
System.out.println("전략 A 실행");
}
}
// 전략 B
class StrategyB implements Strategy {
public void execute() {
System.out.println("전략 B 실행");
}
}
// 컨텍스트 (제어의 역전 담당)
class Context {
private final Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void run() {
strategy.execute(); // 실행 흐름을 컨텍스트가 제어 (IoC 적용)
}
}
// 클라이언트 코드
public class Main {
public static void main(String[] args) {
Context context = new Context(new StrategyA()); // 실행 전략을 외부에서 주입
context.run();
}
}
☑️ 클라이언트가 직접 실행할 전략을 결정하지않음
☑️ 실행흐름은 Context가 관리 → IoC 적용
DI : Dependency Injection 의존성 주입
필요로하는 객체를 스스로 생성하는 것이 아닌 외부로 부터 주입받는 설계 패턴
- 의존관계 주입(DI) 를 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.
- DI는 IoC 를 구현하는 한가지 방법이다.
- 코드의 결합도를 낮추고, 각 컴포넌트의 독립성을 증가시킨다.
생성자 주입
//생성자 주입
public class Controller{
private final Service service;
public Controller(Service service){
this.service = service;
}
}
- 생성자의 호출 시점에 1회 호출되는 것이 보장된다.
- 주입받은 객체가 변하지않거나(불변성), 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용 가능 → OCP : 확장에는 열려있으나 변경에는 닫혀있어야 한다.
- test코드 작성시 프레임워크 없이 순수 java코드로 작성 가능 (Spring test는 컴포넌트들을 등록하고 초기화 하는 비용 소요)
- 주입하는 객체가 누락된 경우 컴파일 시점에서 오류 발견 가능
- 위 같은 이유로 Spring에서는 생성자 주입을 권장
Setter 주입
//setter 주입
public class Controller{
private Service service;
public void SetSetvice(Service service){
this.service= service;
}
}
- 생성자 주입과 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다.
필드 주입
//필드 주입
@Autowired
private Service service;
- 필드 주입을 이용하면 코드가 간결해져서 과거에 많이 이용되었었다.
- 하지만 필드 주입은 외부에서 접근이 불가능하고, 테스트 코드의 중요성이 부각됨에 따라 필드의 객체를 수정할 수 없는 필드 주입은 거의 사용되지 않았다.
- 또한 필드 주입은 반드시 DI 프레임워크가 존재해야 하므로 반드시 사용을 지양해야 한다.(Autowired)
참고 블로그
다양한 의존성 주입방법, 생성자 주입을 사용해야 하는 이유 : https://mangkyu.tistory.com/125
+)
데브코스에서 매주 금요일에 진행하는 RBF!
이번엔 나름 준비를 열심히해서 혼자 보기 아까운 마음에 포스팅해봅니다 총총.. 💨