[스프링 부트] 게시판 만들기 프로젝트 - 10. 회원가입 및 로그인 화면

2023. 4. 19. 23:45스프링부트 프로젝트

1. HTML

HTML(HyperText Markup Language)은 웹 페이지를 만들기 위한 마크업 언어로, 웹 페이지의 구조와 콘텐츠를 정의

 

△ html 구성

2. home.html

처음 보여지는 홈 화면으로 로그인 전과 로그인 후가 상단 nav바의 상태가 다름

<!DOCTYPE html>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head th:replace="board/fragments/header">
</head>
<!-- NAV -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
	<div class="container">
		<a th:href="@{/board/home.do}">
			<h3 class="navbar-brand">Board</h3>
		</a>
		<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
			data-bs-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">
			<div class="navbar-nav ms-auto mb-2 mb-lg-0">
				<th:block th:if="${loginId == null}">
					<div class="nav-item">
						<a class="btn btn-secondary" th:href="@{/board/home.do}">홈</a>
					</div>
					<div class="nav-item">
						<a class="btn btn-secondary mx-1" th:href="@{/board/register.do}">회원가입</a>
					</div>
					<div class="nav-item">
						<a class="btn btn-secondary" th:href="@{/board/login.do}">로그인</a>
					</div>
				</th:block>
				<th:block th:if="${loginId != null}">
					<div class="nav-item">
						<a class="btn btn-secondary" th:href="@{/board/mypost.do}">내가
							쓴 글</a>
					</div>
					<div class="nav-item">
						<a class="btn btn-secondary mx-1" th:href="@{/board/write.do}">글쓰기</a>
					</div>
					<div class="nav-item">
						<a class="btn btn-secondary " th:href="@{/board/logout.do}">로그아웃</a>
					</div>
				</th:block>
			</div>
		</div>
	</div>
</nav>
<!-- NAV 끝 -->
<body>

	<header class="py-5 bg-light border-bottom mb-4">
		<div class="container">
			<div class="text-center my-5">
				<h1 class="fw-bolder">Welcome to Board Home!</h1>
			</div>
		</div>
	</header>
	<!-- Page content-->
	<div class="container">
		<div class="row">
			<!-- Blog entries-->
			<div class="col-lg-8">
				<!-- 게시물 리스트 -->
				<div class="card mb-4" th:if="${not #lists.isEmpty( boardList )}"
					th:each="row : ${boardList}">
					<a th:href="@{/board/view.do(postno=${row.postno})}">
						<div class="card-body p-3">
							<div class="card-title fs-1 " th:text="${row.title}"></div>
							<div class="card-content mt-4 mb-3 "></div>
							<div class="card-id fs-5 mb-3 " th:text="${row.id}"></div>
							<span class="small text-muted"
								th:text="${#dates.format(row.wrtdate, 'yyyy-MM-dd' )}"></span> 
							<!-- <span class="small text-muted" th:text="${row.commentCnt}"></span> -->
						</div>
					</a>
				</div>
				<div th:unless="${not #lists.isEmpty( boardList )}">
					<h2 class="my-5 text-center">게시글이 없습니다.</h2>
				</div>

				<!-- Pagination-->
				<nav aria-label="Pagination"
					th:if="${not #lists.isEmpty( boardList )}">
					<hr class="my-0" />
					<ul class="pagination justify-content-center my-4">
						<th:block th:if="${pr.startPage > pr.pagePerBlock}">
							<li class="page-item"><a
								th:href="@{/board/home.do(pageNum=1 )}"> <i
									class="fs-3 bi bi-caret-left-fill"></i>
							</a></li>
							<li class="page-item"><a
								th:href="@{/board/home.do(pageNum=${pr.startPage}-1) }"> <i
									class="fs-3 bi bi-caret-left"></i>
							</a></li>
						</th:block>
						<li class="page-item"
							th:each="pNum : ${#numbers.sequence(pr.startPage, pr.endPage)}"
							th:class="${pr.page == pNum} ? 'active-btn' : 'non-active-btn'">
							<a class="page-link" th:href="@{/board/home.do(pageNum=${pNum})}"
							th:text="${pNum}" name=pageNum></a>
						</li>
						<th:block th:if="${pr.endPage < pr.totalPage}">
							<li class="page-item"><a
								th:href="@{/board/home.do(pageNum=${pr.endPage+1})}"> <i
									class="fs-3 bi bi-caret-right"></i>
							</a></li>
							<li class="page-item"><a
								th:href="@{/board/home.do(pageNum=${pr.totalPage})}"> <i
									class="fs-3 bi bi-caret-right-fill"></i>
							</a></li>
						</th:block>
					</ul>
				</nav>
			</div>

			<!-- Side widgets-->
			<div class="col-lg-4">
				<!-- Search widget-->
				<div class="card mb-5">
					<form action="/board/home.do">
						<div class="card-header">검색</div>
						<div class="card-body">
							<div class="input-group">
								<select class="mx-1" name="option">
									<option value="all">전체</option>
									<option value="title">제목</option>
									<option value="content">내용</option>
									<option value="id">작성자</option>
								</select> <input class="form-control mx-1" name="keyword" type="text"
									placeholder="검색어를 입력하세요..." aria-describedby="button-search" />
								<button class="btn btn-primary" type="submit">
									<i class="bi bi-search"></i>
								</button>
							</div>
						</div>
					</form>
				</div>
			</div>
		</div>
	</div>
	<footer th:replace="board/fragments/footer "> </footer>
	<script type="text/javascript">
	const dropdownElementList = document.querySelectorAll('.dropdown-toggle')
	const dropdownList = [...dropdownElementList].map(dropdownToggleEl => new bootstrap.Dropdown(dropdownToggleEl))
	</script>
