스프링 핵심 원리-02 스프링 핵심 원리 이해1&2

수갱22·2023년 5월 12일
0

spring

목록 보기
7/14

1. 프로젝트 생성

프로젝트 생성




-빠른 실행을 위해 IntelliJ 변경

2.비즈니스 요구사항과 설계

(1) 회원

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

(2) 주문과 할인 정책

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

=>회원 데이터, 할인 정책 같은 부분은 지금 결정하기 어려운 부분->인터페이스를 만들고 구현체를 언제든지 갈아끼울 수 있도록 설계

+스프링 없는 순수 자바로만 개발 진행 예정

3. 회원 도메인 설계

회원 도메인 협력 관계


-클라이언트가 회원서비스 호출->회원 서비스가 회원가입/조회 기능 제공->회원 저장소 인터페이스 별도 생성(미확정이므로)
-회원 저장소 역할의 구현은 메모리 회원저장소, DB 회원 저장소, 외부 시스템 연동 회원 저장소 중 선택->아직 결정되지 않았으므로 메모리 회원 저장소로 일단 개발 진행(개발용), 나중에 구현 후 교체

회원 클레스 다이어그램


-MemberService 인터페이스 생성, 구현체로 MemberServiceImpl 생성
-회원 저장소: MemberRepository&구현 클래스로 MemoryMemberRepository와 DbMemberRepository

회원 객체 다이어그램


-클라이언트는 회원 서비스 참조하고 회원 서비스는 메모리 회원 저장소 참조
-회원 서비스: MemberServiceImpl

4. 회원 도메인 개발

(1)회원 엔티티

회원 등급(Grade.java)

package hello.core.member;

public enum Grade {
    BASIC,
    VIP
}

회원 엔티티(Member.java)

package hello.core.member;
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;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Grade getGrade() {
 return grade;
 }
 public void setGrade(Grade grade) {
 this.grade = grade;
 }
}

(2)회원 저장소

회원 저장소 인터페이스(MemberRepository.java)

package hello.core.member;
public interface MemberRepository {
 void save(Member member); //회원 저장
 Member findById(Long memberId); //회원 찾기
}

메모리 회원 저장소 구현체(MemoryMemberRepository.java)

package hello.core.member;
import java.util.HashMap;
import java.util.Map;
public class MemoryMemberRepository implements MemberRepository {
 private static Map<Long, Member> store = new HashMap<>();
 @Override
 public void save(Member member) {
 store.put(member.getId(), member);
 }
 @Override
 public Member findById(Long memberId) {
 return store.get(memberId);
 }
}

+HashMap 은 동시성 이슈가 발생할 수 있다. 이런 경우 ConcurrentHashMap 을 사용하자.

(3)회원 서비스

회원 서비스 인터페이스

package hello.core.member;

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

회원 서비스 구현체

package hello.core.member;

public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository = new
            MemoryMemberRepository();
            
    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }
    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}
 }
}

5. 회원 도메인 실행과 테스트


-이제 위 그림 구현

회원 도메인 - 회원 가입 main

package hello.core;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;

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.findMember(1L);
 System.out.println("new member = " + member.getName());
 System.out.println("find Member = " + findMember.getName());
 }
}


-실행결과 멤버A가 잘 들어갔음을 알 수 있다.
-순수한 자바코드임->위 방법은 좋은 테스트 방식이 아님

test/java/hello.core/member/MemberServiceTest.java

package hello.core.member;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

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.findMember(1L);

        //then
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

회원 도메인 설계의 문제점

-이 코드의 설계상 문제점은 무엇일까요?
-다른 저장소로 변경할 때 OCP 원칙을 잘 준수할까요?
-DIP를 잘 지키고 있을까요?

-의존관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있음
->주문까지 만들고나서 문제점과 해결 방안을 설명

6.주문과 할인 도메인 설계

+주문과 할인 정책

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

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


1. 주문 생성: 클라이언트는 주문 서비스에 주문 생성을 요청한다.
2. 회원 조회: 할인을 위해서는 회원 등급이 필요하다. 그래서 주문 서비스는 회원 저장소에서 회원을 조회한다.(findbyId)
3. 할인 적용: 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임한다.
4. 주문 결과 반환: 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다.
+실제로는 주문 데이터를 DB에 저장하겠지만, 예제가 너무 복잡해 질 수 있어서 생략하고, 단순히 주문 결과를 반환한다.

역할 다음 구현 만듦=역할과 구현을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계

profile
Cyber Security

0개의 댓글