03F. ํ์๊ฐ์
03F. ํ์๊ฐ์ ๊ด๋ จ
์ด๋ฒ์๋ SBB์ ํ์๊ฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํด ๋ณด์.
ํ์๊ฐ์ ๊ธฐ๋ฅ์ ๋ง๋ค์ด ๋ณด์๋ค๋ฉด ์น ํ๋ก๊ทธ๋๋ฐ์ ๊ฑฐ์ ๋ง์คํฐํ๋ค๊ณ ํ ์ ์๋ค. ๊ทธ๋งํผ ํ์๊ฐ์ ๊ธฐ๋ฅ์ ์น ์ฌ์ดํธ์์ ํต์ฌ ์ค์ ํต์ฌ์ด๋ผ ํ ์ ์๋ค.
ํ์ ์ ๋ณด๋ฅผ ์ํ ์ํฐํฐ
์ง๊ธ๊น์ง๋ ์ง๋ฌธ, ๋ต๋ณ ์ํฐํฐ๋ง ์ฌ์ฉํ๋ค๋ฉด ์ด์ ํ์ ์ ๋ณด๋ฅผ ์ํ ์ํฐํฐ๊ฐ ํ์ํ๋ค. ํ์ ์ ๋ณด ์ํฐํฐ์๋ ์ต์ํ ๋ค์๊ณผ ๊ฐ์ ์์ฑ์ด ํ์ํ๋ค.
์์ฑ | ์ค๋ช |
---|---|
username | ์ฌ์ฉ์ ์ด๋ฆ (์ฌ์ฉ์ ID) |
password | ๋น๋ฐ๋ฒํธ |
email | ์ด๋ฉ์ผ |
User ๋๋ฉ์ธ
๊ทธ๋ฆฌ๊ณ ํ์์ ์ง๋ฌธ, ๋ต๋ณ ๋๋ฉ์ธ์ด ์๋๋ฏ๋ก user
๋ผ๋ ๋๋ฉ์ธ์ ์ฌ์ฉํ ๊ฒ์ด๋ค. ๋ค์๊ณผ ๊ฐ์ด com.mysite.sbb.user
ํจํค์ง๋ฅผ ์์ฑํ์.
SiteUser
์ํฐํฐ
๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์๋ฅผ ๊ด๋ฆฌํ SiteUser
์ํฐํฐ๋ฅผ ๋ค์์ฒ๋ผ ์์ฑํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/user/
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;
}
Question
, Answer
์ํฐํฐ์ ๋์ผํ ๋ฐฉ๋ฒ์ผ๋ก SiteUser
์ํฐํฐ๋ฅผ ๋ง๋ค์๋ค. ์ํฐํฐ๋ช
์ User
๋์ SiteUser
๋ก ํ ์ด์ ๋ ์คํ๋ง ์ํ๋ฆฌํฐ์ ์ด๋ฏธ User
ํด๋์ค๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฌผ๋ก ํจํค์ง๋ช
์ด ๋ฌ๋ผ User
๋ผ๋ ์ด๋ฆ์ ์ฌ์ฉํ ์ ์์ง๋ง ํจํค์ง ์ค์ฉ์ผ๋ก ์ธํ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก ์ด ์ฑ
์์๋ User
๋์ SiteUser
๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ช
๋ช
ํ์๋ค.
๊ทธ๋ฆฌ๊ณ username
, email
์์ฑ์๋ @Column(unique = true)
์ฒ๋ผ unique = true
๋ฅผ ์ง์ ํ๋ค. unique = true
๋ ์ ์ผํ ๊ฐ๋ง ์ ์ฅํ ์ ์์์ ์๋ฏธํ๋ค. ์ฆ, ๊ฐ์ ์ค๋ณต๋๊ฒ ์ ์ฅํ ์ ์์์ ๋ปํ๋ค. ์ด๋ ๊ฒ ํด์ผ username
๊ณผ email
์ ๋์ผํ ๊ฐ์ด ์ ์ฅ๋์ง ์๋๋ค.
SiteUser
ํ
์ด๋ธ
SiteUser
์ํฐํฐ๋ฅผ ์์ฑํ๊ณ H2 ์ฝ์์ ์ ์ํ์ฌ ํ
์ด๋ธ์ด ์ ๋ง๋ค์ด์ก๋์ง ํ์ธํด ๋ณด์.
SITE_USER
ํ
์ด๋ธ๊ณผ ์ปฌ๋ผ๋ค ๊ทธ๋ฆฌ๊ณ unique
๋ก ์ค์ ํ ์์ฑ๋ค๋ก ์ธํด ์๊ธด UK_๋ก ์์ํ๋ ์ธ๋ฑ์ค๋ค์ด ๋ณด์ผ ๊ฒ์ด๋ค.
User
๋ฆฌํฌ์งํฐ๋ฆฌ์ ์๋น์ค
์ฌ์ฉ์ ์ํฐํฐ๊ฐ ์ค๋น๋์์ผ๋ ์ด์ User
๋ฆฌํฌ์งํฐ๋ฆฌ์ User
์๋น์ค๋ฅผ ๋ง๋ค์ด ๋ณด์.
User
๋ฆฌํฌ์งํฐ๋ฆฌ
๋ค์๊ณผ ๊ฐ์ด UserRepository
๋ฅผ ์์ฑํ์.
ํ์ผ๋ช :
/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> {
}
SiteUser
์ PK์ ํ์
์ Long
์ด๋ค. ๋ฐ๋ผ์ JpaRepository<SiteUser, Long>
์ฒ๋ผ ์ฌ์ฉํ๋ค.
User ์๋น์ค
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด UserService๋ฅผ ์์ฑํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/user/
UserService.java
package com.mysite.sbb.user;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@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);
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
user.setPassword(passwordEncoder.encode(password));
this.userRepository.save(user);
return user;
}
}
User
์๋น์ค์๋ User
๋ฆฌํฌ์งํฐ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ User
๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ create ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ค. ์ด ๋ ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ๋ ๋ณด์์ ์ํด ๋ฐ๋์ ์ํธํํ์ฌ ์ ์ฅํด์ผ ํ๋ค. ์ํธํ๋ฅผ ์ํด ์ํ๋ฆฌํฐ์ BCryptPasswordEncoder
ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ํธํํ์ฌ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ์ฅํ๋ค.
BCryptPasswordEncoder
๋ BCrypt ํด์ฑ ํจ์(BCrypt hashing function)๋ฅผ ์ฌ์ฉํด์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํ๋ค.
ํ์ง๋ง ์ด๋ ๊ฒ BCryptPasswordEncoder
๊ฐ์ฒด๋ฅผ ์ง์ new๋ก ์์ฑํ๋ ๋ฐฉ์๋ณด๋ค๋ PasswordEncoder
๋น(bean)์ผ๋ก ๋ฑ๋กํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. ์๋ํ๋ฉด ์ํธํ ๋ฐฉ์์ ๋ณ๊ฒฝํ๋ฉด BCryptPasswordEncoder
๋ฅผ ์ฌ์ฉํ ๋ชจ๋ ํ๋ก๊ทธ๋จ์ ์ผ์ผ์ด ์ฐพ์์ ์์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
PasswordEncoder
๋BCryptPasswordEncoder
์ ์ธํฐํ์ด์ค์ด๋ค.
PasswordEncoder
๋น(bean)์ ๋ง๋๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ @Configuration
์ด ์ ์ฉ๋ SecurityConfig
์ @Bean
๋ฉ์๋๋ฅผ ์์ฑํ๋ ๊ฒ์ด๋ค. ๋ค์๊ณผ ๊ฐ์ด SecurityConfig
๋ฅผ ์์ ํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/
SecurityConfig.java
// (... ์๋ต ...)
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
// (... ์๋ต ...)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers(new AntPathRequestMatcher("/**")).permitAll())
.csrf((csrf) -> csrf
.ignoringRequestMatchers(new AntPathRequestMatcher("/h2-console/**")))
.headers((headers) -> headers
.addHeaderWriter(new XFrameOptionsHeaderWriter(
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)))
;
return http.build();
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
์ด๋ ๊ฒ PasswordEncoder
๋ฅผ @Bean
์ผ๋ก ๋ฑ๋กํ๋ฉด UserService
๋ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ ์ ์๋ค.
ํ์ผ๋ช :
/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;
}
}
BCryptPasswordEncoder
๊ฐ์ฒด๋ฅผ ์ง์ ์์ฑํ์ฌ ์ฌ์ฉํ์ง ์๊ณ ๋น์ผ๋ก ๋ฑ๋กํ PasswordEncoder
๊ฐ์ฒด๋ฅผ ์ฃผ์
๋ฐ์ ์ฌ์ฉํ๋๋ก ์์ ํ๋ค.
ํ์๊ฐ์ ํผ
๊ทธ๋ฆฌ๊ณ ํ์ ๊ฐ์
์ ์ํ ํผ ํด๋์ค๋ฅผ ์์ฑํ์. ๋ค์์ฒ๋ผ UserCreateForm
์ ๋ง๋ค์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/user/
UserCreateForm.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 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;
}
username
์ ํ์ํญ๋ชฉ์ด๊ณ ๊ธธ์ด๊ฐ 3-25 ์ฌ์ด์ฌ์ผ ํ๋ค๋ ๊ฒ์ฆ์กฐ๊ฑด์ ์ค์ ํ๋ค. @Size
๋ ํผ ์ ํจ์ฑ ๊ฒ์ฆ์ ๋ฌธ์์ด์ ๊ธธ์ด๊ฐ ์ต์๊ธธ์ด(min
)์ ์ต๋๊ธธ์ด(max
) ์ฌ์ด์ ํด๋นํ๋์ง๋ฅผ ๊ฒ์ฆํ๋ค. password1
๊ณผ password2
๋ "๋น๋ฐ๋ฒํธ"์ "๋น๋ฐ๋ฒํธํ์ธ"์ ๋ํ ์์ฑ์ด๋ค. ๋ก๊ทธ์ธ ํ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ฒ๋ง ํ์ํ์ง๋ง ํ์๊ฐ์
์์๋ ์
๋ ฅํ ๋น๋ฐ๋ฒํธ๊ฐ ์ ํํ์ง ํ์ธํ๊ธฐ ์ํด 2๊ฐ์ ํ๋๊ฐ ํ์ํ๋ค. ๊ทธ๋ฆฌ๊ณ email
์์ฑ์๋ @Email
์ ๋ํ
์ด์
์ด ์ ์ฉ๋์๋ค. @Email
์ ํด๋น ์์ฑ์ ๊ฐ์ด ์ด๋ฉ์ผํ์๊ณผ ์ผ์นํ๋์ง๋ฅผ ๊ฒ์ฆํ๋ค.
ํ์๊ฐ์ ์ปจํธ๋กค๋ฌ
์ด์ ์ฌ์ฉ์ ์ํฐํฐ์ ์๋น์ค ๊ทธ๋ฆฌ๊ณ ํผ์ด ์ค๋น๋์์ผ๋ ํ์๊ฐ์
์ ์ํ User
์ปจํธ๋กค๋ฌ๋ฅผ ๋ง๋ค์ด๋ณด์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/user/
UserController.java
package com.mysite.sbb.user;
import jakarta.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 lombok.RequiredArgsConstructor;
@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:/";
}
}
/user/signup
URL์ด GET์ผ๋ก ์์ฒญ๋๋ฉด ํ์ ๊ฐ์
์ ์ํ ํ
ํ๋ฆฟ์ ๋ ๋๋งํ๊ณ POST๋ก ์์ฒญ๋๋ฉด ํ์๊ฐ์
์ ์งํํ๋๋ก ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ์ ๊ฐ์
์ ๋น๋ฐ๋ฒํธ1
๊ณผ ๋น๋ฐ๋ฒํธ2
๊ฐ ๋์ผํ์ง๋ฅผ ๊ฒ์ฆํ๋ ๋ก์ง์ ์ถ๊ฐํ๋ค. ๋ง์ฝ 2๊ฐ์ ๊ฐ์ด ์ผ์นํ์ง ์์ ๊ฒฝ์ฐ์๋ bindingResult.rejectValue
๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฒ ํ๋ค. bindingResult.rejectValue
์ ๊ฐ ํ๋ผ๋ฏธํฐ๋ bindingResult.rejectValue(ํ๋๋ช
, ์ค๋ฅ์ฝ๋, ์๋ฌ๋ฉ์์ง)
๋ฅผ ์๋ฏธํ๋ฉฐ ์ฌ๊ธฐ์ ์ค๋ฅ์ฝ๋๋ ์ผ๋จ "passwordInCorrect"๋ก ์ ์ํ๋ค.
๋ํ ํ๋ก์ ํธ์์๋ ๋ฒ์ญ๊ณผ ๊ด๋ฆฌ๋ฅผ ์ํด ์ค๋ฅ์ฝ๋๋ฅผ ์ ์ ์ํ์ฌ ์ฌ์ฉํด์ผ ํ๋ค.
ํ์๊ฐ์ ํ ํ๋ฆฟ
์ด์ด์ ํ์๊ฐ์
ํ
ํ๋ฆฟ์ ์์ฑํ์. ๋ค์์ฒ๋ผ signup_form.html
ํ์ผ์ ์์ฑํ์.
ํ์ผ๋ช :
/sbb/src/main/resources/templates/
signup_form.html
<html layout:decorate="~{layout}">
<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>
</html>
ํ์๊ฐ์
์ ์ํ "์ฌ์ฉ์ ID", "๋น๋ฐ๋ฒํธ", "๋น๋ฐ๋ฒํธ ํ์ธ", "์ด๋ฉ์ผ"์ ํด๋น๋๋ input ์๋ฆฌ๋จผํธ๋ฅผ ์ถ๊ฐํ๋ค. <ํ์๊ฐ์
>
๋ฒํผ์ ๋๋ฅด๋ฉด ํผ ๋ฐ์ดํฐ๊ฐ POST ๋ฐฉ์์ผ๋ก /user/signup/
URL๋ก ์ ์ก๋๋ค.
๋ด๋น๊ฒ์ด์ ๋ฐ์ ํ์๊ฐ์ ๋งํฌ ์ถ๊ฐํ๊ธฐ
์ด์ ํ์๊ฐ์ ํ๋ฉด์ผ๋ก ์ด๋ํ ์ ์๋ ๋งํฌ๋ฅผ ๋ด๋น๊ฒ์ด์ ๋ฐ์ ์ถ๊ฐํ์.
ํ์ผ๋ช :
/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>
<!-- (... ์๋ต ...) -->
<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>
ํ์๊ฐ์ ์คํํด ๋ณด๊ธฐ
์ด์ ๋ด๋น๊ฒ์ด์ ๋ฐ์ "ํ์๊ฐ์ " ๋งํฌ๋ฅผ ๋๋ฅด๋ฉด ๋ค์๊ณผ ๊ฐ์ ํ์๊ฐ์ ํ๋ฉด์ด ๋์จ๋ค.
์
๋ ฅ๊ฐ ์ค์์ ๋น๋ฐ๋ฒํธ, ๋น๋ฐ๋ฒํธ ํ์ธ์ ๋ค๋ฅด๊ฒ ์
๋ ฅํ๊ณ <ํ์๊ฐ์
>
์ ๋๋ฅด๋ฉด ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ฌ ํ๋ฉด์ ๋ค์๊ณผ ๊ฐ์ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํด ์ค ๊ฒ์ด๋ค.
์ด์ฒ๋ผ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ํ์๊ฐ์ ๊ธฐ๋ฅ์๋ ํ์ซ๊ฐ ๊ฒ์ฆ, ์ด๋ฉ์ผ ๊ท์น ๊ฒ์ฆ ๋ฑ์ด ์ ์ฉ๋์ด ์๋ค. ์ฌ๋ฐ๋ฅธ ์ ๋ ฅ๊ฐ์ผ๋ก ํ์๊ฐ์ ์ ์๋ฃํ๋ฉด ๋ฉ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ ๊ฒ์ด๋ค.
ํ์๊ฐ์ ๋ฐ์ดํฐ ํ์ธํด ๋ณด๊ธฐ
H2 ์ฝ์์์ ๋ค์์ SQL์ ์คํํ์ฌ ๋ฐ๋ก ์ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ ๋ง๋ ํ์ ์ ๋ณด๋ฅผ ํ์ธํด ๋ณด์.
SELECT * FROM SITE_USER
์ถํํ๋ค. ์ด์ SBB ์๋น์ค์ ํ์๊ฐ์ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์๋ค.
์ค๋ณต ํ์๊ฐ์
์ด๋ฒ์๋ ์ด๋ฏธ ๊ฐ์ ํ ๋์ผํ ์ฌ์ฉ์ID, ๋๋ ๋์ผํ ์ด๋ฉ์ผ ์ฃผ์๋ก ํ์๊ฐ์ ์ ์งํํด ๋ณด์. ์๋ง๋ ๋ค์๊ณผ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
๋์ผํ ์ฌ์ฉ์ID ๋๋ ๋์ผํ ์ด๋ฉ์ผ ์ฃผ์๋ก ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ unique=true
์ค์ ์ผ๋ก ์ธํด ํ์ฉ๋์ง ์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ๋น์ฐํ๋ค. ํ์ง๋ง ํ๋ฉด์ ์ด๋ ๊ฒ 500 ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๊ทธ๋๋ก ๋ณด์ฌ์ฃผ๋ ๊ฒ์ ์ข์ง ์๋ค. ๋ฐ๋ผ์ ํ์๊ฐ์
์ ๋ฐ์ํ๋ ์ค๋ฅ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ฒ๋ฆฌํด ์ฃผ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/user/
UserController.java
package com.mysite.sbb.user;
// (... ์๋ต ...)
import org.springframework.dao.DataIntegrityViolationException;
// (... ์๋ต ...)
public class UserController {
// (... ์๋ต ...)
@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";
}
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:/";
}
}
์ฌ์ฉ์ID ๋๋ ์ด๋ฉ์ผ ์ฃผ์๊ฐ ๋์ผํ ๊ฒฝ์ฐ์๋ DataIntegrityViolationException
์ด ๋ฐ์ํ๋ฏ๋ก DataIntegrityViolationException
์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ "์ด๋ฏธ ๋ฑ๋ก๋ ์ฌ์ฉ์์
๋๋ค."๋ผ๋ ์ค๋ฅ๋ฅผ ํ๋ฉด์ ํ์ํ๋๋ก ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ค๋ฅ์ ๊ฒฝ์ฐ์๋ ํด๋น ์ค๋ฅ์ ๋ฉ์์ง(e.getMessage()
)๋ฅผ ์ถ๋ ฅํ๋๋ก ํ๋ค.
bindingResult.reject(์ค๋ฅ์ฝ๋, ์ค๋ฅ๋ฉ์์ง)
๋ ํน์ ํ๋์ ์ค๋ฅ๊ฐ ์๋ ์ผ๋ฐ์ ์ธ ์ค๋ฅ๋ฅผ ๋ฑ๋กํ ๋ ์ฌ์ฉํ๋ค.
์ด๋ ๊ฒ ์์ ํ๊ณ ๋ค์ ๋์ผํ ์ฌ์ฉ์๋ก ํ์๊ฐ์ ์ ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ์์ ์ธ ์ค๋ฅ๋ฅผ ํ์ํ๋ ํ๋ฉด์ ๋ณผ์ ์๋ค.