IOC와 DI 유의사항

 

1. IOC와 DI의 관계

  • **IOC(제어의 역전)**는 더 큰 개념입니다.
    • 객체의 제어권(생성과 실행 흐름 관리)을 개발자가 아닌 외부(프레임워크, 컨테이너)에 넘기는 설계 원칙입니다.
    • IOC는 객체 지향 설계에서 제어 흐름을 외부로 위임한다는 철학적 개념에 가깝습니다.
  • **DI(의존성 주입)**는 IOC를 구현하는 방식 중 하나입니다.
    • 의존성 주입은 IOC를 실현하는 구체적인 기술적 방법입니다.
    • 객체가 스스로 의존성을 생성하지 않고, 외부에서 필요한 의존성을 주입받는 것을 의미합니다.

즉, DI는 IOC를 구현하기 위한 방법 중 하나이며, 모든 DI는 IOC의 일종이지만, 모든 IOC가 DI는 아닐 수 있습니다.


2. IOC와 DI의 차이

항목 IOC(제어의 역전) DI(의존성 주입)
정의 프로그램의 제어 흐름(객체 생성과 실행)을 개발자가 아닌 외부로 위임하는 설계 원칙. 외부에서 객체의 의존성을 주입하여 결합도를 줄이고 유연성을 높이는 설계 기법.
포커스 제어권(흐름)의 역전: 누가 객체를 생성하고, 실행을 제어할지에 대한 개념적 설계. 의존성(Dependency)의 전달: 객체 간의 관계를 어떻게 설정할지에 대한 구체적인 기술.
범위 설계 철학(개념): 제어권을 외부로 넘기는 큰 틀의 원칙. 구현 방법(기술): 의존성을 외부에서 주입하는 특정한 방식.
실현 방식 DI, 이벤트 기반 시스템, 전략 패턴 등 다양한 방법으로 구현 가능. 주로 DI 컨테이너(Spring, Guice 등)를 사용하거나 수동으로 구현.
목적 결합도를 낮추고 제어 흐름을 외부에서 관리하도록 설계. 구체적인 의존성 전달을 통해 객체 간 관계를 설정.

3. 예시로 이해하기: IOC와 DI

IOC 예시 (제어의 역전)

전통적인 방식 (제어권 개발자 소유):

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class NotificationService {
    private EmailService emailService;

    public NotificationService() {
        // 객체 생성과 의존성을 직접 관리
        this.emailService = new EmailService();
    }

    public void send(String message) {
        emailService.sendEmail(message);
    }
}
  • 문제점:
    • NotificationService가 EmailService의 구체적인 구현체에 강하게 결합.
    • 제어권(객체 생성 및 실행 흐름)이 개발자 코드 내부에 있음.

IOC 적용 (제어권 역전)

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class NotificationService {
    private EmailService emailService;

    // 외부에서 객체를 주입받음 (제어권 역전)
    public NotificationService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void send(String message) {
        emailService.sendEmail(message);
    }
}

class App {
    public static void main(String[] args) {
        // 객체 생성 및 의존성 관리가 외부(App 클래스)로 이동
        EmailService emailService = new EmailService();
        NotificationService notificationService = new NotificationService(emailService);

        notificationService.send("Hello World!");
    }
}
  • 제어권 역전:
    • 객체 생성 및 의존성 설정의 제어권이 App으로 이동.
    • NotificationService는 자신이 사용할 객체를 몰라도 동작.

DI 예시 (의존성 주입)

DI는 위의 IOC를 구현한 구체적인 방식입니다.

// 인터페이스를 통한 DI 적용
interface MessageService {
    void sendMessage(String message);
}

class EmailService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class SmsService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

class NotificationService {
    private final MessageService messageService;

    // 생성자를 통한 의존성 주입
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        messageService.sendMessage(message);
    }
}

public class Main {
    public static void main(String[] args) {
        // EmailService 주입
        MessageService emailService = new EmailService();
        NotificationService emailNotification = new NotificationService(emailService);
        emailNotification.notify("Hello via Email!");

        // SmsService 주입
        MessageService smsService = new SmsService();
        NotificationService smsNotification = new NotificationService(smsService);
        smsNotification.notify("Hello via SMS!");
    }
}
  • DI 특징:
    • NotificationService는 MessageService 인터페이스에 의존하므로 구체적인 구현체를 알 필요가 없습니다.
    • MessageService의 구현체는 외부에서 주입되므로, 의존성을 변경(예: SmsService → PushService)하기 쉽습니다.

