
스프링 시큐리티를 통해 회원가입 기능을 만들어 보아요.
@Getter
@Setter
@Entity
public class SiteUser {
// SiteUser로 한 이유 : 스프링 시큐리티에 이미 User 클래스가 존재. 실습땐 이름을 달리함.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true) //유일한 값만 저장 (중복 방지)
private String username;
private String password;
@Column(unique = true)
private String email;
}
public interface UserRepository extends JpaRepository<SiteUser, Long> {
}
@RequiredArgsConstructor // 생성자 만들어줌.
@Service
public class UserService {
private final UserRepository userRepository;
// 회원 생성
public SiteUser create(String username, String email, String password) {
SiteUser user = new SiteUser();
user.setUsername(username);
user.setEmail(email);
//빈으로 등록한 Password Encoder 객체 주입받아 사용
user.setPassword(passwordEncoder.encode(password));
this.userRepository.save(user);
return user;
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// (중략)
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
PasswordEncoder(BCryptPasswordEncoder 인터페이스)객체를 빈으로 등록해서,
BCryptPasswordEncoder 객체를 직접 생성하지 않고 빈의 객체를 주입받아 사용함.
회원가입을 위한 정보를 담는 폼을 생성함.
@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
private String email;
}
다음은 회원 가입을 위한 컨트롤러를 생성
@RequiredArgsConstructor
@Controller
@RequestMapping("/user")
public class UserController {
private final UserService userService;
@GetMapping("/signup") // 회원가입 페이지 불러오기
public String signup(UserCreateForm userCreateForm) {
return "signup_form";
}
@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개의 패스워드가 일치하지 않습니다.");
return "signup_form";
}
userService.create(userCreateForm.getUsername(),
userCreateForm.getEmail(), userCreateForm.getPassword1());
return "redirect:/";
}
}
URL이 get으로 요청되면 회원 가입 페이지로 이동이 되고, post 요청이 되면 실제 가입을 진행해요
중간에 비밀번호 + 비밀번호확인 정보를 비교하여 같은지 다른지를 확인할 수 있습니다.
rejectValue의 매개변수는 각각 bindingResult.rejectValue(필드명, 오류 코드, 오류 메시지)를 의미해요.
이제 회원가입 페이지가 필요하겠쥬 ?
<div layout:fragment="content" class="container my-3">
<div class="my-3 border-bottom">
<div>
<h4>회원가입</h4>
</div>
</div>
<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>
전체를 form 태그로 감싸고, button의 submit을 post 방식으로 이뤄지게 했습니다.
회원가입 페이지에서의 입력값을 받아오는 자바 코드가 없는데,,, 어떻게 UserCreateForm에 저장됨 ?
AI에 질문을 해보니 내용이 다음과 같습니다.
폼 데이터 바인딩이라는 원리라고 합니다.
4번의 내용이 제가 궁금했던 내용의 정답 같습니다.
@PostMapping("/user/signup")
public String signup(@Valid UserCreateForm userCreateForm, BindingResult bindingResult) {
// ...
}
컨트롤러에서 @Valid를 통해 UserCreateForm 객체에 대한 유효성 검사를 수행합니다.
그 다음 BindingResult 객체에 검사 결과를 담습니다.
즉, UserCreateForm 객체는 HTML 폼의 입력 필드와 1:1로 매핑이 되고 있습니다.
그래서 가입 페이지의 username에 해당하는 입력값은 UserCreateForm 객체의 username속성에 자동으로 설정이 됩니다.
그리고 HTML 코드 중
<form th:action="@{/user/signup}" th:object="${userCreateForm}" method="post">
</form>
이 부분에서, th:object="${userCreateForm}" 을 통해 폼에 바인딩할 객체를 지정합니다.
그리고 th:field="*{username}"을 통해 폼의 입력 필드를 객체의 속성에 바인딩합니다.
- 자동 매핑 : Spring MVC는 폼 데이터와 자바 객체의 속성을 자동으로 매핑합니다.
- 유효성 검사 : @Valid 애노테이션을 사용하여 입력 데이터의 유효성을 검사할 수 있습니다.
- Thymeleaf 연동 : Thymeleaf를 사용하여 폼을 생성하고 데이터를 바인딩할 수 있습니다.
- 커스텀 데이터 바인더 : 기본적인 데이터 바인딩 기능 외에도 커스텀 데이터 바인더를 사용하여 복잡한 데이터 변환을 수행할 수 있습니다.