[스프링 핵심 원리 - 기본편] 스프링 핵심 원리 이해(1)

2024. 8. 25. 15:47·Back-End/Spring

[작성일: 2023. 06. 02]

 

 

비즈니스 요구사항과 설계(순수 자바 개발)

회원 도메인 요구사항

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

 

 

주문과 할인 정책

  • 회원은 상품을 주문할 수 있다.
  • 회원 등급에 따라 할인 정책을 적용할 수 있다.
  • 할인 정책은 모든 VIP는 1000원을 할인해 주는 고정 금액 할인을 적용해야 한다.(나중에 변경가능성 있음.)
  • 할인 정책은 변경 가능성이 높다.(미확정)

 

 

 

 

 

회원 도메인 설계

회원 도메인 협력 관계

  • 회원 저장소라는 인터페이스 생성(메모리 회원 저장소/DB회원 저장소/외부 시스템 연동 회원 저장소 중 선정)

출처 : 김영한님

 

 

회원 클래스 다이어그램

출처 : 김영한님

 

 

회원 객체 다이어그램

  • 회원 서비스: MemberServiceImpl

출처 : 김영한님

 

 

 

 

 

회원 도메인 개발

회원 엔티티

회원 등급

public enum Grade {
    BASIC,
    VIP
}

 

 

 

회원 엔티티

public class Member {
    private Long id;
    private String name;
    private Grade grade;

// 생성자
// getter, setter 만들기
}

 

 

 

회원 저장소

  • 데이터베이스는 아직 확정상태가 아니지만 개발은 진행해야 하므로 가장 단순한 메모리 회원 저장소를 우선 구현해서 개발을 진행해 보도록 한다.
  • HashMap은 동시성 이슈가 발생할 수 있으므로 ConcurrentHashMap을 사용하는 것이 좋다.
public interface MemberRepository {
    void save(Member member);
    Member findById(Long memberId);
}

 

public class MemberMemberRepository implements MemberRepository{
    private static Map<Long, Member> store = new HashMap<>();  
    // HashMap은 동시성 이슈가 있을 수 있음.

    @Override
    public void save(Member member) {
        store.put(member.getId(), member);
    }

    @Override
    public Member findById(Long memberId) {
        return store.get(memberId);
    }
}

 

 

 

회원 서비스

public interface MemberService {
    void join(Member member);
    Member findById(Long memberId);
}

 

