๐ Spring Boot Project ์์ฑ
๐พ Header / Login / Register Page
๐ฆ PROJECT ์์ฑ โ stussy.clone.project spring initializr
Settings - Build, Execution, Deployment
- Build Tools - Maven โก๏ธ Maven Home Path : Bundled
- Compiler - โ
Build Project Automatically
application.yml ๋ก ๋ฐ๊พธ๊ณ
server - port ,
spring - mvc(static-path-pattern) , datasource(driver, url, id, pw) ์ค์ .
โป database driver โ External Libraries ์์ ์ฌ์ฉํ๋ org.db.jdbc:db ยท ยท ยท ํ์ผ์ ์ฐพ์์ ๋ฃ์ด์ค๋ค.
MyBatis
pom.xml โ dependency ์ถ๊ฐ
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
ย ย ย ย ๋ง์ด๋ฐํฐ์ค mappers ์ค์ (xml ํ์ผ์ ๋ถ์ฌ๋ฃ๊ธฐ)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
โซ๏ธ application.yml
server:
port: 8000
spring:
mvc:
static-path-pattern: /static/**
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb:// . . . :3306/ . . . ?allowMultiQueries=true
# allowMultiQueries=true ; ์๋ธ์ฟผ๋ฆฌ ๊ฐ๋ฅํ๋๋ก ํ๋ ์ต์
username: . . .
password: . . .
security:
user:
name: admin
password: . . .
mybatis:
mapper-locations:
- /mappers/*.xml
๐ฆ ๊ธฐ๋ณธ ํจํค์ง ์ค์
java - com . . .
> config
> controller
ย ย ย > api
> domain
> dto
> repository
> service
resources
> mappers
> static
ย ย ย ย > css
ย ย ย ย > js
> templates
โซ๏ธ header
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account</title>
<!-- โญ GoogleFont -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/header.css">
<link rel="stylesheet" href="/static/css/account.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<div id="container">
<header>
<div class="header-container">
<div class="header-flex">
<div class="header-left-container">
<div class="logo-container">
<img class="site-logo" src="/static/images/logo/site_logo.png" alt="site_logo">
</div>
<div class="menu-container">
<div class="site-nav-menu">
<a href="#ALL" class="site-nav-parent">SHOP</a>
</div>
</div>
</div>
<div class="header-right-container">
<button class="site-header-button">SEARCH</button>
<button class="site-header-button">BAG(<span class="bac-count">1</span>)</button>
<button class="site-header-button"><img class="korea-img" src="/static/images/logo/korea_img.PNG" alt="korea"></button>
</div>
</div>
</div>
<nav class="nav-invisible">
<div class="site-nav-mega">
<a href="/collections/new-arrivals" class="site-nav-child-link">NEW</a>
<a href="/collections/denim" class="site-nav-child-link">DENIM</a>
<a href="/collections/tees" class="site-nav-child-link">TEES</a>
<a href="/collections/sweats" class="site-nav-child-link">SWEATS</a>
<a href="/collections/overdyed" class="site-nav-child-link">OVERDYED</a>
<a href="/collections/pants" class="site-nav-child-link">PANTS</a>
<a href="/collections/shorts" class="site-nav-child-link">SHORTS</a>
<a href="/collections/outerwear" class="site-nav-child-link">OUTERWEAR</a>
<a href="/collections/knits" class="site-nav-child-link">KNITS</a>
<a href="/collections/tops-shirts" class="site-nav-child-link">TOPS & SHIRTS</a>
<a href="/collections/headwear" class="site-nav-child-link">HEADWEAR</a>
<a href="/collections/accessories" class="site-nav-child-link">ACCESSORIES</a>
<a href="/collections/all" class="site-nav-child-link">ALL</a>
</div>
<div class="site-nav-mega">
<a href="/account/login" class="site-nav-child-link">ACCOUNT</a>
<a href="/pages/customer-support" class="site-nav-child-link">CONTACT</a>
<a href="/blogs/features" class="site-nav-child-link">FEATURES</a>
<a href="/blogs/chapters" class="site-nav-child-link">CHAPTERS</a>
</div>
</nav>
<script src="/static/js/header.js"></script>
</header>
<main>
<!-- ๋ก๊ทธ์ธ login / ํ์๊ฐ์
register -->
</main>
<footer>
</footer>
</div>
<!--โญ login โก๏ธ --> <script src="/static/js/login.js"></script>
<!--โญ register โก๏ธ --> <script src="/static/js/register.js"></script>
</body>
</html>
โป header - js ยท css ์๋ต . . . ย
โซ๏ธ login.html
. . .
<main>
<div class="login">
<h1 class="login-title">ACCOUNT</h1>
<div class="login-container">
<section class="login-new">
<h2 class="login-subheader">ํ์๊ฐ์
</h2>
<p class="login-content">ํ์๊ฐ์
์ ํ์๋ฉด, ์ฃผ๋ฌธ ์กฐํ์ ๊ฐ์ธ์ ๋ณด ๊ด๋ฆฌ ๋ฐ ๋น ๋ฅธ ์ฒดํฌ์์ ๋ฑ ๋ค์ํ ํํ์ ๋๋ฆฌ์ค ์ ์์ต๋๋ค.</p>
<button type="button" class="black-button login-button">์ ๊ท๊ฐ์
</button>
</section>
<section class="login-current">
<โญform action="/account/login" method="post">
<h2 class="login-subheader">๋ก๊ทธ์ธ</h2>
<!-- ๋ก๊ทธ์ธ ์์ฒญ ์คํจ ์ Error ๋ฅผ ๋์์ค -->
<div class="account-errors">
<ul>
<li th:text="${error}"></li>
</ul>
</div>
<input type="email" class="login-input" name="email" placeholder="์ด๋ฉ์ผ" required>
<input type="password" class="login-input" name="password" placeholder="๋น๋ฐ๋ฒํธ" required>
<a href="" class="login-show-recover">๋น๋ฐ๋ฒํธ๋ฅผ ์์ผ์
จ๋์?</a>
<button ๐type="submit" class="black-button login-button">๋ก๊ทธ์ธ</button>
</form>
</section>
</div>
</div>
</main>
. . .
<form> action : ํผ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ URL ์ฃผ์
ํ์๋ฆฌํ๋ View Template(๋ทฐ ํ ํ๋ฆฟ)์ ์ผ์ข ์ผ๋ก, ์ปจํธ๋กค๋ฌ๊ฐ ์ ๋ฌํ๋ Model(Key, Value) ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ๋์ ์ผ๋ก ํ๋ฉด์ ๋์์ค๋ค.
โซ๏ธ login.js
// ์ ๊ท๊ฐ์
const registerGoButton = document.querySelectorAll(".login-button")[0];
// ๋ก๊ทธ์ธ
const loginButton = document.querySelectorAll(".login-button")[1];
registerGoButton.onclick = () => {
location.href = "/account/register";
}
loginButton.onclick = () => { // form ์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๋ด์์ ์ ์ถ
const loginform = document.querySelector("form");
}
โซ๏ธ register.html
. . .
<main>
<div class="login">
<h1 class="login-title">ACCOUNT</h1>
<div class="login-container">
<section class="login-current">
<h2 class="login-subheader">์ ๊ท ํ์๊ฐ์
</h2>
<!-- ValidationError ๋ฅผ ๋์์ค -->
<div class="account-errors errors-invisible">
<ul>
</ul>
</div>
<div class="login-name">
<div class="login-half">
<input type="text" class="login-input" placeholder="์ด๋ฆ">
</div>
<div class="login-half">
<input type="text" class="login-input" placeholder="์ฑ">
</div>
</div>
<input type="email" class="login-input" placeholder="์ด๋ฉ์ผ">
<input type="password" class="login-input" placeholder="๋น๋ฐ๋ฒํธ">
<button type="button" class="black-button login-button">ํ์ธ</button>
<a href="/account/login" class="login-return">๋ก๊ทธ์ธ์ผ๋ก ๋์๊ฐ๊ธฐ</a>
</section>
</div>
</div>
</main>
. . .
โซ๏ธ register.js
const registerButton = document.querySelector(".login-button");
const registerInputs = document.querySelectorAll(".login-input");
registerButton.onclick = () => {
let registerInfo = {
lastName: registerInputs[0].value,
firstName: registerInputs[1].value,
email: registerInputs[2].value,
password: registerInputs[3].value
}
$.ajax({
async: false,
type: "post",
url: "/api/account/register",
โ๏ธ contentType: "application/json",
โ๏ธ data: JSON.stringify(registerInfo), // JSON ์ผ๋ก ๋ณํํด์ ๋ณด๋
dataType: "json",
success: (response) => { // ํ์๊ฐ์
์ฑ๊ณต ์ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋
location.replace("/account/login");
},
error: (error) => {
console.log(error);
validationError(error.responseJSON.data);
}
});
}
function validationError(error) {
const accountErrors = document.querySelector(".account-errors");
const accountErrorList = accountErrors.querySelector("ul");
const errorValues = Object.values(error); // โก๏ธ error์ value๋ค์ ๋ฐฐ์ด๋ก ๋ฆฌํดํจ.
accountErrorList.innerHTML = "";
errorValues.forEach((value) => {
accountErrorList.innerHTML += `
<li>${value}</li>
`;
});
// โป error ๊ฐ ์์ ๊ฒฝ์ฐ์๋ง ๋ฉ์๋๊ฐ ์คํ๋จ.
// ๊ธฐ๋ณธ์ ์ผ๋ก ๋ณด์ด์ง ์๋๋ก ํ๊ณ , ๋ฉ์๋๊ฐ ์คํ ๋๋ ๊ฒฝ์ฐ ๋ณด์ด๋๋ก ํจ. (css ์ค์ )
accountErrors.classList.remove("errors-invisible");
}
โป css ์๋ต . . . ย
์ฐธ๊ณ ๐ง
- ์คํ๋ง ๊ฐ์ด๋ ์ฐธ๊ณ ์ฌ์ดํธ ๋ชจ์
- ์นด์นด์ค ์ฃผ์ API
- ๋ก์ปฌ(Local) API ๊ตฌํ ๋ฐฉ๋ฒ
๐ข ์๊ฐ ๐ค
์ด๋ฒ์ฃผ๋ ์ ๋ฐ๋นด๋ค