본문 바로가기
  • soobinhand의 기술 블로그
Computer Science/SpringBoot

[SpringBoot] 회원 관리 예제 - 백엔드 개발

by soobinhand 2021. 11. 5.
728x90

목차

  1. 비즈니스 요구사항 정리
  2. 회원 도메인과 리포지토리 만들기
  3. 회원 리포지토리 테스트 케이스 작성
  4. 회원 서비스 개발
  5. 회원 서비스 테스트

이번 장에서는 회원 관리를 직접 해보는 예제를 다뤄볼 것입니다. 이는 백엔드 개발에 해당됩니다.

 

비즈니스 요구사항 정리

  • 데이터 : 회원 ID, 이름 (굉장히 간단하게만 해볼겁니다.)
  • 기능 : 회원 등록, 조회
  • 아직 데이터 저장소가 선정되지 않음. (가상의 시나리오)

웹 어플리케이션의 계층 구조

그림은 웹 어플리케이션의 계층구조입니다.

  • 컨트롤러 : 웹 MVC의 컨트롤러 역할
  • 서비스 : 핵심 비즈니스 로직 구현 (회원 ID는 중복이 안된다 같은 로직 등등)
  • 리포지토리 : DB에 접근, 도메인 객체를 DB에 저장하고 관리.
  • 도메인 : 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 DB에 저장되고 관리됨.
  • 아직 데이터 저장소가 선정되지 않아서, 우선 interface로 구현 클래스를 변경할 수 있도록 설계합니다.
  • 데이터 저장소는 RDB, NoSQL 등등 다양한 저장소를 고민중인 상황으로 가정합니다.
  • 개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용합니다.

 

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

  • 우선 패키지로 domain, repository를 만들어줍니다.
  • domain 안에 Member 클래스.
  • repository 안에 MemberRepository 인터페이스와 MemoryMemberRepository 클래스를 만들어줍니다.
// Member 클래스
public class Member {

    private Long id;
    private String name;

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

 

//MemberRepository 인터페이스
public interface MemberRepository {
    Member save(Member member); //회원 저장
    Optional<Member> findById(Long id);
    Optional<Member> findByName(String name);
    List<Member> findAll();
}

 

// MemoryMemberRepository 클래스
public class MemoryMemberRepository implements MemberRepository{

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny();
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }
}

우선 이렇게 만들어줍니다. 

 

테스트를 해보고 싶습니다. 테스트를 해봅시다.

 

회원 리포지토리 테스트 케이스 작성

  • 실무에선 테스트 케이스를 작성하는 것이 너무나도 당연한 것입니다.
  • 개발한 기능을 실행해서 테스트 할 때 자바의 main메소드를 실행하거나, 웹 어플리케이션 컨트롤러를 통해 해당 기능을 실행합니다. 이러한 방법은 오래 걸린다는 단점이 있고, 반복해서 할 때 비효율적입니다. 
  • 그래서 자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이런 단점을 극복합니다.
  • 위의 코드가 실제로 동작하는지 테스트 케이스를 작성해봅시다.
  • Main 패키지 밑에 test 패키지로 이동해서 똑같은 패키지들을 작성해줍니다.
  • 그리고 보통은 테스트할거Test 클래스로 만들어줍니다.
public class MemoryMemberRepositoryTest {
    MemberRepository repository = new MemoryMemberRepository();
    @Test
    public void save(){
        Member member = new Member();
        member.setName("Hello");
        repository.save(member);
        Member result =  repository.findById(member.getId()).get();
        Assertions.assertThat(member).isEqualTo(result);

    }

    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("nonono");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("hihihih");
        repository.save(member2);

        Member result = repository.findByName("nonono").get();

        Assertions.assertThat(result).isEqualTo(member1);
    }

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName("no");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("yes");
        repository.save(member2);
    }
}

 

회원 서비스 개발

  • service 패키지를 생성 후, MemberService 클래스를 구현해줍니다.
public class MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    //회원가입
    public Long join(Member member){
        //중복 회원 불가
        memberRepository.findByName(member.getName())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });

        memberRepository.save(member);
        return member.getId();
    }

    // 전체 회원 조회
    public List<Member> findMembers(){
        return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }
}
728x90

댓글