예제를 통한 스프링 핵심 원리 이해 1

인프런 스프링 핵심 원리 기본편 강의를 들으며 정리한 글 입니다.

오늘 포스팅할 강의 내용

간단한 회원 주문 예제를 만들어보면서 객체지향, 스프링 핵심 원리를 공부해본다.

프로젝트 생성

  • Java 11
  • Gradle
  • Spring Boot : 2.3.*

비즈니스 요구사항

회원

  • 회원을 가입하고 조회할 수 있다.
  • 회원은 일반, VIP 두 가지가 있다.
  • 회원 데이터는 자체 DB 로 구축할 수도 있고, 외부 시스템을 연동할 수도 있다. (미확정)

주문

  • 회원은 상품을 주문할 수 있다.
  • 회원 등급에 따라 할인 정책을 적용할 수 있다.
  • 할인 정책은 모든 VIP 는 1000원을 할인해준다. (추후 변경 가능)
  • 할인 정책은 변경 가능성이 높다, 오픈 직전까지 정해지지 않을 수도 있다.

위의 요구사항을 보면 아직 미정인 요구사항이 많다. 개발자는 역할과 구현을 분리 후 역할 (인터페이스) 를 설계해 일단 개발을 시작해야 한다.

회원 도메인 개발

강의를 보기 전에 회원 도메인을 설계해봤다. 설계의 핵심은 아무래도

회원 데이터는 자체 DB 로 구축할 수도 있고, 외부 시스템을 연동할 수도 있다. (미확정)

이지 않을까? 아직 정해지지 않은 회원 데이터 저장소를 MemberRepository 인터페이스로 추상화 해야할거 같다.

MemberRepository.java
MemberService.java

이렇게 해놓고 강의를 들어봤다. 강의에서도 마찬가지로 회원 저장소의 역할과 구현을 분리하는 것을 설계의 포인트로 잡고, MemoryMemberRepository 라는 MemberRepository 구현체를 만들었다.

그리고 MemberService 인터페이스와 MemberServiceImpl 구현체를 따로 만들었다. 이 부분은 개인의 스타일로 service 인터페이스와 구현체가 1 대 1 이라 딱히 인터페이스와 구현체를 분리할 이유가 없다면 service 구현체 하나만 만들고 추후에 분리할 필요가 생길때 (모듈 분리 등으로) 리팩토링 하는것도 좋다고 한다.

MemberServiceImpl.java

MemberServiceImpl 에서 MemoryMemberRepository 를 생성하면서 다음 설계의 문제점을 생각해보게 했다.

회원 도메인 설계의 문제점

MemberServiceImpl 가 MemoryMemberRespository 를 의존하고 있다. 이는 DIP 위반이다. 그로 인해 회원 저장소 시스템을 메모리가 아닌 자체 DB 나 외부 시스템을 쓰게 되면 MemberServiceImpl 을 수정해야 할 것이다. OCP 위반이다.

그리고 설계를 하면서 다이어그램 그리는 것도 배웠다.

객체가 어떤 협력관계를 가지고 있는지 그림으로 그려보자

  • 기획자도 볼 수 있는 그림이어야 한다.
  • 객체의 역할에 집중해보자
  • 아직 정해지지 않았고, 자주 바뀌는 것이 무엇인가를 생각해봐라 -> 예제에서는 회원 저장소

클래스 다이어그램을 그려 보자

  • 코드 상에서의 클래스 참조 관계

객체 다이어그램을 그려 보자

  • 실행시에 객체들 참조 관계 (인터페이스 구현체들을 참조하고 있을것)

주문 할인 도메인 개발

주문 할인 도메인도 마찬가지로 일단 강의를 듣기 전에 설계해 보았다. 역시 설계의 포인트가 되는 요구 사항은 할인 정책에 대한 변경 가능성 인거 같다.

회원 등급에 따라 할인 정책을 적용할 수 있다.

할인 정책은 모든 VIP 는 1000원을 할인해준다. (추후 변경 가능)

할인 정책은 변경 가능성이 높다, 오픈 직전까지 정해지지 않을 수도 있다.

할인 정책의 역할을 하는 인터페이스를 도출 후, 할인 정책을 구현한 클래스를 만들고 할인 정책이 필요한 클라이언트에게 인터페이스를 제공해야 할거 같은 느낌이 강하게 왔다.

회원 도메인 개발 파트에서 다이어그램 그리는 것도 배웠으니 코드를 짜기전에 객체 협력 다이어그램을 그려 봤다.

주문 할인 객체 협력 다이어그램

할인 없는 할인 정책은 고정 금액 할인 정책에서 0원을 할인해주면 되는 것인데, 여러 할인 정책 구현체들을 만들어 보고 싶어서 굳이 넣었다.

그리고 객체 협력 다이어그램을 직접 그려보니 이런것 들이 좋았다.

  • 객체의 역할에 집중할 수 있었다.
  • 기획자나 개발을 모르는 사람에게 내 설계를 공유할 수 있다.

그리고 첨부 하지는 않았지만 클래스 다이어그램도 그려 보았다. 예제가 간단하기 때문에 그런걸 수도 있지만 클래스 간의 의존관계가 한눈에 파악되어서 좋았다. 필요없는 의존관계가 있는지, 의존관계가 순환 하지는 않는지 쉽게 파악됐다.

OrderServiceImpl.java
  1. 회원을 조회한다.
  2. DiscountService 에게 할인 정책을 물어본다.
  3. 회원, 상품, 할인정책을 가지고 Order 를 만든다.

Item 도메인 로직 (ItemRepository, ItemService) 은 복잡성을 고려하여 만들지 않았다.

DiscountService.java

회원에 대한 할인 정책을 결정하는 역할을 하는 인터페이스다.

회원의 등급 뿐만 아니라 다른 정보 가지고 할인 정책을 결정할 수 있을거 같아 Member 를 인자로 받았다.

Discount.java

할인 정책 역할을 하는 인터페이스다. 원래 금액에서 세부 구현에 따라 할인이 적용된 금액을 리턴 한다.

Order.createOrder

주문은 간단하게 id, member, item, orderPrice 를 가지고 있게 했다.

그리고 강의를 들어보고 내가 설계한 구조와 차이점을 살펴봤다.

강의에서도 할인 정책에 대한 역할과 구현을 나누는 것을 설계 포인트로 잡았다.

DiscountPolicy.java

할인 정책 역할을 하는 인터페이스다. member 에 따라서 다른 할인 금액을 리턴한다. 출력이 할인이 적용된 금액인 나의 설계와 다른 부분이다. 할인할 금액을 리턴한다. 다시 생각해보니 할인 정책 역할을 하는 객체가 정책에 따라 할인할 금액만 리턴하면 되는 것인데, 할인이 적용된 금액을 리턴하는 것이 이상했다.

DiscountPolicy 구현체

VIP 이면 1000 을 할인해주고, 아니면 0 을 할인해준다.

강의 듣고 수정한 OrderServiceImpl.java

OrderServiceImpl 에서 DiscountPolicy 과 구현체 FixDiscontPolicy 를 의존하고 있다. 아마 이어지는 강의에서 수정할 것 이다.

강의 듣고 수정한 Order.createOrder

Order 에서 원래 상품 금액과 할인 금액을 따로 가지고 있다.

원래 요구사항에 맞는 적절한 설계인거 같다.

객체의 역할에 집중해서 설계하는 것이 중요하다는 걸 상기시켜주는 섹션이었다.

다음 강의에서는 DI 와 객체지향의 5원칙을 가지고 구조를 개선시키는 강의가 될 거 같다.

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store