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

이가현·2022년 9월 22일
0

스프링

목록 보기
2/6

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

  • 회원 레포지토리 인터페이스 ( *인터페이스는 C++의 가상함수와 비슷 )
    -- 기능별로 작성
public interface MemberRepository {
      Member save(Member member);
      Optional<Member> findById(Long id);
      Optional<Member> findByName(String name);
      List<Member> findAll();
}
  • 회원 레포지토리 메모리 ( 구현부 )
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 List<Member> findAll() {
 return new ArrayList<>(store.values());
  }
      @Override
      public Optional<Member> findByName(String name) {
          return store.values().stream()
                  .filter(member -> member.getName().equals(name))
                  .findAny();
}
      public void clearStore() {
          store.clear();
} }

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

  • 자바의 JUnit을 이용하여 , 속도가 빠르고 반복 실행이 가능하며 여러 테스트를 한번에 실행할 수 있다.
 class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();
	@Test
    public void save() {
		//given
        Member member = new Member();
        member.setName("spring");
        
		//when
        repository.save(member);
        
		//then
        Member result = repository.findById(member.getId()).get();
        assertThat(result).isEqualTo(member); // assertThat은 test 결과를 확인하기 위한 JUnit 중 하나
    }
}
  • given, when, then으로 나눠서 작성
  • 테스트는 각각 독립적으로 실행되어야 한다. 테스트 순서에 의존관계가 있는 것은 좋은 테스트가 아니다.

📒 AfterEach 📒

@AfterEach
    public void afterEach() {
        repository.clearStore();
    }
  • 한번에 여러 테스트를 실행하면 메모리 DB에 직전 테스트의 결과가 남을 수 있다.
    --> @AfterEach 를 사용해서 각 테스트가 종료될 때 마다 메모리 DB에 저장된 데이터를 삭제한다.

📒 BeforeEach 📒

@BeforeEach
  public void beforeEach() {
      memberRepository = new MemoryMemberRepository();
      memberService = new MemberService(memberRepository);
  }
  • 각 테스트 실행 전에 호출된다. 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존관계도 새로 맺어준다.

📍 회원 서비스 개발

❌ 기존 방식 ❌

public class MemberService {
	private final MemberRepository memberRepository = new MemoryMemberRepository();
}
  • 회원 서비스가 메모리 회원 리포지토리를 직접 생성하게 함
    ⬇️
public class MemberService {
      private final MemberRepository memberRepository;
      public MemberService(MemberRepository memberRepository) {
          this.memberRepository = memberRepository;
}
...
}
  • MemberService 입장에서, 외부에서 memberRepository를 넣어줄 수 있도록
    (=DI 가능하게 ) 변경

📍 회원 서비스 테스트

class MemberServiceTest {
	MemberService memberService;
	MemoryMemberRepository memberRepository;
    
	@BeforeEach
	public void beforeEach() {
    	memberRepository = new MemoryMemberRepository();
    	memberService = new MemberService(memberRepository);
    }
    
    // MemoryMemberRepository와 MemberService는 서로 다른 리포지토리인데 같은 메모리를 사용하게 하기 위한 코드
    
    @AfterEach
	public void afterEach() {
    	memberRepository.clearStore();
	}
    
    // 테스트 주도 개발에서 함수 이름은 한글로 많이 씀.
    @Test
	public void 중복_회원_예외() throws Exception {
		//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)); 
            // 예외가 발생해야 한다. 
            // assertThrows는 Try-catch 대신에 예외 처리하려고 쓰는 것임.
            
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
	}
}

0개의 댓글