Header Banner
GG Logo

Future Engineering

기술의 최전선을 기록합니다.

기술 자료/BackEnd/코드 예시로 이해하는 제어의 역전(IoC)과 의존성 주입(DI)의 개념

코드 예시로 이해하는 제어의 역전(IoC)과 의존성 주입(DI)의 개념

BackEnd5개월 전

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 객체를 직접 생성합니다.

  • CarEngine이 강하게 결합되어 있어 확장성이 낮습니다.

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의 주요 방식

  1. 생성자 주입

  2. 세터 주입

  3. 인터페이스 주입

생성자 주입 예시

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를 쉽게 구현할 수 있도록 지원합니다.