티스토리 뷰

 

도메인 분석 설계

회원 기능 회원 등록 회원 조회 상품 기능 상품 등록 상품 수정 상품 조회 주문 기능 상품 주문 주문 내역 조회 주문 취소 기타 요구사항 상품은 재고관리가 필요하다. 상품의 종류는 도서 , 음반

write-read.tistory.com

계층형 구조 사용

  • controller , web : 웹 계층
  • service : 비즈니스 로직 , 트랜잭션 처리
  • repository : JPA를 직접 사용하는 계층 , 엔티티 매니저 사용
  • domain : 엔티티가 모여있는 계층 , 모든 계층에서 사용

패키지 구조

  • jpabook.jpashop
    • domain
    • exception
    • repository
    • service
    • web
1. 서비스 , 리포지토리 계층 개발 
2. 테스트 케이스 작성 , 검증
3. 웹 계층 적용

 

회원 리포지토리

@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)
                .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 주입

 

회원 서비스

@Service
@Transactional(readOnly = true)
//@Transactional을 메소드 레벨에 적용하여 readOnly를 구분하여주면 성능면에 이점이 많다.
@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) {
        // EXCEPTION 회원 중복 체크
        List<Member> members = memberRepository.findByName(member.getName());
        if(!members.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를 생략할 수 있다.
    • final 키워드를 추가하면 컴파일 시점에 memberRepository를 설정하지 않는 오류를 체크할 수 있다.

 

회원 기능 테스트

@SpringBootTest
@Transactional
class MemberServiceTest {

    @Autowired
    MemberService memberService;
    @Autowired
    EntityManager em;

    @Test
    //@Rollback(false)
    // 테스트에서 Transactional은 Rollback 되기 때문에 Rollback을 false로 하여 commit되게 한다.
    public void 회원가입 () throws Exception {
        // given
        Member member = new Member();
        member.setName("jeong");

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

        // then
        //em.flush(); // Transactionl 떄문에 Rollback 되어야 하지만 강제로 반영
        Assert.assertEquals(member , memberService.findOne(saveId));
    }

    @Test
    public void 중복_회원_예외() throws Exception {
        // given
        Member member = new Member();
        member.setName("jeong1");

        Member member2 = new Member();
        member2.setName("jeong1");
        // when
        memberService.join(member);

        // then
        IllegalStateException exception =
                Assertions.assertThrows(IllegalStateException.class ,() -> memberService.join(member2));
        Assertions.assertEquals("이미 존재하는 회원입니다." , exception.getMessage());
    }
}
  • @SpringBootTest : 스프링 부트 띄우고 테스트 , 이게 없으면 @Autowired 다 실패
  • @Transactional : 반복 가능한 테스트 지원 , 각각의 테스트를 실행할 때 마다 트랜잭션을 시잔하고 테스트가 끝나면 트랜잭션을 강제로 롤백 , (이 어노테이션이 테스트 케이스에서 사용될 때만 롤백)

 

테스트 케이스를 위한 설정

  • 테스트는 격리된 환경에서 실행하고 , 끝나면 데이터를 초기화하는 것이 좋다. 그런 면에서 메모리 DB를 사용하는 것이 가장 이상적이다.
  • 추가로 테스트 케이스를 위한 스프링 환경과 , 일반적으로 애플리에키션을 실행하는 환경은 보통 다르므로 설정 파일을 다르게 사용하자
  • 다음과 같이 간단하게 테스트용 설정 파일을 추가하면 된다.
  • test/resources/application.yml
spring:
#  datasource:
#    url: jdbc:h2:mem:test
#    username: sa
#    password:
#    driver-class-name: org.h2.Driver
#
#  jpa:
#    hibernate:
#      ddl-auto: create
#    properties:
#      hibernate:
#        format_sql: true
logging.level:
  org.hibernate.SQL: debug
  org.hibernate.type: trace
  
  // #은 yml의 주석이다
  • 이제 테스트에서 스프링을 실행하면 이 위치에 있는 설정 파일을 읽는다.
  • (만약 이 위치에 없으면 src/resources/application.yml을 읽는다)
  • 스프링 부트는 datasource 설정이 없으면 기본적으로 메모리 DB를 사용하고 ,
  • driver-class도 현재 등록된 라이브러를 보고 찾아준다.
  • ddl-auto도 create-drop모드로 동작한다.
  • 따라서 데이터소스나 JPA관련된 별도의 추가 설정을 하지 않아도 된다.

 

 

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

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

www.inflearn.com

'기록 > 스프링 부트 와 JPA 활용' 카테고리의 다른 글

웹 계층 개발  (0) 2021.01.31
주문 도메인 개발  (0) 2021.01.30
상품 도메인 개발  (0) 2021.01.30
도메인 분석 설계  (0) 2021.01.27
스프링 부트와 JPA 프로젝트 환경설정  (0) 2021.01.24