실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 섹션4. 회원 도메인 개발
CS/김영한 스프링 강의

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 섹션4. 회원 도메인 개발

섹션3의 애플리케이션 구현 준비

 

 

이제 섹션 4에선 db랑 소통하는 레퍼지토리 짜고, 서비스 짜고, 이걸 테스트하는 코드를 짜본다.

 

EntityManager를 저렇게 정의만 해도 lombok이 final 붙은건 가져오기끔 @RequiredArgsConstructor를 한다. 사실 이걸 위해 @Autowired쓰고 아니면 생성자에 붙이고.. 해야 되는데 계속 더 간단한 방법이 나온다.

EntityManger의 createQuery의 특이점은 안의 내용이 결국은 sql문으로 변환되지만 여기선 모델이 아닌 엔터티를 받는다는데 있다.

 

@Service
@Transactional(readOnly = true) // 읽기 전용이면 성능이 좋아짐
@RequiredArgsConstructor // final이 있는 필드만 가지고 생성자를 만들어줌 (lombok 기능)
public class MemberService {

    private final MemberRepository memberRepository;

//    @Autowired // 생성자가 하나면 생략 가능
//    public MemberService(MemberRepository memberRepository) {
//        this.memberRepository = memberRepository;
//    }

    /**
     * 회원 가입
     */
    @Transactional
    public Long join(Member member) {
        // 같은 이름이 있는 중복 회원 X
        validateDuplicateMember(member); // 중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        List<Member> findMembers = memberRepository.findByName(member.getName());
        if (!findMembers.isEmpty()) {
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        }
    }

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

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

}

여기서 조심할 점은 .save()를 해서 안의 persist를 사용할 텐데 이 줄이 끝난다고 바로 db에 적용되는게 아니다. 저 함수를 호출한 @Transactional 함수가 끝나야 적용된다.

 

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
class MemberServiceTest {

    @Autowired
    MemberService memberService;
    @Autowired
    MemberRepository memberRepository;
    @Autowired
    EntityManager em;

    @Test
    public void 회원가입() throws Exception {
        // given
        Member member = new Member();
        member.setName("kim");

        // when
        Long savedId = memberService.join(member);

        // then
        em.flush(); // DB에 쿼리를 날림. 그래도 어차피 @Transactional 때문에 끝나면 롤백됨.
        assertEquals(member, memberRepository.findOne(savedId));
    }

    @Test
    public void 중복_회원_예외() throws Exception {
        // given
        Member member1 = new Member();
        member1.setName("kim");

        Member member2 = new Member();
        member2.setName("kim");

        // when
        memberService.join(member1);
        Assertions.assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        // 예외가 발생해야 한다.

        // then
    }

}

실제 db를 사용하는것이 아닌 인메모리 테스트를 위해, 즉 실제 product 실행 환경과 테스트 환경 db를 분리하기 위해 test쪽에도 application 설정 파일을 만들어준다. 원래 h2:mem:test와 같이 메모리에서 돌린다는걸 명시해야 하지만 테스트 환경에선 메모리 테스트가 기본값이기 때문에 생략해도 된다.