실전! 스프링 부트와 JPA 활용1(웹 애플리케이션 개발)

Ch04. 회원 도메인 개발 - 회원 Service 개발

webmaster 2021. 12. 5. 16:13
728x90
@Service
@Transactional(readOnly = true)
//기본적으로 JPA는 트렌잭션 안에서 동작해야 한다.//javax,spring에서 제공하는것 두가지가 있지만 spring에서 사용하기 때문에 spring에서 제공하는것을 쓰는게 좋다.
//JPA가 조회하는것을 최적화 해준다, 읽기 전용임으로
@RequiredArgsConstructor//final 필드 값을 채워준다.
public class MemberService {
    /* 주입 방법 1 : 필드 주입
    @Autowired
    private MemberRepository memberRepository;// 해당 주입을 바꿀수가 없기떄문에 최근에는 많이 사용하지 않느다.
    */
    /* 주입 방법 2 : Setter 주입, 중간에 바뀔수 있는 문제가 있다.
    private MemberRepository memberRepository;

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
    */
    //주입 방법 3 : 생성자 주입 , 한번 생성되면 바뀔일이 없기 때문에 생성자에서 생성하여 준다
    private final MemberRepository memberRepository;//변경될 일이 없기 때문에 final로 해주어 컴파일시 에러를 잡는다.

    /*
    public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
    */

    /**
     *회원 가입
     */
    @Transactional
    public Long join(Member member){
        //회원은 중복되면 안되기 때문에 validation
        validateDuplicateMember(member);
        memberRepository.save(member);
        //em.persist를 하게되면 member ID에 값이 있다(영속화가 되기 떄문에)
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        //EXCEPTION
        List<Member> findMembers = memberRepository.findByName(member.getName());
        //동시에 Member A라는 이름으로 등록을 하게 될경우 문제가 발생
        //실무에서는 Member의 Name을 유니크 제약조건을 또 걸어 최악을 상황을 고려한다.
        if(!findMembers.isEmpty()){
            throw new IllegalStateException("이미 존재하는 회원입니다");
        }
    }

    //회원 전체 조회

    public List<Member> findMembers(){
        return memberRepository.findAll();
    }

    //한건만 조회
    @Transactional(readOnly = true)
    public Member findOne(Long memberId){
        return memberRepository.findOne(memberId);
    }

}
  • @Service
    • Repository와 마찬가지로 내부에 @Component 어노테이션이 존재하며 스프링 빈으로 등록시켜준다.
  • @Transactional
    • 기본적으로 JPA는 트렌젝션 안에서 동작해야 한다.
    • Javax, spring 두 가지에서 제공하는 것이 있지만 현재는 스프링 위에서 동작시키기 때문에 spring에서 제공하는 것을 사용하는 것을 추천한다.
    • 읽기 전용일 때는 readOnly 속성을 true로 하여 최적화를 해준다(default는 false이다)
  • Join : 회원가입 메서드
    • 중복되면 안 되기 때문에 Validation check를 해준다.
      • validation에서 이름이 존재할 경우 IllegalStateException을 발생시킨다
    • 중복이 없다면 저장
      • 참고) em.persist를 하게 될 경우 해당 엔티티는 그 순간부터 DB에 값을 넣지 않았어도 JPA Context에 영속화가 되기 때문에 Id와 같은 Field값이 채워져 있음을 증명할 수가 있다.
      •  참고) SpringBootData Jpa 라이브러리 같은 경우 @Autowire로도 @PersistenceContext를 한 것과 똑같이 지원을 해주기 때문에 일관성 있게 Repository를 주입할 수가 있다.
@RequiredArgsConstructor
public class MemberRepository {
    private final EntityManager em;
}

스프링 주입 방법 3가지

필드 주입 

@Autowired
private MemberRepository memberRepository;
  • 문제 : 개발자가 원하는 데로 주입받는 것을 수정할 수가 없다.

Setter 주입

private MemberRepository memberRepository;

@Autowired
public void setMemberRepository(MemberRepository memberRepository){
	this.memberRepository = memberRepository;	
}
  • 문제 : Setter를 사용하기 때문에 테스트 시점이나, 원하지 않는 시점에 값 변경이 가능하기 때문에 문제가 있다.

생성자 주입 

@RequiredArgsConstructor//final 필드 값을 채워준다.
public class MemberService {
	private final MemberRepository memberRepository;
}
  • 생성되는 시점에만 주입해 주기 때문에 안전하다.
  • 생성자가 하나면, @Autowired를 생략할 수 있다.
  • final로 값을 주입하지 않을 경우 컴파일 에러를 잡을 수 있기 때문에 final 필드로 많이 사용한다.

 

728x90