1-4. 회원 도메인 개발

지니🧸·2023년 2월 1일
0

Spring Boot & JPA

목록 보기
4/35

본 문서는 인프런의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 (김영한) 강의를 공부하며 작성한 개인 노트입니다.

순서: 회원 엔티티 코드 다시 보기 > 회원 리포지토리 개발 > 서비스 개발 > 기능 테스트

💐 회원 리포지토리 개발

@PersistenceContext

  • 스프링이 entity manager를 만들어서 주입(injection)해줌
@PersistenceContext
private EntityManager em;

em.createQuery(JPAQuery, 반환타입)

  • JPA 쿼리를 직접 입력하여 사용
  • findAll() 등의 메서드에 사용됨
public List<Member> findAll() {
      return em.createQuery("select m from Member m", Member.class)
              .getResultList();
}

Named parameters

public List<Member> findByName(String name) {
    return em.createQuery("select m from Member m where m.name = :name", Member.class)
            .setParameter("name", name)
            .getResultList();
}
  • 쿼리 내 :variable.setParameter에서 정의됨
    • .setParameter(쿼리 내 변수명, 변수값)

🌾 회원 서비스 개발

@Transactional

  • JPA의 모든 데이터 변경/로직은 가급적이면 트랜잭션 안에서 실행되어야함
  • 클래스 레벨에 에노테이션
  • 옵션: readOnly
    • @Transactional(readOnly = true)
    • JPA가 조회만 하는 기능에서는 성능 최적화 가능
    • 조회 기능이 많은 클래스는 클래스 전체에 이 옵션을 적용하고 데이터 변경이 필요한 메서드에만 @Transactional 에노테이션 추가

중복 회원 방지 기능

private void validateDuplicateMember(Member member) {
    List<Member> findMembers = memberRepository.findByName(member.getName());
    if (!findMembers.isEmpty()) {
        throw new IllegalStateException("이미 존재하는 회원입니다");
    }
}
  • 같은 이름의 두 회원이 동시 가입할 때 이 메서드가 동시호출 되면서 로직이 실패할 수 있음
  • 솔루션: 디비에 UNIQUE 제약 걸기

@Autowired

@Autowired 
private MemberRepository memberRepository;
  • 단점: 변경할 수 없음
    • 테스트 등 때문에 바꿔야할 수도 있음

솔루션1: setter injection

private MemberRepository memberRepository;

@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}
  • 단점: 런타임에 누군가 바꿔버릴 수도 있음
  • 동작 중에 바꿀 이유 없음

솔루션2: 생성자 Injection

private MemberRepository memberRepository;

@Autowired
public MemberService(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}
  • 장점: 런타임 중에 바뀌지 않음, 테케에도 활용 가능
  • 생성자가 하나일 경우에는 스프링이 @Autowired 에노테이션 없이도 자동 injection

솔루션3: @AllArgsConstructor

  • 클래스 레벨 에노테이션
  • 생성자 injection 자동으로 만들어줌

솔루션4: @RequiredArgsConstructor

  • 클래스 레벨 에노테이션
  • final 설정된 필드만 생성자 injection에 포함하여 자동 생성
  • 스프링 부트가 EntityManager도 (@PersistentContext 대체로) @Autowired로 injection 지원을 해주기 때문에 솔루션 3/4 적용 가능

🪷 회원 기능 테스트

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

@RunWith

  • 스프링과 테스트 통합

@SpringBootTest

  • 스프링 부트 띄우고 테스트
  • @Autowired을 위해 꼭 필요

@Transactional

  • 테스트에도 데이터 변경이 필요함
  • 테스트마다 트랜잭션 시작
  • @Transactional이 있어야 데이터 롤백 가능
  • 자동으로 트랜잭션마다 롤백해서 디비에 반영이 안됨

강제 반영하는 솔루션

  1. @Rollback(false)
  2. em.flush();
@Autowired private EntityManager em;

public void 회원가입() {
	...
    em.flush();
}

fail("");

  • org.junit.Assert.*
  • 테스트케이스에서 이 라인까지 오면 안됨 > 오면 실패한 것

테스트케이스를 위한 설정

  1. 테스트는 케이스 격리된 환경에서 실행 & 끝난 후 데이터 초기화가 효과적
  • 메모리 DB 사용이 이상적
  1. 테스트 케이스를 위한 스프링 환경과 애플리케이션 실행 환경은 보통 다름
  • 설정 파일을 다르게 이용하자
  • test 디렉토리 안에 application.yml을 따로 만들면 테스트 실행 시 main의 설정 파일을 무시하고 test 디렉토리 내의 파일을 참조한다
  • 본 파일의 spring.datasource.url을 메모리로 바꾸면됨
  • 스프링 부트는 기본 셋팅이 메모리 모드기 때문에 datasource, jpa 등 모두 주석처리 가능
profile
우당탕탕

0개의 댓글