12. 회원 서비스 개발

멤버서비스 클래스는

회원 리포지토리와 도메인을 이용해서 실제 비즈니스 로직을 작성하는 부분이다.

우선 회원가입을 만들자.

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.Optional;

public class MemberService {

    //! 우선 서비스를 만들기 위해 회원 리포지토리가 필요하다.
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 회원 가입
     */
    public Long join(Member member) {
        // 같은 이름이 있는 중복 회원X
        Optional<Member> result = memberRepository.findByName(member.getName()); 
        // ctrl + alt + v : 변수를 추출해준다.

        result.ifPresent(m -> {
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        });
        // ifPresent는 result가 null이 아니라 값이 있으면 이때 로직이 동작하는거다. 
        // (Optional 이기 때문에 가능한것임)
        // 이전에는 Optional이 없어서 그냥  `if null이 아니면` 이런식으로 로직을 짰을거다.
        // 근데 Optional로 감싸면, Optional안에 객체가 담겨져 있는거다.
        // 그래서 Optional을 이용한 여러 메서드를 쓸 수 있음.

        // 그래서 과거에는 if null 이런식으로 코드를 짰다면 지금은
        // null의 가능성이 있는 얘들을 Optional로 감싸고 그걸 감싼덕분에 ifPresent를 쓸 수 잇는거다.
        // 뭐 Member member1 = result.get(); 
        // 이런식으로 직접꺼내도 되지만 직접적으로 바로끝내는건 권장하지 않음.

        memberRepository.save(member);
        return member.getId();

    }
}

추가적으로 Optional에서 orElseGet 메서드를 많이 쓰는데,

값이 있으면 꺼내고, 값이 없으면 여기있는 메서드를 실행해라, 아니면 default 값을 넣어서 실행해라! 라는 의미다.

( 여기 잘 이해안감 )

Optional 쓸때 추가적인 팁이 있는데,

Optional<Member> result = memberRepository.findByName(member.getName()); 

이렇게 Optional쓸때, 바로 반환하는것은 별로 좋지 않다. 안이쁘다 일단. 그래서 어떻게 권장하냐면

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.Optional;

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 회원 가입
     */
    public Long join(Member member) {

        memberRepository.findByName(member.getName())
                .ifPresent(M -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
        // 이렇게 result 따로 잡지말고 바로 갖다 쓰는걸 권장한다.

        memberRepository.save(member);
        return member.getId();
    }
}

이런식으로 좀더 보기좋게 짤 수 있다.

근데 이부분

 memberRepository.findByName(member.getName())
                .ifPresent(M -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });

여기서도 또 findByName 해서 로직이 쭉 나오는데, 이럴때는 메서드를 뽑는게 좋다.
단축키 : ctrl + alt + shift + t (리팩토링과 관련된 여러가지 메서드가 나온다)

저 method를 선택하면 다음과 같이 extracted 라는 메서드를 생성하게 된다.

메서드 이름은 validateDuplicateMember 로 변경하자.

최종적으로 다음과 같이 리팩토링이 된다.

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.Optional;

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 회원 가입
     */
    public Long join(Member member) {
        validateDuplicateMember(member); // 중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

    // 중복 회원 검증 메서드를 따로 뽑음
    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
            .ifPresent(M -> {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
            });
    }
}

지금 계속해오면서 repository는

단순히 저장소에 넣었다 뻈다 하는 느낌이 코드에 묻어난다.

그에 반해 서비스 클래스는 join, findMembers처럼 네이밍이 좀더 비즈니스에 가깝다.

그래서 보통 서비스 클래스는 비즈니스에서 갖고온 용어를 써야한다. 그래야 기획자 측에서 회원가입이 이상하다고 하면 개발자가 아 join을 살펴보자! 이런식으로 서로 매칭과 협업이된다.

그래서 서비스 같은 경우는 보통 비즈니스에 의존적으로 설계하고, 리포지토리는 서비스 보다는 단순히 기계적으로, 개발스럽게 용어를 선택한다. 그냥 단순히 데이터를 넣어, 이것 자체가 리포지토리의 역할이니까!


전체 회원 조회는 굉장히 쉽다. 그냥 findAll() 메서드를 사용하면 된다.

하나의 회원도 그냥 findById() 메서드 쓰면된다.

/**
 * 전체 회원 조회
 */
public List<Member> findMembers() {
    return memberRepository.findAll();
}

/**
  * 한 회원 조회
  */
public Optional<Member> findOne(Long memberId) {
    return memberRepository.findById(memberId);
}

최종 서비스 클래스의 코드는 다음과 같다.

[hello.hellospring.service패키지의 MemberService 클래스]

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.Optional;

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 회원 가입
     */
    public Long join(Member member) {
        validateDuplicateMember(member); 
        memberRepository.save(member);
        return member.getId();
    }
         // 중복 회원 검증 메서드
    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
            .ifPresent(M -> {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
            });
    }

    /**
     * 전체 회원 조회
     */
     public List<Member> findMembers() {
         return memberRepository.findAll();
     }

    /**
      * 한 회원 조회
      */
    public Optional<Member> findOne(Long memberId) {
        return memberRepository.findById(memberId);
    }    
}

이 비즈니스 로직이 잘 동작하는지 ( 회원가입시 중복 제거 코드가 잘 작동하는지)
테스트로 검증 해야 함!

 

출처 : 인프런의 김영한 선생님 강의를 정리한 글입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

댓글

Designed by JB FACTORY