본문 바로가기

Spring

Spring [IoC와 DI]

설명에 들어가기 앞서, 먼저 단어를 정리합니다.

객체 지향의 원리 중 OCP 원칙과 DIP원칙

OCP원칙이란? 개방-폐쇄 원칙, 소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.

DIP원칙이란? 의존 관계를 맺을 때, 변화하기 쉬운 것보다 변화하기 어려운 것에 의존하는 원칙. DIP를 만족하는 설계는 변화에 유연한 시스템이 된다.

요구사항 : 주문과 할인 도메인 설계

  • 주문과 할인 정책
    • 회원은 상품을 주문할 수 있다.
    • 회원 등급에 따라 할인 정책을 적용할 수 있다.
  • 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인 적용 (나중에 변경될 수 있음)
  • 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수도 있다. (미확정)

여기서 의문이 들 수 있는 부분은 다음과 같다.

할인 정책은 변경될 가능성이 높네, 되도록이면 할인 정책에 의존이 덜 되게 하고 싶은데, 방법이 없을까?

객체 지향적 언어의 특징을 적용해 볼 수 있는데, 바로 인터페이스를 통한 동적 인스턴스 할당이다.

public interface MemberRepository{
	void save(Member member);
}
...
public MemoryMemberRepository implements MemberRository{
	Connector con;
	void save(Member member){
    	con.execute(member);
    }
}
...
public DatabaseMemberRepository implements MemberRepository{
	Connector con;
    void save(Member member){
    	con.execute(member);
    }
}
....
//실제 사용하는 부분
public class ServiceImpl{
	private final MemberRepository;
    public ServiceImpl(MemberRepository mp)
    {
    	mp = new MemoryMemberRepository();
        //mp = new DatabaseMemberRepository(); //코드의 변경을 최소화하여 갈아끼울 수 있다!
    }
    void MemberSave(Member member)
    {
    	mp.save(member);
    }
}

인터페이스는 변경되지 않고, 구현체만 갈아끼울 수 있기 때문에 코드의 변경을 최소화할 수 있다는 장점이 있다.

이제 스프링에서는 이러한 의존 문제를 최소화하는 방법은 어떤지 알아보자

AppConfig

구현체들을 주입하는 역할을 수행하는 설정파일로, 위에 나온 변경조차 프로그래머가 하지 않도록, 런타임에 지정한 구현 객체들을 주입해준다. 이러한 역할을 수행해주는 객체를 DI (Dependency Injection) 컨테이너라 한다.

또한 의존 관계가 프로그래머에서 프레임워크로 넘어가기 때문에 IoC (Inversion of Control : 의존 관계의 역전)의 다른 말이기도 하다.

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>
<!-- cglib란 구현체가 필요해요! -->

 

 

 

여기서 프레임워크와 라이브러리의 차이점을 간단히 알아보자.

프레임워크 : 내가 작성한 코드를 대신 제어하고, 실행함 (JUnit)

라이브러리 : 내가 작성한 코드가 직접 제어의 흐름을 담당함

 

이제 코드로 알아보자.

스프링에서는 애노테이션이 붙어있는 객체들을 찾아서 역할을 구분하게 되는데, AppConfig같은 설정파일은 @Configuration을 붙인다.

@Configuration
public class AppConfig {
  @Bean
  public MemberService memberService() {
      return new MemberServiceImpl(memberRepository());
  }
  @Bean
  public OrderService orderService() {
      return new OrderServiceImpl(
      memberRepository(),
      discountPolicy());
  }
  @Bean
  public MemberRepository memberRepository() {
      return new MemoryMemberRepository();
  }
  @Bean
  public DiscountPolicy discountPolicy() {
      return new RateDiscountPolicy();
  }
}

각 메서드에 @Bean을 붙이게 되는데, 이렇게 하면 스프링 컨테이너에 스프링 빈으로 등록된다.

여기서 스프링 빈은 우리가 new 연산자로 객체를 만드는게 아닌, 컨테이너 (ApplicationContext)가 대신 만들어서 안에 담고있는 객체를 의미한다.

 

의존성 주입의 세 가지 방법

1. 생성자 주입

2. 필드 주입

3. Setter 메서드 주입

스프링에서 권장하는 방법은 생성자를 통한 주입이다. 필수적으로 사용해야하는 의존성 없이는 인스턴스를 만들지 못하도록 강제할 수 있기 때문이다.

 

Reference

스프링 핵심 원리 - 기본편