본문 바로가기

SPRING/Spring

[스프링| 스프링 입문 | 코드로 배우는 스프링] 회원 서비스 개발

public class MemberService {

    // 회원 정보를 저장하고 관리하는 리포지토리 객체를 선언합니다.
    private final MemberRepository memberRepository = new MemoryMemberRepository();

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

        validateDuplicateMember(member); // 중복 회원을 검증합니다.
        memberRepository.save(member); // 리포지토리에 회원 정보를 저장합니다.
        return member.getId(); // 저장된 회원의 ID를 반환합니다.
    }

    // 중복 회원 검증을 위한 메소드
    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName()) // 이름으로 회원을 조회합니다.
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다."); // 조회된 회원이 있으면 예외를 발생시킵니다.
                });
    }

    /*
    전체 회원 조회 메소드
     */
    public List<Member> findMembers() {
        return memberRepository.findAll(); // 리포지토리에서 모든 회원 정보를 조회하여 반환합니다.
    }

    // 특정 회원을 ID로 조회하는 메소드
    public Optional<Member> findOne(Long memberId) {
        return memberRepository.findById(memberId); // ID로 회원을 조회하고 결과를 Optional 객체로 반환합니다.
    }
}

join 메소드

join 메소드는 MemberService 클래스 내에서 회원 가입을 처리하는 역할을 수행합니다. 이 메소드는 새로운 회원(Member 객체)을 받아서, 시스템에 해당 회원을 등록하는 과정을 담당합니다. 각 단계는 명확한 목적을 가지고 있으며, 다음과 같이 구성되어 있습니다:

  1. 중복 회원 검증: validateDuplicateMember(member)를 호출하여 전달받은 회원이 데이터베이스에 이미 존재하는지 확인합니다. 이는 데이터 중복을 방지하고, 데이터베이스의 무결성을 유지하는 중요한 단계입니다. 만약 같은 이름을 가진 회원이 이미 존재한다면, IllegalStateException이 발생하고, 회원 가입 과정은 중단됩니다.
  2. 회원 정보 저장: 중복 검증을 통과한 후, memberRepository.save(member)를 통해 회원 정보를 데이터베이스에 저장합니다. 이는 MemberRepository 인터페이스의 save 메소드를 호출하는 것으로, 실제 저장 방법은 이 인터페이스를 구현하는 클래스에 따라 달라집니다.
  3. 회원 ID 반환: 저장된 회원의 ID를 반환합니다. member.getId()를 통해 접근하는 이 ID는 회원을 유일하게 식별하는 키 역할을 합니다. 회원 가입이 성공적으로 완료된 후, 이 ID를 통해 외부에서 회원을 참조하거나 추가 작업을 수행할 수 있습니다.

이 메소드는 회원 가입을 처리하는 핵심적인 기능을 제공하며, 시스템의 사용자 관리 부분에서 중요한 역할을 합니다. 또한, 이 메소드의 구현은 데이터의 무결성을 보장하고, 시스템의 신뢰성을 높이는 데 기여합니다.

 

validateDuplicateMember 메소드

validateDuplicateMember 메소드는 MemberService 클래스 내에 존재하며, 회원 가입을 진행하기 전에 동일한 이름을 가진 회원이 이미 데이터베이스에 등록되어 있는지를 검증하는 역할을 합니다. 이 메소드는 중복 회원을 방지하여 데이터의 일관성을 유지하는 데 중요한 기능을 수행합니다.

메소드 기능 설명

  1. 이름으로 회원 조회: memberRepository.findByName(member.getName())는 memberRepository의 findByName 메소드를 호출하여, 전달받은 member 객체의 이름과 일치하는 회원 정보를 데이터베이스에서 조회합니다.
  2. 조회 결과 처리: 조회 결과는 Optional<Member> 타입으로 반환됩니다. Optional은 자바 8에서 도입된 클래스로, 결과값이 null일 가능성이 있는 경우에 사용하여 NullPointerException을 방지합니다.

람다식과 Optional 문법

  • Optional 사용: Optional 클래스는 값이 존재할 수도 있고, 존재하지 않을 수도 있는 컨테이너 객체입니다. 이 클래스는 주로 결과가 null이 될 수 있는 상황에서 사용되어 null 체크 로직을 간결하게 처리할 수 있도록 돕습니다. Optional<Member>는 Member 객체가 있을 수도 있고 없을 수도 있음을 의미합니다.
  • ifPresent 메소드: Optional 객체에서 ifPresent(Consumer<? super T> consumer) 메소드는 객체가 값을 포함하고 있을 경우, 즉 Optional이 비어있지 않을 경우에 주어진 람다식을 실행합니다. 여기서 사용된 람다식 m -> { throw new IllegalStateException("이미 존재하는 회원입니다."); }는 조회된 회원이 존재할 경우 예외를 발생시키는 명령을 수행합니다. m은 조회된 Member 객체를 나타내며, 해당 객체가 존재하면 IllegalStateException 예외를 던집니다.

람다식의 장점

  • 간결성: 람다식을 사용함으로써 복잡한 익명 클래스를 사용하지 않고도 명료하고 간결한 코드를 작성할 수 있습니다.
  • 읽기 쉬움: 코드의 의도가 명확해지므로 다른 개발자가 코드를 이해하기 쉽습니다.

