기존의 문제점

client가 객체를 생성하고 연결하면 중간에 서버에서 객체에 대한 설정이 바뀔 경우 클라이언트의 소스를 같이 고쳐야한다. 

AppConfig 등장

애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들자.

package hello.core;

import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class AppConfig {
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
	}
    public OrderService orderService() {
    	return new OrderServiceImpl(
        	new MemoryMemberRepository(),
            	new FixDiscountPolicy());
    }
}

객체의 생성과 연결은 AppConfig 가 담당한다.
DIP 완성: MemberServiceImpl MemberRepository 인 추상에만 의존하면 된다. 이제 구체 클래스를 몰라도 된다.
관심사의 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다. MemberServicdImpl, OrderServiceImpl 은 기능을 실행하는 책임만 지면 된다.

AppConfig 리팩토링

중복을 제거하고, 역할에 따른 구현이 보이도록 리팩터링 하자. 

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
public class AppConfig {
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    public OrderService orderService() {
    	return new OrderServiceImpl(
                memberRepository(),
                discountPolicy());
	}
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
	}
    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
	}
}

new MemoryMemberRepository() 이 부분이 중복 제거되었다. 이제 MemoryMemberRepository 를 다른 구현체로 변경할 때 한 부분만 변경하면 된다.
AppConfig 를 보면 역할과 구현 클래스가 한눈에 들어온다. 애플리케이션 전체 구성이 어떻게 되어있는지 빠르게 파악할 수 있다

결론 : AppConfig의 등장으로 애플리케이션이 크게 사용 영역과, 객체를 생성하고 구성(Configuration)하는 영역으로 분리되었다.
          SRP, DIP, OCP 적용.

IoC, DI, 그리고 컨테이너

IoC(Inversion of Control) : 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)이라 한다.

  • 프레임워크 vs 라이브러리
    프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크가 맞다. (JUnit)
    반면에 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것은 프레임워크가 아니라 라이브러리다.

의존관계 주입 DI(Dependency Injection)

  • 정적인 클래스 의존 관계
클래스 다이어그램
  • 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계

객체 다이어그램

AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을  IoC 컨테이너 또는 DI 컨테이너 한다. (어셈블러, 오브젝트 팩토리)

 

AppConfig 스프링 기반으로 변경

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.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();
	} 
}

AppConfig 설정을 구성한다는 뜻의  @Configuration 붙여준다.

메서드에 @Bean 붙여준다. 이렇게 하면 스프링 컨테이너에 스프링 빈으로 등록한다.

 

//        AppConfig appConfig = new AppConfig();
//        MemberService memberService = appConfig.memberService();

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);​

순수 java 코드에서 선언한 AppConfig 대신 스프링 컨테이너를 적용한다.

 

스프링 컨테이너

  • ApplicationContext 를 스프링 컨테이너라 한다.
  • 스프링 컨테이너는 @Configuration 이 붙은 AppConfig 를 설정(구성) 정보로 사용한다. 여기서 @Bean 이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.
  • 스프링 빈은 @Bean 이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다. ( memberService , orderService )

'Spring > core' 카테고리의 다른 글

의존관계 자동 주입  (0) 2022.04.23
컴포넌트 스캔  (0) 2022.04.03
싱글톤 컨테이너  (0) 2022.04.02
스프링 컨테이너와 스프링 빈  (0) 2022.04.02
객체지향 프로그래밍이란?  (0) 2022.01.31

+ Recent posts