Spring Boot/Spring Boot JPA-활용편1 강의 정리

[SpringBoot-JPA 활용편1] 자바 ORM 표준 JPA 프로그래밍 - 활용편1: 4. 회원 도메인 개발

조찬국 2024. 2. 26. 22:41
728x90

강의 출처: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1

해당 강의는 Inflearn에 등록된 김영한님의 Spring Boot 강의입니다.

이번 시간에는 회원 도메인의 리포지토리,서비스(회원가입,조회) ,테스트케이스 작성하며 실제 JPA에서의 비즈니스 로직을 공부해보자.

1. 회원 리포지토리 개발

@Repository //스프링 빈에 등록(Component Scan 가능)
@RequiredArgsConstructor //@PersistenceContext 없이 자동으로 MemberRepostory의 생성자 주입을 함
public class MemberRepository {

    //JPA 표준 annotation: 
    // @PersistenceContext
    private final EntityManager em; //★★스프링이 엔티티 매니저 생성 후 영속성 컨테스트(PersistenceContext)에 주입


    //1.회원 저장 메서드
    public void save(Member member)
    {
        em.persist(member);
    }
    //2.회원 단건 조회 메서드
    public Member findOne(Long id)
    {
        return em.find(Member.class,id);
    }
    //3.회원 리스트 조회 메서드
    public List<Member> findAll()
    {
        //회원 객체 전체 조회(리스트로 조회)는 JPQL(테이블이 아닌 "객체"에 대해 SQL문구 작성) 필요.
        return  em.createQuery("select m from Member as m", Member.class)
                .getResultList();
    }
    //4.회원 이름(같은 이름이면 모두) 조회 메서드 
    public List<Member> findByName(String name)
    {   
        // .setParameter("name",name)로 정한 파라미터 (키,값)에 대해 ":name"로 파라미터를 받겠다는 의미
        return  em.createQuery("select m from Member as m where m.name= :name", Member.class)
                .setParameter("name",name)
                .getResultList();
    }
}

2. 회원 서비스 개발

package jpabook.jpashop.service;

//★★트랜잭션은 스프링 프레임워크거를 갖고올 것★★
import org.springframework.transaction.annotation.Transactional;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@Transactional(readOnly = true) //데이터 변경은 반드시 트랜잭션 안에서 실행
@RequiredArgsConstructor
public class MemberService {

    //★★생성자 의존성 주입(서비스->레포)★★
    //테스트 용이성: 생성자 의존성 주입을 사용하면 의존성을 모의(mock) 또는 가짜(fake) 객체로 대체하여
    // 단위 테스트를 수행할 수 있다. 테스트 중에 의존 객체를 쉽게 대체할 수 있기 때문에 테스트의 효율성과 안정성이 향상된다.
    private final MemberRepository memberRepository;
    /* lombok의 @RequiredArgsConstructor 를 통해 자동 구현됨.
    @Autowired
    public MemberService(MemberRepository memberRepository)
    {
        this.memberRepository=memberRepository;
    }
    */
    //비즈니스 로직 기능: 등록, 조회
    //1. 회원 등록(가입)
    @Transactional
    public Long join(Member member)
    {
        validateDuplicateMember(member); //중복 회원 검증 메서든
        memberRepository.save(member); // 중복x이므로 저장
        return member.getId();
    }
    //1.1. 중복 회원 검증 메서드
    private void validateDuplicateMember(Member member) {
        //이름으로 조회된 멤버들
        List<Member> findMembers=memberRepository.findByName(member.getName());
        //해당 이름이 존재하면 에러처리
        if(!findMembers.isEmpty())
        {
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        }

    }

    //2. 회원 전체 조회
    @Transactional(readOnly = true) //조회는 이렇게 하면 성능이 좋다
    public List<Member> findAllmembers()
    {
        return memberRepository.findAll();
    }
    //3. 회원 단건 조회
    @Transactional(readOnly = true)
    public Member findOne(Long memberId)
    {
        return memberRepository.findOne(memberId);
    }
}

@RequiredArgsConstructor: @PersistenceContext나 @Autowired 없이 자동으로 MemberRepostory의 생성자 주입을 함. ( 생성자가 하나면, @Autowired 를 생략할 수 있다. )

 

이때 반드시 final키워드를 써야한다. final 키워드를 추가하면 컴파일 시점에 memberRepository 를 설정하지 않는 오류를 체크할 수 있다.(보통 기본 생성자를 추가할 때 발견)

 

3. 회원 기능 테스트

@RunWith(SpringRunner.class) //junit을 spring과 실행
@SpringBootTest //스프링 부트 컨테이너에서 테스트(autowired)
@Transactional //트랜잭션
public class MemberServiceTest {

    //테스트 2개: 회원가입, 중복_회원_예외
    //서비스, 레포지토리
    @Autowired
    MemberService memberService;
    @Autowired MemberRepository memberRepository;

    @Test //@Rollback(value = false) //commit까지해서 데베에 쌓였는지까지 확인 가능
    public void 회원가입() throws Exception {
        //given(가입하고자 하는 회원이 주어질 때)
        Member member = new Member();
        member.setName("CHO");
        member.setAddress(new Address("seoul","낙성대역 8길","68"));

        //when(그 회원을 가입 시키면?)
        Long saveId=memberService.join(member);

        //then(데베에서 조회한 회원과 given의 회원이 같냐?
        assertEquals(member,memberRepository.findOne(saveId));

    }
    //
    @Test(expected = IllegalStateException.class) //IllegalStateException 에러가 발생해야한다.
    public void 중복_회원_예외() throws Exception {
        //given(같은 이름이 회원일때)
        Member member1 =new Member();
        member1.setName("park");

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

        //when(그회원 두명 모두 저장해서 IllegalStateException에러가 나면 테스트 성공)
        memberService.join(member1);
        memberService.join(member2); //같은 이름이므로 예외가 발생한다.

        //then(테스트 실패)
        fail("예외가 발생해야 합니다.");
    }

}

두번째 테스트때 같은 이름을 추가하면 에러가 발생하여 중복이름으로 가입이 되면 안된다.

 

 

다음은 Test에 사용된 기술 설명이다.

테스트 결과

728x90