4. IOC와 DI의 일상적인 비유

IOC(제어의 역전):

  1. 택배 서비스:
    • 전통적인 방식: 고객이 직접 가게에서 물건을 사고 가져옵니다. (고객이 제어권을 가짐)
    • IOC 방식: 택배 회사가 물건을 배송하고, 고객은 물건만 받습니다. (제어권이 택배 회사로 역전)
  2. 자동차 서비스:
    • 전통적인 방식: 운전자가 직접 자동차 수리를 관리.
    • IOC 방식: 정비소에 맡기면 정비사가 알아서 자동차를 고침.

DI(의존성 주입):

  1. 셰프와 재료:
    • 전통적인 방식: 셰프가 요리 재료를 직접 구입.
    • DI 방식: 주방 보조가 재료를 준비하고, 셰프는 받은 재료로 요리만 합니다.
  2. 콘센트와 전기:
    • 전통적인 방식: 전자제품이 직접 전기를 생성.
    • DI 방식: 콘센트(외부)가 전기를 공급하고, 전자제품은 연결만 합니다.

5. 요약

항목 IOC(제어의 역전) DI(의존성 주입)
개념 객체 생성과 제어 흐름을 외부로 위임하는 설계 원칙. 외부에서 객체의 의존성을 주입하여 결합도를 줄이는 기법.
관계 큰 철학적 개념(원칙). IOC를 구현하기 위한 구체적인 방법.
목적 객체 간 결합도를 낮추고 제어 흐름을 외부에서 관리. 객체의 의존성을 외부에서 설정해 다형성과 테스트 용이성 확보.
일상 비유 택배 회사가 고객 대신 물건을 배송. 셰프가 주방 보조로부터 재료를 주입받아 요리.

결론: DI는 IOC를 실현하기 위한 방법입니다. IOC는 큰 설계 원칙이고, DI는 그 원칙을 구현하는 하나의 도구입니다. 두 개념은 밀접히 연결되어 있지만, DI는 의존성을 주입하는 구체적인 기술이고, IOC는 객체 제어 흐름을 외부로 넘기는 더 큰 철학적 개념입니다.

 

 

IOC (Inversion of Control) 제어의 역전

 

IOC란?

**IOC(제어의 역전)**은 객체 생성 및 의존성 관리를 개발자가 아닌 외부에서 제어하는 설계 원칙입니다.

  • 기존에는 프로그램이 직접 객체를 생성하고 메서드를 호출하여 제어 흐름을 관리했지만, IOC에서는 이 역할을 외부(조립자, 설정자 또는 프레임워크)가 담당합니다.
  • 이는 객체 간의 결합도를 줄이고, 유연성과 확장성을 높이는 데 도움을 줍니다.
  • 원래는 각 클래스에서 필요한 객체를 생성해서 사용했지만, IOC는 이 객체나 의존성을 담당하는 클래스를 따로 만들고 이 클래스에 다른 클래스가 접근해서 해당 기능을 사용하는 방식이다. 결국 다양성, 결합도 감소를 위해 객체, 의존성을 담당하는 클래스를 따로 만들었다는 말

IOC의 핵심 개념

  1. 객체 생성과 의존성 주입의 외부 위임:
    • 프로그램이 직접 객체를 생성하지 않고, 필요한 객체를 외부에서 받아 사용합니다.
    • 예: 스프링 프레임워크는 컨테이너가 객체를 생성하고 필요한 의존성을 주입합니다.
  2. 제어 흐름의 역전:
    • 기존에는 개발자가 프로그램의 제어 흐름을 관리했지만, IOC에서는 프레임워크나 외부 설정자가 제어 흐름을 관리합니다.
    • 객체는 단순히 요청을 처리하는 역할만 수행하고, 어떤 객체가 주입될지는 알지 못합니다.
  3. 결합도를 낮추고 확장성을 높임:
    • 구체적인 구현체가 아닌 추상화된 인터페이스에 의존하도록 설계하여 다형성을 극대화합니다.

