새로운 프로젝트 bbs를 만들자.
2.6.8 변경 후
세 가지 체크해주고
Spring Boot DevTools
Spring Web
Thymeleaf
우측 상단에 Edit Configurations 클릭
Build project automatically 클릭
두개 클릭해준다.
그럼 이 상태에서 OK
package dev.dmchoi.bbs.controllers 만들어주고
UserController 클래스 만들어 @Controller, @RequestMapping 추가
내용 추가
pexels 에서 video 다운로드 후
resources/static 에서
user 디렉토리 > resources 디렉토리 안에 background-video.mp4파일 추가
templates에 user 디렉토리 > login.html에 내용 추가
아무 내용이나 입력하고 컨트롤+R을 누르지 않고 브라우저에서 새로고침만 하여도 이렇게 글이 뜬다.
html을 입력하자.
이미지 파일 소스를 불러오기 위해 링크 연결을 해야한다.
커맨드 + 세미콜론을 누르면 나오는 창에서 중간 부분에 + 클릭 후
mp4가 들어있는 링크를 적어주고 OK 후 apply
추가되었다면 OK
그럼 login.html에 링크 연결이 된다.
<video autuplay loop muted th:src="@{/user/resources/videos/background-video.mp4}"></video>
그 다음 내용 추가
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>로그인</title>
<link rel="stylesheet" th:href="@{/user/resources/stylesheets/login.css}">
</head>
<body>
<div class="background">
<div class="cover"></div>
<video autoplay loop muted class="video" th:src="@{/user/resources/videos/background-video.mp4}"></video>
</div>
<form class="login-form">
<h1 class="title">로그인</h1>
<div class="input-container">
<label class="label">
<span class="text">이메일</span>
<input class="input" maxlength="50" name="email" placeholder="이메일" type="email">
</label>
<label class="label">
<span class="text">비밀번호</span>
<input class="input" maxlength="100" name="password" placeholder="비밀번호" type="password">
</label>
<!-- <label class="label">-->
<!-- <input class="input" name="remember" type="checkbox">-->
<!-- <span class="text">이메일 기억하기</span>-->
<!-- </label>-->
<input class="button" type="submit" value="로그인">
<ul class="menu">
<li class="item">
<span class="text">이메일 혹은 비밀번호를 분실하셨나요?</span>
<a class="link" href="#">계정 복구</a>
</li>
<li class="item">
<span class="text">아직 계정이 없으신가요?</span>
<a class="link" href="#">회원 가입</a>
</li>
</ul>
</div>
</form>
</body>
</html>
video태그 설명
autoplay : 비디오가 로드 되면 자동 실행
loop : 비디오 반복 재생
muted : 비디오의 소리 음소거 결정. 쓰면 음소거됨
css 내용도 추가
@charset "UTF-8";
:root {
--global-back-color-field: 255, 255, 255;
--global-back-color-dark: 0, 5, 10;
--global-back-color-light: 245, 250, 255;
--global-border-color-field: 220, 225, 230;
--global-text-color-active: 0, 0, 0;
--global-text-color-dark: 245, 250, 255;
--global-text-color-inactive: 120, 125, 130;
--global-text-color-inverted: 255, 255, 255;
--palette-blue: 80, 160, 240;
}
h1, h2, h3, h4, h5, h6 {
font: unset;
margin-block-start: unset;
margin-block-end: unset;
margin-inline-start: unset;
margin-inline-end: unset;
padding-inline-start: unset;
}
input {
appearance: none;
background-color: unset;
border: none;
border-image: none;
color: unset;
font: unset;
outline: none;
padding: unset;
}
ol, ul {
list-style-type: none;
margin-block-start: unset;
margin-block-end: unset;
margin-inline-start: unset;
margin-inline-end: unset;
padding-inline-start: unset;
}
body > .background {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed;
z-index: 0;
}
body > .background > .cover {
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(0.25rem);
background-color: rgba(20, 25, 30, 80%);
position: absolute;
z-index: 1;
}
body > .background > .video {
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
z-index: 0;
}
body > .login-form {
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
z-index: 1;
}
body > .login-form > .title {
background-color: rgb(var(--global-back-color-dark));
color: rgb(var(--global-text-color-dark));
cursor: default;
font-size: 2rem;
font-weight: lighter;
padding: 1.25rem 2.25rem;
user-select: none;
-webkit-user-select: none;
}
body > .login-form > .input-container {
align-content: stretch;
background-color: rgb(var(--global-back-color-light));
color: rgb(0, 5, 10);
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 1.75rem 2.25rem;
}
body > .login-form > .input-container > :not(:last-child) {
margin-bottom: 1rem;
}
body > .login-form > .input-container > .label {
align-content: stretch;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
body > .login-form > .input-container > .label > .text {
color: rgb(var(--global-text-color-inactive));
font-size: 0.75rem;
margin-bottom: 0.375rem;
}
body > .login-form > .input-container > .label > .input {
background-color: rgb(var(--global-back-color-field));
border: 0.0625rem solid rgb(var(--global-border-color-field));
border-radius: 0.25rem;
color: rgb(var(--global-text-color-inactive));
padding: 0.75rem 1rem;
transition-duration: 250ms;
transition-property: border, color;
transition-timing-function: ease;
}
body > .login-form > .input-container > .label > .input:focus {
border: 0.0625rem solid rgb(var(--palette-blue));
color: rgb(var(--global-text-color-active));
}
body > .login-form > .input-container > .button {
background-color: rgb(var(--palette-blue));
border-radius: 0.25rem;
color: rgb(var(--global-text-color-inverted));
cursor: pointer;
padding: 0.625rem 1rem;
}
body > .login-form > .input-container > .button:hover {
filter: brightness(110%);
}
body > .login-form > .input-container > .button:active {
filter: brightness(90%);
}
body > .login-form > .input-container > .menu {
font-size: 0.8rem;
}
body > .login-form > .input-container > .menu > .item + .item {
margin-top: 0.25rem;
}
body > .login-form > .input-container > .menu > .item > .text {
color: rgb(var(--global-text-color-inactive));
}
body > .login-form > .input-container > .menu > .item > .link {
color: rgb(var(--palette-blue));
text-decoration: none;
}
@media screen and (max-width: 40rem) {
body > .login-form {
width: 95vw;
}
}
font-awesome에 들어가 회원가입해주자.
(근데 얘는 텍스트다.)
그리고 kit -> 소스복사
html에 js소스링크 복붙해준다
원하는 아이콘 등을 검색하여 html링크를 끌고 올 수 있다.
<i class="icon fa-solid fa-envelope"></i>
<i class="icon fa-solid fa-key"></i>
이메일과 비밀번호 icon을 추가할 것.
다음 resources에 scripts 디렉토리 생성 후 login.js 파일 만든다.
(바로 위 사진에서 확인 가능)
그리고 작성
html 추가
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>로그인</title>
<link rel="stylesheet" th:href="@{/user/resources/stylesheets/login.css}">
<script src="https://kit.fontawesome.com/0d51ab0f86.js" crossorigin="anonymous"></script>
<script th:src="@{/user/resources/scripts/login.js}" defer></script>
</head>
<body>
<div class="background">
<div class="cover"></div>
<video autoplay loop muted class="video" th:src="@{/user/resources/videos/background-video.mp4}"></video>
</div>
<form class="form login" id="login-form">
<div class="cover" rel="cover"></div>
<h1 class="title">로그인</h1>
<div class="input-container">
<label class="label">
<span class="text">이메일</span>
<input class="input" maxlength="50" name="email" placeholder="이메일" type="email">
<i class="icon fa-solid fa-envelope"></i>
</label>
<label class="label">
<span class="text">비밀번호</span>
<input class="input" maxlength="100" name="password" placeholder="비밀번호" type="password">
<i class="icon fa-solid fa-key"></i>
</label>
<input class="button blue" type="submit" value="로그인">
<ul class="menu">
<li class="item">
<span class="text">이메일 혹은 비밀번호를 분실하셨나요?</span>
<a class="link" href="#" data-link-mode="spa" data-link-func="recover">계정 복구</a>
</li>
<li class="item">
<span class="text">아직 계정이 없으신가요?</span>
<a class="link" href="#" data-link-mode="spa" data-link-func="register">회원 가입</a>
</li>
</ul>
</div>
</form>
<form class="form register aside" id="register-form">
<div class="cover visible" rel="cover"></div>
<h1 class="title">회원가입</h1>
<div class="input-container">
<label class="label">
<span class="text">이메일</span>
<input class="input" maxlength="50" name="email" placeholder="이메일" type="email">
<i class="icon fa-solid fa-envelope"></i>
</label>
<label class="label">
<span class="text">비밀번호</span>
<input class="input" maxlength="100" name="password" placeholder="비밀번호" type="password">
<i class="icon fa-solid fa-key"></i>
</label>
<label class="label">
<span class="text">비밀번호 재입력</span>
<input class="input" maxlength="100" name="password-check" placeholder="비밀번호 재입력" type="password">
<i class="icon fa-solid fa-key"></i>
</label>
<label class="label">
<span class="text">닉네임</span>
<input class="input" maxlength="10" name="nickname" placeholder="닉네임" type="text">
<i class="icon fa-solid fa-user"></i>
</label>
<label class="label">
<span class="text">연락처</span>
<input class="input" maxlength="11" name="contact" placeholder="연락처(- 없이)" type="text">
<i class="icon fa-solid fa-user"></i>
</label>
<input class="button blue" type="submit" value="회원가입">
<input class="button" type="button" value="돌아가기" data-link-mode="spa" data-link-func="registerCancel">
</div>
</form>
</body>
</html>
그리고 css 좀 더 수정
@charset "UTF-8";
:root {
--global-back-color-field: 255, 255, 255;
--global-back-color-dark: 0, 5, 10;
--global-back-color-light: 245, 250, 255;
--global-border-color-field: 220, 225, 230;
--global-text-color-active: 0, 0, 0;
--global-text-color-dark: 245, 250, 255;
--global-text-color-inactive: 120, 125, 130;
--global-text-color-inverted: 255, 255, 255;
--palette-blue: 80, 160, 240;
--palette-gray: 120, 125, 130;
}
h1, h2, h3, h4, h5, h6 {
font: unset;
margin-block-start: unset;
margin-block-end: unset;
margin-inline-start: unset;
margin-inline-end: unset;
padding-inline-start: unset;
}
input {
appearance: none;
background-color: unset;
border: none;
border-image: none;
color: unset;
font: unset;
outline: none;
padding: unset;
}
ol, ul {
list-style-type: none;
margin-block-start: unset;
margin-block-end: unset;
margin-inline-start: unset;
margin-inline-end: unset;
padding-inline-start: unset;
}
body {
width: 100%;
min-height: 100%;
margin: unset;
perspective: 50vw;
position: absolute;
}
body > .background {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed;
z-index: 0;
}
body > .background > .cover {
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(0.25rem);
background-color: rgba(20, 25, 30, 80%);
position: absolute;
z-index: 1;
}
body > .background > .video {
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
z-index: 0;
}
body > .form {
top: 50%;
left: 50%;
min-width: 25rem;
position: absolute;
transform: translate(-50%, -50%);
transition-duration: 500ms;
transition-property: opacity, transform;
transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
z-index: 1;
}
body > .form.login.aside {
transform: translate(calc(-50% + 2.5rem), -50%) rotateY(-10deg) scale(90%);
z-index: 0;
}
body > .form.register {
}
body > .form.register.aside {
opacity: 0;
pointer-events: none;
transform: translate(calc(-50% - 2.5rem), -50%) rotateY(10deg) scale(90%);
z-index: 0;
}
body > .form > .cover {
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(var(--global-back-color-dark), 90%);
opacity: 0;
pointer-events: none;
position: absolute;
transition-duration: 150ms;
transition-property: opacity;
transition-timing-function: linear;
z-index: 1;
}
body > .form > .cover.visible {
opacity: 1;
pointer-events: all;
}
body > .form > .title {
background-color: rgb(var(--global-back-color-dark));
color: rgb(var(--global-text-color-dark));
cursor: default;
font-size: 1.75rem;
font-weight: lighter;
padding: 1.125rem 2.25rem;
user-select: none;
-webkit-user-select: none;
}
body > .form > .input-container {
align-content: stretch;
background-color: rgb(var(--global-back-color-light));
color: rgb(0, 5, 10);
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 1.75rem 2.25rem;
}
body > .form > .input-container > :not(:last-child) {
margin-bottom: 1rem;
}
body > .form > .input-container > .label {
align-content: stretch;
display: flex;
flex-direction: column;
justify-content: flex-start;
position: relative;
}
body > .form > .input-container > .label > .text {
color: rgb(var(--global-text-color-inactive));
font-size: 0.75rem;
margin-bottom: 0.375rem;
}
body > .form > .input-container > .label > .input {
background-color: rgb(var(--global-back-color-field));
border: 0.0625rem solid rgb(var(--global-border-color-field));
border-radius: 0.25rem;
color: rgb(var(--global-text-color-inactive));
padding: 0.7rem 1rem 0.8rem 2.625rem;
transition-duration: 250ms;
transition-property: border, color;
transition-timing-function: ease;
}
body > .form > .input-container > .label > .input:focus {
border: 0.0625rem solid rgb(var(--palette-blue));
color: rgb(var(--global-text-color-active));
}
body > .form > .input-container > .label > .input:focus + .icon {
color: rgb(var(--global-text-color-active));
}
body > .form > .input-container > .label > .icon {
bottom: 1rem;
left: 1rem;
color: rgb(var(--global-text-color-inactive));
pointer-events: none;
position: absolute;
transition-duration: 250ms;
transition-property: color;
transition-timing-function: ease;
}
body > .form > .input-container > .button {
background-color: rgb(var(--palette-gray));
border-radius: 0.25rem;
color: rgb(var(--global-text-color-inverted));
cursor: pointer;
padding: 0.625rem 1rem;
}
body > .form > .input-container > .button.blue {
background-color: rgb(var(--palette-blue));
}
body > .form > .input-container > .button:hover {
filter: brightness(110%);
}
body > .form > .input-container > .button:active {
filter: brightness(90%);
}
body > .form > .input-container > .menu {
font-size: 0.8rem;
}
body > .form > .input-container > .menu > .item + .item {
margin-top: 0.25rem;
}
body > .form > .input-container > .menu > .item > .text {
color: rgb(var(--global-text-color-inactive));
}
body > .form > .input-container > .menu > .item > .link {
color: rgb(var(--palette-blue));
text-decoration: none;
}
@media screen and (max-width: 40rem) {
body > .form {
width: 95vw;
}
}
결과물을 확인해보자.
add(String)
지정한 클래스 값을 추가한다.
만약 추가하려는 클래스가 이미 존재한다면 무시.
remove(String)
지정한 클래스 값을 제거한다.
존재하지 않는 클래스라면? 에러 발생 X
contains(String)
지정한 클래스 값이 존재하는지 확인.
true, false 값을 반환.
replace(old, new)
old class를 new class로 대체
item(Number)
인덱스 값을 활용하여 클래스 값을 반환
some() 메서드는 배열 안의 어떤 요소라도 주어진 판별 함수를 통과하는지 테스트합니다.
참고: 빈 배열에서 호출하면 무조건 false를 반환합니다.
setAttribute()는 선택한 요소(element)의 속성(attribute) 값을 정합니다.