스프링 핵심 원리 이해1 - 예제 만들기

udonehn·2023년 1월 22일
0

프로젝트 생성

start.spring.io에 접속해 프로젝트를 생성한다.

  • 순수 java로 코딩을 할 예정이기 때문에 Dependencies는 추가하지 않는다.

비즈니스 요구사항과 설계

회원 도메인 설계

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

회원 도메인 협력 관계

  • 사용할 저장소는 지정되지 않았다.

회원 클래스 다이어그램

  • 사용할 Repository는 지정되지 않았다. 저장소 역할(interface MemberRepository)만 만들고, 나중에 지정된 후 구현 클래스를 생성한다.

회원 객체 다이어그램

회원 도메인 협력 관계는 기획자들도 볼 수 있는 그림이다. 개발자들은 이를 바탕으로 구체화하여 회원 클래스 다이어그램을 만들어낸다. 하지만 구현체(MemoryMemberRepository, DbMemberRepository 등)는 동적으로 결정되기 때문에 클래스 다이어그램만으로는 판단하기 어렵다. 따라서 회원 객체 다이어그램이 사용된다.

회원 도메인 개발

회원 클래스 다이어그램을 참고한다.

  • src.main.java.hello.core 아래 member 패키지를 생성한다.
  • member 패키지 아래 Grade enum과 Member 클래스를 생성한다.
  • Intellij 단축키 Alt + Ins를 사용해 쉽게 코드를 작성할 수 있다.

member.Grade

<코드>

  • 회원 등급은 BASIC과 VIP 두 가지이다.

enum??

member.Member

<코드>

  • id, name, grade 세 변수가 있으며 생성자(Constructor)와 getter, setter 메서드로 구성되어 있다.

member.MemberRepository

  • member 패키지 아래 MemberRepository interface를 생성한다.
  • Intellij 단축키 Alt + Enter를 사용해 쉽게 코드를 작성할 수 있다.

<코드>

  • 회원 저장과 Id로 회원을 찾는 두 가지 기능이 있다.

member.MemoryMemberRepository

  • member 패키지 아래 MemberRepository interface의 구현체 MemoryMemberRepository를 생성한다.

<코드>

  • 메모리에 저장하기 때문에 개발 중 테스트 용도로 적합하다.

member.MemberService

  • member 패키지 아래 MemberService interface를 생성한다.

<코드>

  • 회원 가입과 회원 조회 두 가지 기능이 있다.

member.MemberServiceImpl

  • member 패키지 아래 MemberService interface의 구현체 MemberServiceImpl를 생성한다.
    • 구현체가 하나일 때에는 관례상 interface 클래스 이름 뒤에 Impl을 추가한다.
  • IntelliJ의 단축키 Ctrl+Shift+Enter을 이용해 쉽게 코드를 완성할 수 있다. (줄바꿈과 함께 세미콜론)

<코드>

  • private final MemberRepository memberRepository = new MemoryMemberRepository(); : 다형성에 의해 interface MemberRepository가 아닌 Override 한 MemoryMemberRepository가 실행된다.

업캐스팅??

회원 도메인 실행과 테스트

회원 객체 다이어그램을 참고한다. 실제로 동적으로 동작하는 것을 테스트해야 하기 때문이다.

MemberApp

  • core 아래 MemberApp 클래스를 생성한다.
  • IntelliJ에서 psvm을 입력하면 public static void main이 자동으로 생성된다.
  • IntelliJ에서 soutv를 입력하면 System.out.println("member.getName() = " + member.getName());과 같은 코드를 쉽게 작성할 수 있다.

<코드>

  • member객체가 잘 저장되었는지 테스트한다.
    • 저장한 member 객체와 이를 findMember 메소드로 검색한 객체가 일치하는지 알아본다.
    • 다음과 같은 결과가 출력되므로 잘 저장된다는 것을 알 수 있다. member = memberA\n findMember = memberA
  • 순수 Java 코드로 작성한 코드이다.
    • 이는 한계가 있기 때문에 JUnit이라는 테스트 프레임워크를 사용한다.

test.java.hello.core.member.MemberServiceTest

  • test.java.hello.core 아래 member 패키지를 생성한다.
  • member 패키지 아래 MemberServiceTest 클래스를 생성한다.

<코드>

  • hello.core.MemberApp에서의 테스트와 원리는 같다.
    • Assertions.assertThat(member).isEqualTo(findMember); : member 객체와 findMember 객체가 일치하는지 알아본다.
  • 오류가 없기 때문에 실행시 초록색 체크가 표시된다.
    • 오류가 있을 시 콘솔 메시지를 통해 쉽게 오류를 찾아낼 수 있다.
  • 중요 : Assertions를 import 할 때에는 org.assertj.core.api를 import 해야한다.

