반응형
스프링 부트와 JPA, Validation, Thymeleaf를 활용해
회원 정보를 안전하게 저장하고
회원가입 폼의 유효성 검증 및
비밀번호 암호화(BCrypt)까지 실제 서비스에 필요한
회원가입 기능의 전체 과정을 구현합니다.
📌 예제 코드 구성
1) 회원 엔티티(SiteUser.java)
package com.mysite.sbb.user;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.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;
}
- @Entity: JPA가 관리하는 회원 테이블
- @Column(unique = true): 아이디/이메일 중복 방지
2) 리포지토리(UserRepository.java)
package com.mysite.sbb.user;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<SiteUser, Integer> {
}
- 회원 데이터 접근용 JPA 인터페이스
3) 서비스(UserService.java)
package com.mysite.sbb.user;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
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)); // 비밀번호 암호화
userRepository.save(user);
return user;
}
}
- 비밀번호를 BCrypt로 암호화 후 DB에 저장
4) 컨트롤러(UserController.java)
package com.mysite.sbb.user;
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 jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@RequestMapping("/user")
@Controller
public class UserController {
private final UserService userService;
@GetMapping("/signup")
public String signup(UserForm userForm) {
return "signup_form";
}
@PostMapping("/signup")
public String signup(@Valid UserForm userForm, BindingResult bindingResult) {
if (bindingResult.hasErrors())
return "signup_form";
if (!userForm.getPassword().equals(userForm.getPasswordCheck())) {
bindingResult.rejectValue("passwordCheck", "passwordincorrect", "비밀번호가 서로 다릅니다.");
return "signup_form";
}
this.userService.create(userForm.getUsername(), userForm.getEmail(), userForm.getPassword());
return "redirect:/";
}
}
- GET: 회원가입 폼 화면
- POST:
- 폼 검증 후 오류 있으면 폼 재출력
- 비밀번호/확인 일치 여부도 추가로 검증
- 모든 값이 올바르면 회원 생성
5) 폼 데이터 클래스(UserForm.java)
package com.mysite.sbb.user;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserForm {
@Size(min = 3, max = 15)
@NotEmpty(message = "아이디가 비어있습니다.")
private String username;
@NotEmpty(message = "비밀번호가 비어있습니다.")
private String password;
private String passwordCheck;
@Email
@NotEmpty(message = "이메일이 비어있습니다.")
private String email;
}
- @NotEmpty, @Email, @Size 등으로
아이디, 비밀번호, 이메일 유효성 검사
6) 회원가입 폼(signup_form.html)
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3 mx-3">
<!-- 제목 -->
<h2 class="border-bottom">회원가입</h2>
<form th:action="@{/user/signup}" method="post" th:object="${userForm}">
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
<!-- 아이디 -->
<div class="mb-3 row">
<label for="username" class="col-sm-2 col-form-label">아이디</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:field="*{username}">
</div>
</div>
<!-- 비밀번호 -->
<div class="mb-3 row">
<label for="password" class="col-sm-2 col-form-label">비밀번호</label>
<div class="col-sm-10">
<input type="password" class="form-control" th:field="*{password}">
</div>
</div>
<!-- 비밀번호 확인 -->
<div class="mb-3 row">
<label for="passwordCheck" class="col-sm-2 col-form-label">비밀번호 확인</label>
<div class="col-sm-10">
<input type="password" class="form-control" th:field="*{passwordCheck}">
</div>
</div>
<!-- 이메일 -->
<div class="mb-3 row">
<label for="email" class="col-sm-2 col-form-label">이메일</label>
<div class="col-sm-10">
<input type="email" class="form-control" th:field="*{email}">
</div>
</div>
<input type="submit" class="btn btn-primary" value="회원 가입">
</form>
</div>
</html>
- 에러 메시지는 fragment로 공통 처리
- 각 필드는 th:field로 UserForm에 바인딩
💡포인트 정리
- 비밀번호는 반드시 암호화(BCrypt 등)해서 저장
- @Valid/@NotEmpty/@Email 등으로 서버측 폼 검증
- 비밀번호 확인은 bindingResult.rejectValue()로 직접 검증
- JPA의 @Column(unique = true)로 아이디/이메일 중복방지(실제 서비스에서는 중복 검사 로직도 추가 필요)
- 입력 오류는 fragment로 공통 처리
📌정리하자면, 엔티티-서비스-컨트롤러-폼-뷰를 모두 아우르는
완성도 높은 회원가입 기능을 구현했습니다.
이제 누구나 안전하게 회원가입할 수 있습니다!
'Spring > 실습 정리' 카테고리의 다른 글
Spring Boot 27강 - UserRole(회원 역할) 관리 및 MainController(메인 페이지) 분리 (0) | 2025.07.16 |
---|---|
Spring Boot 26강 - 질문(게시글) 수정/삭제/작성자 표시와 인증 기반 권한 체크 (0) | 2025.07.16 |
Spring Boot 25강 - 로그인·로그아웃·회원가입(동적 메뉴/예외처리/인증처리) 완성 (2) | 2025.07.10 |
Spring Boot 23강 - Spring Security 기본 적용과 모든 경로 허용 설정 (1) | 2025.07.09 |
Spring Boot 22강 - 게시판 페이징 처리와 페이지네이션 구현 (1) | 2025.07.08 |
Spring Boot 21강 - 네비게이션 바를 Thymeleaf Fragment로 공통화하기 (0) | 2025.07.07 |