일상적인 예시로 이해하기

  1. 식당 운영 시스템:
    • 전통적인 방식: 셰프가 요리 재료를 직접 고르고, 요리를 만들고, 손님에게 서빙까지 직접 관리.
      • 셰프는 재료에 강하게 의존하므로, 재료가 바뀌면 모든 요리 방식을 수정해야 함.
    • IOC 방식: 식당 주인이 셰프에게 필요한 재료(의존성)를 전달하고, 셰프는 재료를 이용해 요리를 만듦.
      • 셰프는 재료를 직접 고르지 않으며, 어떤 재료가 주어질지 신경 쓰지 않음.
  2. 택시 호출 서비스:
    • 전통적인 방식: 승객이 직접 택시를 잡고, 특정 기사와 거래를 설정.
      • 특정 택시에 의존하므로 변경이 어려움.
    • IOC 방식: 승객은 호출 앱(플랫폼)을 사용해 택시를 요청하고, 플랫폼이 적합한 택시를 배정.
      • 승객은 배정된 기사에 대해 미리 알 필요가 없고, 플랫폼이 적절한 택시를 알아서 선택.
  3. 전자제품과 전원:
    • 전통적인 방식: 전자제품(객체)이 직접 전원을 생성.
      • 전자제품마다 전력 공급 방식이 달라지면 제품 내부를 수정해야 함.
    • IOC 방식: 전자제품은 콘센트에서 제공되는 전원을 이용.
      • 전자제품은 전원의 구체적인 공급 방식을 몰라도 작동 가능.

 

 

그래서 흐름 제어? 는 뭔 말일까?

 

**"실행을 제어한다"**는 말은 프로그램에서 어떤 객체가 메서드를 호출하고, 로직을 진행하며, 전체 흐름을 주도할지를 결정하는 주체가 누구인지를 의미합니다. 전통적인 방식에서는 개발자가 직접 객체를 생성하고 필요한 메서드를 호출하며 실행 흐름을 주도했습니다. 반면, **IOC(제어의 역전)**에서는 이 제어 흐름이 외부 프레임워크나 컨테이너로 넘어갑니다. = 기존에는 프로그래머가 클래스에서 어떤 클래스를 불러와서 실행하고 ~~~ 와 같은 실행의 흐름을 직접 구성해주었다면, 스프링에서는 이걸 스프링 프레임워크가 대신한다. 흔히 사용하는 어노테이션과 생성자의 객체 매개변수를 통해 DI를 수행한다.

 

+ 생성자의 객체 매개변수 의 경우 클래스 내부에서 객체를 정의하는 것이 아닌 외부 클래스에서 객체를 만들고 그걸 객체 매개변수 로 인식하기만 하니 이것또한 IOC에 해당한다.

 

+ "의존성을 주입받는다"는 말은, 클래스가 직접 필요한 객체를 생성하지 않고, 외부에서 만들어진 객체를 전달받아 사용한다 = 외부 객체에 의존하겠다. 이말이다.


1. 실행 흐름의 제어란?

전통적인 방식 (제어 흐름 개발자 소유):

  • 개발자가 직접 모든 객체를 생성하고, 그 객체의 메서드를 호출하여 실행을 제어합니다.
  • 실행의 주체는 개발자 코드 내부에 있습니다.

IOC 방식 (제어 흐름 외부 위임):

  • 객체의 생성 및 실행 흐름(메서드 호출의 순서와 방식)을 외부 컨테이너(예: 스프링)에 맡깁니다.
  • 개발자는 객체의 실행 방법이나 관계를 정의하지 않고, 외부에서 주입된 객체에만 집중합니다.
  • 실행의 주체가 외부로 넘어가므로 "제어의 역전"이 이루어진 것입니다.

2. 코드로 이해하기

(1) 전통적인 방식: 실행 흐름을 개발자가 직접 관리

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class NotificationService {
    private EmailService emailService;

    public NotificationService() {
        // 객체 생성 및 관계 설정
        this.emailService = new EmailService();
    }

    public void notify(String message) {
        // 실행 흐름을 직접 제어
        emailService.sendEmail(message);
    }
}

public class Main {
    public static void main(String[] args) {
        NotificationService notificationService = new NotificationService();
        notificationService.notify("Hello, World!");
    }
}

실행 흐름 설명:

  1. Main 메서드에서 개발자가 NotificationService 객체를 생성.
  2. NotificationService는 내부적으로 EmailService 객체를 생성.
  3. notify 메서드를 호출하며, 실행 흐름을 개발자가 직접 제어.

