- 해당 게시물은 인프런 "스프링 핵심 원리 - 기본편" 강의를 참고하여 작성한 글입니다.
- 자세한 코드 및 내용은 강의를 참고해 주시길 바랍니다.
강의링크 -> 스프링 핵심 원리 - 기본편 (김영한)
회원
- 회원을 가입하고 조회할 수 있다.
- 회원은 일반과 VIP 두 가지 등급이 있다.
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)
주문과 할인 정책
- 회원은 상품을 주문할 수 있다.
- 회원 등급에 따라 할인 정책을 적용할 수 있다.
할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
- 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루
고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)
요구사항을 확인해보니 확정되지 않은 사항들이 많음을 확인할 수 있습니다. 이는 인터페이스를 활용하여 객체 지향적으로 설계하면 될 것 같습니다.
Member
클래스 다이어그램입니다. 데이터 저장 위치가 아직 정해지지 않았다고 하니 MemberRepository
인터페이스를 구현하고 인터페이스를 상속받아 구현체를 개발합니다. 이 챕터에서는 데이터를 메모리에 저장하는 상황으로 가정MemoryMemberRepository
를 구현하겠습니다.
MemberServiceImpl
의 Impl에 대해 궁금해하실 수 있는데 인터페이스에 대한 구현체가 하나만 있는 경우 관례상 인터페이스명에 Impl을 붙여서 구현체 이름을 만든다고 합니다.
Member
엔티티입니다. Grade는 enum
으로 BASIC, VIP가 있습니다.
public class Member {
private Long id;
private String name;
private Grade grade;
public Member(Long id, String name, Grade grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
public Long getId() {
return id;
}
... // getter and setter
MemberRepository
인터페이스입니다. 회원 가입, 조회 기능을 요구했으니 save
와 findById
메소드를 선언합니다.
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
MemberRepository
를 상속받아 MemoryMemberRepository
을 개발합니다. 같은 방법으로 MemberService
인터페이스를 구현하고 MemberServiceImpl
을 개발하면 됩니다. 후에, DB 저장 위치가 확정돼도 다형성을 지키면서 유연하게 변경할 수 있을 것입니다.
주문과 할인 도메인 설계는 회원 도메인 설계에 비해 복잡합니다. 요구사항에서 할인정책은 변경될 수 있다고 했으니, 인터페이스로 구현하여 역할과 구현으로 구분해 줍니다. 전체적인 로직은 클라이언트가 주문을 생성하면 우선 MemberRepository
에서 회원 조회를 통해 회원 등급을 확인합니다. 그 후 확인한 회원 등급을 갖고 DiscountPolicy
에서 할인 금액을 반환합니다. 이후 OrderService
는 할인 금액을 포함한 주문 정보를 반환합니다.
DiscountPolicy
입니다. 할인 금액을 반환해 주는 역할인데 그 전에 회원의 등급을 확인해야하기 때문에 Member
를 파라미터로 받습니다. 요구사항에서는 등급이 VIP인 경우에 1000원을 할인해 준다고 했으니 인터페이스를 상속받아 FixDiscountPolicy
구현체를 개발합니다.
public interface DiscountPolicy {
int discount(Member member, int price);
}
Order
엔티티입니다. 주문정보를 담고 있습니다.
public class Order {
private Long memberId;
private String itemName;
private int itemPrice;
private int discountPrice;
public Order(Long memberId, String itemName, int itemPrice, int discountPrice) {
this.memberId = memberId;
this.itemName = itemName;
this.itemPrice = itemPrice;
this.discountPrice = discountPrice;
}
public int calculatePrice() {
return itemPrice - discountPrice;
}
... // getter and setter
}
OrderService
인터페이스입니다.
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
OrderService
인터페이스를 상속받아 구현체를 개발할 때는 우선 memberRepository
를 통해 Member
객체를 얻습니다. 그리고 얻은 Member
객체를 DiscountPolicy
의 파라미터로 주어야 할인 금액을 얻을 수 있습니다.
스프링의 도움 없이 순수 자바 코드로 객체 지향의 특성을 최대한 지키면서 요구사항에 맞는 코드를 작성해 봤습니다. 그렇지만, 앞에서 배운 OCP
, DIP
를 잘 지키고 있다고 보기는 어렵습니다.