스프링 핵심 원리 이해 ☝

Yerim·2021년 8월 13일
0

Spring

목록 보기
2/9
post-thumbnail

✔ Inflearn 강의 수강 내용 정리글입니다!


주요 도메인 설계와 개발

본 강의에서는 간단한 회원, 주문 기능을 포함한 프로젝트 예제를 구현한다

회원 관리

💡 회원 도메인 설계

  • 회원 가입, 회원 조회
  • 회원 등급은 VIP, 일반 두 가지
  • 회원 데이터의 관리 방법은 미정 (인터페이스 활용)

📍 회원 도메인 협력 관계

각 객체들이 어떤 프로세스로 협력하는지 나타낸 것이다
클라이언트(웹 MVC에서는 controller와 같은)가 회원 서비스를 호출하면 회원 서비스는 회원 저장소를 호출하여 회원 관리 작업을 수행한다
이때 회원 저장소는 미정 상태이기 때문에 인터페이스의 구현 클래스로 어떤 회원 저장소를 사용할 것인지 추후에 결정할 수 있다

📍 회원 클래스 다이어그램

회원 서비스와 회원 저장소가 어떤 class를 통해 실행될 지 구체적으로 나타낸 다이어그램이다
여기서 인터페이스는 MemberService, MemberREpository 이고 구현 클래스는 MemberServiceImpl, MemoryMemberRepository, DbMemberRepository 이다

📍 회원 객체 다이어그램

최종적으로 작업 수행 시 어떤 객체를 호출하여 사용할 것인지 나타낸다
여기서 호출하는 회원 서비스는 MemberServiceImpl 이고 메모리 회원 저장소이므로 MemoryMemberRepository 이다

💡 회원 도메인 개발

  • 다음은 회원 저장소의 인터페이스와 구현 클래스를 구현한 코드의 일부분을 나타낸 것이다
    (간결한 확인을 위해 함수 구현 부분 코드와 불필요한 부분은 삭제했다)
public interface MemberRepository {
 void save(Member member);
 Member findById(Long memberId);
}
public class MemoryMemberRepository implements MemberRepository {

 @Override
 public void save(Member member) {
 }
 
 @Override
 public Member findById(Long memberId) {
 }
}

MemoryMemberRepository 클래스가 MemberRepository 인터페이스를 구현했다
만약 회원 저장소가 Memory가 아닌 DB 등 다른 것으로 바뀌게 된다면 새로운 구현 클래스를 생성하여 사용할 수 있다

  • 다음은 회원 서비스를 위한 인터페이스와 구현 클래스를 구현한 코드의 일부분을 나타낸 것이다
public interface MemberService {
 void join(Member member);
 Member findMember(Long memberId);
}
public class MemoryMemberRepository implements MemberRepository {

private final MemberRepository memberRepository = new MemoryMemberRepository();

 @Override
 public void save(Member member) {
 }
 
 @Override
 public Member findById(Long memberId) {
 }
}

본 프로젝트에서 사용할 MemberService 구현 클래스는 하나이므로 관용적으로 클래스 이름을 MemberServiceImpl 로 작성한다
현재는 구현 클래스가 하나이므로 MemberServiceImpl 를 사용해야 하는 이유에 대해 의문이 생길 수도 있다
그러나 만약, 일반 회원과 관리자로 회원 서비스의 구분이 생일 경우 등 회원 서비스의 유연한 기능 확장을 위해서는 MemberService 인터페이스를 구현한 구현 클래스를 사용하는 것이 좋다 (앞 글 참고)

💡 회원 도메인 실행과 테스트

작성한 코드가 제대로 동작하는지 확인하기 위해서는 테스트 코드의 작성이 필수적이다
테스트 코드 작성을 위해 Junit을 활용한다

  • 다음은 Junit을 이용해 작성한 테스트 코드의 일부를 나타낸 것이다
@Test
 void join() {
 //given
 Member member = new Member(1L, "memberA", Grade.VIP);
 //when
 memberService.join(member);
 Member findMember = memberService.findMember(1L);
 //then
 Assertions.assertThat(member).isEqualTo(findMember);
 }

새로 생성하여 가입한 회원 정보가 회원 저장소에 제대로 저장됐는지 테스트하기 위한 코드이다


상품 주문과 할인

💡 주문과 할인 도메인 설계

  • 회원은 상품을 주문
  • 회원 등급에 따른 할인 정책 적용
  • 할인 정책은 나중에 변경 가능

📍 주문 도메인 협력 관계

1. 주문 생성 : 클라이언트는 주문 서비스에 주문 생성을 요청한다
2. 회원 조회 : 할인을 위해서는 회원 등급이 필요하다. 그래서 주문 서비스는 회원 저장소에서 회원을
조회한다.
3. 할인 적용 : 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임한다
4. 주문 결과 반환 : 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다

📍 주문 도메인 클래스 다이어그램

주문과 할인 기능이 어떤 class를 통해 실행될 지 구체적으로 나타낸 다이어그램이다
각 인터페이스의 구현 케이스가 상황에 따라 선택되어 수행된다

📍 주문 도메인 객체 다이어그램

최종적으로 작업 수행 시 어떤 객체를 호출하여 사용할 것인지 나타낸다

💡 주문과 할인 도메인 개발

  • 할인 정책 인터페이스와 구현 클래스
public interface DiscountPolicy {
 int discount(Member member, int price);
}
public class FixDiscountPolicy implements DiscountPolicy {
 
 @Override
 public int discount(Member member, int price) {
 }
}

할인 정책은 언제든지 바뀔 수 있기 때문에 DiscountPolicy 인터페이스를 이용하여 새로운 할인 정책을 구현할 수 있다

  • 주문 서비스 인터페이스와 구현 클래스
public interface OrderService {
 Order createOrder(Long memberId, String itemName, int itemPrice);
}
public class OrderServiceImpl implements OrderService {
 private final MemberRepository memberRepository = new MemoryMemberRepository();
 private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
 
 @Override
 public Order createOrder(Long memberId, String itemName, int itemPrice) {
 }
}

주문 서비스 구현 클래스 또한 여기서는 한 가지만 사용할 것이기 때문에 OrderServiceImpl 로 클래스 이름을 설정한다

💡 주문과 할인 도메인 실행과 테스트

  • 다음은 Junit을 이용해 작성한 테스트 코드의 일부를 나타낸 것이다
@Test
 void createOrder() {
 long memberId = 1L;
 Member member = new Member(memberId, "memberA", Grade.VIP);
 memberService.join(member);
 Order order = orderService.createOrder(memberId, "itemA", 10000);
 Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
 }

회원 정보에 따라 할인 정책이 제대로 적용되었는지 확인하기 위해 작성된 테스트 코드이다


📍 회원 서비스 구현 클래스 MemberServiceImpl 에서 작성된 코드를 보면 회원 저장소의 구현 클래스를 직접 작성하여 생성하고 있다
📍 주문 서비스 구현 클래스 OrderServiceImpl 에서도 회원 저장소와 할인 정책 구현 클래스를 직접 작성하여 생성하고 있다
📍 OCP, DIP 원칙이 지켜지지 않고 있다
📍 의존 관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점 ➡ 어떻게 해결할 것인가?


[출처 - Inflearn : 스프링 핵심 원리 - 기본편]
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

profile
Backend-Developer

0개의 댓글