[Spring boot & JPA] 04. 회원 도메인 개발

PADO·2021년 2월 2일
0

Spring boot & JPA

목록 보기
5/6
post-thumbnail

🌱 회원 도메인 개발

구현 기능

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

순서

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

 

회원 리포지토리 개발

기술 설명

@Repository 컴포넌트 스캔 대상이 되어 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환

@PersistenceContext 스프링이 생성한 엔티티 메니저( EntityManager ) 주입
→ @Autowired와 생성자로 대체 → @Autowired 생략 → @RequiredArgsContructor로 대체
(스프링 데이터 JPA를 사용하면 @Autowired로 EntityManager 도 주입 가능)

@Repository
@RequiredArgsConstructor
public class MemberRepository {

    private final EntityManager entityManager;
		...
}

@PersistenceUnit 엔티티 메니터 팩토리( EntityManagerFactory ) 주입

기능 설명

  • save()

    public void save(Member member) {
            entityManager.persist(member);
    }

    persist: 영속성 컨텍스트에 Member 객체를 넣어 트랜잭션이 커밋 되는 시점에 DB에 반영

  • findOne()

    단건 조회 → 첫번째 파라미터엔 타입, 두번째 파라미터엔 PK

  • findAll()

  • findByName()

    public List<Member> findByName(String name) {
            return entityManager.createQuery("select m from Member m where m.name = :name", Member.class)
                    .setParameter("name", name)
                    .getResultList();
    }

    :name 으로 파라미터 바인딩

 

회원 서비스 개발

기술 설명

@Service 컴포넌트 스캔의 대상이 되어 자동으로 스프링 빈으로 등록

@Transactional 트랜잭션, 영속성 컨텍스트

  • JPA의 모든 데이터 변경이나 로직들은 가급적 transaction 안에서 실행 되어야 함.
  • *readOnly=true* 옵션: 조회 성능 최적화
    → 데이터의 변경이 없는 읽기 전용 메서드에 사용
    → 영속성 컨텍스트를 플러시 하지 않으므로 약간의 성능 향상(읽기 전용에는 다 적용)
  • 데이터베이스 드라이버가 지원하면 DB에서 성능 향상

@Autowired 생성자 Injection 많이 사용, 생성자가 하나면 @Autowired 생략 가능

  • 생성자 주입 방식을 권장
  • 변경 불가능한 안전한 객체 생성 가능
  • 생성자가 하나면, @Autowired 를 생략할 수 있다.
  • final 키워드를 추가하면 컴파일 시점에 memberRepository 를 설정하지 않는 오류를 체크할 수 있다. (보통 기본 생성자를 추가할 때 발견)

@RequiredArgsContructor final 필드(필수 필드)를 주입하는 생성자만 생성해주는 애노테이션

@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;
		...
}

기능 설명

  • join()

    중복 회원 검증

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

    실무에서는 검증 로직이 있어도 멀티 쓰레드 상황을 고려해서 회원 테이블의 회원명 컬럼에 유니크 제약 조건을 추가하는 것이 안전하다.

  • findMembers()

  • findOne()

 

회원 기능 테스트

테스트 요구사항

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

기술 설명

@RunWith(SpringRunner.class) 스프링과 테스트 통합

@SpringBootTest 스프링 부트 띄우고 테스트(이게 없으면 @Autowired 다 실패)

@Transactional 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백 ****(이 어노테이션이 테스트 케이스에서 사용될 때만 롤백)

기능 설명

  • 회원가입 테스트
@Test
    public void join() {
        // given
        Member member = new Member();
        member.setName("jeongwon");

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

        // then
        Assertions.assertEquals(member, memberRepository.findOne(savedId));
    }

JPA의 같은 트랜잭션 안에서 pk(id)값이 똑같으면(= 같은 엔티티) 같은 영속성 컨텍스트에서 관리가 된다.

  • 중복 회원 예외처리 테스트
@Test(expected = IllegalStateException.class)
    public void validateDuplicateMembers() {
        // given
        Member member1 = new Member();
        member1.setName("jeongwon");

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

        // when
        memberService.join(member1);
        memberService.join(member2);

        // then
        fail("예외가 발생해야 한다.");
    }

fail 해당 위치의 코드가 실행되면 안 될 때 테스트를 실패시키고 종료한다.

테스트에서 외부 DB를 사용?

테스트는 케이스 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다.

→ 그런 면에서 메모리 DB를 사용하는 것이 가장 이상적이다.

추가로 테스트 케이스를 위한 스프링 환경과, 일반적으로 애플리케이션을 실행하는 환경은 다르므로 설정 파일을 다르게 사용한다.

  • test/resources/application.yml
url: jdbc:h2:mem:test
  • Spring boot는 별도 설정이 없다면 메모리 모드로 실행
spring:
  logging.level:
    org.hibernate.SQL: debug

스프링 부트는 datasource 설정이 없으면, 기본적으로 메모리 DB를 사용하고, driver-class도 현재 등록된 라이브러리를 보고 찾아준다. 추가로 ddl-auto 도 create-drop (애플리케이션 종료 시점에 drop 쿼리를 날려 모두 초기화) 모드로 동작한다. 따라서 데이터소스나, JPA 관련된 별도의 추가 설정을 하지 않아도 된다.

0개의 댓글