-
#Spring boot-7 애플리케이션 구현 준비SPRING-BOOT 2021. 3. 3. 14:21
[출처] 인프런 김영한 강사님 -실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
<구현 요구사항>
실제 동작하는 화면을 먼저 확인한다.
기능 목록
· 회원 기능
· 회원 등록
· 회원 조회
· 상품 기능
· 상품 등록
· 상품 수정
· 상품 조회
· 주문 기능
· 상품 주문
· 주문 내역 조회
· 주문 취소
예제를 단순화 하기 위해 다음 기능은 구현X
· 로그인과 권한 관리X
· 파라미터 검증과 예오 처리 단순화
· 상품은 도서만 사용
· 카테고리는 사용X
· 배송 정보는 사용X
<애플리케이션 아키텍처>
계층형 구조 사용
· controller, web: 웹 계층 (여기서는 contoller가 바로 Repository에 접근하여 유연하게 사용 할수 있도록 할것임.)
· service: 비즈니스 로직, 트랜잭션 처리
· repository: JPA를 직접 사용하는 계층, 엔티티 매니저 사용
· domain: 엔티티가 모여 있는 계층, 모든 계층에서 사용
패키지 구조
· jpabook.jpashop
· domain
· exception(공통예외)
· repository
· service
· web
개발 순서: 서비스, 리포지토리 계층을 개발하고, 테스트 케이스를 작성해서 검증, 마지막에 웹 계층 적용
회원 도메인 개발
구현 기능
· 회원 등록
· 회원 목록 조회
순서
· 회원 엔티티 코드 다시 보기
· 회원 리포지토리 개발
· 회원 서비스 개발
· 회원 기능 테스트
회원 리포지토리 개발
회원 리포지토리 코드
package jpabook.jpashop.Repository; import jpabook.jpashop.domain.Member; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.util.List; @Repository @RequiredArgsConstructor//Autowired가 사용가는하다는 것은 public class MemberRepository { //@PersistenceContext // 1. spring이 em 을만들어서 여기다가 주입해주게 된다. //@Autowired // 2.스프링 부트라서 @PersistenceContext를 안쓰고 @Autowired가 사용가능 하다. 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(); //ctrl_alt_ n 합쳐서 인라인 만들기 } 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 : 스프링 빈으로 등록(안에 @Component 어노테이션이 있음)
compoent scan의 대상이되서 scan이 된다.
, JPA 예외를 스프링 기반 예외로 예외 변환
· @PersistenceContext : 엔티티 메니저( EntityManager ) 주입
· @PersistenceUnit : 엔티티 메니터 팩토리( EntityManagerFactory ) 주입
기능 설명
· save()
public void save(Member member){ em.persist(member); }
· findOne()
public Member findOne(Long id){ return em.find(Member.class,id); //type, pk }
· findAll()
public List<Member> findAll(){ return em.createQuery("select m from Member m", Member.class) //jpql, 반환객체 .getResultList(); //ctrl_alt_ n 합쳐서 인라인 만들기 }
· findByName()
public List<Member> findByName(String name){ return em.createQuery("select m from Member m where m.name =:name",Member.class) .setParameter("name",name) .getResultList(); }
회원 서비스 개발
회원 서비스 코드
package jpabook.jpashop.service; import jpabook.jpashop.Repository.MemberRepository; import jpabook.jpashop.domain.Member; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @Transactional(readOnly = true) //jpa의 모든 데이터 변경이나 로직은 트랜잭션 안에서 다일어나야한다. //영속섣컨텍스트를 flush안하고 더티체킹을 안해서 성능상 이점이있음. //읽기 전용이기 때문에 디비한태도 부화를 덜어줄 수 있음. @RequiredArgsConstructor //4.lombok 주입 public class MemberService { //1. 필드주입 //주입하기 까다로움 //@Autowired //private MemberRepository memberRepository; //2. setter 주입 //메서드로 주입하기 때문에 가짜 memberRepository를 주입할수 있음 //단점 -> runtime(실제 application 돌아가는 시점에)에 누군가 이걸 변경 할 수 있음. //@Autowired //public void setMemberRepository(MemberRepository memberRepository){ // this.memberRepository = memberRepository; //} //3. 생성자 주입 //private final MemberRepository memberRepository; // //public MemberService(MemberRepository memberRepository) { // this.memberRepository = memberRepository; //} //lombok private final MemberRepository memberRepository; //회원 가입 @Transactional //jpa의 모든 데이터 변경이나 로직은 트랜잭션 안에서 다일어나야한다. public Long join(Member member){ validateDuplicateMember(member);//중복 회원 검증 memberRepository.save(member); return member.getId(); } private void validateDuplicateMember(Member member) { //EXCEPTION List<Member> findMembers = memberRepository.findByName(member.getName()); if(!findMembers.isEmpty()){ throw new IllegalStateException("이미 존재하는 회원입니다."); } } //회원 전체 조회 public List<Member> findMembers(){ return memberRepository.findAll(); } //회원 단건 조회 public Member findOne(Long id){ return memberRepository.findOne(id); } }
기술 설명
· @Service
· @Transactional : 트랜잭션, 영속성 컨텍스트
· readOnly=true : 데이터의 변경이 없는 읽기 전용 메서드에 사용, 영속성 컨텍스트를 플러시 하지 않으므로
약간의 성능 향상(읽기 전용에는 다 적용)
· 데이터베이스 드라이버가 지원하면 DB에서 성능 향상
· @Autowired
· 생성자 Injection 많이 사용, 생성자가 하나면 생략 가능
기능 설명
· join()
· findMembers()
· findOne()
참고: 실무에서는 검증 로직이 있어도 멀티 쓰레드 상황을 고려해서 회원 테이블의 회원명 컬럼에 유니크 제 약 조건을 추가하는 것이 안전하다.
참고: 스프링 필드 주입 대신에 생성자 주입을 사용하자
필드 주입(발전 1단계)
public class MemberService { @Autowired MemberRepository memberRepository; ... }
주입하기 까다로움
setter 주입(발전 2단계)
@Autowired public void setMemberRepository(MemberRepository memberRepository){ this.memberRepository = memberRepository; }
메서드로 주입하기 때문에 가짜 memberRepository를 주입할수 있음
단점 -> runtime(실제 application 돌아가는 시점에)에 누군가 이걸 변경 할 수 있음.생성자 주입(발전 3단계)
public class MemberService { private final MemberRepository memberRepository; public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } ... }
· 생성자 주입 방식을 권장
(한번 생성할 때 다 완성이 되버리기 때문에 중간에 set을통해 memberRepository를 바꿀 수 없다.)
· test case를 작성할 때 직접 주입을 해줘야하는데 이런 잘 놓치기 쉬운부분에 에러 표시가 뜨기때문에
얘는 이게 필요해, 이걸 의존하고 있어를 알 수 있다.
· 변경 불가능한 안전한 객체 생성 가능
· 생성자가 하나면, @Autowired 를 생략할 수 있다.
· final 키워드를 추가하면 컴파일 시점에 memberRepository 를 설정하지 않는 오류를 체크할 수 있다.
(보통 기본 생성자를 추가할 때 발견)
lombok(발전 4단계)
@RequiredArgsConstructor public class MemberService { private final MemberRepository memberRepository; ... }
참고: 스프링 데이터 JPA를 사용하면 EntityManager 도 주입 가능
@RequiredArgsConstructor 는 final이 있는 필드만 가지고 생성자를 만들어준다.
* MemberService 최종 코드*
@Service @Transactional(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) { 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); } }
'SPRING-BOOT' 카테고리의 다른 글
#Spring boot-8 회원 기능 테스트 (0) 2021.03.04 #Spring boot-7_1 TEST CASE 작성 중 발생 에러 (0) 2021.03.04 #Spring boot-6 도메인 분석 설계 (0) 2021.03.01 #Spring boot-5 JPA와 DB 설정, 동작확인 (0) 2021.03.01 #Spring boot-4 H2 데이터베이스 설치 (0) 2021.02.26