반응형

# 예제 프로젝트 진행 (스프링 없는 순수 자바로만 개발 진행)

  • 프로젝트 환경 설정을 편리하기 위해 스프링 부트 사용, 해당 예제 프로젝트는 스프링 없는 순수 자바로만 개발 진행.
  • 역할(인터페이스) 구현(구현체) 나누어서 개발 진행.
  • 필요 준비물 : Java 11 / IDE (IntelliJ or Eclipse)

## 프로젝트 생성

스프링 부트 스타터 이용 스프링 프로젝트 생성

Project : Gradle Project

Language : Java

Spring Boot : SNAPSHOR or M 붙지 않은 가장 최신 버전 선택.

Group : hello

Artifact : core (프로젝트 빌드명)

Packaging : Jar

Java : 11

Dependencies : 선택 없이 진행. (의존관계)

  • GENERATE 로 압축 파일 내려받은 후 별도의 위치에 압축해제, 인텔리제이 실행해서 압축해제한 프로젝트 폴더 내 build.gradle 선택하여 프로젝트 실행.
  • 설정이 모두 받아진 후 CoreApplication 실행하여 정상적으로 실행되는지 확인.
  • 설정 변경 : Build and run using 과 Run tests using 을 IntelliJ IDEA로 변경. (윈도우의 경우 File -> Settings -> Gradle 입력)

## 비즈니스 요구사항과 설계

비즈니스 요구사항

1) 회원

  • 회원 가입, 조회
  • 회원 등급 존재 (일반, VIP)
  • 회원 데이터 : 자체 DB 구축 or 외부 시스템과 연동 가능성. (미확정)

2) 주문과 할인 정책

  • 회원은 상품 주문 가능.
  • 회원 등급에 따라 할인 정책 적용.
  • 할인 정책 : VIP는 1000원을 할인 (고정 금액 할인 -> 변경 가능성 존재, 미확정)

미확정된 부분 관련

  • 역할과 구현을 분리 즉, 인터페이스와 구현체를 나누어 미확정된 부분에 대응할 수 있도록 함.

## 회원 도메인 설계

회원 도메인 요구사항

  1. 회원 가입, 회원 조회
  2. 회원 등급 존재 (일반, VIP)
  3. 회원 데이터 (자체 DB 구축 or 외부 시스템 연동 - 미확정)

회원 도메인 협력 관계

  • 클라이언트 -> 회원 서비스 호출 (회원 가입 / 회원 조회 기능) -> 회원 저장소 생성 (인터페이스)
  • 회원저장소(인터페이스) 의 역할(구현체)에 해당하는 메모리 회원 저장소, DB 회원 저장소, 외부 시스템 연동 회원 저장소 구현체 생성 (메모리 회원 저장소의 경우 재 시작 시 데이터 초기화(메모리 휘발성) 되므로 개발용도로 사용.

회원 클래스 다이어그램

  • MemberService (인터페이스) - MemberServiceImpl (MemberService 의 구현체(구현 클래스))
  • MemberRepository (인터페이스) - MemoryMemberRepository, DbMemberRepository (MemberRepository 의 구현체(구현 클래스))

회원 객체 다이어그램 (객체 간 참조)

  • 클라이언트 -> 회원 서비스(MemberServiceImpl) -> 메모리 회원 저장소

## 회원 도메인 개발

  • 인텔리제이 단축키 확인방법 : 윈도우의 경우 File -> Settings -> keymap 검색 해당 항목에서 찾고자 하는 항목 찾아서 사용하면 된다.

Grade

package hello.core.member;

public enum Grade {
    BASIC,
    VIP
}

Member

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

MemberRepository (인터페이스)

package hello.core.member;

public interface MemberRepository {

    void save(Member member);

    Member findById(Long memberId);
}

MemoryMemberRepository (MemberRepository 구현체)

package hello.core.member;

import java.util.HashMap;
import java.util.Map;

public class MemoryMemberRepository implements MemberRepository {

    // 동시성 이슈로 인해 실무에서는 ConcurrentHashMap 사용 해야 함.
    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);
    }
}

MemberService (인터페이스)

package hello.core.member;

public interface MemberService {
    void join(Member member);

    Member findMember(Long memberId);
}

MemberServiceImp (MemberService 구현체)

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

## 회원 도메인 실행 및 테스트

  • 위 작업 내역 정상 동작하는지 테스트.

테스트 방법1) 메인 메서드를 이용한 테스트 (좋은 방법 아님.)

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 memberA = new Member(1L, "memberA", Grade.VIP);
        memberService.join(memberA);

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

테스트 방법2) Junit 테스트 프레임워크 이용.

  • 테스트 코드 작성은 필수임. (반드시!)
  • test 에 구조 동일하게 생성 하여 테스트 코드 작성.

package hello.core.member;

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

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

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

현재 회원 도메인 설계 문제점

  • 다른 저장소 변경 시 OCP 원칙 준수?
  • DIP 잘 지키고 있는지?
  • 의존관계 문제. (MemberServiceImpl 에서 아래와 같이 MemberRepository 와 MemoryMemberRepository 모두 의존 중)
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();
반응형

+ Recent posts