회원가입 뷰
> 부트스트랩
>> 네비게이션 바 만들기
>> 폼 만들기
> 타임리프
>> SignUpForm 타입 객체를 폼 객체로 설정하기
> 웹(HTML, CSS, JavaScript)
>>제약 검증 기능 사용하기
>>> 닉네임 (3~20자, 필수 입력)
>>> 이메일 (이메일 형식, 필수 입력)
>>> 패스워드 (8~50자, 필수 입력)
스프링 환경 설정
서버 재시작할 필요 없이 빌드를 수정하고 바로 html로 변경된 부분을 확인할 수 있다.
developmentOnly 'org.springframework.boot:spring-boot-devtools'
html에 타임리프(thymeleaf) 설정
<html lang="en" xmlns:th="http://www.thymeleaf.org">
href의 값을 Thymeleaf 렌더링 할 때 @{/}으로 바꿔줌 / Thymeleaf렌더링 하지 않을 때는 그냥 href 값 사용
th:href="@{/}" (@는 서블릿 컨텍스트 루트에 따라 알아서 값을 치환해줌 ; 루트를 앱으로주면 알아서 앱으로 치환됨)
뷰 작성
sign-up.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Dot Study</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<style>
.container{
max-width: 100%;
}
</style>
</head>
<body class="bg-light">
<nav th:fragment="main-nav" class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="/" th:href="@{/}">
<img src="/images/logo.png" width="30" height="30">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<form th:action="@{/search/study}" class="form-inline" method="get">
<input class="form-control mr-sm-2" name="keyword" type="search" placeholder="스터디 찾기" aria-label="Search" />
</form>
</li>
</ul>
<ul class="navbar-nav justify-content-end">
<li class="nav-item" >
<a class="nav-link" href="#" th:href="@{/login}">로그인</a>
</li>
<li class="nav-item" >
<a class="nav-link" th:href="@{/sign-up}">가입</a>
</li>
</ul>
</div>
</nav>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
</body>
</html>
thymeleaf으로 렌더링안할때에도 그냥 html로 렌더링이 가능하니깐 href="#"를 설정해준다.
<a class="nav-link" href="#" th:href="@{/login}">로그인</a>
sign-up.html
<!-- 계정만들기 -->
<div class="container">
<div class="py-5 text-center">
<h2>계정 만들기</h2>
</div>
<div class="row justify-content-center">
<form class="needs-validation col-sm-6" action="#"
th:action="@{/sign-up}" th:object="${signUpForm}" method="post" novalidate>
<div class="form-group">
<label for="nickname">닉네임</label>
<input id="nickname" type="text" th:field="*{nickname}" class="form-control"
placeholder="whiteship" aria-describedby="nicknameHelp" required minlength="3" maxlength="20">
<small id="nicknameHelp" class="form-text text-muted">
공백없이 문자와 숫자로만 3자 이상 20자 이내로 입력하세요. 가입후에 변경할 수 있습니다.
</small>
<small class="invalid-feedback">닉네임을 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('nickname')}" th:errors="*{nickname}">Nickname Error</small>
</div>
<div class="form-group">
<label for="email">이메일</label>
<input id="email" type="email" th:field="*{email}" class="form-control"
placeholder="your@email.com" aria-describedby="emailHelp" required>
<small id="emailHelp" class="form-text text-muted">
스터디올래는 사용자의 이메일을 공개하지 않습니다.
</small>
<small class="invalid-feedback">이메일을 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</small>
</div>
<div class="form-group">
<label for="password">패스워드</label>
<input id="password" type="password" th:field="*{password}" class="form-control"
aria-describedby="passwordHelp" required minlength="8" maxlength="50">
<small id="passwordHelp" class="form-text text-muted">
8자 이상 50자 이내로 입력하세요. 영문자, 숫자, 특수기호를 사용할 수 있으며 공백은 사용할 수 없습니다.
</small>
<small class="invalid-feedback">패스워드를 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password Error</small>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit"
aria-describedby="submitHelp">가입하기</button>
<small id="submitHelp" class="form-text text-muted">
<a href="#">약관</a>에 동의하시면 가입하기 버튼을 클릭하세요.
</small>
</div>
</form>
</div>
<footer th:fragment="footer">
<div class="row justify-content-center">
<img class="mb-2" src="/images/test_logo.jpg" alt="" width="100">
<small class="d-block mb-3 text-muted">© 2020</small>
</div>
</footer>
</div>
<script type="application/javascript" th:fragment="form-validation">
(function () {
'use strict';
window.addEventListener('load', function () {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
Array.prototype.filter.call(forms, function (form) {
form.addEventListener('submit', function (event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated')
}, false)
})
}, false)
}())
</script>
signUpForm을 Form object으로 설정 -> form태그를 채우는 객체로 사용
th:object="${signUpForm}"th:action="@{/sign-up}" th:object="${signUpForm}" method="post" novalidate>
nickname을 input의 필드(input 파라미터 네임, 밸류)로 사용
<input id="nickname" type="text" th:field="*{nickname}" class="form-control"
document에서 needs-validation을 class로 가지고 있는 form을 가져온 다음 적용
<form class="needs-validation col-sm-6" action="#"
<script type="application/javascript" th:fragment="form-validation">
(function () {
'use strict';
window.addEventListener('load', function () {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
Array.prototype.filter.call(forms, function (form) {
form.addEventListener('submit', function (event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated')
}, false)
})
}, false)
}())
</script>
needs-validation 폼
폼이 유효하지 않으면 폼 제출이 안되도록 설정
닉네임 제약 조건 설정 (에러시 invalid-feedback 노출)
<input id="nickname" type="text" th:field="*{nickname}" class="form-control" placeholder="whiteship" aria-describedby="nicknameHelp" required minlength="3" maxlength="20">
<small id="nicknameHelp" class="form-text text-muted"> 공백없이 문자와 숫자로만 3자 이상 20자 이내로 입력하세요. 가입후에 변경할 수 있습니다. </small>
이메일 제약조건 설정 (에러시 invalid-feedback 노출)
<input id="email" type="email" th:field="*{email}" class="form-control" placeholder="your@email.com" aria-describedby="emailHelp" required>
패스워드 제약조건 설정 (에러시 invalid-feedback 노출)
<input id="password" type="password" th:field="*{password}" class="form-control" aria-describedby="passwordHelp" required minlength="8" maxlength="50">
<small id="passwordHelp" class="form-text text-muted">
8자 이상 50자 이내로 입력하세요. 영문자, 숫자, 특수기호를 사용할 수 있으며 공백은 사용할 수 없습니다. </small>
백엔드 로직 작성
이젠 SignUpForm을 만들어주면 된다.
SignUpForm.java
/**
* 회원가입할 때 받아올 데이터
*/
@Data
public class SignUpForm {
private String nickname;
private String email;
private String password;
}
AccontController.java
@Controller
public class AccountController {
@GetMapping("/sign-up")
public String signUpForm(Model model){
model.addAttribute("signUpForm", new SignUpForm()); //add Code
return "account/sign-up";
}
}
SecurityConfig.java에 아래의 코드를 추가
static 리소스들 security필터 적용해제 (/static/images 파일 접근 허용)
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
결과화면
테스트 코드 작성
마지막으로 테스트코드 작성해준다.
@SpringBootTest
@AutoConfigureMockMvc
class AccountControllerTest {
@Autowired
private MockMvc mockMvc;
@DisplayName("회원 가입 화면 보이는지 테스트")
@Test
public void signUpForm() throws Exception{
mockMvc.perform(get("/sign-up"))
.andExpect(status().isOk())
.andExpect(view().name("account/sign-up"))
.andExpect(model().attributeExists("signUpForm"));
}
}
TIP) model.addAttribute()
"abc" 을 생락해도 된다. new Abc() 클래스 이름과 상응하는 attribute값에 자동으로 대입됨.
model.addAttribute("signUpForm" , new SignUpForm()); -> model.addAttribute(new SignUpForm()); 으로 축소 가능
참고
'Dot Programming > Spring Clone' 카테고리의 다른 글
[스프링 웹앱 프로젝트 #6]회원가입 폼 서브밋 처리 (0) | 2020.11.05 |
---|---|
[스프링 웹앱 프로젝트 #5]회원 가입 폼 서브밋 검증 (0) | 2020.11.03 |
[스프링 웹앱 프로젝트 #3]회원 가입 컨트롤러 (0) | 2020.11.02 |
[스프링 웹앱 프로젝트 #2]계정 도메인 생성 (0) | 2020.11.02 |
[스프링 웹앱 프로젝트 #1]스프링 프로젝트 생성 (0) | 2020.11.02 |