Spring Boot

Spring Boot - Inversion of Control (IoC), Dependency Injection (DI) and Bean

sounglikane 2024. 10. 7. 17:58

1.  제어의 역전 (Ioc)

IoC는 프로그램에서 제어의 흐름을 반전시키는 설계 원칙입니다. 전통적인 프로그래밍에서는 프로그래머가 코드를 작성하여 다른 코드 조각을 호출하는 방식입니다. IoC에서는 제어가 반전되어, 프레임워크나 컨테이너가 흐름을 제어하게 됩니다.

 

예시: 간단한 application에서 UserService 클래스가 새 사용자에게 환영 이메일을 보내는 경우를 생각해봅시다.

 

   1-1. IoC 없이:

public class UserService {
    private EmailService emailService;

    public UserService() {
        this.emailService = new EmailService(); // UserService에서 제어
    }

    public void registerUser(String email) {
        // 사용자 등록 로직
        emailService.sendWelcomeEmail(email);
    }
}

위 코드에서 UserService 클래스는 EmailService의 instance를 직접 제어합니다.

 

   1-2. IoC와 함께:

public class UserService {
    private EmailService emailService;

    public UserService(EmailService emailService) {
        this.emailService = emailService; // Framework에 제어를 위임
    }

    public void registerUser(String email) {
        // 사용자 등록 로직
        emailService.sendWelcomeEmail(email);
    }
}

여기서 EmailService는 UserService에 주입되므로, 의존성의 생성 및 관리는 반전됩니다.

 

2. 의존성 주입 (DI)

DI는 IoC의 특정 유형으로, 클래스에 의존성을 제공하는 방식입니다. 이를 통해 더 많은 유연성과 쉬운 테스트가 가능해지며, 구현을 교체해도 의존 클래스는 변경할 필요가 없습니다.

 

예시: UserService 예제를 계속해서 보겠습니다.

public class EmailService {
    public void sendWelcomeEmail(String email) {
        // 이메일 보내기 로직
        System.out.println("Sending welcome email to " + email);
    }
}

public class UserService {
    private EmailService emailService;

    // 생성자 주입 (DI의 한 유형)
    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void registerUser(String email) {
        // 사용자 등록 로직
        emailService.sendWelcomeEmail(email);
    }
}

이 예제에서 UserService는 생성자를 통해 EmailService의 인스턴스를 받습니다. 이것을 생성자 주입이라고 합니다. Setter 주입도 있으며, 세터 메소드를 통해 의존성을 설정합니다.

 

3. IoC와 DI를 효율적으로 사용할 때와 방법

  1. IoC 사용: 코드의 결합도를 줄이고 framework가 흐름을 관리하도록 할 때 사용합니다. 이렇게 하면 다른 구성 요소와의 통합이 용이하고, 긴밀한 결합을 줄일 수 있습니다.
  2. DI 사용: 클래스의 테스트 용이성을 향상시키기 위해 사용합니다. 의존성이 외부에서 제공되므로, 단위 테스트 중에 쉽게 모의 객체(mock)나 스텁(stub)으로 교체할 수 있습니다.
  3. Spring Boot와 같은 프레임워크는 @Autowired@Component와 같은 주석을 통해 IoC 및 DI를 쉽게 사용할 수 있게 해줍니다. 예를 들어:
@Service
public class UserService {
    private final EmailService emailService;

    @Autowired // Spring이 주입을 처리함
    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void registerUser(String email) {
        emailService.sendWelcomeEmail(email);
    }
}

 

 

4. 요약

  • IoC는 프로그램의 제어 흐름이 일반적인 패턴에서 반전된다는 더 넓은 원칙입니다.
  • DI는 의존성이 내부에서 생성되는 것이 아니라 외부에서 주입되는 IoC의 특정 구현입니다.

IoC와 DI를 사용함으로써 더 유연하고 유지 관리가 용이하며 test 가능한 application을 만들 수 있습니다.

 

4.Spring에서의 Bean

Bean은 Spring IoC 컨테이너에 의해 관리되는 객체를 의미합니다. Spring은 애플리케이션의 컴포넌트를 정의하고 관리하기 위해 IoC를 활용하며, 이때 사용되는 객체가 바로 Bean입니다. Spring에서 Bean을 정의하는 방법은 여러 가지가 있지만, 일반적으로는 어노테이션을 사용합니다.

 

예시: Spring에서 Bean을 정의하고 사용하는 예를 보겠습니다.

import org.springframework.stereotype.Service;

@Service
public class EmailService {
    public void sendWelcomeEmail(String email) {
        // 이메일 보내기 로직
        System.out.println("Sending welcome email to " + email);
    }
}

@Service
public class UserService {
    private final EmailService emailService;

    // 생성자 주입
    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void registerUser(String email) {
        // 사용자 등록 로직
        emailService.sendWelcomeEmail(email);
    }
}

 

   4-1. IoC와 Bean의 관계

  • IoC는 Spring framework가 객체의 생성과 관리 책임을 가지도록 합니다.
  • Bean은 IoC container(컨테이너)가 관리하는 객체로, 생성과 life cycle이 IoC에 의해 제어됩니다.

   4-2. Spring의 IoC Container(컨테이너)

   Spring IoC 컨테이너는 Bean의 생성, 조립, 관리, 그리고 의존성 주입을 수행합니다. Spring은 ApplicationContext와 같은 컨테이너를 통해 이러한 작업을 처리합니다.

 

 

IoC container 사용 에시: 

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // UserService Bean 가져오기
        UserService userService = context.getBean(UserService.class);
        userService.registerUser("example@example.com");
    }
}

 

 

   4-3. 요약

  • IoC는 제어의 흐름을 반전시켜 개발자가 객체의 생성을 관리하지 않도록 합니다.
  • Bean은 Spring IoC 컨테이너에 의해 관리되는 객체로, 의존성 주입을 통해 필요한 객체를 주입받습니다.

IoC와 Bean을 활용하면 코드의 결합도를 줄이고, test 가능성을 높이며, 더 유연한 application 구조를 만들 수 있습니다.