spring 회원 관리 예제 실습 : 회원 도메인과 리포지토리 만들기, 테스트 케이스 작성

호연지기·2023년 5월 27일
0

✌️ 회원관리 프로젝트

비즈니스 요구사항

  • 데이터 : 회원ID, 이름
  • 기능 : 회원 등록, 조회
  • 아직 데이터 저장소가 선저오디지 않음(가상의 시나리오)
  • 일반적인 웹 애플리케이션 계층 구조
  • 서비스 : 핵심 비즈니스 로직 구현
  • 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
  • 도메인 : 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨
  • 아직 데이터 저장소가 선정되지 않아서, 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계
  • 데이터 저장소는 RDB, NoSQL 등등 다양한 저장소를 고민중인 상황으로 가정
  • 개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용

처음엔 객체 지향이 너무 어색하고 헷갈렸으나 이것도 익숙해져서인지 이제는 좀 반갑고..친숙해지기 시작했다.

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

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

idname 선언 후 control + Enter키로 Getter and Setter를 세팅한다.

멤버의 정보를 작성한 뒤 이 녀석들을 저장할 저장소를 만들어준다.

💻 MemberRepository 소스 (Interface)

public interface MemberRepository {
    Member save(Member member); //저장소에 회원 저장
    Optional<Member> findById(Long id); //id를 찾는 기능
    Optional<Member> findByName(String name); //이름을 찾는 기능
    List<Member> findAll(); //전체 정보를 찾는 기능
}

💻 MemoryMemberRepository 소스

MemberRepository 인터페이스에서 선언한 기능의 세부 코드를 작성한다.

//회원 정보 저장 메소드 불러오기(implements)
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) {
        //null값이 나올 것을 위한 Optional 처리
        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());
    }
}

회원 이름과 이름을 등록하고 검색해서 가져올 수 있는 처리
코드를 작성했으면 제대로 돌아가는지 테스트가 필요하다.

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

test 디렉토리에 테스트용 클래스를 생성하고 테스트용 코드를 작성한다.

테스트를 해야하는 이유?
개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나, 웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 실행한다. 이러한 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기 어렵고 여러 테스트를 한번에 실행하기 어렵다는 단점이 있다. 자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다.

💻 MemoryMemberRepository 소스

@Test
public void save(){
    Member member = new Member();
    member.setName("spring");

    repository.save(member);
    //id를 가지고 와서 같은지 비교하는 테스트
    Member result = repository.findById(member.getId()).get();
    System.out.println("result = "+ (result == member));

    //검증
    //Assertions.assertEquals(member, result);
    assertThat(member).isEqualTo(result);
}

@Test 를 어노테이션하고 저장된 id가 회원정보와 일치하는지 assertThat(member).isEqualTo(result);
명령어로 확인한다.

테스트 하려면 해당 메소드 왼쪽의 테스트 run() 버튼을 눌러서 결과값을 확인할 수 있다.

전체 메소드를 동시에 테스트 하려면 클래스 시작점에서 테스 버튼을 누르면 된다.

테스트에서 주의해야 할 점!

테스트는 메소드가 작성된 순서대로 진행되지 않으므로 저장된 데이터끼리 엉켜서 정상 작동하는 코드임에도 오류가 발생할 수 있다.

findByName()에서 에러가 발생한 모습

그래서 각 테스트마다 테스트 완료 후 저장소 안의 데이터를 지워주는 작업을 해줘야 한다.

💻 MemoryMemberRepository 소스

public void clearStore() {
    store.clear();
}

💻 test/MemoryMemberRepository 소스

//테스트가 끝난 다음에는 저장소 안의 데이터를 지워줘야 한다.
@AfterEach
public void afterEach(){
    repository.clearStore();
}

저장소 안의 데이터를 지우기 위해 MemoryMemberRepository 클래스에 clearStore()를 만들어주고 테스트 클래스에서 @AfterEach로 하나의 테스트가 끝날 때마다 clearStore() 명령어가 작동하게끔 설정해주었다.

3개의 테스트 모두 정상작동되는 것 확인!

📅 DATE

2023.05.27 작성

profile
사람의 마음에 차 있는 너르고 크고 올바른 기운

0개의 댓글