스프링 부트 쇼핑몰 프로젝트 with JPA (4)

스프링 시큐리티를 이용한 회원가입 및 로그인 -1

📌 스프링 시큐리티 설정 추가하기

<pom.xml>

📌 회원가입 기능 구현하기

package com.shop.constant;

public enum Role {
    USER, ADMIN
}

👆🏻회원이 일반 유저인지, 관리자인지 구분하기 위한 Role 클래스

<pom.xml>

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
package com.shop.dto;

import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

@Getter
@Setter
public class MemberFormDto {

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

    @NotEmpty(message = "이메일은 필수 입력 값입니다.")
    private String email;

    @NotEmpty(message = "비밀번호는 필수 입력 값입니다.")
    @Length(min=8, max=16, message = "비밀번호는 8자 이상, 16자 이하로 입력해주세요")
    private String password;

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

👆🏻회원가입 화면으로부터 넘어온 회원의 정보를 담을 MemberFormDto 클래스. 회원가입 시 서버로 넘어오는 값을 검증하기 위해 javax.validation 어노테이션 사용.

ENTITY

package com.shop.entity;

import com.shop.constant.Role;
import com.shop.dto.MemberFormDto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.persistence.*;

@Entity
@Table(name="member")
@Getter@Setter
@ToString
public class Member{

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

    private String name;

    @Column(unique = true)
    private String email;

    private String password;

    private String address;

    @Enumerated(EnumType.STRING)
    private Role role;


    public static Member createMember(MemberFormDto memberFormDto, PasswordEncoder passwordEncoder){
        Member member = new Member();
        member.setName(memberFormDto.getName());
        member.setEmail(memberFormDto.getEmail());
        member.setAddress(memberFormDto.getAddress());
        String password = passwordEncoder.encode(memberFormDto.getPassword());
        member.setPassword(password);
        member.setRole(Role.ADMIN);
        return member;
    }
}

👆🏻회원정보를 저장하는 Member 엔티티. 이메일을 통해 회원을 구분하므로 중복된 값이 데이터베이스에 들어올 수 없도록 @Column(unique = true)를 설정함. createMember()는 Member 엔티티를 생성하는 메소드이다.

REPOSITORY

package com.shop.repository;

import com.shop.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {

    //회원 가입 시 중복 회원 검사(쿼리 메소드)
    Member findByEmail(String email);
}

👆🏻MemberRepository는 Member 엔티티를 데이터베이스에 저장함. 이메일로 중복된 회원이 있는지 검사하기 위해 쿼리 메소드를 작성함.

SERVICE

package com.shop.service;

import com.shop.entity.Member;
import com.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import 
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {

    private final MemberRepository memberRepository;

    public Member saveMember(Member member){
        validateDuplicateMember(member);
        return memberRepository.save(member);
    }

    private void validateDuplicateMember(Member member){
        Member findMember = memberRepository.findByEmail(member.getEmail());
        if(findMember != null) {
            throw new IllegalStateException("이미 가입된 회원입니다.");
        }
    }   
}

👆🏻@Transactional 어노테이션을 선언하면 로직을 실행하다가 에러가 발생 시, 로직이 실행되기 전으로 롤백 시켜줌. @RequiredArgsConstructor 어노테이션은 final이나 @NonNull이 붙은 필드에 생성자를 생성해줘 빈을 주입함.

CONTROLLER

package com.shop.controller;

import lombok.RequiredArgsConstructor;
import com.shop.dto.MemberFormDto;
import com.shop.service.MemberService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
@RequestMapping("/members")
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    private final PasswordEncoder passwordEncoder;

    @GetMapping("/new")
    public String memberForm(Model model){
        model.addAttribute("memberFormDto", new MemberFormDto());
        return "member/memberForm";
    }
    
    @PostMapping("/new")
    public String memberForm(@Valid MemberFormDto memberFormDto, BindingResult bindingResult, Model model){

        if(bindingResult.hasErrors()){
            return "member/memberForm";
        }

        try{
            Member member = Member.createMember(memberFormDto, passwordEncoder);
            memberService.saveMember(member);
        } catch (IllegalStateException e){
            model.addAttribute("errorMessage", e.getMessage());
            return "member/memberForm";
        }


        return "redirect:/";
    }
}

👆🏻회원가입 페이지로 이동할 수 있도록 @GetMapping으로 memberForm 메소드 작성. 검증하려는 객체 앞에 @Valid 어노테이션 선언하고, bindingResult를 파라미터로 추가하면 검사 결과가 bindingResult에 담김. bindingResult.hasErrors()일 경우 회원가입 페이지로 이동함. Member 엔티티를 생성하여 데이터베이스에 저장함. 회원 가입 시 중복 회원 예외가 발생하면 에러 메시지를 뷰로 전달함.

package com.shop.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MainController {

    @GetMapping("/")
    public String main(){
        return "main";
    }
}

좋은 웹페이지 즐겨찾기