문제점:

  • 모든 객체의 생성, 관계 설정, 실행 흐름을 개발자가 직접 관리하므로 코드가 복잡해짐.
  • 다른 의존성(SmsService)으로 변경하려면, 개발자가 기존 코드를 수정해야 함.

(2) IOC 방식: 실행 흐름을 외부 컨테이너에 위임

interface MessageService {
    void sendMessage(String message);
}

class EmailService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class SmsService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

class NotificationService {
    private MessageService messageService;

    // 의존성을 외부에서 주입받음
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        // 외부에서 주입된 객체를 사용해 실행 흐름을 진행
        messageService.sendMessage(message);
    }
}

class AppConfig {
    // 외부 컨테이너 역할: 객체 생성 및 관계 설정
    public static NotificationService createNotificationService(String type) {
        MessageService messageService;
        if (type.equalsIgnoreCase("email")) {
            messageService = new EmailService();
        } else if (type.equalsIgnoreCase("sms")) {
            messageService = new SmsService();
        } else {
            throw new IllegalArgumentException("Unknown type");
        }
        return new NotificationService(messageService);
    }
}

public class Main {
    public static void main(String[] args) {
        // 실행 흐름을 AppConfig(외부 컨테이너)가 관리
        NotificationService notificationService = AppConfig.createNotificationService("email");
        notificationService.notify("Hello via Email!");

        NotificationService smsNotification = AppConfig.createNotificationService("sms");
        smsNotification.notify("Hello via SMS!");
    }
}

실행 흐름 설명:

  1. AppConfig가 객체를 생성하고 의존성을 주입하여 실행 흐름을 관리.
  2. NotificationService는 어떤 구체적 객체가 사용되는지 알 필요 없이, 주입된 객체만 사용.
  3. Main 메서드는 AppConfig를 호출하여 필요한 객체를 가져오고 실행.

3. "실행 흐름을 외부로 넘긴다"의 본질

IOC에서 실행 흐름이 외부로 넘어간다는 것은 다음과 같은 점을 포함합니다:

  1. 객체 생성:
    • 개발자가 객체를 직접 생성하지 않고, 외부 컨테이너가 생성.
    • 개발자는 컨테이너가 제공한 객체를 사용만 함.
  2. 의존성 관리:
    • 개발자가 객체 간의 관계를 직접 설정하지 않음.
    • 외부 컨테이너가 필요한 객체를 주입하여 의존성을 관리.
  3. 실행 순서 제어:
    • 실행되는 메서드나 로직의 흐름을 외부에서 관리.
    • 개발자는 "어떤 객체가 주입될지", "어떻게 동작할지"를 신경 쓰지 않고, 주어진 역할에만 집중.

4. 일상적인 비유로 이해

전통적인 방식 (개발자가 직접 제어):

  • 상황: 개인이 직접 집을 짓는 경우
    1. 필요한 재료(의존성)를 직접 구매.
    2. 벽돌을 쌓고, 전기 배선을 연결하며, 도배까지 직접 실행.
    3. 모든 과정(흐름)을 스스로 제어.

IOC 방식 (실행 흐름 외부 위임):

  • 상황: 건축회사를 고용하여 집을 짓는 경우
    1. 건축회사(컨테이너)가 필요한 재료를 관리.
    2. 전기공, 배관공, 목수를 투입(의존성 주입)하여 작업.
    3. 집주인은 "이런 집을 원한다"는 요구만 하고, 실행 흐름은 건축회사가 관리.

5. 결론: 실행 흐름의 역전이란?

  • 전통적인 방식에서는 개발자가 객체 생성, 관계 설정, 메서드 호출 등 모든 실행 흐름을 직접 관리.
  • IOC에서는 실행 흐름(객체 생성, 의존성 주입, 메서드 호출 등)을 외부 컨테이너(예: 스프링 프레임워크)에 위임.
  • 개발자는 객체 간의 관계나 실행 흐름을 신경 쓰지 않고, 주어진 역할에만 집중할 수 있습니다.

즉, "실행을 제어한다"는 것은 객체 생성, 의존성 설정, 로직 실행 흐름을 포함하며, IOC에서는 이를 외부에서 관리하여 더 유연하고 확장 가능한 구조를 제공합니다.


