각진 세상에 둥근 춤을 추자

[CRUD] 1. 페이지 화면 구현 본문

프로젝트/CRUD

[CRUD] 1. 페이지 화면 구현

circle.j 2023. 12. 30. 13:59

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; 
}