(13) 기능구현 - 회원가입

회원가입 기능을 구현하자


시작하기 전에

개요 : 질문과 답변을 할 수 있는 게시판 서비스를 스프링부트를 통해 만들어 본다.

학습사이트 : https://wikidocs.net/book/7601

예제 코드 : https://github.com/pahkey/sbb


사전설정(중요)

사전에 스프링 시큐리티 설정을 해주어야 한다.

https://wikidocs.net/162150


엔티티 설정


회원정보 엔티티 설정


  • 회원정보 엔티티 속성
속성 설명
username 사용자 이름 (사용자 ID)
password 비밀번호
email 이메일

회원가입 기능을 구현하기 위해 필요한 엔티티에는 위와 같은 속성이 필요하다.


  • User 도메인 생성

1

회원 도메인 패키지를 생성한다.


SiteUser 엔티티

스프링 시큐리티에 User클래스가 있으므로, SiteUser엔티티를 만들자.


  • SiteUser.java 생성
// 경로 : sbb/src/main/java/com/mysite/sbb/user/SiteUser.java

package com.mysite.sbb.user;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
public class SiteUser {
	
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true) // 중복방지
    private String username;

    private String password;

    @Column(unique = true) // 중복방지
    private String email;
    
}


  • SiteUser테이블 확인

2

unique로 설정한 속성들로 인해 생긴 인덱스 확인


회원 리포지터리와 서비스

질문과 답변처럼 회원도 리포지터리와 서비스를 만들어준다.

3


User리포지터리 설정


  • SecurityConfig.java 수정
// 경로 sbb/src/main/java/com/mysite/sbb/SeurityConfig.java
package com.mysite.sbb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration // 스프링의 환경설정 파일임을 의미하는 에너테이션
@EnableWebSecurity // 모든 요청URL이 스프링 시큐리티의 제어를 받도록 만드는 에너테이션
public class SecurityConfig {
	
	// 스프링 시큐리티 세부설정하기
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
		
		// 로그인을 하지 않더라도 모든 페이지에 접근할 수 있음
		http.authorizeHttpRequests().antMatchers("/**").permitAll()
		
			// H2 콘솔은 예외처리하기
			.and()
				.csrf().ignoringAntMatchers("/h2-console/**")
			
			// X-Frame-Options 설정
			.and()
				.headers()
				.addHeaderWriter(new XFrameOptionsHeaderWriter(
						XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
				;
		return http.build();
	}
	
	// 암호화 방식 설정
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

}

사용자의 비밀번호 암호화 방식을 설정해준다


  • UserRepository.java 생성
// 경로 : sbb/src/main/java/com/mysite/sbb/user/UserRepository.java

package com.mysite.sbb.user;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<SiteUser, Long> {
}


  • UserService.java 생성
// 경로 : sbb/src/main/java/com/mysite/sbb/user/UserService.java

package com.mysite.sbb.user;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    // 회원 데이터 생성 메서드
    public SiteUser create(String username, String email, String password) {
        SiteUser user = new SiteUser();
        user.setUsername(username);
        user.setEmail(email);
        user.setPassword(passwordEncoder.encode(password));
        this.userRepository.save(user);
        return user;
    }
}

회원가입 기능 구현


회원가입 폼 구현


  • UserCreateForm.java 생성
// 경로 : sbb/src/main/java/com/mysite/sbb/user/UserCreateForm.java

package com.mysite.sbb.user;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserCreateForm { // 회원생성 메서드
	
    @Size(min = 3, max = 25) // 사이즈 조건
    @NotEmpty(message = "사용자ID는 필수항목입니다.")
    private String username;

    @NotEmpty(message = "비밀번호는 필수항목입니다.")
    private String password1;

    @NotEmpty(message = "비밀번호 확인은 필수항목입니다.")
    private String password2;

    @NotEmpty(message = "이메일은 필수항목입니다.")
    @Email // Email 규칙 적용
    private String email;
}

회원생성의 조건들을 설정해 주었다


  • UserController.java 생성
// 경로 : sbb/src/main/java/com/mysite/sbb/user/UserController.java

