[Spring] IoC, DI, 컨테이너

김엄지·2024년 5월 27일
0

Spring

목록 보기
20/21

들어가는 말

애플리케이션을 설계할 때, 객체를 생성하고 연결하는 역할과 실행하는 역할을 분리하는 것이 중요하다. 관심사 분리 !

AppConfig 클래스는 애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고 연결하는 설정 클래스로 사용된다.

먼저 필요한 개념들을 간략하게 정리하고 순수한 자바 코드와 Spring Framework를 사용한 AppConfig 클래스를 살펴보자.

  1. IoC, DI 개념 정리
  2. 순수한 자바 코드로 작성한 AppConfig 클래스
  3. Spring Framework를 사용하여 AppConfig 클래스 수정
    • Spring 설정 파일 > AppConfig
    • 사용 코드
  4. 사용된 애노테이션 @Configuration, @Bean에 대해서

IoC, DI 개념 정리

IoC (Inversion of Control, 제어의 역전)

IoC는 프로그램의 제어 흐름을 개발자가 아닌 외부(프레임워크나 컨테이너)에서 담당하게 하는 설계 원칙이다.

절차적 프로그래밍에서는 개발자가 객체의 생성, 의존성 주입, 메서드 호출 등을 직접 제어하지만, IoC를 적용하면 제어권을 프레임워크나 컨테이너에 위임하게 된다. 이를 통해 애플리케이션의 결합도를 낮추고, 유연성과 테스트 용이성을 높일 수 있게 된다.

DI (Dependency Injection, 의존성 주입)

DI는 IoC의 한 방법으로, 객체가 자신이 의존하는 객체를 직접 생성하지 않고 외부에서 주입받는 방식이다. 객체 간의 결합도를 낮추고, 코드의 재사용성과 테스트 용이성을 높일 수 있다. 주로 생성자 주입, 세터 주입, 인터페이스 주입 방식으로 구현된다.

1. 생성자 주입

필요한 의존성을 생성자의 매개변수로 받아서 주입하는 방식

public class Service {
    private Repository repository;

    public Service(Repository repository) {
        this.repository = repository;
    }
}

2. 세터 주입

필요한 의존성을 세터 메서드를 통해 주입하는 방식

public class Service {
    private Repository repository;

    public void setRepository(Repository repository) {
        this.repository = repository;
    }
}

3. 인터페이스 주입

필요한 의존성을 주입받기 위한 메서드를 인터페이스로 정의하고, 이를 구현하는 방식

public interface RepositoryAware {
    void setRepository(Repository repository);
}

public class Service implements RepositoryAware {
    private Repository repository;

    @Override
    public void setRepository(Repository repository) {
        this.repository = repository;
    }
}

순수한 자바 코드로 AppConfig 클래스

AppConfig 코드

DI 컨테이너의 역할을 수행하며, 의존성을 주입하고 객체를 생성하는 방식으로 DI를 수동으로 구현하고 있다.

DIP 완성: MemberServiceImpl은 MemberRepository인 추상에만 의존한다.

public class AppConfig {

    public MemberService memberService() {
        // MemberServiceImpl 생성 시 MemberRepository 주입
        return new MemberServiceImpl(MemberRepository());
    }

    public OrderService orderService() {
        // OrderServiceImpl 생성 시 MemberRepository와 DiscountPolicy 주입
        return new OrderServiceImpl(MemberRepository(), discountPolicy());
    }

    // 메서드
    public static MemberRepository MemberRepository() {
        // MemoryMemberRepository 인스턴스 생성
        return new MemoryMemberRepository();
    }

    public DiscountPolicy discountPolicy() {
        // RateDiscountPolicy 인스턴스 생성
        return new RateDiscountPolicy();
    }
}

제어의 역전 IoC

  • AppConfig 클래스는 MemberServiceImpl과 OrderServiceImpl 인스턴스를 생성할 때 필요한 의존성(MemberRepository, DiscountPolicy)을 직접 생성하여 주입한다.
  • 이는 객체가 자신이 의존하는 객체를 직접 생성하지 않고, AppConfig 클래스가 이를 대신 생성하여 주입한다.
  • 객체는 자신의 의존성을 직접 관리하지 않고 외부에서 주입받게 되어 제어의 역전이 이루어진다.

의존성 주입 DI

  • memberService() 메서드는 MemberServiceImpl 인스턴스를 생성할 때 MemberRepository 인스턴스를 주입한다.
  • orderService() 메서드는 OrderServiceImpl 인스턴스를 생성할 때 MemberRepository와 DiscountPolicy 인스턴스를 주입한다.

이는 생성자 주입 방식(Constructor Injection)으로, 생성자를 통해 필요한 의존성을 주입하는 형태이다.


Spring Framework를 사용한 AppConfig 클래스

기존에는 AppConfig 에서 직접 객체를 생성하고 DI를 했지만, 지금부터는 애너테이션 기반의 자바 설정 클래스로 스프링 컨테이너를 만들어보자.

AppConfig 클래스는 Spring 설정 파일로, 이를 통해 Spring 컨테이너가 객체를 관리하도록 한다. 각 메서드가 @Bean 애노테이션으로 정의되어 Spring 컨테이너에 의해 로드되며 객체의 생성과 의존성 주입이 자동으로 이루어진다.

AppConfig 코드

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(MemberRepository());
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(MemberRepository(), discountPolicy());
    }

    @Bean
    public static MemberRepository MemberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }
}

사용 코드

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService", MemberService.class); // 이름, 타입

사용된 애노테이션

@Configuration

@Configuration 애노테이션은 이 클래스가 하나 이상의 @Bean 메서드를 정의하고, Spring 컨테이너에 의해 빈 정의를 생성하고 제공하는 소스임을 나타낸다. 이는 설정 클래스임을 명시적으로 나타내며, Spring 컨테이너가 이 클래스를 처리하여 필요한 빈들을 등록하게 한다.

@Bean

@Bean 애노테이션은 메서드가 Spring 컨테이너에서 관리하는 빈 객체를 생성하는 팩토리 메서드임을 나타낸다. 이 애노테이션을 사용하여 객체를 정의하면, Spring 컨테이너는 해당 메서드를 호출하여 반환된 객체를 빈으로 등록하고 관리한다. 이를 통해 DI가 이루어지며, 필요한 의존성을 자동으로 주입할 수 있다.


참고 코드

  • 인프런 스프링 핵심 원리 - 기본편
profile
나만의 무언가를 가진 프로그래머가 되자

0개의 댓글