본문 바로가기
Back-End/Spring Boot

회원 관리 예제 - 백엔드 개발 / 비즈니스 요구사항 정리, 회원 도메인과 리포지토리 만들기

by 달의 조각 2022. 2. 23.
이 글은 김영한 님의 스프링 입문 강의(https://inf.run/hcic)를 수강하며 학습한 내용을 정리한 글입니다.

 

1. 비즈니스 요구사항 정리

  • 데이터: 회원ID, 이름
  • 기능: 회원 등록, 조회
  • 아직 데이터 저장소가 선정되지 않음(가상의 시나리오)

 

일반적인 웹 애플리케이션 계층 구조

  • 컨트롤러: 웹 MVC의 컨트롤러 역할
  • 서비스: 핵심 비즈니스 로직 구현
  • 리포지토리: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
  • 도메인: 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨

 

클래스 의존 관계

  • 아직 데이터 저장소가 선정되지 않아서, 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계
  • 데이터 저장소는 RDB, NoSQL 등등 다양한 저장소를 고민중인 상황으로 가정
  • 개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용

 

 

2. 회원 도메인과 리포지토리 만들기

 

회원 객체

1) domain 패키지 생성, Member 클래스 생성(Member.java 생성됨)
2) 데이터: 회원ID, 이름 작성
3) Getter and Setter 추가

id: 고객이 저장하는 id가 아닌 시스템이 저장하는 id

 

회원 리포지토리 인터페이스

1) repository 패키지 - MemberRepository 인터페이스 생성

2) 회원을 등록하는 sava와 검색하는 findById, findByName, findAll 기능 4개 작성
    ˙Optional: Java 8 기능, findById, findByName가 Null일 경우가 존재하므로 Optional로 감싼다
    ˙List 인터페이스: 중복 허용, 저장 순서 유지

 

회원 리포지토리 메모리 구현체

1) MemberRepository인터페이스를 구현하는 MemoryMemberRepository 클래스 생성

2) save를 하기 위해서는 저장할 메모리가 필요하므로
    store라는 이름의 Map을 생성(Key는 Id이고 Value는 Member)

3) Key값(Id)을 생성할 sequence 변수 생성(증감 연산자를 이용해서 Id를 부여할 것이다)

4) 4가지 메서드 구현
   ˙save 메서드(매개변수 member): ID 값 생성, 생성한 ID 값과 member를 Map에 저장
   ˙findById 메서드(매개변수 id): 일치하는 id를 반환, Null일 경우 생각하기

Optional

Java8에서는 Optional<T> 클래스를 사용해 NPE(NullPointerException)를 방지할 수 있도록 한다
Optional<T>는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 한다

Optional 클래스는 value에 값을 저장하기 때문에 null이더라도 바로 NPE가 발생하지 않는다
편리한 여러 메서드가 존재하므로 Null이 발생할 가능성이 있다면 Optional로 해당 객체를 감싸 주는 것이 좋다



Optional.ofNullable(value)

null인지 아닌지 확신할 수 없는 객체를 담고 있는 Optional 객체를 생성
해당 객체가 null인지 아닌지 불확실한 상황에서는 이 메소드를 사용한다


   ˙findByName 메서드(매개변수 Name): 람다를 사용하여 루프를 돌린다
      member.getName()과 파라미터로 입력받은 name을 비교하여 하나라도 찾으면 반환한다

          전체 흐름: store.values()에 stream().filter().findAny() 사용
          store의 values에 반복해서 필터를 적용하고 하나라도 일치하면 이를 반환한다
          필터 내용: member.getName()이 파라미터로 넘어 온 name과 일치한가?

   ˙findAll 메서드: Map의 member들 List로 반환

 

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import java.util.*;

/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
public class MemoryMemberRepository implements MemberRepository{

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L; //0, 1, 2와 같은 Key값을 생성해 준다

    @Override
    public Member save(Member member) {
        member.setId(++sequence); //Id 지정
        store.put(member.getId(), member); //store에 저장(Map에 저장)
        return member; //저장된 결과 반환
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id)); //Null이 발생될 가능성이 있으면 Optional로 감싼다
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream() //람다, 루프를 돌린다
                .filter(member -> member.getName().equals(name)) //member.getName()이 파라미터로 넘어 온 name과 같은지 확인
                .findAny(); //하나라도 찾으면 반환
    }

    @Override
    public List<Member> findAll() { //Map을 사용했지만 반환은 리스트임을 주의, 실무에서는 리스트 많이 사용
        return new ArrayList<>(store.values()); //member들 반환
    }
}

 

6) 작성한 소스 코드 검증하기(테스트 케이스 작성)

댓글