728x90

✨ DB를 이용해 게시판을 만들기 전에, 간단히 연습할 수 있는 방법입니다.

Chrome Browser - F12(or Inspect) - Application - Storage - Local Storage
위의 경로를 찾아 들어가면, 내 브라우저에만 저장되는 공간이 있다. 이걸 이용해 간단하게 게시판을 구현할 수 있다.

📢 Local Storage 사용법

1. 데이터 저장하기
localStorage.setItem( 명칭, 저장할 내용 );

2. 데이터 불러오기
localStorage.getItem( 명칭 )

글은 여러 개이며 하나의 글에 필요한 데이터번호, 제목, 내용, 작성자, 작성일, 조회이기 때문에
하나의 글을 저장할 때는 배열로 저장하고, 하나의 글에 들어간 데이터는 Object로 저장해주면 간단히 저장할 수 있다.
예를 들어, objArr = [ { num: 1, title: 'title1'... }, { num: 2, title: 'title2'... }, ... ] 형식으로 저장하면 된다.

📢 게시판을 만들어가는 작업 순서

1. input과 form tag를 이용해 글을 작성하는 부분을 만들어주고, 이 내용을 받아 localStorage에 저장해준다.
2. localStorage에서 데이터를 받아와, DOM을 이용해 목록을 출력해준다.
3. 글 제목을 누르면, 글 내용이 출력되도록 해준다.
4. 한 화면에는 최대 5개의 글만 보여주며, 그 이상의 글은 인덱스 처리를 해주고 눌렀을 때 해당 목록을 보여준다.

😃 보기 쉽게 하기 위해서, 싱글 페이지글 목록, 글 내용, 작성 부분을 한 번에 보여주도록 구성하였다.
이 과정에서 글 번호가 곧 데이터 index이므로 꼬이지 않도록 Sort해주는 작업이 필요하며,
각 글 목록마다 id를 다르게 부여하여, event를 받아줄 수 있도록 해주는 작업이 필요하다.

아래는 예제가 실제로 실행되는 모습이다.

페이지 실행 첫 번째 화면
글 제목을 눌렀을 때, 내용을 보여주는 화면
2번째 글 목록 화면
3번째 글 목록 화면
글 작성하는 부분
위에서 작성한 글 내용 보기

이렇게 연습을 하고, DB와 함께 진행할 때는 어떻게 적용하고 개발을 진행해 나가야 할지 고민하면 될 듯 하다.

궁금한 점이 있으시면 언제든 댓글 달아주세요!

