3 분 소요

[공지사항] [해당 내용은 인프런 김영한의 ‘‘스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술’’ 강의 입니다.] 강의를 듣고 이해한 내용을 바탕으로 직접 필기한 내용입니다. 인프런 : 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술



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



비지니스 요구사항 정리

  • 데이터 : 회원 ID, 이름
  • 기능 : 회원 등록, 조회
  • 아직 데이터 저장소가 선정되지 않음 (가상의 시나리오)
    • NOSQL로 할지, 관계형으로 할지~~ 정해지지 않음
image-20240214141531328
  • 컨트롤러
    • 웹 MVC의 컨트롤러 역할

    • 핵심 비지니스 로직 구현

  • 리포지토리
    • 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
  • 도메인
    • 비지니스 도메인 객체 ex) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨.



클래스 의존관계

데이터 저장소

  • 향후 DB 에 따라 저장소를 교체해야 하기 때문에 우선 회원 리포지토리를 인터페이스로 설계한다.
    • 데이터 저장소는 RDB, NoSQL 등 다양한 저장소를 고민중인 상황으로 가정.
  • 개발은 해야하기때문에 초기 개발 단계는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용한다.

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

src -> main -> java -> hello.hellospring -> domain 패키지 생성

  • id, name선언후 get set 을 포함한 클래스를 만든다.
package hello.hellospring.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;
    }
}



src -> main -> java -> hello.hellospring -> repository 폴더생성 -> MemberRepository 인터페이스 생성

  • save(Member member): 멤버를 저장하는 메서드.
  • findById(Long id): ID로 멤버를 찾는 메서드.
  • findByName(String name): 이름으로 멤버를 찾는 메서드.
  • findAll(): 모든 멤버를 조회하는 메서드.
public interface MemberRepository {
    Member save(Member member);
    Optional<Member> findById(Long id);
    Optional<Member> findByName(String name);
    List<Member> findAll();
}



src -> main -> java -> hello.hellospring -> repository 폴더생성 -> MemoryMemberRepository 클래스 구현

  • 위의 인터페이스를 구현한다. 맥북에서는 option + Enter로 인터페이스의 함수를 구현할 수 있다.
  • save(Member member): 멤버를 저장하는 메서드입니다. 멤버의 ID는 sequence를 증가시켜 부여하고, HashMap인 store에 저장합니다.
  • findById(Long id): 주어진 ID에 해당하는 멤버를 찾아 Optional으로 감싸서 반환합니다.
  • findByName(String name): 주어진 이름에 해당하는 멤버를 찾아 Optional으로 감싸서 반환합니다.
  • findAll(): 현재 저장된 모든 멤버를 리스트로 반환합니다.
/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려 */
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 메서드를 통해서 실행하거나, 웹 어플리케이션의 컨트롤러를 통해 해당 기능을 실행한다. 하지만 이 방법은 오래걸리며 여러 테서트를 한번에 실행하기 어렵다.

자바의 Jnit 이라는 프레임 워크로 테스트를 실행해서 이러한 문제를 해결한다.

test -> java -> hello.hellospring -> repository 패키지 생성 -> MemoryMemberRepositoryTest 클래스 생성

  • 앞에서 만든 MemoryMemberRepository 객체를 선언한 다음
  • @Test 를 import 한다. 이때 Jnuit 을 사용한다. import org.junit.jupiter.api.Test;
  • 그다음 앞에서 정의했던 함수를 작성하고 실행하면 된다.
      1. MemoryMemberRepository에 새로운 멤버(Member 객체)를 저장하는지 확인합니다.
      1. 저장된 멤버를 findById 메서드를 사용하여 다시 가져오는지 확인합니다.
      1. 가져온 멤버와 원래의 멤버가 값이 일치하는지 equals 메서드를 통해 검증합니다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.junit.jupiter.api.Test;

public class MemoryMemberRepositoryTest {
    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();
        System.out.println("result = " + (result.equals(member)));

    }
}


함수를 실행하면

result = true 가 출력되어야한다.



  • findByName의 test code이다.
@Test
public void findByName() {
   
  // 1. Member 객체 생성 및 저장
    Member member1 = new Member();
    member1.setName("spring1");
    repository.save(member1);

    Member member2 = new Member();
    member2.setName("spring2");
    repository.save(member2);

    // 2. 특정 이름("spring2")을 가진 멤버 찾기
    Member result = repository.findByName("spring2").get();

    // 3. 검증: 찾아온 멤버와 기대값(member2)이 동일한지 확인
    Assertions.assertThat(result).isEqualTo(member2);
}



  • findAll()의 test코드이다.
@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();
    Assertions.assertThat(result.size()).isEqualTo(2);
}



이제 이 test 코드를 전체 실행하려 했지만 에러가 났다. 그 이유는 spring 특성 상 순서의 의존적이게 설계 하면 안되기 때문이다.

따라서 Test 한개가 끝나면 데이터를 clear 해줘야한다.

  • 기존에 만든 MemoryMemberRepository 에 들어가서 함수를 추가해준다.

    public void clearStore(){
        store.clear();
    }
    

그 후 다시 Test로 돌아와서

  • @AfterEach 선언해준다.
    • 메소드가 끝날 때마다 콜백 해준다.
public class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();

       @AfterEach
    public void afterEach() {
        repository.clearStore();

    }



다시 전체 코드를 돌려보면 순서와 상관없이 오류 없이 돌아가는 것을 확인 할 수 있다.

태그:

카테고리:

업데이트:

댓글남기기