제육's 휘발성 코딩
반응형

1. 회원 도메인 개발

1.1. 구현 기능

  • 회원 등록
  • 회원 목록 조회

1.2. 순서

  • 회원 레포지토리 개발
  • 회원 서비스 개발
  • 회원 기능 테스트

1.3. 회원 레포지토리 개발

<code />
package jpabook.jpashop.repository; import jpabook.jpashop.domain.Member; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.util.List; @Repository public class MemberRepository { @PersistenceContext private EntityManager em; public void save(Member member) { em.persist(member); } public Member findOne(Long id) { return em.find(Member.class, id); } public List<Member> findAll() { return em.createQuery("select m from Member m", Member.class) .getResultList(); } public List<Member> findByName(String name) { return em.createQuery("select m from Member m where m.name =:name", Member.class) .setParameter("name", name).getResultList(); } }
  • @PersistenceContext : 엔티티 매니저 주입
  • @PersistenceUnit : 엔티티 매니저 팩토리 주입 ( 매니저만 주입받으면 사용가능하므로 해당 애노테이션을 사용하는 경우는 거의 없다.)
  • @Repository : 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환
  • createQuery를 보면 Jpql을 사용했다. 파라미터가 들어가는 경우 : 를 사용하고 setParameter로 값을 넣어준다.

 

1.4. 회원 서비스 개발

<code />
package jpabook.jpashop.service; import jpabook.jpashop.domain.Member; import jpabook.jpashop.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class MemberService { private final MemberRepository memberRepository; /** * 회원 가입 */ @Transactional public Long join(Member member) { 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); } }
  • @Transactional : 트랜잭션으로 관리하도록 지정 , readOnly=true 는 변경이 없는 읽기 전용 메서드임을 명시하여, 영속성 컨텍스트에 플러시하지 않게 되어 약간의 성능향상이 발생한다.
  • 생성자 주입은 @RequiredArgsContructor 를 사용하면 간단하게 표현할 수 있다.

 

1.5. 회원 기능 테스트

<code />
package jpabook.jpashop.service; import jpabook.jpashop.domain.Member; import jpabook.jpashop.repository.MemberRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest @Transactional class MemberServiceTest { private final MemberService memberService; private final MemberRepository memberRepository; @Autowired public MemberServiceTest(MemberService memberService, MemberRepository memberRepository) { this.memberService = memberService; this.memberRepository = memberRepository; } @Test public void 회원가입() { // given Member member = new Member(); member.setName("sasca"); // when Long savedId = memberService.join(member); // then assertEquals(member, memberRepository.findOne(savedId)); } @Test public void 중복_회원_예외() { // given Member member1 = new Member(); member1.setName("sasca"); Member member2 = new Member(); member2.setName("sasca"); // when memberService.join(member1); // then assertThrows(IllegalStateException.class, () -> { memberService.join(member2); }); } }
  • Junit5 기반으로 테스트 코드를 작성했다.
  • 테스트 시점에서 @Transactional 은 실행할 때마다 트랙잭션으로 만들어서 커밋하고 끝나면 강제로 롤백한다.

 

1.5.1. 메모리 DB 사용법

<code />
# test/resources/application.yml 생성 spring: # datasource: # url: jdbc:h2:mem:test # username: sa # password: # driver-class-name: org.h2.Driver # jpa: # hibernate: # ddl-auto: create # properties: # hibernate: # show_sql: true # format_sql: true logging.level: org.hibernate.SQL: debug org.hibernate.type: trace
  • 스프링부트는 테스트환경에서 test/resources/application.yml 이 존재하면 테스트단에서 해당 yml을 찾아간다. 이 때, datasource 설정이 없으면 기본적으로 메모리 DB를 사용하고 create-drop 모드로 동작한다. 즉, 다음과 같이 주석처리가 되어있어도 자동으로 설정해준다.
  • 테스트 케이스는 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다. 그런면에서 메모리 DB를 사용하는 것이 이상적이다.

본 포스팅은 '인프런 - 김영한님 강의(실전! 스프링 부트와 JPA 활용1)'를 토대로 정리한 내용입니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^