프로그래밍/Spring

[Spring boot / JPA] 4. 회원 도메인 개발

daykim 2023. 7. 1. 16:40
아래 강의 정리
 

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런 | 강의

실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다., - 강

www.inflearn.com

목차

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

 

회원 기능

  • 회원 등록 : save
  • 회원 목록 조회 : findOne, findAll, findByName

 

회원 리포지토리 개발


  • @Repository : 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환한다.
  • @PersistenceContext : 스프링이 엔티티 매니저(EntityManager)를 만들어 injection 해준다.
  • JPQL은 SQL과 거의 같지만, SQL은 테이블 대상 쿼리라면, JPQL 엔티티 대상 쿼리다.
  • @PersistenceUnit : 엔티티 메니터 팩토리 주입한다.

 

 

회원 서비스 개발


  • @Service : 들어가면 @Component 있다.
  • em.persist()하면 영속성 컨텍스트에 멤버 객체를 올린다. 이 때 영속성 컨텍스트는 key, value가 있는데 key값이 id가 된다. DB PK와 매핑한게 key가 된다. @GeneratedValue하면 DB마다 다른데, 알아서 id값이 생성됨이 보장된다.
    영속성 컨텍스트에 값을 넣어야 하는데, key는 PK 값이다. 그러며 동시에 id에 값을 채워준다. DB에 들어간 시점이 아니어도 그렇게 해준다.
  • @Transactional : 트랜잭션, 영속성 컨텍스트
    •  JPA의 데이터 변경이나 로직은 모두 트랜잭션 안에서 실행되어야 한다.
    • readOnly=true : 데이터 변경이 없는 읽기 전용 메서드에서 사용한다. 영속성 컨텍스트를 플러시 하지 않으므로 약간의 성능 향상(읽기 전용에는 다 적용)
    • 데이터베이스 드라이버가 지원하면, DB에서 성능 향상된다.
    • 현재 프로젝트에선, class 위에 @Transactional(readOnly = true)를 해두고, 읽기가 아닌 메서드에는 따 @Transactional을 적용해두었다.
      기본은 @Transactional(readOnly=false)이기 때문에, 따로 설정한 것은 이것이 우선권을 가진다. 뭐 이건 개인 선택이다.
  • validateDuplicateMember를 설정해도 문제가 발생할 수 있다.
    둘 이상이 동시에 통과하면, 동시에 save를 호출하게 될 수 있고, 그러면 둘이 같은 이름으로 저장될 수 있다. 현재 프로젝트에선 이게 문제가 된다.
    따라서 한 번더 최후의 방어를 해야한다. 멀티 쓰레드 상황을 고려해 제약조건 이런거 잘 고려해봐야한다.

 

// 1. 필드 주입
@Autowired
private MemberRepository memberRepository;

// 2. 생성자 주입
private final MemberRepository memberRepository;

@Autowired // 생성자 하나면, 생략 가능
public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}
  • 보통 필드 주입으로 많이 쓰지만 단점이 많다.
    • 변경 불가능한 안전한 객체 생성이 가능하다.
    • access할 수 없으니 값을 바꾸지 못해, 테스트 할 때랑 불편하다.
  • 그래서 생성자 주입 setter Injection을 많이 사용한다.
    • 가장 권장하는 방법이다.
    • 테스트코드 작성할 때 값을 직접 주입할 수 있다.
    • final 키워드를 추가하면, 컴파일 시점에 memberRepository를 설정하지 않는 오류를 체크할 수 있다.

 

Lombok

  • @AllArgsConstructor : 필드의 생성자를 만들어준다.
  • @RequiredArgsConstructor : final이 있는 필드만 가지고 생성자를 만들어준다.
  • : 스프링 데이터 JPA를 사용하면 EntityManager 도 주입 가능하다. 그래서 @Require~로 바

 

 

회원 기능 테스트


테스트 요구사항

  • 회원가입을 성공해야 한다.
  • 회원가입 할 때 같은 이름이 있으면, 예외가 발생한다.

 

기술 설명

  • @RunWith(SpringRunner.class) : 스프링과 테스트 통합
  • @SpringBootTest : 스프링 부트 띄우고 테스트 (이게 없으면 @Autowired 다 실패한다.)
  • @Transactional : 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다, 트랜잭션을 시작하고, 테스트가 끝나면 트랜잭션을 강제로 롤백한다. (이 어노테이션이 테스트 케이스에서 사용될 때만 롤백)

  • 회원가입 테스트를 실행해보면, insert문이 생기지 않았다.
    메서드 타고 들어가서 save()를 보면, persist()를 한다. DB마다 전략이 다르지만, 기본적으로 persist()를 해도 Insert문이 발생하지 않는다. 특히 generate value 전략에서.
    -> DB 트랜잭션이 commit될 때, flush()가 되면서 insert문이 나간다. 따라서 commit이 엄청 중요하다.
    여기선 @Transactional로 인해 롤백이 되는데, 확인을 하고싶다면, @Rollback(false)하거나, 엔티티 매니저 만들어flush()하면 된다.
  • fail() : Assert에서 제공해주는 메서드. 코드가 여기까지 오면, 무언가 잘못된 것이라 판단하고 fail을 띄운다.
  • 아래 옵션을 추가하면, 코드를 깔끔하게 작성할 수 있다.
@Test(expected = IllegalStateException.class)

 

테스트 케이스를 위한 설정

  • 테스트는 케이스 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다.
  • 그런 면에서 메모리 DB를 사용하는 것이 가장 이상적이다.
  • 테스트 케이스를 위한 스프링 환경과, 일반적으로 애플리케이션을 실행하는 환경은 보통 다르므로, 설정 파일을 다르게 사용하자.
  • test 폴더에 resources 디렉토리를 추가하면, 테스트에선 이 폴더의 application.yml이 우선권을 가진다.
    이 파일의 url 값을 변경해준다.
    h2database.com -> Cheat Sheet -> Embedded -> In-Memor
    이러면 H2 종료해도 테스트 잘 된다.
  • 그런데, datasource 설정이 없으면, 기본적으로 메모리 DB를 사용하고, driver-class도 현재 등록된 라이브러리를 보고 찾아준다.
  • 추가로 ddl-auto도 createdrop 모드로 동작한다. 따라서 데이터 소스나, JPA 관련된 별도의 추가 설정을 하지 않아도 된다.