이 메소드는 회원 관리 시스템에서 데이터의 정확성을 보장하는 중요한 역할을 수행함으로써, 시스템의 신뢰성을 높이는 데 기여합니다.

private void validateDuplicateMember(Member member) {
    Optional<Member> result = memberRepository.findByName(member.getName()); // 이름으로 회원 조회

    if (result.isPresent()) { // Optional 객체가 값을 포함하고 있으면 (즉, 회원이 이미 존재하면)
        throw new IllegalStateException("이미 존재하는 회원입니다."); // 예외 발생
    }
}
  1. 회원 조회: findByName 메소드를 호출하여 이름이 일치하는 회원을 조회하고, 그 결과를 Optional<Member> 타입의 result 변수에 저장합니다.
  2. 결과 확인: isPresent() 메소드를 사용하여 result가 값을 포함하고 있는지 확인합니다. 이 메소드는 Optional 객체가 비어 있지 않을 경우 true를 반환합니다.
  3. 예외 처리: 만약 회원이 이미 존재한다면 (즉, result.isPresent()가 true일 때), IllegalStateException을 던져 사용자에게 이미 존재하는 회원임을 알립니다.

이 방법은 람다식을 사용하는 것보다 코드가 약간 더 길어지지만, Optional의 기본적인 사용 방법을 그대로 활용하면서도, 람다식에 익숙하지 않은 상황에서도 이해하고 사용하기 쉽습니다.

 

findMembers 메소드

findMembers 메소드는 MemberService 클래스의 일부로, 데이터베이스 또는 다른 형태의 저장소에서 모든 회원의 정보를 조회하여 반환하는 기능을 수행합니다. 이 메소드는 MemberRepository 인터페이스의 findAll 메소드를 호출하여 구현됩니다. 여기서 findAll 메소드는 구현된 저장소에서 모든 회원 데이터를 리스트 형태로 추출하고 이를 반환합니다.

메소드 작동 방식

  1. 데이터 조회: memberRepository.findAll() 호출을 통해 저장소(데이터베이스, 메모리 등)에 저장된 모든 회원 정보를 조회합니다. 이 메소드는 저장된 모든 회원을 포함하는 리스트를 반환합니다.
  2. 리스트 반환: 조회된 회원 정보의 리스트가 그대로 findMembers 메소드를 호출한 측에 반환됩니다. 반환 타입은 List<Member>입니다.

사용 사례

이 메소드는 관리자 패널, 사용자 관리 페이지, 통계 보고 등 여러 시나리오에서 유용하게 사용될 수 있습니다. 예를 들어, 시스템 관리자가 모든 회원의 목록을 확인하거나, 특정 기능을 실행하기 전에 사용자의 전체 목록을 불러오는 경우 등에 활용할 수 있습니다.

주의점

  • 성능 문제: 만약 저장소에 매우 많은 회원 데이터가 존재한다면, findAll 메소드 호출 시 성능 저하가 발생할 수 있습니다. 따라서 대규모 데이터셋을 다룰 때는 페이징(pagination)이나 캐싱(caching) 같은 기술을 고려해야 할 수 있습니다.
  • 보안 문제: 회원 데이터에는 개인 정보가 포함되어 있을 수 있으므로, 이 데이터를 처리할 때는 데이터 보호 규정과 보안 요구 사항을 철저히 준수해야 합니다.

findOne 메소드

findOne 메소드는 MemberService 클래스에 정의되어 있으며, 특정 회원의 정보를 그 회원의 ID를 통해 조회하는 기능을 수행합니다. 이 메소드는 MemberRepository 인터페이스의 findById 메소드를 호출하여 구현됩니다.

메소드 작동 방식

  1. ID로 회원 조회: 이 메소드는 memberRepository.findById(memberId)를 호출합니다. 이 호출은 저장소(데이터베이스, 메모리 등)에서 전달된 memberId와 일치하는 회원 정보를 검색합니다.
  2. 결과 반환: 조회 결과는 Optional<Member> 타입으로 반환됩니다. Optional<Member>는 회원 정보가 존재할 수도 있고, 존재하지 않을 수도 있음을 나타냅니다. 즉, 만약 요청한 ID와 일치하는 회원이 저장소에 없다면, Optional 객체는 비어 있을 것입니다 (Optional.empty()). 반면 회원이 존재한다면, Optional은 해당 회원 객체를 감싸고 있을 것입니다 (Optional.of(member)).

사용 사례 및 장점

  • 안전한 회원 정보 조회: 회원 정보가 존재하지 않는 경우를 안전하게 처리할 수 있도록 Optional을 사용합니다. 이는 NullPointerException을 방지하는 데 유용하며, 회원 정보가 없을 때의 로직을 명시적으로 처리할 수 있게 합니다.
  • 유연한 응답 처리: 호출자는 Optional 객체를 받음으로써 회원 정보의 존재 여부에 따라 다양한 로직을 실행할 수 있습니다. 예를 들어, 회원 정보가 없는 경우 사용자에게 적절한 메시지를 보여주거나 다른 처리를 할 수 있습니다.

주의점

  • Optional 사용의 이해: Optional을 사용함으로써, 코드의 가독성과 안전성은 향상되지만, Optional을 올바르게 사용하는 방법을 이해하고 있어야 합니다. 불필요한 사용은 오히려 코드를 복잡하게 만들 수 있습니다.