각진 세상에 둥근 춤을 추자
[CRUD] 1. 페이지 화면 구현 본문
1. 구현 화면 목록
- User: 로그인, 회원가입
- Board: 글 목록, 글 쓰기, 글 보기, 글 수정
(1) User - 로그인 화면
# 기능: 로그인, 아이디 찾기, 비밀번호 찾기, 회원가입, 소셜 로그인
▽ 로그인 화면 HTML 코드
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div id="wrapper">
<header>
<h3>Board System v1.0</h3>
<p>
<span>홍길동</span>님 반갑습니다.
<a href="#">[로그아웃]</a>
</p>
</header>
<main>
<div class="ly_login">
<h1>LOGIN</h1>
<div class="login_wrap">
<form class="loginForm">
<div>
<input class="idForm" type="text" name="uid" placeholder="아이디">
</div>
<div>
<input class="passForm" type="text" name="pass" placeholder="비밀번호">
</div>
<div class="rememberLogin">
<label>
<input type="checkbox" name="rememberMe" id="rememberMe">로그인 유지
</label>
</div>
<button class="btn btnLogin" type="submit">로그인</button>
</form>
<div class="login-sub">
<a href="#">아이디 찾기</a>
<div>|</div>
<a href="#">비밀번호 찾기</a>
<div>|</div>
<a href="#">회원가입</a>
</div>
<div class="login-sns">
<div>SNS 계정 간편 로그인/회원가입</div>
<div>
<a href="#"><img src="../img/kakao-talk.png" alt="카카오 로그인"></a>
<a href="#"><img src="../img/naver.png" alt="네이버 로그인"></a>
<a href="#"><img src="../img/google.png" alt="구글 로그인"></a>
</div>
</div>
</div>
</div>
</main>
<footer>
<p>ⓒCopyright by circle.co.kr</p>
</footer>
</div>
</body>
</html>
(2) User - 회원가입 화면
# 기능: 회원가입, 유효성 검사, chptcha(예정)
▽ 회원가입 화면 HTML 코드
더보기
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>register</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script src="/js/User/UserRegister.js" defer></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<body>
<div id="wrapper">
<header>
<h3>Board System v1.0</h3>
<p>
<span>홍길동</span>님 반갑습니다.
<a href="#">[로그아웃]</a>
</p>
</header>
<main>
<div class="ly_register">
<form th:action="@{/user/register}" method="post">
<div class="register-wrap">
<div class="join-form form1">
<div class="join-item id">
<div class="join-subTitle">아이디</div>
<input type="text" id="inputId" name="uid" placeholder="아이디">
</div>
<div class="join-item password">
<div class="join-subTitle">비밀번호</div>
<input type="password" id="inputPass1" placeholder="비밀번호">
</div>
<div class="join-item password2">
<div class="join-subTitle">비밀번호</div>
<input type="password" id="inputPass2" name="pass" placeholder="비밀번호 재입력">
</div>
</div>
<div class="join-error">
<div class="error-text" id="check_id1" style="display:none">
* 아이디: 6~12자의 영문, 숫자만 사용 가능합니다.
</div>
<div class="error-text" id="check_id2" style="display:none">
* 아이디: 필수 정보입니다.
</div>
<div class="error-text" id="check_pw" style="display:none">
* 비밀번호: 6~12자의 영문 소문자, 숫자만 사용 가능합니다.
</div>
<div class="error-text" id="check_pw2" style="display:none">
* 비밀번호: 필수 정보입니다.
</div>
<div class="error-text" id="check_pwc" style="display:none">
* 비밀번호: 동일한 비밀번호를 입력해야합니다.
</div>
</div>
<div class="join-form form2">
<div class="join-item name">
<div class="join-subTitle">이름</div>
<input type="text" id="inputName" name="name" placeholder="이름">
</div>
<div class="join-item birth">
<div class="join-subTitle">생년월일</div>
<input type="text" id="inputBirth" name="birth" placeholder="생년월일 8자리">
</div>
<div class="join-item phone">
<div class="join-subTitle">휴대전화번호</div>
<input type="text" id="inputHp" name="hp" placeholder="휴대전화번호 -제외">
</div>
</div>
<div class="join-error">
<div class="error-text" id="check_name" style="display:none">
* 이름: 필수 정보입니다.
</div>
<div class="error-text" id="check_name2" style="display:none">
* 이름: 한글 또는 영문만 가능합니다.
</div>
<div class="error-text" id="check_birth" style="display:none">
* 생년월일: 필수 정보입니다.
</div>
<div class="error-text" id="check_birth2" style="display:none">
* 생년월일: 8자리 숫자만 입력가능합니다.
</div>
<div class="error-text" id="check_ph" style="display:none">
* 휴대전화번호: 필수 정보입니다.
</div>
<div class="error-text" id="check_ph2" style="display:none">
* 휴대전화번호: 11자리 숫자만 입력가능합니다.
</div>
</div>
<div class="join-form form3">
<div class="join-item postcode">
<div class="join-subTitle">주소</div>
<div class="join-contents">
<input type="text" placeholder="우편번호" id="btnPostCode" name="zip" readonly>
<input type="text" placeholder="기본주소" id="addr1" name="addr1" readonly>
<input type="text" placeholder="상세주소" id="addr2" name="addr2">
</div>
</div>
</div>
<div class="join-error">
<div class="error-text" id="check_post" style="display:none">
* 우편번호: 필수 정보입니다.
</div>
</div>
<div class="join-btn">
<button class="btn btnRegister" id="btnRegister" type="submit">회원가입</button>
</div>
</div>
</form>
</div>
</main>
<footer>
<p>ⓒCopyright by circle.co.kr</p>
</footer>
</div>
</body>
</html>
(3) Board - 글 목록 화면
# 기능: 글 목록, 페이지, 검색
▽ 글 목록 화면 HTML 코드
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>list</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div id="wrapper">
<header>
<h3>Board System v1.0</h3>
<p>
<span>홍길동</span>님 반갑습니다.
<a href="./user/login.html">[로그아웃]</a>
</p>
</header>
<main>
<div class="content">
<div class="search">
<select>
<option>제목</option>
<option>작성자</option>
</select>
<input type="text" placeholder="검색어를 입력해주세요">
<button class="btn search">검색</button>
</div>
<table>
<colgroup>
<col style="width: 5%;">
<col style="width: 5%;">
<col style="width: 55%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tr>
<th> </th>
<th>No</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회</th>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>1</td>
<td>title</td>
<td>writer</td>
<td>2023.12.28</td>
<td>2</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>1</td>
<td>title</td>
<td>writer</td>
<td>2023.12.28</td>
<td>2</td>
</tr>
</table>
<div class="pagination">
<a href="#" class="num prev">이전</a>
<a href="#" class="num current">1</a>
<a href="#" class="num">2</a>
<a href="#" class="num next">다음</a>
</div>
<div class="post_btns">
<a href="#" class="btn">글쓰기</a>
</div>
</div>
</main>
<footer>
<p>ⓒCopyright by circle.co.kr</p>
</footer>
</div>
</body>
</html>
(4) Board - 글 쓰기 화면
# 기능: 글 쓰기, 파일 첨부, 네이버 에디터
▽ 글 쓰기 화면 HTML 코드
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>write</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div id="wrapper">
<header>
<h3>Board System v1.0</h3>
<p>
<span>홍길동</span>님 반갑습니다.
<a href="#">[로그아웃]</a>
</p>
</header>
<main>
<div class="ly_write">
<div class="write-title">
<input type="text" placeholder="제목을 입력해주세요">
</div>
<div class="view-content">
<textarea></textarea>
</div>
<div class="view-file">
<input type="file">
</div>
</div>
<div class="post_btns">
<a href="#" class="btn">등록</a>
</div>
</main>
<footer>
<p>ⓒCopyright by circle.co.kr</p>
</footer>
</div>
</body>
</html>
(5) Board - 글 보기 화면
# 기능: 글 보기, 파일 다운로드, 댓글
▽ 글 보기 화면 HTML 코드
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>view</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div id="wrapper">
<header>
<h3>Board System v1.0</h3>
<p>
<span>홍길동</span>님 반갑습니다.
<a href="#">[로그아웃]</a>
</p>
</header>
<main>
<div class="ly_view">
<div class="view-head">
<div class="view-title">title</div>
<div class="view-info">
<div class="view-writer">writer</div>
<div class="view-info-sub">
<div class="view-date">2023.12.28 18:07</div>
<div class="view-count">조회 1</div>
</div>
</div>
</div>
<div class="view-content">content</div>
<div class="view-file">file</div>
</div>
<!-- 댓글 목록 -->
<div class="ly_reply">
<div class="reply-head">
<span>댓글</span>
<span>1</span>
</div>
<div class="reply-content">
<div class="reply-info">
<div class="reply-writer">이원정</div>
<div class="reply-date">2023.12.28 18:57</div>
</div>
<div class="reply-text">hi</div>
</div>
</div>
<!-- 댓글 쓰기 -->
<div class="ly_comment">
<div class="comment-writer">이원정</div>
<textarea placeholder="댓글을 남겨보세요"></textarea>
<div class="middle-btn">
<a href="#" class="btn btnRegister">등록</a>
</div>
</div>
<div class="post_btns">
<a href="#" class="btn">수정</a>
<a href="#" class="btn">삭제</a>
</div>
</main>
<footer>
<p>ⓒCopyright by circle.co.kr</p>
</footer>
</div>
</body>
</html>
[참고] 전체 CSS 코드
더보기
/* 태그 초기화 */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
border-collapse: collapse;
font: normal 12px '돋움';
}
ul, ol{list-style: none;}
a {text-decoration: none; color: #111;}
input,textarea {outline: none;}
#wrapper{
height: 100vh;
margin: 0 auto;
}
/* 헤더 */
header{
width: 100%;
height: 35px;
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 15px;
}
header h3 {
font-size: 15px;
font-weight: 500;
color: #777;
}
header p span {
margin-right: 3px;
font-weight: 500;
}
header p a:hover {
text-decoration: underline;
}
/* 메인 */
main {
padding: 50px;
}
/* 검색 */
.search {
padding: 16px 10px;
display: flex;
flex-direction: row;
justify-content: end;
gap: 5px;
}
.search select {
padding: 9px 12px;
border: 1px solid #d3d3d3;
}
.search input {
padding: 9px 12px;
border: 1px solid #d3d3d3;
width: 160px;
}
/* 글 목록 */
.content {
width: 100%;
height: 500px;
margin: 0 auto;
}
.content table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
.content table tr:nth-of-type(1) {
border-top: 2px solid #ebebea;
}
.content table th {
height: 40px;
background: #f9f9f8;
border-bottom: 1px solid #f2f2f2;
text-align: center;
}
.content table td {
height: 30px;
line-height: 30px;
border-bottom: 1px solid #f2f2f2;
padding-left: 12px;
padding-right: 18px;
}
.content table td:nth-of-type(1) {
text-align: center;
}
.content table td:nth-of-type(2) {
text-align: center;
}
.content table td:nth-of-type(4) {
text-align: center;
}
.content table td:nth-of-type(5) {
text-align: center;
}
.content table td:nth-of-type(6) {
text-align: center;
}
/* 페이징 */
.pagination {
width: 100%;
text-align: center;
margin: 30px 0;
}
.pagination .num {
border: 1px solid #d7d7d7;
background-color: #f2f2f2;
padding: 5px 10px;
}
.pagination .current {
border: 1px solid #6d6d6d;
background: #888;
color: #fff;
}
/* CRUD Button */
.post_btns {
width: 100%;
text-align: right;
margin: 30px 0;
}
.btn {
min-width: 56px;
padding: 9px 12px;
background: #eff0f2;
border: 1px solid #d3d3d3;
text-align: center;
}
.post_btns .btn:hover {
text-decoration: underline;
}
/* 푸터 */
footer {
position: absolute;
bottom: 0;
width: 100%;
height: 35px;
background-color: #f7f7f7;
border-top: 1px solid #ebebeb;
border-bottom: 1px solid #ebebeb;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
padding: 0 15px;
}
footer p {
color: #777;
}
/* view */
.ly_view {
border: 1px solid #ebebeb;
padding: 15px;
display: flex;
flex-direction: column;
}
.ly_view .view-head {
display: flex;
flex-direction: column;
align-content: flex-start;
flex-wrap: wrap;
}
.ly_view .view-head .view-title {
font-size: 26px;
width: 100%;
word-wrap: break-word;
word-break: break-word;
}
.ly_view .view-head .view-info {
width: 100%;
display: flex;
flex-direction: column;
align-content: flex-start;
flex-wrap: wrap;
margin-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #ebebeb;
}
.ly_view .view-head .view-info-sub {
display: flex;
flex-direction: row;
gap: 12px;
margin-top: 2px;
}
.ly_view .view-head .view-writer {
font-size: 13px;
font-weight: 600;
}
.ly_view .view-head .view-date {
font-size: 12px;
color: #979797;
}
.ly_view .view-head .view-count {
font-size: 12px;
color: #979797;
}
.view-content {
height: 400px;
overflow-y: auto;
padding: 10px;
font-size: 15px;
}
.view-file {
width: 100%;
border-top: 1px solid #ebebeb;
margin-top: 10px;
padding-top: 10px;
}
.ly_reply {
margin-top: 30px;
}
.reply-head {
display: flex;
flex-direction: row;
gap: 5px;
}
.reply-head span:nth-of-type(1) {
font-size: 13px;
}
.reply-head span:nth-of-type(2) {
font-size: 13px;
font-weight: 700;
}
.reply-content {
background: #f9f9fa;
padding: 20px 15px;
}
.reply-info {
display: flex;
flex-direction: row;
gap: 5px;
}
.reply-writer {
font-size: 13px;
font-weight: 600;
}
.reply-date {
font-size: 12px;
color: #979797;
}
.reply-text {
margin-top: 10px;
padding-left: 10px;
}
.ly_comment {
margin-top: 30px;
border: 1px solid #ebebeb;
padding: 20px;
display: flex;
flex-direction: column;
}
.comment-writer {
font-size: 13px;
font-weight: 600;
}
.ly_comment textarea {
margin-top: 10px;
font-size: 13px;
border: none;
}
.middle-btn {
width: 100%;
text-align: right;
text-align: right;
height: auto;
margin-top: 18px;
}
/* write */
.ly_write {
border: 1px solid #ebebeb;
padding: 15px;
display: flex;
flex-direction: column;
}
.write-title input {
background: #f5f6f8;
width: 100%;
height: 48px;
padding: 13px 16px;
border-radius: 10px;
border: none;
font-size: 16px;
}
/* login */
.ly_login {
padding: 60px 0;
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
flex-wrap: wrap;
}
.ly_login h1 {
font-size: 30px;
font-weight: 800;
}
.ly_login .login_wrap {
width: 300px;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 15px;
gap: 10px;
}
.ly_login .loginForm {
width: 100%;
}
.ly_login .idForm {
width: 100%;
border: 1px solid #d3d3d3;
border-radius: 5px 5px 0 0;
border-bottom: none;
font-size: 15px;
padding: 10px 8px;
}
.ly_login .passForm {
width: 100%;
border: 1px solid #d3d3d3;
border-radius: 0 0 5px 5px;
font-size: 15px;
padding: 10px 8px;
}
.ly_login .rememberLogin {
margin: 10px 0;
}
.ly_login .rememberLogin label {
font-size: 14px;
color: #767676;
display: flex;
align-items: flex-start;
flex-direction: row;
}
.ly_login .rememberLogin label input {
margin-right: 6px;
}
.ly_login .btnLogin {
width: 100%;
}
.ly_login .login-sub {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 9px;
font-size: 14px;
color: #767676;
margin: 10px 0;
}
.ly_login .login-sub a {
color: #333;
}
.login-sns {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.login-sns div:nth-of-type(1) {
font-size: 13px;
color: #767676;
margin: 10px 0;
}
.login-sns div:nth-of-type(2) {
display: flex;
flex-direction: row;
align-items: center;
gap: 15px;
}
.login-sns img {
width: 46px;
}
/* register */
.ly_register {
padding: 60px 0;
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
flex-wrap: wrap;
}
.register-wrap {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.join-form {
display: flex;
flex-direction: column;
}
.join-item {
display: flex;
border: 1px solid #ebebeb;
width: 400px;
padding: 10px;
flex-direction: row;
align-items: center;
}
.join-item.id {
border-radius: 5px 5px 0 0;
border-bottom: none;
}
.join-item.password {
border-bottom: none;
}
.join-item.email {
border-radius: 0 0 5px 5px;
}
.join-item.name {
border-radius: 5px 5px 0 0;
border-bottom: none;
}
.join-item.birth {
border-bottom: none;
}
.join-item.phone {
border-radius: 0 0 5px 5px;
}
.join-subTitle {
width: 30%;
font-size: 14px;
color: #222;
}
.join-item input[type=text] {
width: 65%;
font-size: 14px;
color: #222;
padding: 10px;
border: none;
}
.join-item input[type=password] {
width: 65%;
font-size: 14px;
color: #222;
padding: 10px;
border: none;
}
.join-item label {
text-align: center;
border: 1px solid #ebebeb;
width: 30%;
padding: 6px 0;
border-radius: 6px;
}
.join-btn {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
}
.join-error {
margin: 6px 0;
}
.join-btn .btnRegister {
width: 100%;
border-radius: 5px;
}
.error-text {
font-size: 13px;
margin-bottom: 3px;
color: #ff3f3f;
}
'프로젝트 > CRUD' 카테고리의 다른 글
[CRUD] 5. 게시글 작성 기능 구현 - 단일 파일 업로드 및 다운로드 (1) | 2024.01.10 |
---|---|
[CRUD] 4. 게시글 작성 기능 구현 - 네이버 스마트에디터 (0) | 2024.01.10 |
[CRUD] 3. 로그인 기능 구현 - 스프링 시큐리티 (1) | 2024.01.10 |
[CRUD] 2. 회원가입 기능 구현 - Daum 우편번호 API (0) | 2024.01.10 |