해당 강의는 김영한 강사님의 유료 강의로, 아주 간략하게 배운 부분들을 짚고 넘어가는 식으로 작성하였습니다.
생략된 부분이 많습니다. 전체 소스코드 공개도 금지이므로 블로그에 부분적으로만 올릴 생각입니다.
강의를 보며 포스트잇을 붙이는 느낌으로 제가 보기 위해 작성하는 글이니
학습을 위해서라면 아래 링크의 강의를 직접 들으시는 것을 추천합니다!
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
저번 시간까지 회원, 주문, 할인을 설계하고 개발했다.
마지막에 과연 지금의 정액할인정책에서 정률할인정책으로 깔끔하게 바꿀 수 있는가? 라고 했었다.
이번에 이것에 대해 공부해보도록 할 것이다.
만약 기획자가 나타나서 새로운 할인 정책을 추가해달라고 요구했다고 생각해보자.
그러면 새로운 할인 정책을 추가할텐데, 막상 새로운 할인 정책을 적용하려니 문제점이 발생한다.
DIP, OCP를 못지키는 문제가 발생하게 되고, 이 문제를 해결하기 위해 여러 과정을 거치게 된다.
이 강의의 여러 과정을 거치면서 자연스럽게 스프링의 핵심기능인 스프링 컨테이너가 왜 탄생했는지 알게될 것이다.
제일 마지막에는 만든 순수한 자바 코드를 스프링 컨테이너에서 동작하도록 간단하게 바꿔볼 것이다.
새로운 할인 정책 개발
기획자 : 지금 적용하고 있는 고정 금액 할인이 아니라 정률 할인으로 변경하고 싶다.
FixDiscountPolicy를 RateDiscountPolicy로 갈아끼워보자
RateDiscountPolicy를 작성하고 테스트를 해보자
우클릭 - Generate - Test.. 를 누르면
해당 창이 뜬다. OK를 눌러주자
그러면 이렇게 RateDiscountPolicyTest 파일이 생성된다.
여기서 이제 테스트를 해보자
RateDiscountPolicy가 정말 10% 할인이 되는가를 검증해보자.
어제 했듯이
given : 이런 이런게 주어졌을 때,
when : 이렇게 했을 때,
then : 이렇게 된다.
//given 에 member 객체를 생성하고
Member member = new Member(1L, "memberVIP", Grade.VIP);
//when 에 할인된 금액 구하고
int discount = discountPolicy.discount(member, 10000);
//then 에서 할인된 금액(discount)가 1000원이 맞는지 검사한다.
Assertions.assertThat(discount).isEqualTo(1000);
Grade.VIP일 경우 테스트 결과 )
맞다는 것을 확인할 수 있다.
이렇게 문구를 띄우려면 @DisplayName("~~") 를 사용하면 된다.
그런데 이렇게 성공 테스트도 중요한데, 실패 테스트도 꼭 만들어봐야 한다.
VIP가 아닐 경우 할인이 적용되지 않아야한다는 테스트도 해보자
Grade.BASIC일 경우 테스트 결과 )
기대했던건 1000원인데 실제론 0원이 나왔다는 문구도 친절하게 출력된다.
새로운 할인 정책 적용과 문제점
방금까지 새로운 할인 정책을 추가했고 테스트까지 완료하였다.
이제 아까 만들었던 할인 정책을 적용해보자!
할인 정책을 변경하려면 우선 OrderServiceImpl로 들어가야 한다.
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
위 소스코드에서
FixDiscountPolicy()를 방금 만든 할인 정책인 RateDiscountPolicy()로 변경해야 한다.
난 이걸 보고 이게 왜 문제라는 거지? 잘 갈아끼워지는거 아닌가? 싶었는데 사실 이게 문제점이라고 한다.
역할과 구현을 충실히 분리했느냐? : YES
다형성도 활용하고, 인터페이스와 구현 객체를 분리했느냐? : YES
OCP, DIP 같은 객체지향 설계 원칙을 충실하게 준수했느냐? : NO
클래스 의존관계를 살펴보면 인터페이스 뿐만 아니라 구현 클래스에도 의존하고 있다.
추상에만 의존해야 하는데, 추상 뿐만 아니라 구체에도 의존한다는 것이다.
즉, 이는 DIP 위반이다. [DIP : 구체(구현)에 의존하지 말고 항상 추상(인터페이스)에 의존하라]
이렇게 DIP를 위반했기 때문에 어떤 문제가 생기냐면,
OrderServiceImpl이 FixDiscountPolicy도 의존했기 때문에 FixDiscountPolicy를 빼고
RateDiscountPolicy로 바꿔주어야 한다.
한마디로 FixDiscountPolicy를 RateDiscountPolicy로 바꾸는 순간 OrderServiceImpl의 소스코드도
함께 변경해야 한다는 뜻이다.
여기서 OCP 위반인 것도 확인할 수 있다.
(OCP : 기존의 코드를 변경하지 않으면서 기능을 추가할 수 있도록 설계가 되어야 한다)
문제점 해결하기
그러면 지금까지 짰던 코드를 어떻게 바꿔야할까?
대체 어떻게 이 문제를 해결할 수 있을까?
> DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경해야 한다.
작성한 코드를 예로 들면 OrderServiceImpl이 DiscountPolicy 인터페이스에만 의존하도록 해야 한다는 것이다.
기존 코드 (OrderServiceImpl가 DiscountPolicy 인터페이스와 FixDiscountPolicy 구현 클래스에 의존)
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
변경한 코드 (OrderServiceImpl가 DiscountPolicy 인터페이스에만 의존)
private DiscountPolicy discountPolicy;
그런데 막상 이 코드를 실행하면 NPE (null pointer exception)이 발생한다.
왜냐하면 위 코드에서 discountPolicy에 아무값도 할당되지 않기 때문이다.
이를 해결하기 위해선
누군가가 OrderServiceImpl 클라이언트에 DiscountPolicy의 구현 객체를 대신 생성하고 주입해주어야 한다.
※ 관심사의 분리
기존 코드는 마치 A역할(인터페이스)를 하는 배우(구현체)가 B역할을 하는 배우를 직접 초빙하는 것과 같다.
> 관심사를 분리하자
배우는 본인의 역할을 수행하는데 집중해야하고, 어떤 배우가 와도 같이 공연을 할 수 있어야 한다.
공연을 구성하고, 담당 배우를 섭외하고, 역할에 맞는 배우를 지정하는 별도의 '공연 기획자'가 나올 시점이다.
공연 기획자를 만들고 배우와 공연 기획자의 책임을 확실히 분리하자
AppConfig
애플리케이션 전체 동작 방식을 구성하기 위해, 구현 객체를 생성하고 연결하는 책임을 갖는
별도의 설정 클래스를 만들어보자
1. MemberServiceImpl , MemoryMemberRepository
어디선가 AppConfig를 통해서 memberService()를 불러다 쓰면,
MemberServiceImpl 객체가 생성되는데, 그 때 MemoryMemberRepository가 들어간다.
결국 memberRepository에 MemoryMemberRepository가 들어가게 된다.
생성자를 통해 객체가 생성된 것이 들어간다고 해서 이를 생성자 주입이라고 한다.
객체의 생성과 연결을 AppConfig가 담당하는 것이다.
객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다. 즉, 관심사의 분리가 이루어졌다.
이렇게되면 MemberServiceImpl 는 추상화에만 의존하게 된다. 즉, DIP를 지키게 된다.
MemberServiceImpl 는 이제부터 의존관계에 대한 고민은 외부에게 맡기고 실행에만 집중하게 된다.
클라이언트인 MemberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서
DI (의존관계 주입, 의존성 주입) 이라고 한다.
2. OrderServiceImpl , FixDiscountPolicy
2개라도 같은 원리로 의존성을 주입해주면 된다.
다음 시간엔 작성한 코드의 테스트와 AppConfig 리팩터링에 대해 작성하겠다.
'김영한님의 스프링 강의 학습 > 스프링 핵심 원리' 카테고리의 다른 글
#4 스프링 컨테이너와 스프링 빈 (0) | 2022.11.10 |
---|---|
#3 스프링 핵심 원리 이해2 - 객체지향 원리 적용2 (0) | 2022.10.01 |
#2 스프링 핵심 원리 이해 - 예제 만들기2 (0) | 2022.09.29 |
#2 스프링 핵심 원리 이해 - 예제 만들기1 (0) | 2022.09.28 |
#1 객체 지향 설계와 스프링 (0) | 2022.07.07 |