회원가입 기능 구현하기

JinKyung·2023년 4월 22일
0

SpringBoot 쇼핑몰

목록 보기
12/12

1. 회원 역할 구분

일반 유저인지, 관리자인지 구분

com.shop.constant.Role.java

package com.shop.constant;

public enum Role {
    USER, ADMIN
}

2. 가입정보 DTO

com.shop.dto.MemberFormDto.java

package com.shop.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class MemberFormDto {
    private String name;
    private String email;
    private String password;
    private String address;
}

3. Member 엔티티

com.shop.entity.Item.java

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;

    // Enum 사용시 기본적인 순서 지정되어 있음. 순서가 바뀔 경우를 대비해 EnumType.STRING 옵션을 통해 String으로 지정할 것.드
    @Enumerated(EnumType.STRING)
    private Role role;

// Member 엔티티에 회원 생성 메소드를 만들어서 관리하면 코드가 변경되더라도 한 군데만 수정하면 됨.
    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.USER);
        return member;
    }


}

4. MemberRepository

Member 엔티티를 데이터베이스에 저장하기 위함.

com.shop.repository.MemberRepository

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);
}

5. MemberService

com.shop.service.MemberService

package com.shop.service;

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

@Service
@Transactional
@RequiredArgsConstructor
public class MemberService {
    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("이미 가입된 회원입니다.");
        }
    }
}

6. 회원가입 테스트

1. 테스트 클래스 생성 및 의존성 주입

@SpringBootTest
// 테스트 클래스에 @Transactional 어노테이션은 테스트 실행 후 롤백 처리함. >> 같은 메소드 반복 테스트 가능
@Transactional
@TestPropertySource(locations = "classpath:application-test.properties")

class MemberServiceTest {
    @Autowired
    MemberService memberService;

    @Autowired
    PasswordEncoder passwordEncoder;

    public Member createMember(){
        MemberFormDto memberFormDto = new MemberFormDto();
        memberFormDto.setEmail("test@email.com");
        memberFormDto.setName("홍길동");
        memberFormDto.setAddress("서울시 마포구 합정동");
        memberFormDto.setPassword("1234");
        return Member.createMember(memberFormDto, passwordEncoder);
    }

2. 회원가입 테스트

회원가입 DTO 객체를 Service 객체를 통해서 저장(save)

	@Test
    @DisplayName("회원가입 테스트")
    public void saveMemberTest(){
        Member member = createMember();
        Member savedMember = memberService.saveMember(member);
        // assertEqauls: 저장하려고 요청했던 값과 실제 저장된 데이터 비교. (파라미터: 기대값, 실제 저장값)
        assertEquals(member.getEmail(), savedMember.getEmail());
        assertEquals(member.getName(), savedMember.getName());
        assertEquals(member.getAddress(), savedMember.getAddress());
        assertEquals(member.getPassword(), savedMember.getPassword());
        assertEquals(member.getRole(), savedMember.getRole());
    }

3. 중복 회원가입 테스트

	@Test
   @DisplayName("중복 회원 가입 테스트")
   public void saveDuplicateMemberTest(){
       Member member1 = createMember();
       Member member2 = createMember();
       memberService.saveMember(member1);

       // 예외 처리 테스트. 첫번째 파라미터에는 발생할 예외 타입 넣어주기
       Throwable e = assertThrows(IllegalStateException.class, () -> {
           memberService.saveMember(member2);});

       assertEquals("이미 가입된 회원입니다.", e.getMessage());

   }

7. 회원가입 페이지 만들기

1. MemberController

@RequestMapping("/members")
@Controller
@RequiredArgsConstructor
public class MemberController {
   private final MemberService memberService;
   private final PasswordEncoder passwordEncoder;

   @GetMapping(value = "/new")
   public String memberForm(Model model){
       // Get 요청으로 회원가입 페이지 요청할 경우 MemberFormDto 객체 같이 넘김.
       model.addAttribute("memberFormDto", new MemberFormDto());
       return "member/memberForm";
   }

   @PostMapping(value = "/new")
   // Post 요청으로 넘어온 회원가입 정보를 MemberFormDto 객체로 받음
   public String memberForm(MemberFormDto memberFormDto){

       Member member = Member.createMember(memberFormDto, passwordEncoder);
       memberService.saveMember(member);

       return "redirect:/";
   }

   @Controller
   public class MainController {
       @GetMapping(value = "/")
       public String main(){
           return "main";
       }
   }
}

2. memberForm.html

  • th:object - form submit 할 때, form 의 데이터가 th:object 객체에 매핑되어 보내짐
  • th: field - 해당 필드를 th:object 객체 내부 필드와 매핑, "*{}" 형식으로 th:object 참조
  • th:if="${#fields.hasErrors('필드명')}" - 해당 필드에 에러가 있는 경우
  • th:errors - 해당 값에 에러가 있는 경우 출력
  • csrf 토큰 전송

3. 회원가입 검증

1. 의존성 추가

서버로 넘어오는 값 검증

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. validation 어노테이션

어노테이션설명
@NotEmptyNULL 체크 및 문자열의 경우 길이 0인지 검사
@NotBlankNULL 체크 및 문자열의 경우 길이 0 및 빈 문자열(" ") 검사
@Length최소, 최대 길이 검사
@Email이메일 형식인지 검사
@Max지정한 값보다 작은지 검사
@Min지정한 값보다 큰지 검사
@Null값이 Null인지 검사
@NotNull값이 Null이 아닌지 검사

3. 회원가입 DTO 유효성 검증

@Getter
@Setter
public class MemberFormDto {
    @NotBlank(message = "이름은 필수 입력 값입니다.")
    private String name;
    
    @NotBlank(message = "이메일은 필수 입력 값입니다.")
    @Email(message = "이메일 형식으로 입력해주세요.")
    private String email;
    
    @NotBlank(message = "비밀번호는 필수 입력 값입니다.")
    @Length(min=8, max=16, message = "비밀번호는 8자 이상, 16자 이하로 입력해주세요.")
    private String password;
    
    @NotBlank(message = "주소는 필수 입력 값입니다.")
    private String address;
    
}

4. 유효성 검증

MemberController.java

@PostMapping(value = "/new")
// Post 요청으로 넘어온 회원가입 정보를 MemberFormDto 객체로 받음
// 검증하려는 객체 앞에 @Valid 추가. 검사 후 결과는 bindingResult에 담김. 
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:/";
}
  • 검증하려는 객체 앞에 @Valid 어노테이션 선언.
  • 파라미터로 bindingResult 객체 추가하여 검사 후 결과 담아주기.
  • bindingResult.hasErrors() 호출해 에러가 있다면 회원가입 페이지로 다시 돌아감.
  • 중복된 회원가입이면 예외 발생. 에러메세지 뷰로 전달
  • 문제 없으면 메인 페이지로 redirect

5. 유효성 검증 결과

  • 입력한 데이터에 문제가 있는 경우

  • 회원가입에 성공한 경우 (메인 페이지로 이동)

  • 중복된 회원가입

0개의 댓글