이러한 방식은 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있다.

주문과 할인 도메인 설계

  • 주문과 할인 정책
    • 회원은 상품을 주문할 수 있다.
    • 회원 등급에 따라 할인 정책을 적용할 수 있다.
    • 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
    • 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)

주문 도메인 협력, 역할, 책임

주문 도메인 전체

  • 역할과 구현을 분리하였기 때문에 유연한 변경이 가능하다.

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

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

ㅇㅇ

  • 둘 중 어느것이 되더라도 역할들의 협력 관계를 재사용 할 수 있다.

주문과 할인 도메인 개발

discount.DiscountPolicy

  • src.main.java.hello.core 아래 discount 패키지를 생성한다.
  • discount 패키지 아래 DiscountPolicy interface를 생성한다.
  • IntelliJ의 단축키 F2를 사용하면 오류가 있는 부분으로 바로 이동할 수 있다.

<코드>

  • discount 메소드는 할인 금액을 return 한다.

discount.FixDiscountPolicy

  • discount 패키지 아래 DiscountPolicy interface의 구현체 FixDiscountPolicy를 생성한다.
  • 정액 할인 정책을 구현한다.

<코드>

  • private int discountFixAmount = 1000; : 할인 금액이 1000원이다.
  • public int discount(Member member, int price) : member 객체의 Grade가 VIP라면 1000원을 할인하고, 그렇지 않으면 할인하지 않는다.

order.Order

  • src.main.java.hello.core 아래 order 패키지를 생성한다.
  • order 패키지 아래 Order 클래스를 생성한다.

<코드>

  • memberId, itemName, itemPrice, discountPrice 네 변수가 있으며 생성자(Constructor)와 getter, setter 메소드로 구성되어 있다.
  • public int calculatePrice() : 상품 가격에서 할인 가격을 빼는 메소드이다.
  • public String toString() : IntelliJ의 단축키 Alt+Ins의 toString()을 이용하면 쉽게 작성할 수 있다.

order.OrderService

  • order 패키지 아래 OrderService interface를 생성한다.
  • 클라이언트는 회원id, 상품명, 상품 가격을 전송하고 서버는 주문 결과를 반환한다.
    • 주문 도메인 협력, 역할, 책임을 참고한다.

<코드>

order.OrderServiceImpl

  • order 패키지 아래 OrderService interface의 구현체 OrderServiceImpl를 생성한다.
  • 주문 서비스 역할은 회원 조회(memberRepository)와 할인 적용(discountPolicy)을 담당한다.
  • 단일책임원칙이 잘 지켜졌다.
  • 주문이 오면 회원 정보를 먼저 조회하고, 할인 정책에 회원 정보를 넘긴다. (등급만 넘겨도 된다.)

<코드>

  • Member member = memberRepository.findById(memberId); : 주문이 오면 회원 정보를 먼저 조회한다.
  • int discountPrice = discountPolicy.discount(member, itemPrice); : 할인 정책에 회원 정보를 넘긴다. (프로젝트에 따라 등급만 넘겨도 된다)
  • return new Order(memberId, itemName, itemPrice, discountPrice) : Order를 생성해 반환한다.

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

OrderApp

  • 주문이 잘 작동하는지 알아보기 위해 main 함수를 생성한다.
    • IntelliJ에서 psvm을 사용하면 쉽게 main함수를 생성할 수 있다.

<코드>

  • Member member = new Member(memberId, "memberA", Grade.VIP); : 멤버를 생성한다.
  • Order order = orderService.createOrder(memberId, "itemA", 10000); : 주문을 생성한다. 생성한 맴버의 id가 파라미터로 전송된다.
  • System.out.println("order = " + order); : Order 클래스의 toString() 메소드가 실행된다.
    • Override 되어 있기 때문이다.
      ??

main 함수를 이용한 테스트는 권장되지 않는다. JUnit으로 테스트를 진행한다.

test.java.hello.core.order.OrderServiceTest

  • test.java.hello.core 아래 order 패키지를 생성한다.
  • order 패키지 아래 OrderServiceTest 클래스를 생성한다.

<코드>

  • 중요 : Assertions를 import 할 때에는 org.assertj.core.api를 import 해야한다.

결과

본 포스팅은 김영한 강사의 스프링 핵심 원리 강의를 수강하고 요약한 것으로, 해당 강의의 영상 및 강의자료를 참고하였습니다.

profile
안녕하세요. 만나서 반갑습니다.

0개의 댓글

관련 채용 정보