본문 바로가기

프로그래밍/- Spring

[ 회원 관리 예제 - 백엔드 개발 ] 비즈니스 요구사항 정리 / 회원 도메인과 리포지토리 만들기 / 회원 리포지토리 테스트 케이스 작성

 

비즈니스 요구사항 정리

 

 

➡️ 데이터 : 회원ID, 이름

➡️ 기능 : 회원 등록, 조회

➡️ 아직 데이터 저장소가 선성되지 않음 (가상의 시나리오)

 

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

➡️ 컨트롤러 : 웹 MVC의 컨트롤러 역할

➡️ 서비스 : 핵심 비즈니스 로직 구현 ( ex: 회원은 중복가입이 안됨 등)

➡️ 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리

➡️ 도메인 : 비즈니스 도메인 객체 (ex : 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨)

 

클래스 의존관계

➡️ 아직 데이터 저장소가 선정되지 않았기 때문에 인터페이스로 구현 > 클래스 변경 가능하게 설계

➡️ 데이터 저장소는 RDB, NoSQL 등등 다양한 저장소를 고민중인 상황으로 가정

➡️ 개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용

 

 

 

 

 

 

 

 

 

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

 

package hello.hello_spring.domain;

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


}

➡️회원 객체 생성

 

package hello.hello_spring.repository;

import hello.hello_spring.domain.Member;

import java.util.List;
import java.util.Optional;

public interface MemberRepository {
    Member save(Member member); //회원 저장 후 회원 반환
    Optional<Member> findById(Long id); //저장소에서 id로 검색
    Optional<Member> findByName(String name);   //저장소에서 name으로 검색
    List<Member> findAll(); //모든 회원 리스트 반환
}

➡️ 회원 리포지토리 인터페이스 생성

 

package hello.hello_spring.repository;

import hello.hello_spring.domain.Member;

import java.util.*;

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);   //  sequence 하나씩 올리면서 id 부여
        store.put(member.getId(), member);  //  store에 저장
        return member;  //  해당 member 리턴
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id));  //Optional 기능 이용해 null이 나와도 가능하게 저장소에서 회원 가져옴
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny(); // 파라미터의 name과 같은 member를 반환
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values()); //리스트형식으로 모든 member 리턴
    }
    
    public void clearStore(){
        store.clear();
    }
}

➡️ 회원 리포지토리 생성

 

 

 

 

 

 

 

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

 

 

➡️ 개발한 기능을 테스트 해보고 싶을 때 main메서드나 컨트롤러를 통해 해당 기능을 실행

➡️ 위의 방법은 시간이 오래걸리고 여러 테스트를 한번에 실행하기 어렵다는 단점.

➡️ JUnit이라는 프레임워크를 사용해 테스트를 실행하여 이러한 문제 해결 가능

 

 

 

테스트 폴더에 테스트코드 작성 / 테스트별로 테스트도 가능하지만 클래스에서 실행하면 전체 테스트 실행 가능
정상적으로 실행(두 객체가 같음) / 두 객체가 다를 경우 에러

package hello.hello_spring.repository;

import hello.hello_spring.domain.Member;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class MemorymemberRepositoryTest {

    MemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save(){
        Member member = new Member();
        member.setName("spring");
        repository.save(member);
        Member result = repository.findById(member.getId()).get();

        Assertions.assertEquals(result, member);    //둘이 같은 객체인지 확인
    }
}
Assertions.assertThat(member).isEqualTo(result); //  같은 객체인지 확인

 

➡️save() test

➡️같은 객체인지 확인하는 두가지 방법 ( assertEquals / aseertThat.isEqualTo )

 

 

 

 

 

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

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

    Member result = repository.findByName("spring1").get();
    //.get() 메서드는 Optional을 한번 벗기고 꺼낼 수 있음

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

➡️ findByname() test > 정상 실행

 

 

 

 

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

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

    List<Member> result = repository.findAll();

    Assertions.assertThat(result.size()).isEqualTo(2);
}

➡️ findAll() test > 정상 실행

 

 

 

 

class MemorymemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach  // 메서드가 끝날때마다 해당 메서드 실행
    public void afterEach(){
        repository.clearStore();
    }

@AfterEach로 의존성 제거해주기 전과 후

➡️ test는 순서를 보장해주지 않기 때문에 의존성 문제를 꼭 없애줘야 함

➡️ 각 메서드마다 메모리를 초기화해주기 위해 @AfterEach를 통해 레파지토리 clear

 

 

➡️ (구현 > 테스트)의 순서가 아닌 (테스트 > 구현) 순서로, 테스트를 먼저 만들고 개발을 테스트에 맞춰서 하는 것을 TDD(Test-Driven Development / 테스트 주도 개발)라고 함