내가 진행할 개발 순서는 domain > repository > Test(Jumit5) > service > controlle순으로 개발할 것이다.
package study.spring.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;
}
}
위 코드는 회원가입 할 사람의 아이디와 이름 변수를 선언하고 입력받고, 출력하는 클래스이다.
public interface MemberRepository {
Member save(Member member); // 여기는 원래 null이여야 함. 있는 회원이 또 회원가입 X
// Optional : java8부터 나옴 -> OPtional으로 감싸주면 null일 수 가 없음.
// -> NPE 예외처리 안해도 됨.
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; // sequence -> Long임.
@Override
public Member save(Member member) {
member.setId(++sequence); // 회원가입을 할 때마다 1씩 증가. 1번 회원, 2번 회원.
store.put(member.getId(), member); // key : 1, value : member.
return member; // member 반환.
}
}
위 코드는 회원가입을 할 때 회원 ID와 이름이 저장되는 코드인데, ID는 중복이 되면 안되기 때문에 회원가입이 될 때마다 ++sequence로 회원1, 회원2, 회원3 ...으로 저장된다.
@Override
// 단건 조회
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id)); // 2를 매개변수로 받으면 2번 회원 조회
}
@Override
// 단건 조회
public Optional<Member> findByName(String name) {
return store.values().stream() // steam :
// 멤버의 이름과 입력받은 이름과 같으면 반환.
.filter(member -> member.getName().equals(name))
.findAny();
}
위 코드는 회원의 ID나 이름을 입력받아 저장된 회원을 조회하는 코드인데, stream 메소드를 사용해서 1번째 회원부터 마지막회원까지 돌리면서 filter 메소드로 입력된 회원이름과 같으면 그 이름을 반환해서 조회한다.
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values()); // 모든 값들을 반환.
}
public void clearStore() {
store.clear();
} // 모든 회원들 삭제.
Repository가 제대로 작동되는지 확인하기 위해서 TestCase를 작성한다.
MemoryMemberRepository memberRepository = new MemoryMemberRepository();
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
public void save() throws Exception {
// given
Member member = new Member();
// 회원이름은 Spring, 참고로 ID는 자동으로 1씩 증가함(회원이 한명씩 늘어날 때 마다)
member.setName("Spring");
// when
memberRepository.save(member); // 회원 저장
// then
Member result = memberRepository.findById(member.getId()).get();
// 만약에 내가 저장한 회원이랑, findById로 찾은 회원이랑 일치하다면 테스트케이스 성공.
assertThat(result).isEqualTo(member);
}
멤버이름을 Spring이라 지정후에 저장을 하고, ID로 비교해서 저장한 회원과 찾을 회원이 같으면 테스트케이스가 성공한다.
@Test
public void findByName() throws Exception {
Member member1 = new Member();
member1.setName("Spring1");
memberRepository.save(member1);
Member member2 = new Member();
member2.setName("Spring1");
memberRepository.save(member2);
// when
// 이름이 Spring1인 회원 조회
Member result = memberRepository.findByName("Spring1").get();
// then
// 만약 조회한 회원과 member1이 같다면 테스트케이스 성공
assertThat(result).isEqualTo(member1);
}
위 코드는 이름으로 회원 조회한 것을 테스트한 코드인데, 먼저 1번째, 2번째 회원을 저장하고, 이름인 Spring1인 회원을 result에 저장하고, 그 값이 member1과 같다면 테스트케이스가 성공한다.
@Test
public void findAll() throws Exception {
Member member1 = new Member();
member1.setName("Spring1");
memberRepository.save(member1);
Member member2 = new Member();
member2.setName("Spring1");
memberRepository.save(member2);
// when
// 저장된 회원 모두 조회
List<Member> result = memberRepository.findAll();
// then
// 저장된 회원의 수는 2명임 -> 만약 회원을 저장한 List의 크기가 2라면 테스트케이스 성공.
assertThat(result.size()).isEqualTo(2);
}
위 코드는 회원이 전체 조회 됬는지 테스트하는 코드인데, 먼저 회원 2명을 저장하고, 전체 회원을 조회한 것을 result에 저장하고, 그 길이가 2인지 비교해서 맞으면 테스트케이스가 성공한다.
Service는 비지니스 로직을 처리한다.
// DI(Dependencies Injection) : 의존성 주입.
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원가입
public Long join(Member member) {
validationDuplicateMember(member); // 중복 확인 메서드
memberRepository.save(member);
return member.getId();
}
private void validationDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> { // 만약 있으면.
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
위 코드는 회원 가입후 정보를 저장하는 코드인데, 매개변수로 받은 member가 중복되는지 validationduplicateMember메소드를 호출해 이름이 이미 있으면 이미 존재하는 회원입니다를 출력한다. 없으면 member를 저장하고 ID를 출력한다.
// 전체 회원 조회
public List<Member> findMembers() {
return memberRepository.findAll();
}
// 단건 조회
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
위 코드는 모든 회원을 조회하고 ID만 조회하는 코드이다.
Service가 제대로 작동되는지 확인하기 위해서 TestCase를 작성한다.
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
public void 회원가입() throws Exception {
// given
Member member = new Member();
member.setName("hello");
// when
Long saveId = memberService.join(member);
// then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
위 코드는 회원가입이 잘 되는지 테스트하는 케이스인데, 먼저 memberRepository를 초기화해주고, 먼저 hello라는 회원을 저장하고, 그 회원의 ID를 findMember에 저장한 후, 멤버의 이름과, findMember의 이름이 같으면 테스트케이스가 성공한다.
@Test
public void 중복_회원_예외() throws Exception {
// given
// 고의적으로 name 중복
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));
// then
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
위 코드는 중복 되는 회원은 예외처리하기 위해 일부로 중복되는 이름의 회원을 두개 만든 다음, IllegalStateException이라는 예외가 발생해서 이미 존재하는 회원입니다를 출력하게 한다.