본문 바로가기
Spring Study

Dependency Injection (DI : 의존성주입)

by xogns93 2024. 8. 7.

의존성 주입(Dependency Injection, DI)은 객체 지향 프로그래밍에서 객체의 의존성을 외부에서 주입받는 방식입니다. 이를 통해 코드의 유연성과 재사용성을 높일 수 있습니다. DI를 구현하는 세 가지 주요 방법은 생성자 주입(Constructor Injection), 메서드 주입(Method Injection), 필드 주입(Field Injection)입니다.

세 가지 주요 DI 방법

  1. 생성자 주입(Constructor Injection):
    • 의존성을 생성자 매개변수로 전달받아 주입합니다.
    • 의존성이 반드시 필요할 때 사용하며, 주입받는 의존성이 변경되지 않도록 보장합니다.
  2. 메서드 주입(Method Injection):
    • 의존성을 메서드 매개변수로 전달받아 주입합니다.
    • 주로 설정 메서드를 통해 선택적인 의존성을 주입할 때 사용합니다.
  3. 필드 주입(Field Injection):
    • 의존성을 클래스의 필드에 직접 주입합니다.
    • 가장 간단한 방식이지만, 테스트와 리팩토링이 어려워질 수 있습니다.

 

팩토리 메서드에 의한 의존성 주입

팩토리 메서드 패턴은 객체 생성 로직을 별도의 메서드로 캡슐화하여 클라이언트 코드와 객체 생성 로직을 분리합니다. 이를 통해 의존성 주입을 보다 유연하게 구현할 수 있습니다.

예제: 팩토리 메서드를 통한 의존성 주입

다음은 스프링 프레임워크를 사용하여 팩토리 메서드에 의한 의존성 주입을 구현하는 예제입니다.

서비스 인터페이스와 구현 클래스

public interface MessageService {
    void sendMessage(String message);
}

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

팩토리 클래스

public class ServiceFactory {
    public MessageService createMessageService() {
        return new EmailMessageService();
    }
}

의존성을 주입받는 클래스

public class MessageProcessor {
    private MessageService messageService;

    // 생성자 주입
    public MessageProcessor(MessageService messageService) {
        this.messageService = messageService;
    }

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

Spring 설정 파일 (XML)

<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- ServiceFactory 빈 정의 -->
    <bean id="serviceFactory" class="com.example.ServiceFactory"/>

    <!-- MessageService 빈 정의 -->
    <bean id="messageService" factory-bean="serviceFactory" factory-method="createMessageService"/>

    <!-- MessageProcessor 빈 정의 -->
    <bean id="messageProcessor" class="com.example.MessageProcessor">
        <constructor-arg ref="messageService"/>
    </bean>

</beans>

애플리케이션 클래스

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        MessageProcessor processor = context.getBean("messageProcessor", MessageProcessor.class);
        processor.processMessage("Hello, World!");
    }
}

 

세 가지 주요 DI 방법의 설명

  1. 생성자 주입(Constructor Injection):
    • MessageProcessor 클래스의 생성자를 통해 MessageService를 주입받습니다.
    • 생성자 주입은 객체가 생성될 때 모든 필수 의존성을 주입받아야 하므로, 의존성 주입이 완료된 이후에 객체가 완전한 상태가 됩니다.
    • 장점: 의존성이 변하지 않으며, 객체의 불변성을 유지할 수 있습니다. 필수 의존성을 강제할 수 있습니다.
    • 단점: 의존성이 많아지면 생성자의 매개변수가 많아질 수 있습니다.
  2. 메서드 주입(Method Injection):
    • MessageProcessor 클래스에서 setMessageService와 같은 설정 메서드를 통해 선택적인 의존성을 주입할 수 있습니다.
    • 메서드 주입은 선택적인 의존성을 주입하거나, 의존성이 변경될 수 있는 경우에 사용됩니다.
    • 장점: 선택적인 의존성을 주입할 수 있습니다. 여러 의존성을 설정하는데 유연합니다.
    • 단점: 객체가 완전한 상태가 아닌 경우가 발생할 수 있습니다. 의존성이 누락될 위험이 있습니다.
  3. 필드 주입(Field Injection):
    • MessageProcessor 클래스의 필드에 직접 @Autowired 애너테이션을 사용하여 의존성을 주입합니다.
    • 필드 주입은 가장 간단한 방식이지만, 테스트와 리팩토링이 어려울 수 있습니다.
    • 장점: 간단하고 명료합니다.
    • 단점: DI 프레임워크에 강하게 의존하며, 테스트와 리팩토링이 어려울 수 있습니다.

 

결론

스프링 IoC 컨테이너는 다양한 방법을 통해 의존성을 주입할 수 있습니다. 생성자 주입, 메서드 주입, 필드 주입을 통해 객체 간의 결합도를 낮추고, 코드의 유연성과 테스트 용이성을 높일 수 있습니다. 팩토리 메서드를 사용하여 의존성을 주입하면, 객체 생성 로직을 캡슐화하고 유연성을 높일 수 있습니다. 이러한 다양한 DI 방법을 적절히 활용하면, 더 유연하고 유지보수 가능한 애플리케이션을 개발할 수 있습니다.