[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

비즈니스 요구사항 정리

  • 컨트롤러: 웹 MVC의 컨트롤러 역할
  • 서비스: 핵심 비즈니스 로직 구현 예) 회원 중복 가입 방지
  • 리포지토리: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
  • 도메인: 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨
폴더 구조

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

//Member.java
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;
    }
}
//MeberRepository.java

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();
}
  • null을 반환할 수 있음. 이 때 Optional로 감싸서 반환하는 방법 많이 사용
//MemoryMemberRepository.java

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import java.util.*;


public class MemoryMemberRepository implements MemberRepository{

    @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.ofNullable()로 감싸줌

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream().filter(member -> member.getName().equals(name))
                .findAny();
    } //parameter로 넘어온 name과 같은게 있는지 확인, 찾으면 반환, 아니면 null

    @Override
    public List<Member> findAll() {
        return new ArrayList<>((store.values()));
    } //store에 있는 member들 반환
}
  • option + enter 단축키 -> 필요한 메소드 자동 생성
  • 실무에서 List 많이 사용

 

 

리포지토리 테스트 케이스 작성

  • 회원 리포지토리 클래스가 제대로 작동하는지 확인하기 위한 코드
  • main이나 컨트롤러를 통한 실행은 실행하는데 오래 걸리고 반복실행하기 어렵고 여러 테스트를 한번에 실행하기 어렵다는 단점이 있음  -> JUnit이라는 프레임워크로 테스트를 실행해서 문제 해결
  • src > test>java>hello.hellospring >repository 에 파일 생성
//MemoryMemberRepositoryTest.java

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.*;


class MemoryMemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();


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


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

        repository.save(member);

       Member result = repository.findById(member.getId()).get(); //Optional에서 값을 꺼낼 때 get() 사용 좋은 방법은 아니지만 테스트코드에서는 사용 O
       // System.out.println("result =" +(result ==member));
       //Assertions.assertThat(member).isEqualTo(member); //실행했을 때 초록불 뜨면 정상 동작
        assertThat(member).isEqualTo(member);
    }

    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

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

        Member result = repository.findByName("spring1").get();
        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);
    }

}

 

//MemoryMemberRepository.java

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository{

	...

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

 

  • 전체 테스트를 돌리려면 class를 실행
  • 모든 테스트는 순서와 상관없이 동작이 되도록 설계해야함 -> 테스트가 끝날 때마다 repository를 지워주는 코드를 넣어야함
    • @afterEach : 각 테스트가 종료될 때 마다 실행됨
  • 테스트를 먼저 만들고 구현 클래스를 만들어 개발하는 것 -> 테스트주도 개발 (TDD)

 

회원 서비스 개발

//MemberService.java

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();


    /*회원가입*/

        public Long join(Member member){
            //같은 이름 중복X
            validateDuplicateMember(member);
            return member.getId();
        }

        /*중복 회원 검증 함수*/
        private void validateDuplicateMember (Member member){
            /*Optional<Member> result = memberRepository.findByName(member.getName());
            result.ifPresent(m -> {
            throw new IllegalStateException("이미 존재하는 회원입니다.");   Optional을 바로 반환하는 것 권장 X*/
            memberRepository.findByName(member.getName()).ifPresent(m -> {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
        });
        }
        /*전체 회원 조회*/
        public List<Member> findMembers() {
            return memberRepository.findAll();
        }

        public Optional<Member> findOne(Long memberId) {
            return memberRepository.findById(memberId);
        }

    }
  • command+option+v  단축키 : 반환값 받는 변수 자동 생성
  • 로직은 method로 뽑아내는 것이 좋음. control+t -> Extract Method를 통해 method로 분리

 

 

회원 서비스 테스트

//MemberServiceTest.java

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;


import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {
   /* MemberService memberService = new MemberService();
    MemoryMemberRepository memberRepository = new MemoryMemberRepository(); MemberService에서 생성한 repository와 다른 repository가 생성됨*/

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService=new MemberService(memberRepository);

    }



    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }

    @Test
    void 회원가입() {

//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);
        /* 방법 1
        try{
            memberService.join(member2);
            fail();
        }catch(IllegalStateException e){
            assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

        }*/
        IllegalStateException e = assertThrows(IllegalStateException.class,
                () -> memberService.join(member2));//예외가 발생해야 한다.
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

    }


        @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }
}
//MemberService.java

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

		. . .

    }
  • command + shift + T 단축키 : test 더 간편하게 생성할 수 있는 단축키
  • test 코드는 한글로 적어도 괜찮음. 직관적으로 알아보기 쉬워 현업에서도 자주 사용
  • given-when-then 문법 : 무언가가 주어지고 (given)  실행 했을 때 (when)  결과가 이거여야한다(then)
    • 테스트코드가 길어졌을 때 확인하기 편해짐
  • 외부에서 repository를 넣어줌 : Defendency Injection(DI)
    • @BeforeEach : 각 테스트 실행 전에 호출된다. 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존관계도 새로 맺어준다.

+ Recent posts