[Spring] 2-2. 회원 도메인 설계 및 개발

송광호·2023년 12월 7일

[Spring]

목록 보기
7/41
post-thumbnail

Spring 시리즈는 혼자 공부하며 기록으로 남기고, 만약 잘못 학습 한 지식이 있다면 공유하며 피드백을 받고자 작성합니다.
스프링에 대해 깊게 공부해보고자 인프런의 김영한 강사님께서 강의를 진행하시는 (스프링 핵심 원리 - 기본편) 강의를 수강하며 정리하는 글입니다.
혹여나 글을 읽으시며 잘못 설명된 부분이 있다면 지적 부탁드리겠습니다.


회원 도메인 설계

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

회원 도메인 협력 관계

  • 도메인에 대한 큰 그림
  • 기획자들도 볼 수 있는 그림

회원 클래스 다이어그램

  • 위 그림을 바탕으로 개발자들이 구체화한 그림
  • 실제 서버를 실행하지 않고 클래스들만 분석해서 볼 수 있는 그림
  • 구현체를 어떤걸 넣을지 이런것들은 서버가 뜰 때 동적으로 결정되기때문에 실제 어떤 구현체가 들어가는지 알기는 어렵다. 그래서 객체 다이어그램이라는것이 따로 존재한다.

회원 객체 다이어그램

  • 회원서비스 : MemberServiceImpl

회원 도메인 개발

회원 엔티티

회원 등급

package hello.core.member;

public enum Grade {
    BASIC,
    VIP
}

회원의 등급은 2가지로 나뉘는데 BASIC(일반)등급과 VIP 등급으로 나뉜다.
enum(열거형) 타입으로 작성

회원 엔티티

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;
    }
}
  • 회원 엔티티는 간단하게 필요한 정보만 추가한다.
  • 회원은 Id, 이름, 등급을 가지고있다.
  • 생성자와 getter, setter 메서드를 생성한다.

회원 저장소

회원 저장소 인터페이스

package hello.core.member;

public interface MemberRepository {

    void save(Member member);

    Member findById(Long memberId);
}
  • 회원 저장소 인터페이스는 회원을 저장하고, Id로 회원을 찾아낼 수 있다.

드디어 나왔다 앞에서 배운 역할과 구현 중 역할에 해당하는 인터페이스!

메모리 회원 저장소 구현체

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을 사용하자.

회원 서비스

회원 서비스 인터페이스

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);
    }
}
  • 구현체를 살펴보면 private final MemberRepository memberRepository = new MemoryMemberRepository(); 코드가 보인다. 인터페이스와 실제 구현체인 MemoryMemberRepository를 둘 다 의존하고있다.
  • DIP를 위반하고 있다.

구현체가 하나밖에 없을경우 보통 관례상으로 인터페이스 이름 뒤에 Impl 이라는 단어를 많이 사용한다.

0개의 댓글