StudyRepository
article thumbnail
Published 2023. 4. 3. 14:45
7. 비즈니스 요구사항 (Spring) Spring
728x90

 

 

 

 

 

 

 

 

이번 시간엔 간단한 회원 관리 예제를 다뤄보도록 하겠습니다.

 

 

1. 회원 도메인과 회원 도메인 객체를 저장하고 불러올 수 있는 저장소인 리포지토리 객체 생성

 

2. 회원 리포지토리가 정상적으로 작동하는지 테스트케이스 생성

 

3. 실제 비지니스 로직이 있는 회원 서비스 생성

 

4. 회원 서비스 테스트케이스 생성

 

 

 

 

 

데이터: 회원 ID, 이름

 

기능: 회원 등록, 조회

 

 

 

 

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

 

 

hello.hellospring 하위 패키지에 domain패키지와 repository패키지를 만들고 그 안에 각각 Member 클래스와 MemberRepository 인터페이스를 만들어 줍니다.

 

 

 

Member클래스 코드

package hello.hellospring.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;
    }
}


 

 

 

 

MemberRepository 인터페이스 코드

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

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

public interface MemberRepository {
    Member save(Member member); //회원이 저장소에 저장
    Optional<Member> findById(Long id);//저장소에서 찾아옴
    Optional<Member> findByName(String name);
    List<Member> findAll();


}

 

 

다 만들면 repository패키지 안에 MemoryMemberRepository 클래스를 만든 뒤 다음 코드를 넣어줍니다.

 

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository{

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

    @Override
    //store에 넣기 전에 Member의 id세팅
    public Member save(Member member) {
        member.setId(++sequence); //save할때마다 sequence값 +1
        store.put(member.getId() ,member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id)); //store에서 널이 반환될 가능성이 있기때문에 ofnullable로 감싸줌
    }

    @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() {
        return new ArrayList<>(store.values());
    }
}

 

 

 

<회원 리포지토리 테이스케이스>

개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나, 웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 실행합니다.

 

이러한 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기 어렵고 여러 테스트를 한번에 실행하기 어렵다는 단점이 있습니다.

 

자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결합니다.

 

 

 

test -> java -> hello.hellospring에서  repository패키지를 생성해주고 MemoryMemberRepositoryTest클래스를 생성해주고 다음 코드를 넣어줍니다.

 

 

package hello.hellospring.repository;


import hello.hellospring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.*;

class MemoryMemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach

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

        repository.save(member);

        Member result = repository .findById(member.getId()).get();
        assertThat(member).isEqualTo(result); //asserThat에서 member가 result랑 똑같다.

    }


    @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();

         assertThat(result).isEqualTo(member1);
    }



    @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();

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

    }
}

 

 

 

Assertions.assertThat(member).isEqualTo(result);

위 코드는 

 

 

 

org.assertj.core.api 로 선택해주고

 

 

 

 

 

option + enter키

Add on-demand static import를 해주면

 

 

 

 

 

assertThat(member).isEqualTo(result);

위 코드와 같이 간략하게 표현할 수 있습니다.

 

 

 

 

 

테스트케이스를 실행해보면

 

 

다음과 같이 정상적으로 작동하는것을 확인하실 수 있습니다.

 

 

 

 

다음은 MemberService 클래스입니다.

 

 

 

 

 

service패키지에 MemberService클래스를 생성해 준 뒤 아래의 코드를 넣어줍니다.

 

 

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

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

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();


    /**
     * 회원 가입
     */
    public Long join(Member member) {

        //같은 이름이 있는 중복 회원X
        validateDuplicateMember(member); //중복 회원 검증
        memberRepository.save(member);
        return member .getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                 });
    }

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

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

 

회원가입 시 중복회원인지 검사하고 중복이 아니라면 저장하는 코드입니다.

 

 

 

 

아까와는 다른 방식으로 테스트케이스를 생성해보겠습니다.

 

테스트케이스 생성 단축키: Command + Shift + T

 

 

given - when - then 패턴으로 작성해보겠습니다.

 

given - when - then 은 곧 

준비 - 실행 - 검증 입니다.

 

class MemberServiceTest {

    MemberService memberService = new MemberService();

    @Test
    void 회원가입() {

        //given
        Member member = new Member();
        member.setName("hello");

        //when
        Long saveId = memberService.join(member);


        //then
        Member findMember = memberService.findOne(saveId).get();
        assertThat(member.getName()).isEqualTo(findMember.getName());
    }

 

Member의 이름을 hello라고 설정,

hello라는 아이디가 memberService에 join할 때 findMember로 검증.

 

 

 

 

중복 테스트입니다.

@Test
public void 중복_회원_예외(){
    
    //given
    Member member1 = new Member();
    member1.setName("spring");

    Member member2 = new Member();
    member2.setName("spring");
    
    //when
    memberService.join(member1);
    try{
        memberService.join(member2);
        fail();
    } catch(IllegalStateException e){
        
    }
    
    //then
}

member1과 member2의 이름이 모두 spring이므로 

public Long join(Member member) {

    //같은 이름이 있는 중복 회원X
    validateDuplicateMember(member); //중복 회원 검증
    memberRepository.save(member);
    return member .getId();
}

private void validateDuplicateMember(Member member) {
    memberRepository.findByName(member.getName())
            .ifPresent(m -> {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
             });
}

MemberService코드의 validataDuplicateMember함수의 예외에서 걸려야합니다.

 

2가지 방법이 있습니다.

 

 

1) try - catch문

 

@Test
public void 중복_회원_예외(){
    
    //given
    Member member1 = new Member();
    member1.setName("spring");

    Member member2 = new Member();
    member2.setName("spring");
    
    //when
    memberService.join(member1);
    try{
        memberService.join(member2);
        fail();
    } catch(IllegalStateException e){
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
    }

    //then
}

"이미 존재하는 회원입니다." 라는 문구가 일치하는지 검사하여 catch으로 잡아내는 테스트케이스입니다.

 

 

 

 

2) assertThrows

assertThrows(IllegalStateException.class, () -> memberService.join(member2));

 

IllegalStateException.class가 발생하고, 람다가 넘어가서 어떤 로직을 태울 때 member2를 넣어서 로직을 태울 때 IllegalStateException이 터져야합니다.

 

 

 

잘 실행되는것을 확인하실 수 있습니다.

 

같은 방법으로 메세지를 검증해보도록 하겠습니다.

 

 

IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));

assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

단축키 option+command+v 

 

 

 

 

 

여기서 조심해야 할 것이 있습니다.

 

만약 member.setName을 spring으로 해준다면 에러가 날것입니다.

이유는 join에서 db에 값이 누적되고있기때문입니다.

 

 

 

 

그래서 afterEach()함수를 사용해줍니다.

 

@AfterEach
public void afterEach(){
    memberRepository.clearstore();
}

실행할때마다 db의 값을 다 날려주기 위함입니다.

728x90

'Spring' 카테고리의 다른 글

9. 스프링 빈과 의존관계 (Spring)  (0) 2023.04.04
8. 의존성 주입(dependency injection) (Spring)  (0) 2023.04.03
Intelli J 단축키  (0) 2023.04.03
6. API (Spring 6)  (0) 2023.04.01
5. MVC와 템플릿 엔진 (Spring 5)  (0) 2023.04.01
profile

StudyRepository

@Minseo26262

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!