package com.mysite.sbb.user;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.dao.DataIntegrityViolationException;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Controller
@RequestMapping("/user")
public class UserController {

    private final UserService userService;

    // 회원가입 템플릿 렌더링 - get
    @GetMapping("/signup")
    public String signup(UserCreateForm userCreateForm) {
        return "signup_form";
    }

    // 회원가입 진행 - post
    @PostMapping("/signup")
    public String signup(@Valid UserCreateForm userCreateForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "signup_form";
        }

        // 비밀번호 검증 로직 - 필숫값 검증
        if (!userCreateForm.getPassword1().equals(userCreateForm.getPassword2())) {
            bindingResult.rejectValue("password2", "passwordInCorrect", 
                    "2개의 패스워드가 일치하지 않습니다."); // 오류코드 - passwordInCorrect
            return "signup_form";
        }

        // 중복된 회원 검증
        try {
        userService.create(userCreateForm.getUsername(), 
                userCreateForm.getEmail(), userCreateForm.getPassword1());
        }
        
        // 중복된 회원이면 오류 표시하고 회원가입 폼으로 돌려보내기
        catch(DataIntegrityViolationException e) {
            e.printStackTrace();
            bindingResult.reject("signupFailed", "이미 등록된 사용자입니다.");
            return "signup_form";
        }
        
        // 다른 오류 처리
        catch(Exception e) {
            e.printStackTrace();
            bindingResult.reject("signupFailed", e.getMessage());
            return "signup_form";
        }

        return "redirect:/";
    }
}

비밀번호가 일치하지 않으면 알림을 보내게 하였다.

중복된 회원이면 알림을 보내게 해주었다.


회원가입 템플릿 작성


  • signup_form.html 생성
<!-- 경로 : /sbb/src/main/resources/templates/signup_form.html -->

<!-- layout.html상속 -->
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
    <div class="my-3 border-bottom">
        <div>
            <h4>회원가입</h4>
        </div>
    </div>
    
    <!-- 회원가입 폼 작성 (post방식) -->
    <form th:action="@{/user/signup}" th:object="${userCreateForm}" method="post">
        <div th:replace="form_errors :: formErrorsFragment"></div>
        <div class="mb-3">
            <label for="username" class="form-label">사용자ID</label>
            <input type="text" th:field="*{username}" class="form-control">
        </div>
        <div class="mb-3">
            <label for="password1" class="form-label">비밀번호</label>
            <input type="password" th:field="*{password1}" class="form-control">
        </div>
        <div class="mb-3">
            <label for="password2" class="form-label">비밀번호 확인</label>
            <input type="password" th:field="*{password2}" class="form-control">
        </div>
        <div class="mb-3">
            <label for="email" class="form-label">이메일</label>
            <input type="email" th:field="*{email}" class="form-control">
        </div>
        <button type="submit" class="btn btn-primary">회원가입</button>
    </form>
</div>
</html>


네비게이션 바 수정

<!-- 경로 : /sbb/src/main/resources/templates/navbar.html -->

<nav th:fragment="navbarFragment" 
  class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
    <div class="container-fluid">
    	
    	<!-- 홈 링크 -->
        <a class="navbar-brand" href="/">SBB</a>
        <button class="navbar-toggler" type="button"
         data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" 
            aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        
        <!-- 부트스트랩 반응형 div -->
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            
            	<!-- 로그인 링크 -->
                <li class="nav-item">
                    <a class="nav-link" href="#">로그인</a>
                </li>
                
                <!-- 회원가입 링크 -->
                <li class="nav-item">
                    <a class="nav-link" th:href="@{/user/signup}">회원가입</a>
                </li>
            </ul>
        </div>
    </div>
</nav>

회원가입 링크를 네비게이션에 걸어두었다.


SBB 테스트

회원가입 해보고 테스트를 해보자


  • 회원가입

4

회원가입 완료! 창이 뜨면 좋을 것 같다.


  • Site User 테이블 확인

5

회원가입 정보들이 성공적으로 저장되었다.


  • 필숫값 검증

6

아무 값도 넣지 않고 회원가입시 알림을 보낸다.


  • 중복회원 검증

7

이메일 중복체크






Leave a comment