[스프링부트와 JPA활용 1] 3. 애플리케이션 구현 준비, 4. 회원 도메인 개발

jada·2024년 2월 6일
0

Spring 스터디

목록 보기
17/35

구현 요구사항

앞서 진행한 도메인 설계는 데이터와 관련된 설계였다. 여기서는 무엇을 어디까지 구현할지 설명한다.

  • 회원 기능: 회원 등록, 조회
  • 상품 기능: 상품 등록, 수정, 조회
  • 주문 기능: 상품 주문, 주문 내역 조회, 주문 취소

애플리케이션 아키텍처

계층형 구조 사용

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

패키지 구조

  • jpabook.jpashop
    • domain
    • exception
    • repository
    • service
    • web
      개발 순서: 서비스, 리포지토리 계층을 개발하고, 테스트 케이스를 작성해서 검증, 마지막에 웹 계층 적용

회원 도메인 개발

회원 리포지토리 개발

@Repository를 사용하면 스프링이 스프링 빈으로 등록해준다.(Component scan의 대상이 되기 때문! - @SpringBootApplication이 붙은 클래스의 패키지와 그 하위의 패키지에 있는 것들은 컴포넌트 스캔을 하여 자동으로 스프링 빈으로 등록한다.)

JPA가 제공하는 표준 애노테이션인 @PersistenceContext를 사용해 EntityManager 객체를 선언해주면 스프링이 엔티티 매니저를 만들어 주입해준다.

findAll메서드 같은 경우 JPQL을 작성해줘야 한다. (createQuery메서드 - 인자 첫번째로 JPQL 쓰고, 두번째로 반환 타입 써주면 된다.)

  • SQL을 테이블을 대상으로 쿼리를 날리지만, JPQL은 엔티티를 대상으로 쿼리를 날린다.

findByName메서드에서는 setParameter()를 통해 name을 바인딩해준다.

  • 기본적인 엔티티 매니저 사용 방식:
    • 예 ) em.persist(member); 하면 영속성 컨텍스트에 member를 넣는다. 트랜잭션이 커밋되는 시점에 이것이 반영된다. (DB에 INSERT 쿼리가 날아감)

기술 설명
@Repository : 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환
@PersistenceContext : 엔티티 메니저( EntityManager ) 주입
@PersistenceUnit : 엔티티 메니터 팩토리( EntityManagerFactory ) 주입
기능 설명
save()
findOne()
findAll()
findByName()

회원 서비스 개발

기술 설명

  • @Service
  • @Transactional : 트랜잭션, 영속성 컨텍스트
    • JPA의 모든 데이터 변경이나 로직들은 가급적이면 트랜잭션 안에서 실행되어야 한다.
    • readOnly=true : 데이터의 변경이 없는 읽기 전용 메서드에 사용, 영속성 컨텍스트를 플러시 하지 않으므로 약간의 성능 향상(읽기 전용에는 다 적용)
    • 데이터베이스 드라이버가 지원하면 DB에서 성능 향상
  • @Autowired
    • 필드 인젝션) 나중에 변경하거나 외부에서 액세스할 수 없다는 단점이 있다.
    • setter Injection) 애플리케이션 구동 시점에 세팅(조립)이 끝나고, 애플리케이션 동작 중에 바꿀 일이 거의 없기 때문에 사용하는 것이 좋지 않음
    • 생성자 Injection) 많이 사용, 생성자가 하나면 @Autowired 생략 가능
      • @AllArgsConstructor - 모든 필드 가지고 생성자 만들어줌
      • @RequiredArgsConstructor(권장) - final 있는 필드만 가지고 생성자 만들어줌

기능 설명

  • join()
  • findMembers()
  • findOne()
    참고: 실무에서는 검증 로직이 있어도 멀티 쓰레드 상황을 고려해서 회원 테이블의 회원명 컬럼에 유니크 제약 조
    건을 추가하는 것이 안전하다.
    참고: 스프링 필드 주입 대신에 생성자 주입을 사용하자

회원 기능 테스트

assertEquals(member, memberRepository.findOne(saveId)); 이 부분이 성공한 이유는 JPA에서는 같은 트랜잭션 안에서 PK 값이 똑같으면 같은 영속성 컨텍스트에서 똑같은 엔티티로 관리되기 때문이다.(@Transactional을 사용했으므로 이것이 적용됨)

테스트 로그를 보면 INSERT문이 생성되지 않는다.

  • 그 이유는 데이터베이스 트랜잭션이 커밋될 때 flush가 되면서 DB에 영속성 컨텍스트에 있던 객체에 관한 INSERT문이 날아가게 되는데, 스프링 test에 있는 @Transactional은 커밋하지 않고 롤백하도록 하기 때문!

  • em.flush();를 하면 영속성 컨텍스트에 있는 변경 내용을 DB에 반영한다. -> 하지만 @Transactional에 의해 INSERT된 것이 롤백된다.

  • 강의자료와 다르게 junit5로 작성하였다.

@Test
    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
        IllegalStateException exception = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        assertEquals("이미 존재하는 회원입니다.", exception.getMessage());
    }

기술 설명

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

기능 설명

  • 회원가입 테스트
  • 중복 회원 예외처리 테스트

참고: 테스트 케이스 작성 고수 되는 마법: Given, When, Then
(http://martinfowler.com/bliki/GivenWhenThen.html)
이 방법이 필수는 아니지만 이 방법을 기본으로 해서 다양하게 응용하는 것을 권장한다.

테스트 케이스를 위한 설정

테스트는 케이스 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다. 그런 면에서 메모리 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
# 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 을 읽는다.)

스프링 부트는 datasource 설정이 없으면, 기본적을 메모리 DB를 사용하고, driver-class도 현재 등록된 라이브러리를 보고 찾아준다.

추가로 ddl-auto 도 create-drop 모드로 동작한다. 따라서 데이터소스나, JPA 관련된 별도의 추가 설정을 하지 않아도 된다.

profile
꾸준히 발전하는 개발자가 되자 !

0개의 댓글