스프링 입문 (회원 관리 예제 - Service, Test Case 작성, DI)

ju_bro·2022년 5월 22일
0

Spring

목록 보기
3/5
post-thumbnail

🌠 회원 서비스 개발

  • Service : 핵심 비즈니스 로직 구현
    (중간 부분으로서 실제 중요한 동작이 많이 일어납니다.)
    (주로 Repository에 구현된 메소드를 이용하여 구현❕)

예시 코드 :

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    // 회원가입
    public Long join(Member member){
        validateDuplicateMember(member); // 중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) { // 중복 회원 검증 메소드
        // 같은 이름이 있는 경우 회원 X
        Optional<Member> result = memberRepository.findByName(member.getName());
        // Optional을 사용함으로써 Null값을 처리할 수 있을 뿐만 아니라 Optional의 다양한 메소드를 사용할 수 있다.
        result.ifPresent(m ->{ // ifPresent -> result에 null이 아닌 값이 있으면 동작 (Optional 일 때 사용 가능)
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        });
    }

    // 전체 회원 조회
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }

    // 회원 한 명 조회
    public Optional<Member> findOne(Long memberId) {
        return memberRepository.findById(memberId);
    }
}


🌠 Test Case 작성

  • 테스트를 실행하고자 하는 class에서 단축키 Ctrl + Shift + T 를 이용하여 자동으로 테스트 코드의 뼈대를 만들 수 있습니다.


    단축키 실행 :

    단축키 실행 결과 :

    class MemberServiceTest {
    
      @Test
      void join() {
      }
    
      @Test
      void findMembers() {
      }
    
      @Test
      void findOne() {
      }
    }

    이렇게 단축키를 사용하면 별도의 Package를 만들고 내부에 Test용 class를 선언할 필요 없이 함수의 뼈대까지 빠르게 작성해주어 시간을 단축할 수 있습니다.


    MemberServiceTest

      class MemberServiceTest {
      MemberService memberService = new MemberService();
      MemoryMemberRepository memberRepository = new MemoryMemberRepository();
    
      @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());
      }
    
      @Test
      public void 중복_회원_예외() throws Exception {
          //Given
          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));//예외가 발생해야 한다.
          assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다."); 
          // 메세지가 동일한지 확인, 동일하다면 예외처리가 된 것
      }
    }
  • 위의 코드는 정상 동작하지만 한 가지 문제점이 있습니다.
    바로 Test에 사용되는 memberService와 memberRepository가 각각 new를 사용해 직접 생성되고 있는 것입니다.
    이는 memberService와 memberRepository가 서로 다른 인스턴스(Instance) 임을 의미하고 이는 Test에 사용되는 값들이 달라질 수 있음을 야기하기 때문입니다.

    따라서 사용하는 DB가 같아지도록 같은 인스턴스(Instance)로 만들어 주어야 합니다.



🌠 DI (Dependency Injection) 적용

  • 스프링 IOC 컨테이너 핵심 개념 중 하나인 DI는 의존성 주입이란 말로도 쓰입니다.
    객체 간의 의존 관계를 외부의 조립기가 관리하여 불필요한 의존 관계를 없애거나 줄일 수 있으며 단위테스트 수행이 수월하다는 장점이 있습니다.

  • DI(의존성 주입) : 객체를 직접 생성하는게 아닌 객체를 외부에서 생성해서 주입시켜주는 방식


    기존 MemberService의 객체 생성

      private final MemberRepository memberRepository = new MemoryMemberRepository();

    DI를 적용한 MemberService의 객체 생성

      private final MemberRepository memberRepository;
    
      // Repository를 직접 new로 생성하는 것이 아닌 외부에서 넣어주도록 함으로써 Test시 같은 객체 메모리를 사용할 수 있도록 함. (= DI 가능)
      public MemberService(MemberRepository memberRepository) {
          this.memberRepository = memberRepository;
      }

    MemberServiceTest 객체 생성 부분 수정

    
      MemberService memberService;
      MemoryMemberRepository memberRepository;
    
      @BeforeEach // 각각의 메소드가 시작할 때마다 동작한다.
      public void beforeEach() { 
          memberRepository = new MemoryMemberRepository();
          memberService = new MemberService(memberRepository);
      }
  • Test들은 독립적으로 시행되어야 하기 때문에 BeforeEach 어노테이션을 사용하여 각각의 메소드들이 실행될 때마다 객체를 생성하도록 합니다.

  • Repository 객체를 먼저 생성한 후 Service에 해당 객체를 넣어줌으로써 같은 객체 메모리를 사용할 수 있도록 합니다.



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

https://inf.run/M2nF

profile
Inha University

0개의 댓글