오늘은 게시판 만들어 보자!
이번에는 HTML과 Javascript 파일을 용도에 맞게 분리해서 작성할 예정이다.
이렇게 되면 다른 Javascript 파일에서 사용하는 변수와 데이터들은 이름이 다른 Javascript 파일에서 사용할 수 없다.
그래서 브라우저의 localStorage를 이용할 예정이다.
파일 목록
📦Board
┣ 📂board
┃┣ 📜list.html
┃ ┣ 📜modify.html
┃ ┣ 📜view.html
┃ ┗ 📜write.html
┣ 📂public
┃ ┗ 📂js
┃ ┃ ┣ 📜list.js
┃ ┃ ┣ 📜modify.js
┃ ┃ ┣ 📜view.js
┃ ┃ ┗ 📜write.js
┗ 📜index.html
구현
구현해야 할 사항은 크게 다음과 같다.
- 게시물 작성하기 (Create)
- 리스트로 게시물 보여주기 (Read)
- 게시물 클릭시 해당 게시물 보여주기 (Read)
- 게시물 수정하기 (Update)
- 게시물 삭제하기 (Delete)
여기서 HTML 파일에대한 설명은 하지 않고 자바스크립트 코드에 대해서만 설명을 할 예정이다.
HTML 파일은 아래 코드를 복사해서 생성한다.
HTML
# index.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>
<a href="/index.html">로고</a>
</h1>
<a href="./board/list.html">게시판으로 가기</a>
</body>
</html>
index.html
# board/write.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>
<a href="/index.html">로고</a>
</h1>
<h2>게시판 쓰기</h2>
<form id="writeFrm">
<div>제목 : <input type="text" name="subject" /></div>
<div>작성자 : <input type="text" name="writer" /></div>
<div>내용 : <textarea name="content"></textarea></div>
<input type="submit" value="글작성" />
</form>
<a href="./list.html">뒤로가기</a>
<script src="../public/js/write.js"></script>
</body>
</html>
board/write.html
# board/list.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>
<a href="/index.html">로고</a>
</h1>
<h2>게시판 리스트</h2>
<table border="1">
<thead>
<tr>
<td>번호</td>
<td>글제목</td>
<td>작성자</td>
<td>등록일</td>
<td>조회수</td>
</tr>
</thead>
<tbody></tbody>
</table>
<a href="./write.html">글쓰기</a>
<script src="../public/js/list.js"></script>
</body>
</html>
board/list.html
# board/view.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>
<a href="/index.html">로고</a>
</h1>
<h2>작성한 게시글</h2>
<form id="viewFrm">
<div id="subject">제목 :</div>
<div id="writer">작성자 :</div>
<div id="date">작성일 :</div>
<div id="content">내용 :</div>
</form>
<button id="modify">수정</button>
<a href="./list.html">뒤로가기</a>
<button id="delete">삭제</button>
<script src="../public/js/view.js"></script>
</body>
</html>
board/view.html
# board/modify.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>
<a href="/index.html">로고</a>
</h1>
<h2>글 수정</h2>
<form id="modifyFrm">
<div>제목 : <input type="text" name="subject" /></div>
<div>작성자 : <input type="text" name="writer" /></div>
<div>내용 : <textarea name="content"></textarea></div>
<input type="submit" value="수정완료" />
</form>
<button id="back" style="margin-top: 10px">뒤로가기</button>
<script src="../public/js/modify.js"></script>
</body>
</html>
board/modify.html
Javascript
게시판의 구조는 다음과 같이 만들 예정이다.
게시판 구조
게시물 작성하기 (Create)
# public/js/write.js
제일 먼저 CRUD중 C에 해당하는 게시물을 작성하는 자바스크립트 파일을 만든다.
이벤트를 걸 writeFrm 요소를 선택해 준다.
const writeFrm = document.querySelector("#writeFrm");
console.log(writeFrm);
writeFrm 요소를 선택한 결과
글작성 버튼에 addEventListener로 submit 이벤트를 걸어준다.
submit의 기본동작을 막아준 뒤 이벤트가 잘 걸렸는지 console.log로 이벤트 객체를 출력해본다.
const writeFrm = document.querySelector("#writeFrm");
const submitHandler = (e) => {
e.preventDefault();
console.log(e);
};
writeFrm.addEventListener("submit", submitHandler);
submit 이벤트 작동 확인
이벤트가 잘 걸렸다면 input 박스들과 textarea의 값들을 받아와야 한다.
아래와 같이 코드를 작성한 다음 input 박스들과 textarea에 값을 채우고 글작성을 눌러서 값을 잘 받았는지 확인한다.
const writeFrm = document.querySelector("#writeFrm");
const submitHandler = (e) => {
e.preventDefault();
const subject = e.target.subject.value;
const writer = e.target.writer.value;
const content = e.target.content.value;
console.log(subject);
console.log(writer);
console.log(content);
};
writeFrm.addEventListener("submit", submitHandler);
입력한 값을 잘 받아오는지 확인
값을 잘 받아온다면 값을 담을 공간이 필요하다.
class를 이용해서 객체를 생성할 틀을 만들어 준다.
class Board {
constructor(indexNum, subjectStr, writerStr, contentStr) {
this.index = indexNum;
this.Subject = subjectStr;
this.Writer = writerStr;
this.Content = contentStr;
this.date = recordDate();
this.views = 0;
}
set Subject(value) {
if (value.length === 0) throw new Error("제목을 입력해주세요.");
this.subject = value;
}
set Writer(value) {
if (value.length === 0) throw new Error("작성자를 입력해주세요.");
this.writer = value;
}
set Content(value) {
if (value.length === 0) throw new Error("내용을 입력해주세요.");
this.content = value;
}
}
객체를 생성할 때 4개의 매개변수를 받아 온다.
index 속성에는 매개변수 indexNum 값이 할당된다.
Subject 속성은 setter로 매개 변수 subjectStr을 받게 되면 길이가 0이 아닌지 검사한다음 subject 속성에 subjectStr 값이 할당된다. 값의 내용은 사용자가 작성한 제목이다. 만약 길이가 0이라면 예외를 발생시킨다.
Writer 속성은 setter로 매개 변수 writerStr을 받게 되면 길이가 0이 아닌지 검사한다음 writer 속성에 writerStr 값이 할당된다. 값의 내용은 사용자가 작성한 작성자다. 만약 길이가 0이라면 예외를 발생시킨다.
Content 속성은 setter로 매개 변수 contentStr을 받게 되면 길이가 0이 아닌지 검사한다음 content 속성에 contentStr 값이 할당된다. 값의 내용은 사용자가 작성한 내용이다. 만약 길이가 0이라면 예외를 발생시킨다.
date 속성에는 아래에 작성할 recordDate 함수의 호출 결과가 할당된다. 값의 내용은 현재 날짜다.
views 속성은 조회수를 위한 속성으로 기본값이 0이 할당된다.
const recordDate = () => {
const date = new Date();
const yyyy = date.getFullYear();
let mm = date.getMonth() + 1;
let dd = date.getDate();
mm = (mm > 9 ? "" : 0) + mm;
dd = (dd > 9 ? "" : 0) + dd;
const arr = [yyyy, mm, dd];
return arr.join("-");
};
recordDate 함수는 현재 날짜를 yyyy-mm-dd 형식으로 반환해주는 함수이다.
객체가 잘 생성되는지 확인해본다.
객체 생성 확인
여기서 잠깐 list.js 파일을 작성해야 한다.
우리는 localStorage를 이용해 데이터를 저장할 예정이다.
데이터를 저장할때 우리가 생성한 객체를 배열에 담을 예정이다. 그래야 관리하기 편하기 때문이다.
배열을 생성하는 시점은 list.js 파일이 로드되는 시점이어야 하는데 일반적인 게시판은 처음 들어갔을 때 보이는 화면이 게시글을 목록이다.
이 말은 localStorage의 데이터를 처음 가져오는 시점이 list.js 파일이 로드되는 시점이라는 말이다.
localStorage는 비어있으면 null 값을 반환하는데 이를 이용해 비어있으면 빈 배열을 넣어줄 예정이다.
# board/list.js
getItem이라는 메서드로 localStorage에 저장된 데이터를 불러올 수 있다.
let boardsStr = localStorage.getItem("boards");
console.log(boardsStr);
Local Storage가 비어있으면 null 값을 반환
Local Storage가 비어있는 모습
localStorage가 비어있으면 빈 배열을 저장하는 코드이다.
JSON.stringify는 인자를 JSON 문자열로 변해준다.
setItem 메서드로 저장을 하려면 첫번째 인자는 key 두번째 인자는 value 값인데 이 값이 string이어야 한다.
그래서 나중에 데이터를 수정하려면 JSON 문자열을 받아와서 다시 배열로 바꿔주는 작업이 필요하다.
let boardsStr = localStorage.getItem("boards");
if (boardsStr === null) {
const listStr = JSON.stringify([]);
localStorage.setItem("boards", listStr);
boardsStr = listStr;
}
Local Storage에 빈 배열이 저장된 모습
# board/write.js
다시 write.js 파일을 작성한다.
이제는 submit 이벤트가 발생하면 객체를 생성해서 localStorage에 저장해주어야 한다.
다음과 같이 코드를 작성한다.
const submitHandler = (e) => {
e.preventDefault();
const subject = e.target.subject.value;
const writer = e.target.writer.value;
const content = e.target.content.value;
try {
// boards 가져오기
const boardsObj = JSON.parse(localStorage.getItem("boards"));
// 객체 추가
const index = boardsObj.length;
const instance = new Board(index, subject, writer, content);
boardsObj.push(instance);
// boards 저장
const boardsStr = JSON.stringify(boardsObj);
localStorage.setItem("boards", boardsStr);
location.href = "/board/view.html?index=" + index;
} catch (e) {
// 예외 발생시 메시지 출력
alert(e.message);
console.error(e);
}
};
writeFrm.addEventListener("submit", submitHandler);
localStorage에 있는 boards 데이터를 가져와서 객체 형태로 변환한다.
index는 boards 데이터의 배열 길이만큼이다.
새로운 객체를 생성하는데 인자로 boards 데이터의 배열 길이와 input 박스와 textarea에 작성한 값들을 넘겨줘서 초기값을 설정해 준다.
생성한 객체를 boards 배열에 추가해준다.
배열을 다시 JSON 문자열로 변환한다음 localStorage에 저장한다.
view.html로 이동하면서 index 값을 전달해 준다.
만약 예외가 발생하면 예외 메시지를 경고창으로 띄워주고 콘솔 창에도 출력해 준다.
글을 작성한 값이 LocalStorage에 저장되는 모습
예외 처리 모습
Javscript 코드
# public/js/write.js
const writeFrm = document.querySelector("#writeFrm");
class Board {
constructor(indexNum, subjectStr, writerStr, contentStr) {
this.index = indexNum;
this.Subject = subjectStr;
this.Writer = writerStr;
this.Content = contentStr;
this.date = recordDate();
this.views = 0;
}
set Subject(value) {
if (value.length === 0) throw new Error("제목을 입력해주세요.");
this.subject = value;
}
set Writer(value) {
if (value.length === 0) throw new Error("작성자를 입력해주세요.");
this.writer = value;
}
set Content(value) {
if (value.length === 0) throw new Error("내용을 입력해주세요.");
this.content = value;
}
}
const recordDate = () => {
const date = new Date();
const yyyy = date.getFullYear();
let mm = date.getMonth() + 1;
let dd = date.getDate();
mm = (mm > 9 ? "" : 0) + mm;
dd = (dd > 9 ? "" : 0) + dd;
const arr = [yyyy, mm, dd];
return arr.join("-");
};
const submitHandler = (e) => {
e.preventDefault();
const subject = e.target.subject.value;
const writer = e.target.writer.value;
const content = e.target.content.value;
try {
// boards 가져오기
const boardsObj = JSON.parse(localStorage.getItem("boards"));
// 객체 추가
const index = boardsObj.length;
const instance = new Board(index, subject, writer, content);
boardsObj.push(instance);
// boards 저장
const boardsStr = JSON.stringify(boardsObj);
localStorage.setItem("boards", boardsStr);
location.href = "/board/view.html?index=" + index;
} catch (e) {
// 예외 발생시 메시지 출력
alert(e.message);
console.error(e);
}
};
writeFrm.addEventListener("submit", submitHandler);
# public/js/list.js
let boardsStr = localStorage.getItem("boards");
if (boardsStr === null) {
const listStr = JSON.stringify([]);
localStorage.setItem("boards", listStr);
boardsStr = listStr;
}