Jin's Dev Story

[스프링 부트 쇼핑몰 프로젝트 with JPA] 2-1. 회원가입 로직 본문

Web & Android/스프링 부트 쇼핑몰 프로젝트 with JPA

[스프링 부트 쇼핑몰 프로젝트 with JPA] 2-1. 회원가입 로직

woojin._. 2023. 10. 15. 10:07
이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.

1. 회원 역할(Role) 구분

  • kr.spring.member.constant.java, enum클래스 생성
  • 일반 유저 / 관리자
package kr.spring.member.constant;
// 회원 역할 구분 enum 클래스
public enum Role {
    USER, ADMIN
}

2. 회원 가입 정보 DTO

  • 회원 가입 화면으로부터 넘어오는 가입정보를 담을 DTO 객체
// validation 추가 -> null 값 접근하기 위해
implementation 'org.springframework.boot:spring-boot-starter-validation:3.0.1'
package kr.spring.member.dto;
// 회원가입 화면으로부터 넘어오는 가입정보를 담을 Dto
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.Length;

@Getter
@Setter
@ToString
public class MemberFormDto {

    @NotBlank(message = "이름은 필수 항목입니다.")
    private String name;

    @NotEmpty(message = "이메일은 필수 항목입니다.")
    @Email(message = "이메일 형식으로 입력하세요.")
    private String email;

    @NotEmpty(message = "비밀번호는 필수 항목입니다.")
    @Length(min = 4, max = 12, message = "최소 4자, 최대 12자를 입력하세요.")
    private String password;

    @NotEmpty(message = "주소는 필수 항목입니다.")
    private String address;

}

3. Member Entity

  • 회원 정보를 저장하는 Member Entity(Model) 생성
  • Member 객체를 생성하기 위해 Member 객체 안에 createMember() static 메소드 생성
package kr.spring.member.entity;
// 회원 엔티티
import jakarta.persistence.*;
import kr.spring.member.constant.Role;
import kr.spring.member.dto.MemberFormDto;
import kr.spring.utils.entity.BaseEntity;
import lombok.*;
import org.springframework.security.crypto.password.PasswordEncoder;

@Entity
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Member extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id")
    private Long id;

    private String name;

    @Column(unique = true)
    private String email;
    private String password;
    private String address;

    @Enumerated(EnumType.STRING) // 추가를 안하면 열거형은 숫자로 들어감
    private Role role;

    //사용자 만들기 -> 받아온 DTO를 entity로 변경해준다
    public static Member createMember(MemberFormDto memberFormDto, PasswordEncoder passwordEncoder) {
        //먼저 Member entity를 만들어준다.
        Member member = new Member();
        member.setName(memberFormDto.getName());  //실제 웹에서 받아올 이름이니까 dto에서 받아온다
        member.setEmail(memberFormDto.getEmail());
        member.setAddress(memberFormDto.getAddress());
        member.setRole(Role.USER);  //기본으로 다 학생으로 가입

        String password = passwordEncoder.encode(memberFormDto.getPassword());  //일단 비밀번호를 플레인 텍스트로 받아온다. 후 encode를 이용해서 암호화
        member.setPassword(password);  //암호화된 패스워드를 넣는다

        //만든 member 리턴
        return member;
    }
}

4. MemberRepository

  • 회원가입 시 중복 여부를 판단하기 위해서 이메일로 회원을 검사하도록 쿼리 메소드 작성
package kr.spring.member.repository;

import kr.spring.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

//저장도 하고 데이터를 가져올 수도 있지만 디테일하게 메소드를 만들어서 사용할 수도 있음!
//jpql을 이용하거나 querydsl을 이용하거나
public interface MemberRepository extends JpaRepository<Member, Long> {

    // 문자로 된 이메일을 넣어서 검색하겠다.
    // 이메일이 있거나 없거나. 이걸 member로 해도 되지만 자바에서는 최근에 Optional을 사용한다.
    // 결과가 있을 수도 있고, 없을 수도 있는 상태
    // Optional<Member> findByEmail(String email);
    Member findByEmail(String email);  // 이메일 받아오기

}

5. MemberService

  • @RequiredArgsConstructor를 사용하여 memberRepository DI
package kr.spring.member.service;

import kr.spring.member.entity.Member;
import kr.spring.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

// DB로 값을 가져오거나 controller로 가기 전에 따로 처리해야 할 기능이 있다
@Service
@Transactional
@Log4j2
//final이 붙은 애들을 올려준다 이게 싫으면 @autowired 쓰면 됨
//일반적으로 controller나 service에서 쓰면 상관이 없는데 test에서는 이걸 쓰면 에러가 난다.
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {

    private final MemberRepository memberRepository;

    public Member saveMember(Member member) {
        // 중복확인
        validateDuplicate(member);
        // 중복확인 후 회원 저장
        return memberRepository.save(member);
    }

    private void validateDuplicate(Member member) {
        //이메일로 찾았을 때 값이 있는지 없는지 판단
        Member findMember = memberRepository.findByEmail(member.getEmail());
        // DB에서 이메일이 검색이 되면 이미 등록되어있는 회원이라는 알림 표시
        if(findMember != null) {
            throw new IllegalStateException("이미 등록된 사용자 입니다.");
        }

    }
}