public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findById(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

 

 

 

 

 

회원 도메인 실행과 테스트

순수 자바코드 테스트

  • 이런 테스트는 좋은 방법 아니므로 사용하지 말자.
public class MemberApp {
    public static void main(String[] args) {
        MemberService memberService = new MemberServiceImpl();
        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findById(1L);
        System.out.println("new member = " + member.getName());
        System.out.println("findMember = " + findMember.getName());
    }
}

 

 

 

Junit 테스트

public class MemberServiceTest {

    MemberService memberService = new MemberServiceImpl();

    @Test
    void join() {
        // given, 이런 게 주어졌을 때
        Member member = new Member(1L, "memberA", Grade.VIP);

        // when, 이렇게 하면
        memberService.join(member);
        Member findMember = memberService.findById(1L);
        
        // then, 이렇게 된다.
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

테스트 결과

 

 

 

 

 

주문과 할인 도메인 설계

주문 도메인 전체

출처: 김영한님

 

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

 

 

 

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

출처: 김영한님

 

 

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

  • 회원을 메모리가 아닌 실제 DB에서 조회하고, 정률 할인 정책을 지원해도 주문 서비스를 변경하지 않아도 된다.
  • 협력 관계를 그대로 재사용할 수 있다.

출처: 김영한님

 

 

 

 

 

주문과 할인 도메인 개발

할인 정책

public interface DiscountPolicy {
    // @return 할인 대상 금액
    int discount(Member member, int price);
}

 

public class FixDiscountPolicy implements DiscountPolicy{

    private int discountFixAmount = 1000; // 1000원 할인

    @Override
    public int discount(Member member, int price) {
        if (member.getGrade() == Grade.VIP) {
            return discountFixAmount;
        } else{
            return 0;
        }
    }
}

 

 

 

주문 엔티티

public class Order {
    private Long memberId;
    private String itemName;
    private int itemPrice;
    private int discountPrice;
// 생성자, getter, setter 생략

    @Override
    public String toString() {
        return "Order{" +
                "memberId=" + memberId +
                ", itemName='" + itemName + '\'' +
                ", itemPrice=" + itemPrice +
                ", discountPrice=" + discountPrice +
                '}';
    }
}

 

 

 

주문 서비스

  • 주문 생성 요청이 오면 회원 정보를 조회하고 할인 정책을 적용한 다음 주문 객체를 생성해서 반환한다.
  • 메모리 회원 리포지토리와 고정 금액 할인 정책을 구현체로 생성한다.
public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);
}

 

public class OrderServiceImpl implements OrderService{

    private MemberRepository memberRepository = new MemoryMemberRepository();
    private DiscountPolicy discountPolicy = new FixDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

 

 

 

 

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

순수 자바코드 테스트

  • 이런 테스트 방법은 좋은 방법이 아니므로 사용하지 말자.
public class OrderApp {
    public static void main(String[] args) {
        MemberService memberService = new MemberServiceImpl();
        OrderService orderService = new OrderServiceImpl();

        Long memberId=1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member); // 우선 회원 가입 진행

        Order order = orderService.createOrder(memberId, "itemA", 10000);
        System.out.println("order = " + order);
        System.out.println("order.calculatePrice = " + order.calculatePrice());
    }
}

테스트는 성공...

 

 

Junit 테스트

public class OrderServiceTest {

    MemberService memberService = new MemberServiceImpl();
    OrderService orderService = new OrderServiceImpl();

    @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);
    }
}

전체 테스트 실행

 

 

 

 

 

 

 

 

 

 


🐣 출처: 인프런 김영한님 강의

 

이 글은 인프런의 김영한님 스프링 강의를 보고 작성한 글입니다.

강의를 들으면서 정리한 글이므로 틀린 내용이나 오타가 있을 수 있습니다.

 

저작자표시 비영리 변경금지 (새창열림)
'Back-End/Spring' 카테고리의 다른 글
  • [스프링 핵심 원리 - 기본편] 스프링 핵심 원리 이해(3)
  • [스프링 핵심 원리 - 기본편] 스프링 핵심 원리 이해(2)
  • [스프링 핵심 원리 - 기본편] 객체지향 설계와 스프링
  • [스프링 입문] AOP
뚜비
뚜비
1년차 백엔드&iOS 개발자의 감자 탈출 블로그 🥔🥔
  • 뚜비
    뚜비의 개발로그
    뚜비
  • 전체
    오늘
    어제
  • 글쓰기     관리
    • Devlog
      • Back-End
        • Java
        • Spring
        • JPA
        • HTTP
        • Security
        • Back-End
        • Front-End
      • 알고리즘
      • iOS
        • Swift
      • Database
      • Tips
        • Git & GitHub
        • A to Z
      • 프로젝트
      • 생각정리
  • 태그

    HTTP
    데이터베이스
    알고리즘
    김영한
    Swift
    성능최적화
    jsp
    생성자
    스프링
    프로그래머스
    의존성주입
    변수
    다형성
    JPA
    자바
    javascript
    Java
    Database
    DB
    게시판만들기
    html
    sql
    spring
    Security
    Spring Security
    객체
    최주호
    자바스크립트
    백준
    MVC
  • hELLO· Designed By정상우.v4.10.0
뚜비
[스프링 핵심 원리 - 기본편] 스프링 핵심 원리 이해(1)
상단으로

티스토리툴바