[Day-7] 회원 도메인, 리포지토리, 서비스, 회원 기능 테스트WEB JAVA SPRING/PROJECT2023. 8. 25. 16:10
Table of Contents
<애플리케이션 구현 준비>
<회원 도메인 개발>
#파일구조
(@Repository ==DAO)
#회원 리포지토리 개발
MemberRepository
@Repository //Component scan해서 spring bean 자동등록
public class MemberRepository {
@PersistenceContext //spring이 jpa가 Entity Manager를 만들어서 EntityManager를 여기에 주입
private EntityManager em;
//jpa가 member를 저장하는 로직
//EntitlyManager가 persist하면 영속성 컨택스트에 Member Entity를 넣고 트랜잭션이 커밋되는 시점에 DB에 insert쿼리 날라가서 저장
public void save(Member member) {
em.persist(member);
}
//조회 로직 -- 회원id로 단건 조회
public Member fineOne(Long id) {
//find : JPA가 제공하는 메서드
return em.find(Member.class, id); //(타입, pk) id값을 넘긴 후 member를 찾아서 반환
}
//List 조회된 결과 반환해서 회원목록 뿌림
public List<Member> findAll() {
//jpql 쿼리, 반환타입
//sql이랑 기능적이랑 문법은 거의 동일 - > sql로 변환되기 때문
//sql은 테이블을 대상으로 변환되지만 이거는 Entitly객체를 대상으로 변환
return em.createQuery("select m from Member m where m.name = :name", 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();
}
}
#회원 서비스 개발
MemberService
@Service //스프링이 제공함
@Transactional(readOnly = true) //JPA의 데이터 변경시에 꼭 있어야 함 (트랜잭션 안에서 변경할 것) 그래야 lazy로딩같은것도 된다.
// (readOnly = true) : jpa 성능 좋게 해줌 (읽기전용 -> 부하를 많이 주지 않음)
//@AllArgsConstructor //필드의 모든 생성자 만들어줌
@RequiredArgsConstructor //final이 있는 필드만 생성자 만들어줌
public class MemberService {
private final MemberRepository memberRepository; //생성자
//회원가입 //쓰기에는 true 넣으면 데이터 변경안되니까 넣지 말것
@Transactional //이렇게 따로 설정하면 이게 우선순위 (기본값 readOnly=false)
public Long join(Member member) {
validateDuplicateMember(member); //중복회원 검증 로직
memberRepository.save(member); //save : MemberRepository 함수
return member.getId(); //값이 있다는게 보장이 된다. persist 하는 순간
}
//중복체크
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();
}
//단건 조회 (id로)
public Member findOne(Long memberId) {
return memberRepository.fineOne(memberId);
}
}
<참고> --3번으로 쓸것
1.field injection (스프링이 스프링빈에 등록되어있는 MemberRepository를 injection 해준다)
@Autowired
private MemberRepository memberRepository;
단점이 많다. 테스트할때 못바꾼다. 엑세스 할 수 있는 방법이 없다. field + private라
2.setter injection
@Autowired
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository =memberRepository;
}
//장점 : 주입하기 편리함
//단점 : 한번뭔가 런타임을 하는 시점에 누군가가 바꾸면 ..?
3.생성자 injection
장점: 변경 불가능한 안전한 객체 생성 가능
생성자가 하나면, @Autowired 를 생략할 수 있다.
final 키워드를 추가하면 컴파일 시점에 memberRepository 를 설정하지 않는 오류를 체크할 수 있다
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
#회원 기능 테스트
테스트 요구사항
- 회원가입을 성공해야 한다.
- 회원가입 할 때 같은 이름이 있으면 예외가 발생해야 한다.
MemberServiceTest
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional //이건 롤백(=DB에 있는거 다버린다)
public class MemberServiceTest {
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
/*
그런데 실행하면 콘솔창에 insert문이 뜨지 않는다..
왜냐면
JPA는 DB트랜잭션이 커밋될때 그때 영속성 컨택스트에 있는 jpa객체인 member가 만들어 지면서 insert쿼리가 나가는 것
그런데 스프링에서의 @Transactional은 기본값이 롤백
그래서 @Rollback(false)로 설정하면 롤백안하고 커밋해버림
이거 설정하면 insert쿼리 나온다
*/
@Test
@Rollback(false)
public void 회원가입() throws Exception {
//given : 이런게 주어졌을때
Member member = new Member(); //member 만들고
member.setName("길");
//when : 이렇게 하면
Long saveId = memberService.join(member);
//then : 이렇게 된다.
assertEquals(member, memberRepository.fineOne(saveId)); //멤버가 멤버리파지토리에서 멤버를 찾는다 정상적으로 가입됐는지 확인
//트랜젝션 안에서 fk값 같으면 같은 영속성 컨택스를 가져서 하나로 관리된다.
}
@Test
public void 회원중복() throws Exception {
//given
Member member1 = new Member();
member1.setName("김");
Member member2 = new Member();
member2.setName("김");
//when
memberService.join(member1);
try {
memberService.join(member2); //예외가 발생해야 한다.
} catch (IllegalStateException e) {
return;
}
//then
fail("예외가 발생해야한다."); //이거 출력되면 잘못작성된 코드
}
테스트 케이스를 위한 설정 테스트는 케이스 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다.
그런 면에서 메모리 DB를 사용하는 것이 가장 이상적이다.
application.yml
spring: #띄어쓰기 제대로.....
# datasource: #2칸
# url: jdbc:h2:mem:testdb #메모리 모드로 동작
# username: sa
# password:
# driver-class-name: org.h2.Driver
#
#
# jpa: #2칸
# hibernate: #4칸
# ddl-auto: create #6칸
# properties: #4칸
# hibernate: #6칸
#
# # show_sql: true #8칸
# format_sql: true #8칸
#스프링 부트에서는 3번째줄 url 안 바꿔도 그냥 메모리모드에서 테스트 실행한다.
logging.level: #없음
org.hibernate.SQL: debug #2칸
#org.hibernate.type: trace #2
'WEB JAVA SPRING > PROJECT' 카테고리의 다른 글
[Day-9] 주문 엔티티, 리파지토리, 서비스 (0) | 2023.08.25 |
---|---|
[Day-8] 상품 엔티티, 리파지토리, 서비스 (0) | 2023.08.25 |
[Day-6] 엔티티 설계 (연관관계) (0) | 2023.08.25 |
[Day-5] 엔티티 클래스 개발2 (0) | 2023.08.25 |
[Day-4] 도메인 모델과 테이블 설계, 엔티티 클래스 개발 1 (0) | 2023.08.25 |