이 강의를 통해 정말 단순한 예제를 가지고 스프링 생태계가 전반적으로 어떤 식으로 개발이 일어나는 지를 알아보는 것이 목표이기 때문에, 비즈니스 요구사항은 매우 단순한 형태로 구현하도록 한다.
회원 객체는 시스템이 생성하는 id 값을 저장하는 변수와 사용자의 이름을 저장할 name 변수를 포함하는 Member 도메인을 생성한다.
Getter & Setter 까지 생성
package hello.hellospring.domain;
public class Member {
private Long id; // client가 정하는 ID값이 아닌, 시스템잉 정하는 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;
}
}
회원 객체를 저장할 repository package를 생성한다.

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();
}
Optional은 java8에 들어간 기능인데, findByID나 findByName으로 가져온 값이 null일 수 있기 때문에, null을 처리하기 위해 Optional로 감싸서 반환하는 방법이다.
MemoryMemberRepository 클래스를 생성하고, 작성한 MemberRepository를 implements 한다.
인터페이스에 커서를 두고 단축키 alt + Enter 를 하여 implement methods로 함수들을 생성한다.
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<>(); // Id는 Long, name = Member로 맵핑
private static long sequence = 0L; // 0, 1, 2 키 값 생성
@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));
//Null이 반환될 가능성이 있으면 Optional로 감싼다.
}
@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(); //하나라도 찾는 것, 끝까지 찾아도 없으면 Null 반환
}
public void clearStroe(){
//저장소를 지움
store.clear();
}
}
개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나, 웹 어플리케이션의 컨트롤러를 통해서 해당 기능을 실행한다. 이 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기 어렵고 여러 테스트를 한번에 실행하기 어렵다는 단점이 있다. 자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다.
src/test/java 하위 폴더에 생성한다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.*;
class MemoryMemberRepositoryTest2 {
MemoryMemberRepository repository = new MemoryMemberRepository();
@Test
public void save(){
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(member.getId()).get();
//Assertions.assertEquals(result, member);
//이 assertEquals 방식보단 아래 방식 assertThat을 선호한다
assertThat(member).isEqualTo(result);
// Assertions에 커서 두고 alt + enter 단축키 눌러서 import static 시켜줄 수 있다.
}
@Test
public void findByName() {
Member member1 = new Member();
member1.setName("spring");
repository.save(member1);
Member member2 = new Member(); //위에거 복사하고 shift + F6로 변수 이름 한번에 변경 가능
member2.setName("spring");
repository.save(member2);
Member result = repository.findByName("spring1").get();
// ctrl + alt + V 변수 이름 설정 가능
// shift + F6 변수명 일괄 변경 가능
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);
}
}
위 코드를 작성하고 실행하게 되면 다음과 같은 에러가 나타나게 된다.

테스트에서 순서는 보장이 안된다. 모든 테스트의 순서는 메서드 별로 따로 동작하게끔 설계되어있다.
findAll( )에서 "spring1", "spring2" 를 저장하였는데, findByName( ) 에서 호출하면서 다른 객체가 나타나게 되어 에러가 발생한 것이다.
그래서 각 메서드 별로 테스트가 끝나면 메모리를 비워줘야한다.
MemoryMemberRepository만 테스트하고 있으므로, MemberRepository가 아닌 MemoryMemberRepository에 아래 메서드를 추가해준다.
public void clearStroe(){
store.clear();
}
MemoryMemberRepositoryTest 클래스에 아래 코드를 적절히 삽입해준다.
@AfterEach
public void afterEach(){
repository.clearStroe();
}
혼자 개발한다면 테스트 코드가 없어도 크게 문제가 되진 않겠지만, 협업을 하게 되는 경우, 소스 코드가 길어진다면 테스트 코드 없이 개발하는 것은 거의 불가능하므로, 깊이 있게 공부하도록 하자.