IOC의 장점

  1. 유연성:
    • 새로운 기능 추가 시 기존 코드 수정이 필요 없음(OCP 원칙 준수).
  2. 결합도 감소:
    • 객체 간의 관계를 인터페이스로 추상화하여 변경에 강함.
  3. 테스트 용이성:
    • Mock 객체를 쉽게 주입하여 테스트 가능.
  4. 코드 재사용성:
    • 객체를 재사용할 수 있어 개발 속도가 빨라짐.

IOC 요약 표

항목 내용
정의 객체의 생성 및 의존성 관리를 개발자가 아닌 외부에서 제어하는 설계 원칙.
핵심 개념 1. 객체 생성 및 관리 외부 위임2. 제어 흐름 역전3. 결합도 감소와 다형성 극대화
장점 유연성, 유지보수 용이, 결합도 감소, 테스트 용이성, 코드 재사용성 증가
일상적인 예시 1. 식당에서 주인이 재료를 제공2. 택시 호출 앱3. 전자제품과 콘센트
대표적인 활용 프레임워크 스프링 프레임워크 (DI 컨테이너를 활용해 IOC 구현)

결론

IOC는 객체 지향 설계에서 **의존성 주입(DI)**과 함께 사용되어 프로그램의 유연성과 확장성을 극대화합니다. 이는 객체 간의 강한 결합을 제거하고, 코드 변경 없이 새로운 기능을 추가하거나 기존 기능을 교체할 수 있게 합니다.
일상 속 예시와 코드를 함께 보면, IOC는 **"중간에서 객체 관계를 설정하고 관리하는 조립자 역할을 외부에 위임"**하는 개념임을 쉽게 이해할 수 있습니다.

 

 

DI (Dependency Injection) 의존성 주입

 

1. DI란?

**DI(의존성 주입)**는 객체 간의 의존성을 외부에서 주입하여 객체 간의 결합도를 줄이고 유연성을 높이는 설계 원칙입니다.

  • 의존성(Dependency): 한 객체가 다른 객체를 사용할 때 그 객체를 의존한다고 합니다.
  • 의존성 주입(Injection): 의존하는 객체를 코드 내부에서 직접 생성하지 않고, 외부에서 주입받는 방식입니다.

2. DI의 핵심 개념

  1. 의존성 분리:
    • 객체는 필요한 의존성을 스스로 생성하지 않습니다.
    • 대신 외부에서 의존성을 주입받아 사용합니다.
  2. 유연성과 재사용성:
    • 객체가 구체적인 구현체가 아닌 인터페이스에 의존하므로, 의존성을 쉽게 교체할 수 있습니다.
    • 테스트 시 Mock 객체를 주입하거나, 새로운 구현체를 추가하는 것이 용이합니다.
  3. DI의 유형:
    • 생성자 주입(Constructor Injection): 의존성을 생성자를 통해 주입.
    • 세터 주입(Setter Injection): 세터 메서드를 통해 의존성을 주입.
    • 필드 주입(Field Injection): 필드에 직접 주입(주로 프레임워크에서 사용).

3. DI의 일상적인 예시

  1. 가전제품과 콘센트:
    • 의존성: 가전제품(객체)은 전기(의존성)가 필요합니다.
    • 전통적인 방식: 가전제품이 자체적으로 전기를 생성해야 합니다.
    • DI 방식: 가전제품은 콘센트(외부 주입)에서 전기를 받아 사용합니다. 가전제품은 전기의 세부 공급 방식(예: 태양광, 수력)을 몰라도 동작합니다.
  2. 택배 서비스:
    • 의존성: 고객은 물건을 받으려면 택배 서비스(의존성)가 필요합니다.
    • 전통적인 방식: 고객이 직접 물건을 구매하고 배달까지 처리.
    • DI 방식: 택배회사가 물건을 고객에게 전달. 고객은 물건이 어디서 왔는지 알 필요 없이, 사용만 하면 됩니다.
  3. 식당의 셰프와 재료:
    • 의존성: 셰프는 요리를 하려면 재료가 필요합니다.
    • 전통적인 방식: 셰프가 직접 재료를 구하고 요리를 합니다.
    • DI 방식: 주방 보조(외부 주입)가 재료를 제공하고, 셰프는 재료를 받아 요리만 합니다. 셰프는 재료가 어디서 왔는지 몰라도 요리를 할 수 있습니다.

4. 코드로 이해하기

1) 전통적인 방식

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending email: " + message);
    }
}

class NotificationService {
    private EmailService emailService;

