스프링 입문 (회원 관리 예제 - Domain + Repository, Test Case 작성)

ju_bro·2022년 5월 16일
0

Spring

목록 보기
2/5
post-thumbnail

🌠 일반적인 웹 애플리케이션 계층 구조



  • Controller : 웹 MVC의 컨트롤러 역할
    - 가장 바깥 부분으로서 요청 및 응답을 처리합니다.



  • Service : 핵심 비즈니스 로직 구현
    - 중간 부분으로서 실제 중요한 동작이 많이 일어납니다.



  • Domain : 비즈니스 도메인 객체,
    ex) 회원정보와 같이 주로 DB에 저장하고 관리되는 것
    - DB의 Table 역할을 하는 클래스

예시 코드 :

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;
    }
}

  • Repository : DB에 접근, Domain 객체를 DB에 저장하고 관리
    - DB의 SQL 역할을 하는 인터페이스
    - JPA 가 만들어주는 것 이외의 SQL 문을 사용하고 싶다면 여기에 새로 정의하면 됩니다.

예시 코드 :

  1. Interface
public interface MemberRepository {
    Member save(Member member);
    Optional<Member> findById(Long id); // Optional -> null 값을 처리할 수 있도록
    Optional<Member> findByName(String name);
    List<Member> findAll();
}

  1. 구현체
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());
    }
}


🌠 Test Case 작성

개발한 기능을 실행해서 테스트할 때 Java의 main 메서드 또는 웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 실행하는데 이러한 방법은 준비하고 실행하는 것이 오래 걸릴 뿐만 아니라 반복 실행하기 어려우며 여러 테스트를 한 번에 실행하기 어렵습니다.

📌 Java는 JUnit이라는 프레임워크로 테스트를 실행해서 문제를 해결합니다.


1. 테스트 코드를 작성해야 하는 이유

  • 코드의 안정성을 높일 수 있고, 기능을 추가하거나 수정하면서 발생하는 부작용을 줄일 수 있습니다.

  • 서버를 실행하는 등의 시간을 절약할 수 있습니다.

  • 필요한 데이터를 미리 기입하고, 테스트가 끝나고 정리하는 등의 행동이 불필요 합니다.

  • 단위테스트의 경우 테스트가 매우 빠릅니다.

  • 개발자가 작성한 메소드가 어떻게 동작하고, 어떤 결과를 반환했으면 하는가를 작성한 것이기 때문에 코드를 처음 보는 개발자들이 코드의 동작을 더 수월하게 이해할 수 있습니다.

2. 테스트 코드의 동작

예시 코드 :

@Test
  public void save() {
      Member member = new Member();
      member.setName("spring");

      repository.save(member);

      Member result = repository.findById(member.getId()).get();
      Assertions.assertEquals(member, result); // 기댓값과 실제 값이 일치하는지
  }

1. 기댓값과 실제 값이 일치하는 경우

2. 기댓값과 실제 값이 일치하지 않는 경우


위에서 볼 수 있듯이 테스트 코드를 사용하면 각각의 메소드의 기댓값과 실제 값을 직관적으로 확인하여 정상적으로 동작하는지 손쉽게 확인할 수 있었습니다.

하지만, 여러 메소드의 테스트를 동시에 실행한다면 아래와 같은 문제가 발생할 수 있습니다.

3. 코드의 문제가 없으나 Test에 실패하는 상황

모든 메소드는 순서와 상관없이 즉, 의존관계가 없이 메소드 별로 따로 동작하도록 설계되어야 합니다.
따라서 여러 메소드의 테스트를 동시에 실행하는 경우 순서가 보장되지 않고 실행되는데 이때, 다른 메소드에서 저장된 값이 Test에 사용되어 코드의 문제가 없는 경우에도 Test에 실패하는 상황이 생길 수 있습니다.

아래의 코드들을 추가하여 이를 해결할 수 있습니다.

MemoryMemberRepository

  public void clearStore() {
      store.clear(); // 저장된 값 삭제
  }

MemoryMemberRepositoryTest

  @AfterEach // 각각의 메소드가 끝날 때마다 동작한다. (콜백 메소드)
  public void afterEach() {
      repository.clearStore();
  }

적용 결과

위의 코드들을 추가함으로서 하나의 테스트가 끝날 때마다 저장소 혹은 공용 데이터들을 깔끔히 지워줄 수 있습니다.



이 글은 인프런 "김영한" 님의 [스프링 입문] 강의를 듣고 개인적으로 학습하기 위해 정리한 글입니다. 🙂

https://inf.run/M2nF

profile
Inha University

0개의 댓글