비버놀로지

[Spring JPA] 1-4. 회원 가입 : 뷰 본문

LANGUAGE STUDY/Spring

[Spring JPA] 1-4. 회원 가입 : 뷰

KUNDUZ 2021. 4. 30. 14:54
728x90

회원가입을 위해 만들었던 sign-up.html을 수정합니다.

 

<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Sign-up Page</title>
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
	rel="stylesheet">
<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/beaver.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" sec:authorize="!isAuthenticated()"><a
					class="nav-link" th:href="@{/login}">로그인</a></li>
				<li class="nav-item" sec:authorize="!isAuthenticated()"><a
					class="nav-link" th:href="@{/sign-up}">가입</a></li>
				<li class="nav-item" sec:authorize="isAuthenticated()"><a
					class="nav-link" th:href="@{/notifications}"> <i
						th:if="${!hasNotification}" class="fa fa-bell-o"
						aria-hidden="true"></i> <span class="text-info"
						th:if="${hasNotification}"><i class="fa fa-bell"
							aria-hidden="true"></i></span>
				</a></li>
				<li class="nav-item" sec:authorize="isAuthenticated()"><a
					class="nav-link btn btn-outline-primary" th:href="@{/new-study}">
						<i class="fa fa-plus" aria-hidden="true"></i> 스터디 개설
				</a></li>
				<li class="nav-item dropdown" sec:authorize="isAuthenticated()">
					<a class="nav-link dropdown-toggle" href="#" id="userDropdown"
					role="button" data-toggle="dropdown" aria-haspopup="true"
					aria-expanded="false"> <svg
							th:if="${#strings.isEmpty(account?.profileImage)}"
							th:data-jdenticon-value="${#authentication.name}" width="24"
							height="24" class="rounded border bg-light"></svg> <img
						th:if="${!#strings.isEmpty(account?.profileImage)}"
						th:src="${account.profileImage}" width="24" height="24"
						class="rounded border" />
				</a>
					<div class="dropdown-menu dropdown-menu-sm-right"
						aria-labelledby="userDropdown">
						<h6 class="dropdown-header">
							<span sec:authentication="name">Username</span>
						</h6>
						<a class="dropdown-item"
							th:href="@{'/profile/' + ${#authentication.name}}">프로필</a> <a
							class="dropdown-item">스터디</a>
						<div class="dropdown-divider"></div>
						<a class="dropdown-item" href="#" th:href="@{'/settings/profile'}">설정</a>
						<form class="form-inline my-2 my-lg-0" action="#"
							th:action="@{/logout}" method="post">
							<button class="dropdown-item" type="submit">로그아웃</button>
						</form>
					</div>
				</li>
			</ul>
		</div>
	</nav>

	<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">
				<small class="d-block mb-3 text-muted">&copy; 2020</small>
			</div>
		</footer>
	</div>
	<script
		src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js"></script>
		<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>
</body>
</html>

 

위의 코드를 통해 회원가입 뷰를 제작할 수 있다.

 

위의 코드를 제작하면서 th라는 타임 리프를 사용해 주었는데

 

<html lang="en" xmlns:th="https://www.thymeleaf.org">

위와 같은 코드를 추가해 주어야 타임리프를 사용할 수 있다.

 

그리고 위의 코드를 작성하고 실행을 해보면 아래와 같은 에러를 볼 수 있다.

 

 

이러한 에러가 발생하는 이유는 아직 SignUpForm이라는 컨트롤러가 만들어져 있지 않고, DTO도 만들어져 있지 않아서 이 두가지를 추가해 주어야 한다.

 

먼저, DTO에 SignUpForm을 추가해 준다.

위의 이미지는 현재까지의 구성이다.

 

그리고 아래와 같이 DTO를 추가해 준다.

package com.studyolle.repository.dto;

import lombok.Data;

@Data
public class SignUpForm {
	private String nickname;
	private String email;
	private String password;
	
}

 

그리고 컨트롤러를 추가해 준다.

 

package com.studyolle.account.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.studyolle.repository.dto.SignUpForm;

@Controller
public class AccountController {
	
	@GetMapping("sign-up")
	public String signUpForm(Model model) {
		model.addAttribute("signUpForm",new SignUpForm());
		return "account/sign-up";
	}
	
}

 

이전에 작성되어있는 코드에서 위와같이 변경을 해주고 다시 실행을 하게되면 문제없이 실행이 되는것을 확인할 수 있을 것이다.

 

위와 같이 실행이 되는 것을 확인할 수 있다.

 

하지만 위에 보면 이미지가 깨져서 나오는 것을 볼 수 있다.

 

 

네트워크에서 확인해 보면 이미지를 불러 올때 403 에러가 나는 것을 확인할 수 있다.

 

이러한 문제가 발생하는 이유는 SpringSecurity에서 로컬에 있는 이미지를 불러오는 것이 막아 불러올 수 없는 것이다. 그래서 SecurityConfig에 이러한 문제가 발생하지 않도록 예외 처리를 해주어야 한다.

 

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .mvcMatchers("/node_modules/**")
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }

위와 같은 코드를 SecurityConfing.java에 상속을 시켜주면 위와 같은 에러를 막고 이미지를 불러오게 된다.

 

package com.studyolle.account.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
@AutoConfigureMockMvc
public class AccountControllerTest {

	@Autowired private MockMvc mockMvc;
	
	@DisplayName("회원 가입 화면 보이는지 테스트")
	@Test
	void signUpForm() throws Exception {
		mockMvc.perform(get("/sign-up"))
		.andDo(print())
		.andExpect(status().isOk())
		.andExpect(view().name("account/sign-up"))
		.andExpect(model().attributeExists("signUpForm"));
	}
}

 

 

위의 코드는 테스트코드이다. 위와같이 추가를 통해 테스트를 해볼 수 있다.

 

 

 

 

 

728x90
Comments