비즈니스 요구사항 정리
- 데이터 : 회원 ID, 이름
- 기능 : 회원 등록, 조회
- 아직 데이터 저장소가 선정되지 않음(가상의 시나리오)
일반적인 웹 애플리케이션 계층 구조
- 컨트롤러 : 웹 MVC의 컨트롤러 역할
- 서비스 : 핵심 비지니스 로직 구현
- 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
- 도메인 : 비지니스 도메인 객체, 예) 회원, 주문 , 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨
1. Repository interface 구현
- 현재 비지니스 로직에서 데이터 저장소가 선정되지 않았다고 가정하기 때문에, 인터페이스로 구현 차후에 선정될 시 변경함.
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(); // 전체 조회
}
2. Member 도메인 구현
- id는 자동으로 추가되게 long으로 설정
package hello.hellospring.domain;
public class Member {
Long id;
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
3. MemoryMemberReposoitory 인터페이스 구현체 구현
- 저장소는 모든 클래스에서 데이터를 사용함으로 static으로 구현하였고, 맵을 통해 key값으로 출력될 수 있게 설계
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 seq = 0L;
@Override
public Member save(Member member) {
member.setId(++seq);
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());
}
public void clearStore() {
store.clear();
}
}
4. 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 static org.assertj.core.api.Assertions.*;
public 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);
assertThat(repository.findById(member.getId()).get()).isEqualTo(member);
}
@Test
public void findByName() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
assertThat(repository.findByName("spring1").get()).isEqualTo(member1);
}
@Test
public void findById() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
assertThat(repository.findById(member1.getId()).get()).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);
assertThat(repository.findAll().size()).isEqualTo(2);
}
}
모든 테스트는 순서와 상관없이 함수 별로 따로 동작하게 해야 된다.
때문에 afterEach 함수를 통해서 각 함수가 실행될 때마다 MemoryMemberRepository를 클리어로 초기화해줘야 된다.
- @afterEach : 각 함수가 끝나는 시점에 항상 실행시켜줌
- @Test : junit 언노테이션으로 해당하는 함수가 테스트 메서드이며 각자 실행시켜 준다는 것을 명시
- assertThat : 기대한 값과 실제 값이 맞는지 확인하는 함수이다.
이를 통해서 우리는 순서와 상관없이 각각의 함수들을 테스트하고 잘 돌아가는지 테스트를 할 수 있다.
물론 클래스 전체를 동작시킬 수 도 있다.
결론
나는 개인적으로 다른 것보다 테스트하는 클래스를 만들고 각각의 함수 별로 테스트 하는 것을 처음 접해보았다. 항상 개발을 하다 보면 테스트보다는 동작을 우선시하게 되는데, 이러한 기능이 있다면, 내가 생각지 못한 예외들에 대해서 좀 더 유연하게 대처할 수 있을 것 같고, 이런 간단한 테스트 말고 TDD도 공부해야겠다는 생각이 들었다.
이 글은 인프런의
제목 : 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
강사 : 김영한 님의 동영상을 참조해 만들었습니다.
'Spring > 스프링 입문' 카테고리의 다른 글
회원 웹 기능 - 등록, 출력 (0) | 2022.03.08 |
---|---|
자바 코드로 직접 스프링 빈 등록하기 (0) | 2022.03.07 |
컴포넌트 스캔과 자동 의존관계 설정 (0) | 2022.03.07 |
회원 관리 예제 - 백엔드 개발 (2) (0) | 2022.03.07 |
스플링 웹 개발 기초 - 정적 컨텐츠, MVC와 템플릿 엔진 (0) | 2022.03.06 |