</html>

△ home.html

 

 

△ 로그인 전 home 화면

 

△ 로그인 후 home 화면

 

 

3. register.html

회원가입 화면

<!DOCTYPE html>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head th:replace="board/fragments/header">
</head>
<!-- NAV -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
	<div class="container">
		<a th:href="@{/board/home.do}">
			<h3 class="navbar-brand">Board</h3>
		</a>
		<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
			data-bs-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 ms-auto mb-2 mb-lg-0">
				<th:block th:if="${loginId == null}">
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/home.do}">홈</a></li>
					<li class="nav-item"><a class="btn btn-secondary mx-1"
						th:href="@{/board/register.do}">회원가입</a></li>
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/login.do}">로그인</a></li>
				</th:block>
				<th:block th:if="${loginId != null}">
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/home.do}">홈</a></li>
					<li class="nav-item"><a class="btn btn-secondary mx-1"
						th:href="@{/board/write.do}">글쓰기</a></li>
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/logout.do}">로그아웃</a></li>
				</th:block>
			</ul>
		</div>
	</div>
</nav>
<!-- NAV 끝 -->
<body>


	<h1 class="text-center mt-5 mb-4">회원 가입</h1>

	<form name="addMember" method="post" action="/board/addMember.do" onsubmit="return passwordCheck()">
		<div id="app" class="text-center">
			<div>
				<label for="inp-type-2" class="control-label">아이디</label>
				<div class="idbox mb-3">
					<input type="text" id="id" name="id" placeholder="아이디" size="20"
						autofocus required/>
						<div id="msg-box"><span name="msg" id="msg" th:text="${msg}"style="color: red;"></span></div>
				</div>
			</div>
			<div>
				<label for="inp-type-2" class="control-label">비밀번호</label>
				<div class="pwbox mb-3">
					<input type="password" id="pw" name="pw" placeholder="비밀번호" required />
				</div>
			</div>
			<div>
				<div class="pwbox mb-3">
					<input type="password" id="pwCheck" name="pwCheck" placeholder="비밀번호 확인" required/>
				</div>
			</div>
			<div>
				<label for="inp-type-2" class="control-label">이름</label>
				<div class="namebox mb-5">
					<input type="text" id="name" name="name" placeholder="이름" size="20" required/>
				</div>
			</div>
			<div class="btn_wrap text-center mb-5">
				<button class="btn btn-primary" type="submit" id="register" value="회원가입">회원가입</button>
				<a th:href="@{/board/home.do}" class="btn btn-secondary">뒤로가기</a>
			</div>
		</div>
	</form>
</body>
<footer th:replace="board/fragments/footer "> </footer>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
	function passwordCheck() {
	    var pw = document.getElementById('pw').value;
	    var pwCheck = document.getElementById("pwCheck").value;
	    if (pw != pwCheck) {
	    	alert("비밀번호가 일치하지 않습니다.");
	        return false;
	    } else {
	        return true;
	    }
	}
</script>
</html>

△ register.html

 

△ 회원가입 화면

 

△ 중복된 아이디가 있을 경우                                                                    △비밀번호 확인이 일치하지 않는 경우

 

4. login.html

로그인 화면

<!DOCTYPE html>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head th:replace="board/fragments/header">
</head>
<!-- NAV -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
	<div class="container">
		<a th:href="@{/board/home.do}">
			<h3 class="navbar-brand">Board</h3>
		</a>
		<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
			data-bs-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 ms-auto mb-2 mb-lg-0">
				<th:block th:if="${loginId == null}">
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/home.do}">홈</a></li>
					<li class="nav-item"><a class="btn btn-secondary mx-1"
						th:href="@{/board/register.do}">회원가입</a></li>
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/login.do}">로그인</a></li>
				</th:block>
				<th:block th:if="${loginId != null}">
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/home.do}">홈</a></li>
					<li class="nav-item"><a class="btn btn-secondary mx-1"
						th:href="@{/board/write.do}">글쓰기</a></li>
					<li class="nav-item"><a class="btn btn-secondary"
						th:href="@{/board/logout.do}">로그아웃</a></li>
				</th:block>
			</ul>
		</div>
	</div>
</nav>
<!-- NAV 끝 -->
<body>

	<h1 class="text-center mt-5">LOGIN</h1>
	<div class="card">
		<div class="card-body">
			<form name="login" method="post" action="/board/run.do" >
				<div id="app" class="text-center">
					<div>
						<label for="inp-type-2" class="control-label">아이디</label>
						<div class="idbox mb-3">
							<input type="text" id="id" name="id" placeholder="아이디" size="20" autofocus required/>
						</div>
					</div>
					<div>
						<label for="inp-type-2" class="control-label" >비밀번호</label>
						<div class="pwbox mb-3">
							<input type="password" id="pw" name="pw" placeholder="비밀번호" size="20" required/>
						</div>
					</div>
					<div class="msg-box mb-5"><span name="msg" id="msg" th:text="${msg}"style="color: red;"></span></div>
					<div class="btn_wrap text-center mb-5">
						<button class="btn btn-primary" type="submit" id="login" value="로그인">로그인</button>
						<a th:href="@{/board/home.do}" class="btn btn-secondary">HOME</a>
					</div>
				</div>
			</form>
		</div>
	</div>
</body>
<footer th:replace="board/fragments/footer "> </footer>
<script type="text/javascript" th:inline="javascript">

</script>
</html>

△ login.html

 

 

△ 로그인 화면

 

△ 비밀번호를 틀린 경우                                                                               △ 아이디가 존재하지 않는 경우

 

△ 로그인 성공 시 alert창