애플리케이션 아키텍처
SpringBoot에서 늘 사용하던 애플리케이션 아키텍처 구조이다. 계층형 구조를 사용한다.
- Controller, web : 웹 계층
- service : 비즈니스 로직, 트랜잭션 처리
- repository : JPA를 직접 사용하는 계층, 엔티티 매니저 사용
- domain : 엔티티가 모여 있는 계층, 모든 계층에서 사용한다.
회원 도메인 개발
구현할 기능은 회원 등록하고 회원 목록을 조회하는 것이다.
순서는 회원 리포지토리를 개발 -> 회원 서비스를 개발 -> 회원 기능을 테스트하는 순이다.
회원 리포지토리 개발
@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)//트랜잭션 처리, 읽기가 많은 곳에서는 readOnly = true 설정
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
/**
* 회원 가입
*/
@Transactional
public Long join(Member member){
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member 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);
}
}
@Autowired는 생성자 injection 많이 사용한다. 생성자가 하나면 생략이 가능하다.
하지만 강사님은 생성자 주입 방식을 권장한다고 하신다. 이렇게 하면 변경 불가능한 안전한 객체를 생성 가능하게 한다. final 키워드를 추가하면 컴파일 시점에 memberRepository를 설정하지 않는 오류를 체크할 수 있다. 또 lombock에서는 @RequiredArgsConstructor라는 것이 있어 final로 되어 있는 것은 자동으로 생성자 주입을 시켜준다. 이를 통해 쉽게 주입할 수 있다. 또 스프링 데어터 JPA는 @RequiredArgsConstructor가 EntityManager도 주입 가능하게 해준다.
회원 기능 테스트
테스트를 만드는 단축키는 shift+ctrl+T이다.
테스트 요구 사항
- 회원가입을 성공해야 한다.
- 회원가입 할 때 같은 이름이 있으면 예외가 발생해야 한다.
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception{
Member member = new Member();
member.setName("kim");
Long saveId = memberService.join(member);
assertEquals(member, memberRepository.findOne(saveId));
}
@Test(expected = IllegalStateException.class)//예외가 illegalStateException이면 성공한 것이다.
public void 중복_회원_예외() throws Exception{
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
memberService.join(member1);
memberService.join(member2); //예외가 발생
fail("예외가 발생해야 한다.");
}
}
기술 설명
- @RunWith(SpringRunner.class) : 스프링과 테스트 통합
- @SpringBootTest : 스프링 부트 띄우고 테스트(이게 없으면 @Autowired 다 실패)
- @Transactional : 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백 (이 어노테이션이 테스트 케이스에서 사용될 때만 롤백) 그러므로 DB에는 내용이 들어가지 않는다.
테스트 케이스를 위한 설정
테스트는 케이스에서 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다. 이렇게 하면 귀찮기 때문에 메모리DB를 사용하는 것이 가장 이상적이다. test파일에 resources폴더를 추가하고 거기에 application.yml을 추가한다. 그 후 datasource : url : jdbc:h2:mem:testdb라고 하면 신기하게도 H2Databse를 꺼도 사용이 가능해진다!!
여기서 더 신기한건 SpringBoot는 이런 설정이 없이도 자동으로 Mem에서 test를 진행한다!!
sprint boot는 create-drop이 디폴트이다 그러므로 마지막에 모든 데이터베이스를 초기화한다.
강의 출저
'Spring JPA 공부 > 실전! 스프링 부트와 JPA 활용1' 카테고리의 다른 글
웹 계층 개발(회원) (0) | 2023.01.06 |
---|---|
주문 도메인 개발 (0) | 2023.01.05 |
상품 도메인 개발 (0) | 2023.01.04 |
도메인 분석 설계 (0) | 2023.01.03 |
프로젝트 환경설정 (0) | 2023.01.02 |