본문 바로가기

Spring

3장 회원관리 예제

728x90
반응형
SMALL

비지니스 요구사항

 - 비지니스 요구사항을 정리하여 데이터, 기능 들을 확립 하는 것이 중요하다.

 

  • 일반적인 웹 애플리케이션 계층 구조
    • 컨트롤러 : 웹 MVC 의 컨트롤러 역할
    • 서비스 : 핵심 비지니스 오직 구현
    • 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
    • 도메인 : 비지니스 도메인 객체
  • 클래스 의존 관계
    • 아직 데이터 저장소가 선정 되지 않았을 때, 인터페이스로 구현 클래스로 변경 할 수 있도록 설계한다.
    • 데이터 저장소는 RDBMS, NoSQL 등의 저장소를 고민
    • 개발 초기 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사한다.

 

 

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

 - 해당 강의의 실습을 진행하였음

실습 디렉토리

  • 회원 도메인을 만들어주기 위해서 우선 main/java/ 안에 domain 이라는 package를 생성
  • domain 안에 Member 클래스 생성
public class Member {
    private Long id;
    private String name;
    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }


}

 

  • main/java 안에 repository 라는 package 생성
  • repository 안에 MemberRepository 라는 interface 생성
import com.example.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();

}

 

  • 같은 디렉토리에서 MemeoryMemverRepository 라는 클래스 생성
import com.example.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
    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이라는 프레임워크로 테스트를 실행해서 위 문제를 해결할 수 있음

 

test 디렉토리

  • test 디렉토리 안에 프로젝트명 안에 repository package 생성하고 [테스트할파일명Test] 라는 이름으로 만들어줌
import com.example.hellospring.domain.Member;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.List;

class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();

    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();
        Assertions.assertThat(member).isEqualTo(null);
    }

    @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();
        Assertions.assertThat(result).isEqualTo(member1);
    }

    @Test
    public void finaAll(){
        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);
    }

}

 - 테스트 하고자 하는 함수 하나하나 추가해주어 맞는지 틀린지를 확인

 - @afterEach 태크를 통해 함수 하나가 끝날때 마다 실행하게 만듬

 - test 할 내용이 너무 많을 때

위와 같이 실행시키면 모든 함수에 대해서 Test가 가능함

 

 

 

회원 서비스 개발

 - 서비스를 개발하는 방식에 대한 설명

import com.example.hellospring.domain.Member;
import com.example.hellospring.repository.MemberRepository;
import com.example.hellospring.repository.MemoryMemberRepository;

import java.util.*;
public class MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    /**
     * 회원가입
     */
    public Long join(Member member) {
        //같은 이름이 있는 중복 회원은 안죔
        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);
    }
}

위 코드를 main/java/[프로젝트 명] 안에 service 라는 package를 만들어 주고 MemberService라는 class를 만들어서 넣어줌

 - 회원 가입 : join 함수이며 안에 같은 이름이 있는 중복 회원은 안되게끔 구현

 - 전체 회원 조회 : findMembers 함수이며 memberrepository 안에 있는 데이터 전부를 list 안에 담겨서 받음

 - 한명의 회원 검색 : findOne 함수이며 memberId를 통해서 한명의 회원을 찾음

 

위 와 같이 구현한 것들을 전시간에 배웠던 testcase를 만들어서 test해보는 방식으로 디버깅

 

 

회원 서비스 테스트

 - 만들어 줬던 MemberService 클래스를 에서 cmd+shift + t 를 누르면 

위와 같은 화면이 뜬다. test하고자 하는 함수를 다 선택하고 OK 를 눌러주면

해당 디렉토리의 testcase가 만들어짐

 

import com.example.hellospring.domain.Member;
import com.example.hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }


    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }
    @Test
    void 회원가입() {
        //given
        Member member = new Member();
        member.setName("hello");
        //when
        Long saveId = memberService.join(member);
        //then
        Member findMember = memberService.findOne(saveId).get();
        Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
    }
    
    @Test
    public void 중복_회원_예외() {
        //given
        Member member1 = new Member();
        member1.setName("spring");
        
        Member member2 = new Member();
        member2.setName("spring");
        //when
        memberService.join(member1);
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
        //        try{
//            memberService.join(member2);
//            fail();
//        }catch (IllegalStateException e){
//            Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
//        }
        //then
    }

    @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }
}
  • test case를 만들때는 given(주어졌을 때) -> when (이런 상황이면)-> then(이런 결과 값)이 중요
  • 현재 한국말로 된 함수명은 한국말로 해도 상관이 없다는 걸 보여주기 위함
  • @beforeEach를 통해 실행하기전에 실행되게끔 만들어준다.

MemberService 안에 MemberRepository 를 MemberService를 만들때 안에 넣어주는 형식으로 매번 새로운 repository를 생성할 필요가 없게 만듦

 private final MemberRepository memberRepository;
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

 

//본인이 생각했을 때, 디자인 패턴 (싱글턴 패턴)을 응용한 것이 아닌가 싶습니다.

 

  • 회원가입을 test하는 과정에서 중복 회원 또한 검사해봐야함으로 이에 대한 함수도 만들어 주었음
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
        
---------------------------------------------

try{
memberService.join(member2);
fail();
}catch (IllegalStateException e){

Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
}
  • 위 두개의 코드는 같은 동작을 하는 코드임 assertThrow함수가 어떻게 사용되는지를 보여주신 사례
  • // assertions에 대해서 공부가 필요할 듯 싶다.

 

 

spring 강의 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8

728x90
반응형
LIST