    public NotificationService() {
        // 직접 의존성 생성
        this.emailService = new EmailService();
    }

    public void notify(String message) {
        emailService.sendEmail(message);
    }
}

public class Main {
    public static void main(String[] args) {
        NotificationService notificationService = new NotificationService();
        notificationService.notify("Hello World!");
    }
}

문제점:

  1. NotificationService가 EmailService라는 구체적인 구현체에 강하게 결합되어 있습니다.
  2. 새로운 알림 방식(예: SmsService)을 추가하거나 테스트 시 Mock 객체로 대체하기 어려움.

2) DI 방식: 생성자 주입

// 의존성을 추상화
interface MessageService {
    void sendMessage(String message);
}

// 다양한 구현체
class EmailService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}

class SmsService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

class NotificationService {
    private final MessageService messageService;

    // 생성자를 통해 의존성을 주입받음
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        messageService.sendMessage(message);
    }
}

public class Main {
    public static void main(String[] args) {
        // 의존성을 외부에서 주입
        MessageService emailService = new EmailService();
        NotificationService notificationService = new NotificationService(emailService);
        notificationService.notify("Hello via Email!");

        // 의존성을 다른 구현체로 교체
        MessageService smsService = new SmsService();
        NotificationService smsNotificationService = new NotificationService(smsService);
        smsNotificationService.notify("Hello via SMS!");
    }
}

특징:

  • NotificationService는 MessageService 인터페이스에만 의존합니다.
  • 의존성(EmailService나 SmsService)은 외부에서 주입되므로, NotificationService는 구현체가 무엇인지 알 필요가 없습니다.

3) DI 방식: 스프링 프레임워크를 활용

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// 의존성을 추상화
interface MessageService {
    void sendMessage(String message);
}

@Component
class EmailService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}

@Component
class NotificationService {
    private final MessageService messageService;

    @Autowired
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        messageService.sendMessage(message);
    }
}

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        var context = SpringApplication.run(Main.class, args);

        NotificationService notificationService = context.getBean(NotificationService.class);
        notificationService.notify("Hello via Email!");
    }
}

특징:

  1. 스프링 컨테이너가 객체를 생성하고, 의존성을 자동으로 주입합니다.
  2. 개발자는 객체 생성과 의존성 주입의 번거로움에서 벗어나 비즈니스 로직에 집중할 수 있습니다.

5. DI의 장점

  1. 유연성: 의존성을 쉽게 교체할 수 있어 확장 가능성이 높습니다.
  2. 결합도 감소: 객체 간의 강한 결합을 제거하여 코드의 재사용성과 유지보수성을 높입니다.
  3. 테스트 용이성: Mock 객체를 주입하여 단위 테스트를 쉽게 수행할 수 있습니다.
  4. 코드 가독성: 각 클래스가 자신의 역할에만 집중하므로 코드가 간결해집니다.

6. 요약 표

항목 내용
정의 객체 간의 의존성을 외부에서 주입받아 결합도를 낮추고 유연성을 높이는 설계 방식.
핵심 개념 1. 객체가 직접 의존성을 생성하지 않고 외부에서 주입받음2. 결합도 감소 및 다형성 극대화
DI의 유형 생성자 주입, 세터 주입, 필드 주입
일상적인 예시 1. 가전제품과 콘센트2. 택배 서비스3. 식당의 셰프와 재료
장점 유연성 증가, 결합도 감소, 테스트 용이성, 코드 재사용성 및 가독성 향상
대표적인 활용 프레임워크 스프링 프레임워크 (DI 컨테이너를 통해 자동 의존성 주입)

7. 결론

DI는 객체 간의 의존성을 외부로 분리하여 개발자가 결합도를 낮추고, 코드를 더 유연하게 설계할 수 있도록 돕습니다. 이를 통해 객체는 자신의 역할에 집중하고, 의존성 관리의 복잡성은 외부 컨테이너(프레임워크)가 처리하게 됩니다. 일상적인 예시와 코드로 이해하면 DI는 "객체에 필요한 것을 외부에서 제공받아 유연하고 확장 가능한 구조를 만든다"는 개념으로 쉽게 이해할 수 있습니다.

 

**IOC(제어의 역전)**와 **DI(의존성 주입)**는 밀접하게 연관되어 있지만, 완전히 같은 말은 아닙니다. 둘의 관계는 다음과 같이 설명할 수 있습니다:

 

 

+ Recent posts