
KUKJIN LEE
posted 2 weeks ago
코드 예시로 이해하는 제어의 역전(IoC)과 의존성 주입(DI)의 개념
IoC(Inversion of Control)란 무엇인가?
정의
IoC는 제어의 역전이라는 뜻으로, 프로그램의 흐름을 개발자가 직접 제어하지 않고, 외부의 프레임워크나 컨테이너가 제어를 담당하도록 하는 설계 원칙입니다.
왜 중요한가?
밑에 3가지 이유도 중요하지만 핵심은 재사용성이 아닐까 싶습니다.
-
코드의 유연성과 확장성을 높입니다.
-
의존성 관리가 쉬워집니다.
-
테스트하기 쉬운 구조를 만듭니다.
간단한 비유
IoC를 쉽게 이해하기 위해서 DI까지 묶어서 설명드리겠습니다.
조건 (레고는 무조건 흰색이라고 가정)
-
IoC 적용 전: 레고를 조립하는데, 내가 레고까지 만들고 있는 상황.
-
IoC 적용 후: 레고를 가져와 내가 조립만 하면 됨.
- DI 적용: 레고를 조립하고 있으면 옆에서 누군가 레고에 색을 칠함.
간단한 예시
전통적인 방식 (IoC를 적용하지 않은 경우)
class Engine {
void start() {
System.out.println("Engine started.");
}
}
class Car {
private Engine engine;
public Car() {
this.engine = new Engine(); // 직접 객체를 생성
}
void drive() {
engine.start();
System.out.println("Car is driving.");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.drive();
}
}
-
Car
클래스가Engine
객체를 직접 생성합니다. -
Car
와Engine
이 강하게 결합되어 있어 확장성이 낮습니다.
IoC 적용 후
class Engine {
void start() {
System.out.println("Engine started.");
}
}
class Car {
private Engine engine;
// 외부에서 객체를 주입받음
public Car(Engine engine) {
this.engine = engine;
}
void drive() {
engine.start();
System.out.println("Car is driving.");
}
}
public class Main {
public static void main(String[] args) {
Engine engine = new Engine(); // 객체를 외부에서 생성
Car car = new Car(engine); // 생성자를 통해 주입
car.drive();
}
}
-
Car
클래스는 더 이상Engine
을 직접 생성하지 않습니다. -
Engine
을 외부에서 주입받아 유연성이 증가합니다.
DI(Dependency Injection)란 무엇인가?
정의
DI는 의존성 주입이라는 뜻으로, 객체가 필요로 하는 의존성을 외부에서 제공(주입)하는 디자인 패턴입니다. DI는 IoC의 한 방법입니다.
DI의 장점
-
의존성 관리가 용이: 객체 간의 결합도가 낮아집니다.
-
코드 재사용성 증가: 의존성을 쉽게 교체하거나 확장할 수 있습니다.
-
테스트 편리성: 모의 객체(Mock Object)를 쉽게 주입하여 테스트를 간단히 수행할 수 있습니다.
DI의 주요 방식
-
생성자 주입
-
세터 주입
-
인터페이스 주입
생성자 주입 예시
class Engine {
void start() {
System.out.println("Engine started.");
}
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
void drive() {
engine.start();
System.out.println("Car is driving.");
}
}
public class Main {
public static void main(String[] args) {
Engine engine = new Engine();
Car car = new Car(engine); // 생성자를 통해 Engine 주입
car.drive();
}
}
세터 주입 예시
class Engine {
void start() {
System.out.println("Engine started.");
}
}
class Car {
private Engine engine;
public void setEngine(Engine engine) {
this.engine = engine;
}
void drive() {
engine.start();
System.out.println("Car is driving.");
}
}
public class Main {
public static void main(String[] args) {
Engine engine = new Engine();
Car car = new Car();
car.setEngine(engine); // 세터를 통해 Engine 주입
car.drive();
}
}
IoC와 DI의 관계
-
IoC는 객체의 제어권을 개발자가 아닌 외부로 넘기는 큰 개념입니다.
-
DI는 IoC를 구현하는 한 가지 방법입니다.
IoC |
DI |
---|---|
설계 원칙 |
디자인 패턴 |
객체 생성과 관리의 제어권을 외부로 넘김 |
의존성을 외부에서 주입 |
Spring Framework에서 IoC와 DI
-
IoC 컨테이너: Spring이 제공하는 컨테이너로, 객체의 생성과 생명 주기를 관리합니다.
-
DI 구현:
@Autowired
또는@Bean
등을 사용하여 의존성을 주입합니다.
Spring 예시
생성자 주입
@Component
class Engine {
void start() {
System.out.println("Engine started.");
}
}
@Component
class Car {
private final Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
void drive() {
engine.start();
System.out.println("Car is driving.");
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
Car car = context.getBean(Car.class);
car.drive();
}
}
요약
-
IoC는 객체 제어권을 외부로 넘기는 설계 원칙입니다.
-
DI는 IoC를 구현하는 방법 중 하나로, 객체 간의 의존성을 외부에서 주입합니다.
-
IoC와 DI는 코드의 유연성과 재사용성을 높이고, 테스트를 용이하게 합니다.
-
Spring Framework는 IoC와 DI를 쉽게 구현할 수 있도록 지원합니다.