ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #5 Spring 핵심 원리 - DI를 통한 관심사 분리
    스프링 (inflearn 정리)/스프링 핵심 원리 2021. 9. 1. 09:20

    전 시간의 OrderServiceImpl.java 를 살펴보자.

     

    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

     

    위의 코드는 주문 서비스에서, 직접 할인 정책을 선택하고 있다.

    이것이 문제가 되는 이유는 전 시간의 마지막에 잠깐 설명을 했는데,

    만약 내가 다른 할인 정책을 반영하고 싶다면, 저 new RateDiscountPolicy() 를 다른 클래스로 변경해야한다.

    즉, OrderServiceImpl.java 에서 코드 변경이 일어나는 것이다. (OCP 위반)

     

     

     

    이를 해결하기 위해 AppConfig.java 를 사용할 것인데, 다음과 같은 일을 한다.

    애플리케이션 전체 동작 방법을 구성(config)하기 위해, 실제 동작에 필요한 구현객체를 생성하고, 생성자를 통해 연결(주입)하는 책임을 가지는 별도의 설정 클래스를 만든다.

     


    일단 MemberServiceImpl.java를 살펴보자.

    package hello.core.member;
    
    public class MemberServiceImpl implements MemberService {
    
        private final MemberRepository memberRepository = new MemoryMemberRepository();
    
        @Override
        public void join(Member member) {
            memberRepository.save(member);
        }
    
        @Override
        public Member findMember(Long memberId) {
            return memberRepository.findById(memberId);
        }
    }

     

    지금 여기에서, MemoryMemberReposiotry를 직접 가져와서 쓰고 있다.

    MemoryMemberReposiotry 를 변경하고 싶다면 저 코드를 직접 변경해야 하는 것이다.

    'new MemoryMemberRepository' 위치를 동적으로 사용할 수 있다면, 코드화가 가능할 것이다.

    그래서 우리는 생성자함수(constructor) 를 사용한다. (단축키 Alt + Insert)

     

    public class MemberServiceImpl implements MemberService {
    
        private final MemberRepository memberRepository;
    
        public MemberServiceImpl(MemberRepository memberRepository) {
            this.memberRepository = memberRepository;
        }

    이렇게 하면, 외부에서 memberRepository에 객체만 전달해주면, 어떤 저장소 처리 방식도 끼워넣을 수 있게 되는 것이다.

    즉 외부에서 주입을 해주는 것이다. 이것을 의존성주입(DI)라고 부른다.

     

    여기에서 잘 살펴보면, MemberServiceImpl 이 오직 인터페이스(추상화)에만 의존하고 있고,

    구현체에는 전혀 의존하지 않고 있음을 알 수 있다.

     


    이제 프로젝트 내의 의존성 주입들을 관리해주는 AppConfig.java를 만들보자.

    package hello.core;
    
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import hello.core.member.MemoryMemberRepository;
    
    public class AppConfig {
        public MemberService memberService() {
            return new MemberServiceImpl(new MemoryMemberRepository());
        }
    }

     

    hello.core 바로 밑에 만들었다.

    이제 AppConfig 를 통해 MemberServiceImpl 객체를 생성하고, 인자로 어떤 저장소 객체를 넘겨줄지 선택한다.

     

    이제 의존성 주입에 대한 코드를 여기에 모아놓으면, 프로젝트 내의 모든 의존성이 여기서 관리가 가능해진다.

     


     

     

    이제 다른 클래스들도 이렇게 DI를 통해 관리해주자.

    먼저 OrderServiceImpl 부터 수정해준다.

    public class OrderServiceImpl implements OrderService{
    
        private final MemberRepository memberRepository = new MemoryMemberRepository();
        private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

    이렇게 직접 객체를 생성해주는 것이 아닌,

    public class OrderServiceImpl implements OrderService{
    
        private final MemberRepository memberRepository;
        private final DiscountPolicy discountPolicy;
    
        public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
            this.memberRepository = memberRepository;
            this.discountPolicy = discountPolicy;
        }

    이렇게 constructor 를 통해 DI를 해준다.

     

     

     

    그리고 AppConfig 에서 주입할 객체를 넣어준다.

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

     

     


     

    이제 정말 MemberServiceImpl 과 OrderServiceImpl 은 오로지 자신의 구현에만 신경쓰게 된다.

    의존관계에 대한 고민은 외부(AppConfig.java)에서 처리하게 된 것이다.

     

    이게 바로 의존성 주입(DI) 를 통한 관심사의 분리다.

     

     

     

     

     

     

     

     

     

     

     

     


    이제 다 했다.

    테스트코드도 appconfig를 사용하도록 바꿔주자.

     

     

    일단 맴버 서비스 테스트코드다.

    public class MemberSerivceTest {
    
        MemberService memberService = new MemberServiceImpl();

    지금까지 테스트 코드에서도 이렇게 직접 객체를 생성했었다.

     

     

    public class MemberSerivceTest {
    
        MemberService memberService;
    
        @BeforeEach
        public void beforeEach() {
            AppConfig appConfig = new AppConfig();
            memberService = appConfig.memberService();
        }

    위와 같이 바꿔준다.

     

     

    그리고 오더 서비스 테스트에 가서도 똑같은 형식으로 바꿔준다.

    여기서는 memberservice랑 orderservice를 둘 다 사용하니, 둘 다 appconfig에서 가져온다.

    public class OrderServiceTest {
        MemberService memberService;
        OrderService orderService;
        @BeforeEach
        public void beforeEach() {
            AppConfig appConfig = new AppConfig();
            memberService = appConfig.memberService();
            orderService = appConfig.orderService();
        }

     

     

    참고로 여기서 @beforeEach 어노테이션은, 각 테스트가 시작되기 전에 매번 먼저 실행된다.

    댓글 0

Designed by Tistory.