Full Code (https://github.com/DasolPark/Play-Ground_JavaScript) board.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Board with Local Storage</title>
<link
href="https://fonts.googleapis.com/css?family=Jua&display=swap"
rel="stylesheet"
/>
<style>
* {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-family: 'Jua', sans-serif;
}
body {
padding: 0px;
margin: 0px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 95vh;
background-color: #f6f9fc;
}
 
.main__container {
height: 700px;
padding-top: 10px;
padding-bottom: 10px;
}
 
.board__container,
.contents__container,
.editor__container {
width: 700px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
background-color: #ffffff;
}
 
.board__container {
margin: 10px 0px 0px 0px;
height: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
position: relative;
}
 
.contents__container {
margin: 10px 0px;
height: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
}
 
.editor__container {
margin: 10px 0px;
height: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
}
 
/* Board Lists */
.board {
width: 600px;
text-align: center;
margin-bottom: 15px;
}
 
.board__index-container {
position: absolute;
bottom: 10px;
}
 
.board__index-link {
all: unset;
margin-right: 5px;
}
 
.board__index-link:hover {
cursor: pointer;
color: blue;
}
 
/* Contents */
.contents__titles {
width: 600px;
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
border-bottom: 2px solid rgba(0, 0, 0, 0.08);
margin-bottom: 20px;
}
 
.contents__column {
width: 200px;
display: flex;
justify-content: space-around;
align-items: center;
}
 
.contents__content {
width: 600px;
display: flex;
justify-content: start;
align-items: center;
}
 
/* Editor */
.editor__form {
width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
 
input {
all: unset;
border-radius: 5px;
border: 1px solid rgba(0, 0, 0, 0.5);
}
 
.editor__title-input,
.editor__content-input {
width: 100%;
padding: 5px 10px;
margin-bottom: 10px;
}
 
.editor__content-input {
height: 100px;
}
 
.editor__btn {
all: unset;
border: 1px solid rgba(0, 0, 0, 0.5);
border-radius: 5px;
padding: 5px 10px;
}
 
button:active {
transform: scale(0.93);
}
</style>
</head>
<body>
<main class="main__container">
<div class="board__container">
<table class="board">
<thead class="board__table-head">
<tr class="board__titles">
<th class="board__column">번호</th>
<th class="board__column">제목</th>
<th class="board__column">작성자</th>
<th class="board__column">작성일</th>
<th class="board__column">조회</th>
</tr>
</thead>
<tbody class="board__contents" id="board-body"></tbody>
</table>
<div class="board__index-container" id="index-container"></div>
</div>
<div class="contents__container"></div>
<div class="editor__container">
<form action="#" class="editor__form" id="editor-form">
<input
type="text"
class="editor__title-input"
id="editor-title-input"
placeholder="제목"
/>
<input
type="text"
class="editor__content-input"
id="editor-content-input"
placeholder="내용"
/>
<button class="editor__btn" id="editor-submit-btn">작성 완료</button>
</form>
</div>
</main>
<script>
const boardTableBody = document.querySelector('#board-body');
 
const contentsContainer = document.querySelector('.contents__container');
 
const editorForm = document.querySelector('#editor-form');
const titleInput = document.querySelector('#editor-title-input');
const contentInput = document.querySelector('#editor-content-input');
 
const BOARDLIST_LS = 'boardLists';
const boardListsObj = [];
let nums = 0;
let author = 'David';
let date = new Date();
let views = Math.floor(Math.random() * 99) + 1;
 
function onTitleClick(e) {
contentsContainer.textContent = '';
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
const index = e.target.parentNode.id.replace(/[a-z|-]/gi, '');
 
const contentsTitles = document.createElement('div');
contentsTitles.classList.add('contents__titles');
 
const contentsColumnFirst = document.createElement('div');
contentsColumnFirst.classList.add('contents__column');
 
const contentsTitle = document.createElement('div');
contentsTitle.classList.add('contents__title');
contentsTitle.textContent = lists[index].title;
 
// contents__titles > column >author, date, views
const contentsColumnSecond = document.createElement('div');
contentsColumnSecond.classList.add('contents__column');
 
const contentsAuthor = document.createElement('div');
contentsAuthor.classList.add('contents__author');
contentsAuthor.textContent = lists[index].author;
 
const contentsDate = document.createElement('div');
contentsDate.classList.add('contents__date');
contentsDate.textContent = lists[index].date;
 
const contentsViews = document.createElement('div');
contentsViews.classList.add('contents__views');
contentsViews.textContent = lists[index].views;
 
const contentsContent = document.createElement('div');
contentsContent.classList.add('contents__content');
contentsContent.textContent = lists[index].content;
 
contentsColumnFirst.appendChild(contentsTitle);
 
contentsColumnSecond.appendChild(contentsAuthor);
contentsColumnSecond.appendChild(contentsDate);
contentsColumnSecond.appendChild(contentsViews);
 
contentsTitles.appendChild(contentsColumnFirst);
contentsTitles.appendChild(contentsColumnSecond);
 
contentsContainer.appendChild(contentsTitles);
contentsContainer.appendChild(contentsContent);
}
 
function assignIndex() {
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
if (!lists) {
nums = 0;
} else {
nums = parseInt(lists[lists.length - 1].num) + 1;
}
}
 
function onEditorFormSubmit(e) {
e.preventDefault();
 
const title = titleInput.value;
const content = contentInput.value;
 
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
if (!lists) {
const objArr = [];
objArr.push({
num: `${nums++}`,
title: `${title}`,
author: `${author}`,
date: `${date.getFullYear()}.${date.getMonth() +
1}.${date.getDate()}.`,
views: `${views++}`,
content: `${content}`
});
 
localStorage.setItem(BOARDLIST_LS, JSON.stringify(objArr));
} else {
lists.push({
num: `${nums++}`,
title: `${title}`,
author: `${author}`,
date: `${date.getFullYear()}.${date.getMonth() +
1}.${date.getDate()}.`,
views: `${views++}`,
content: `${content}`
});
 
localStorage.setItem(BOARDLIST_LS, JSON.stringify(lists));
}
 
titleInput.value = '';
contentInput.value = '';
titleInput.focus();
window.location.reload();
}
 
function showBoardLists() {
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
 
lists.forEach((list, index) => {
if (index < 5) {
const tr = document.createElement('tr');
tr.classList.add('board__content');
 
const tdNum = document.createElement('td');
tdNum.classList.add('board__content-column');
tdNum.textContent = list.num;
 
const aTitle = document.createElement('a');
aTitle.href = '#';
aTitle.id = `link-to-content${index}`;
 
const tdTitle = document.createElement('td');
tdTitle.classList.add('board__content-column');
tdTitle.textContent = list.title;
 
aTitle.appendChild(tdTitle);
 
const tdAuthor = document.createElement('td');
tdAuthor.classList.add('board__content-column');
tdAuthor.textContent = list.author;
 
const tdDate = document.createElement('td');
tdDate.classList.add('board__content-column');
tdDate.textContent = list.date;
 
const tdViews = document.createElement('td');
tdViews.classList.add('board__content-column');
tdViews.textContent = list.views;
 
tr.appendChild(tdNum);
tr.appendChild(aTitle);
tr.appendChild(tdAuthor);
tr.appendChild(tdDate);
tr.appendChild(tdViews);
 
boardTableBody.appendChild(tr);
const linkToContent = document.querySelector(
`#link-to-content${index}`
);
linkToContent.addEventListener('click', onTitleClick);
} else if (index === 5) {
const boardIndexMax = Math.ceil(lists.length / 5);
for (let i = 0; i < boardIndexMax; i++) {
const indexContainer = document.querySelector('#index-container');
 
const aIndex = document.createElement('a');
aIndex.classList.add('board__index-link');
 
const spanIndexText = document.createElement('span');
spanIndexText.classList.add('board__index');
spanIndexText.textContent = i + 1;
 
aIndex.appendChild(spanIndexText);
indexContainer.appendChild(aIndex);
 
aIndex.addEventListener('click', () => {
showBoardListsNewPage(i);
});
}
}
});
}
 
function showBoardListsNewPage(pageIndex) {
boardTableBody.textContent = '';
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
const limitPage = pageIndex * 5;
 
lists.forEach((list, index) => {
if (index >= limitPage && index < limitPage + 5) {
const tr = document.createElement('tr');
tr.classList.add('board__content');
 
const tdNum = document.createElement('td');
tdNum.classList.add('board__content-column');
tdNum.textContent = list.num;
 
const aTitle = document.createElement('a');
aTitle.href = '#';
aTitle.id = `link-to-content${index}`;
 
const tdTitle = document.createElement('td');
tdTitle.classList.add('board__content-column');
tdTitle.textContent = list.title;
 
aTitle.appendChild(tdTitle);
 
const tdAuthor = document.createElement('td');
tdAuthor.classList.add('board__content-column');
tdAuthor.textContent = list.author;
 
const tdDate = document.createElement('td');
tdDate.classList.add('board__content-column');
tdDate.textContent = list.date;
 
const tdViews = document.createElement('td');
tdViews.classList.add('board__content-column');
tdViews.textContent = list.views;
 
tr.appendChild(tdNum);
tr.appendChild(aTitle);
tr.appendChild(tdAuthor);
tr.appendChild(tdDate);
tr.appendChild(tdViews);
 
boardTableBody.appendChild(tr);
const linkToContent = document.querySelector(
`#link-to-content${index}`
);
linkToContent.addEventListener('click', onTitleClick);
}
});
}
 
editorForm.addEventListener('submit', onEditorFormSubmit);
 
if (boardTableBody) {
assignIndex();
showBoardLists();
}
</script>
</body>
</html>

+ Recent posts