일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 김영한
- 로그인
- 실무활용
- JPA
- QueryDSL
- 트위터
- 검증 애노테이션
- JPQL
- 임베디드 타입
- 기본문법
- 스프링 mvc
- JPA 활용2
- JPA 활용 2
- Spring Data JPA
- jpa 활용
- 타임리프 문법
- 프로젝트 환경설정
- 스프링MVC
- 일론머스크
- 컬렉션 조회 최적화
- API 개발 고급
- 스프링 데이터 JPA
- 값 타입 컬렉션
- 타임리프
- 불변 객체
- 페이징
- 예제 도메인 모델
- Bean Validation
- 벌크 연산
- 스프링
- Today
- Total
RE-Heat 개발자 일지
[JPA 활용1] [3] 앱 구현 준비 및 회원 도메인 개발 본문
인프런 김영한 님의 강의를 듣고 작성한 글입니다.
[1] 애플리케이션 구현 준비
1. 구현 요구 사항
- 회원 기능
- 회원 등록
- 회원 조회
- 상품 기능
- 상품 등록
- 상품 수정
- 상품 조회
- 주문 기능
- 상품 주문
- 주문 내역 조회
- 주문 취소
- 예제를 단순화하기 위해 다음 기능은 구현하지 않는다.
- 로그인과 권한 관리
- 파라미터 검증과 예외 처리
- 상품은 도서만 사용
- 카테고리는 사용
- 배송 정보는 사용
2. 애플리케이션 아키텍처
계층형 구조를 사용한다.
- controller, web: 웹 계층
- service: 비즈니스 로직, 트랜잭션 처리
- repository: JPA를 직접 사용하는 계층, 엔티티 매니저 사용
- domain: 엔티티가 모여있는 계층, 모든 계층에서 사용
개발순서 : 서비스·리포지토리 계층 개발 → 테스트 케이스 작성 검증 → 웹 계층에 적용
[2] 회원 리포지토리 개발
MemberRepository
@Repository
@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() {
return em.createQuery("select m from Member m", Member.class) //JPQL from의 대상이 엔티티라는 게 좀 다름
.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로 엔티티를 주입하는 대신 생성자 주입으로 대체
② @RequiredArgsConstructor는 final이 붙거나 @NotNull이 붙은 필드의 생성자를 자동으로 생성해 준다.
③ createQuery로 JPQL 사용. :name을 넣고 setParameter로 값을 넣었다.
[3] 회원 서비스 개발
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
//회원 가입
@Transactional(readOnly = false)
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로 읽기 전용 메서드라는 것을 명시. findMembers(), findOne()에 적용.
- 대신 쓰기인 join()엔 @Transactional(readOnly = false)를 명시
② 필드 주입, 세터 주입 등이 있으나, 최근엔 생성자 주입이 대세다.
③ validateDuplicateMember()로 존재하는 회원인지 확인한다고 하더라도 같은 이름이 동시에 가입하는 상황에 처할 수 있음. 그래서 실무에선 memberName에 유니크 제약 조건을 잡아주는 게 안전함.
[4] 회원 기능 테스트
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("kim");
//When
Long savedId = memberService.join(member);
//Then
assertEquals(member, memberRepository.findOne(savedId));
//Assertions.assertThat(member).isEqualTo(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("예외가 발생해야 한다.");
}
}
① @SpringBootTest : 스프링부트 띄우고 테스트(이게 없으면 @Autowired 다 실패)
② @Transactional을 쓰면 실제 DB와 관계없이 테스트 가능하다.
하지만, DB에서 어떻게 동작하는지 확인하고 싶으면 @Rollback(false)를 쓰면 된다.
■ 테스트 케이스를 위한 설정
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
# properties:
# hibernate:
# # show_sql: true
# format_sql: true
logging.level:
org.hibernate.SQL: debug
org.hibernate.type: trace #스프링 부트 2.x, hibernate5
# org.hibernate.orm.jdbc.bind: trace #스프링 부트 3.x, hibernate6
① 테스트에서 스프링을 실행하면 이 설정 파일을 읽는다.
② 스프링부트는 datasource 설정이 없으면 기본적으로 메모리 DB를 사용하고 create-drop 모드로 동작한다. 다시 말해 따로 설정하지 않아도 자동으로 메모리 모드로 돌려버린다는 뜻이다.
③ test 설정파일을 따로 해주면 create-drop 모드로 해도 부담이 없다.
참고] H2 DataBase Engine Cheat Sheet
'백엔드 > JPA' 카테고리의 다른 글
[JPA 활용1] [5] 웹 계층 개발(상편) (0) | 2023.08.26 |
---|---|
[JPA 활용1] [4] 상품·주문 도메인 개발 (0) | 2023.08.25 |
[JPA 활용1] [2] 도메인 분석 설계 (0) | 2023.08.20 |
[JPA 활용1] [1] 프로젝트 환경설정 (0) | 2023.08.19 |
[JPA] [11] 객체지향 쿼리 언어 - 중급 문법 (상편) (0) | 2023.08.19 |