본문 바로가기
JAVA Spring

관심사의 분리와 AppConfig

by ppirae 2022. 3. 16.

OrderServiceImpl에서 할인정책이 변경될때 DIP와 OCP를 위반하는 경우가 발생했다.

어떻게 문제를 해결할 수 있을까 ?

클라이언트 코드인 OrderServiceImpl 은 DiscountPolicy 의 인터페이스 뿐만 아니라 구체 클래스도 함께 의존한다.

그래서 구체 클래스를 변경할 때 클라이언트 코드도 함께 변경해야 한다.

DIP 위반 추상에만 의존하도록 변경(인터페이스에만 의존)

DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.

 

인터페이스에만 의존하도록 설계를 변경하자.

인터페이스에만 의존하도록 설계와 코드를 변경

그런데 구현체가 없는데 어떻게 코드를 실행할 수 있을까? 실제 실행을 해보면 NPE(null pointer exception)가 발생한다.이 문제를 해결하려면 누군가가 클라이언트인 OrderServiceImpl 에 DiscountPolicy 의 구현 객체를 대신 생성하고 주입해주어야 한다.

 

이때 필요한 것이 AppConfig생성자 주입(DI) 이다.

AppConfig

객체의 생성과 연결은 AppConfig 가 담당한다.

DIP 완성: MemberServiceImpl 은 MemberRepository 인 추상에만 의존하면 된다. 이제 구체 클래스를 몰라도 된다.

관심사의 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다.

 

appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl 을 생성하면서 생성자로 전달한다.

클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입이라 한다.


그 다음 MemberApp과 OrderApp이 인터페이스에만 의존하도록 코드를 변경합니다.


AppConfig의 역할들이 좀 더 잘 드러나도록 리팩토링 해보자.

public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    //나중에 DB로 변환하게 되면 이 코드만 수정하면 된다.
    private MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    //나중에 정률 할인 정책으로 변환하게 되면 이 코드만 수정하면 된다.
    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }
}

new MemoryMemberRepository() 이 부분이 중복 제거되었다.

이제 MemoryMemberRepository 를 다른 구현체로 변경할 때 한 부분만 변경하면 된다.

AppConfig 를 보면 역할과 구현 클래스가 한눈에 들어온다.

애플리케이션 전체 구성이 어떻게 되어있는지 빠르게 파악할 수 있다.


이제 정액 할인 정책에서 정률 할인 정책으로 변경할때

FixDiscountPolicy만 RateDiscountPolicy로 변경하면 끝이다 !

AppConfig 에서 할인 정책 역할을 담당하는 구현을 FixDiscountPolicy RateDiscountPolicy 객체로 변경했다.

이제 할인 정책을 변경해도, 애플리케이션의 구성 역할을 담당하는 AppConfig만 변경하면 된다.

클라이언트 코드인 OrderServiceImpl 를 포함해서 사용 영역의 어떤 코드도 변경할 필요가 없다.

 

AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라 한다.

의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라 한다.

또는 어셈블러, 오브젝트 팩토리 등으로 불리기도 한다.


지금까지 순수한 자바 코드만으로 DI를 적용했다. 이제 스프링을 사용해보자.

AppConfig 클래스에 @Configuration 어노테이션을 붙여주고

각각 @Bean을 붙여준다. Bean이 붙은 친구들은 Spring Container에 모두 등록된다.

그 다음 MemberApp과 OrderApp 부분의 기존 코드를 ApplicationContext를 이용해 바꿔준다.

memberApp
orderApp

이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너를 통해서 필요한 스프링 빈(객체)를 찾아야 한다.

스프링 빈은 applicationContext.getBean() 메서드를 사용해서 찾을 수 있다.

기존에는 개발자가 직접 자바코드로 모든 것을 했다면 이제부터는 스프링 컨테이너에 객체를 스프링 빈으로 등록하고, 스프링 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었다.


인프런 김영한님의 스프링 기본을 듣고 작성한 글입니다.

https://inf.run/maWn

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

 

'JAVA Spring' 카테고리의 다른 글

싱글톤 컨테이너  (0) 2022.03.20
스프링 컨테이너와 스프링 빈  (0) 2022.03.18
주문과 할인 도메인  (0) 2022.03.15
Spring 기본편 시작  (0) 2022.03.14
회원관리예제 웹MVC 개발  (0) 2022.01.23

댓글