[Spring Boot2][2] 4. 회원 도메인 개발
📌 구현 기능
- 회원 등록
- 회원 목록 조회
📌 순서
1️⃣ 회원 엔티티 코드 다시 보기
2️⃣ 회원 리포지토리 개발
3️⃣ 회원 서비스 개발
4️⃣ 회원 기능 테스트
🏷 회원 리포지토리 개발
✔️ MemberRepository
package jpabook.jpashop.repository;
import jpabook.jpashop.domain.Member;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import java.util.List;
// @Repository : 컴포넌트 스캔의 대상이 되어 자동으로 스프링 빈에 등록됨
@Repository
// @RequiredArgsConstructor : final이 있는 필드에만 생성자를 만들어 줌
@RequiredArgsConstructor
public class MemberRepository {
private final 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() {
// JPQL 작성
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();
}
}
@Repository
: 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환@PersistenceContext
: 엔티티 메니저(EntityManager
) 주입@PersistenceUnit
: 엔티티 메니터 팩토리(EntityManagerFactory
) 주입
🏷 회원 서비스 개발
✔️ MemberService
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 : 컴포넌트 스캔의 대상이 되어 자동으로 스프링 빈에 등록됨
@Service
// @Transactional : JPA의 모든 데이터 변경이나 로직들은 가급적 트랜잭션 안에서 실행되어야 함
@Transactional(readOnly = true)
// @RequiredArgsConstructor : final이 있는 필드에만 생성자를 만들어 줌
@RequiredArgsConstructor
public class MemberService {
// 멤버 리포지토리 가져오기
private final MemberRepository memberRepository;
/**
* 회원 가입
*/
// reaonly=false 로 우선권을 가짐
@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
: 데이터의 변경이 없는 읽기 전용 메서드에 사용, 영속성 컨텍스트를 플러시 하지 않 으므로 약간의 성능 향상(읽기 전용에는 다 적용)- 데이터베이스 드라이버가 지원하면 DB에서 성능 향상
@Autowired
: 생성자 Injection 많이 사용, 생성자가 하나면 생략 가능
📌 참고
실무에서는 검증 로직이 있어도 멀티 쓰레드 상황을 고려해서 회원 테이블의 회원명 컬럼에 유니크 제약 조건을 추가하는 것이 안전하다!
📌 필드 주입 대신 생성자 주입을 사용하자!
@Repository @RequiredArgsConstructor public class MemberRepository { private final EntityManager em; ... }
- 변경 불가능한 안전한 객체 생성 가능
- 스프링 데이터 JPA를 사용하면
EntityManager
도 주입 가능
🏷 회원 기능 테스트
📌 테스트 요구사항
- 회원가입을 성공해야 한다.
- 회원가입 할 때 같은 이름이 있으면 예외가 발생해야 한다(중복 예외 처리)
✔️ MemberServiceTest
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import static org.junit.Assert.*;
// @RunWith(SpringRunner.class), @SpringBootTest : 이 두가지가 있어야 스프링과 통합해서 테스트를 진행할 수 있음
@RunWith(SpringRunner.class)
@SpringBootTest
// 각 JPA 같은 트랜잭션 안에서 같은 엔티티라면(id값이 똑같다면), 같은 영속성 컨텍스트에서 하나로 관리가 됨
@Transactional
public 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
// 현재 member와 리포지토리에서 가져온 member가 같은 지 확인
assertEquals(member, memberRepository.findOne(savedId));
}
@Test(expected = IllegalStateException.class)
public void 중복_회원_예외() throws Exception {
// given
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
// when
memberService.join(member1);
// 똑같은 이름을 넣었으므로 예외가 발생해야 한다!
memberService.join(member2);
// then
fail("예외가 발생해야 한다.");
}
}
@RunWith(SpringRunner.class)
: 스프링과 테스트 통합@SpringBootTest
: 스프링 부트 띄우고 테스트(이게 없으면@Autowired
다 실패)@Transactional
: 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백(이 어노테이션이 테스트 케이스에서 사용될 때만 롤백)
📌 테스트 케이스를 위한 설정
- 테스트는 케이스 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다.
➡️ 그런면에서 메모리 DB를 사용하는 것이 가장 이상적!
➡️ 추가로 테스트 케이스를 위한 스프링 환경과, 일반적으로 애플리케이션을 실행하는 환경은 보통 다르므로 설정 파일을 다르게 사용하자.
다음과 같이 간단하게 테스트용 설정 파일을 추가하면 된다😉
✔️ test/resources/application.yml
spring:
# datasource:
# url: jdbc:h2:mem:testdb
# username: sa
# password:
# driver-class-name: org.h2.Driver
# jpa:
# hibernate:
# ddl-auto: create-drop
# properties:
# hibernate:
# show_sql: true
# format_sql: true
# open-in-view: false
logging.level:
org.hibernate.SQL: debug
# org.hibernate.type: trace
- 이제 테스트에서 스프링을 실행하면 이 위치에 있는 설정 파일을 읽는다!
(만약 이 위치에 없으면src/resources/application.yml
을 읽는다.)
Author And Source
이 문제에 관하여([Spring Boot2][2] 4. 회원 도메인 개발), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sorzzzzy/Spring-Boot22-4.-회원-도메인-개발저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)