no image
[Git] Git Branch
Git BranchGit에서 Branch란 한 줄로 이어지던 커밋을 나무의 나뭇가지가 뻗어나가듯 커밋을 여러 줄로 만드는 것입니다.Branch를 만드는 이유잘 작동하는 코드를 작성하고 그 코드에 새로운 기능을 추가 싶어서 작업을 추가적으로 작업을 진행하다 보면 커밋이 계속 쌓이게 됩니다. 그러다 만약 그 기능을 추가하지 않기로 한다면 어떻게 해야 할까요?아마 revert를 이용해 되돌리거나 reset을 이용해 되돌리려 할 겁니다.하지만 Branch를 이용해 작동하는 부분(main)은 그대로 두고 옆으로 가지를 만들어서 작업을 하면 어떨까요?추가하고 싶지 않다면 가지를 지우거나 그대로 두어도 작동하는 부분(main)에는 영향을 미치지 않을 것입니다.그렇다면 이제부터 Branch를 다루는 명령어를 알아봅시다..
2022.11.24
Git
no image
[Git] 이전 commit으로 돌아가는 명령어
commit 기록 보기 만약 VS Code의 터미널에서 작업을 하고 있다면 VS Code Extention인 git graph를 설치해서 보면 편합니다. 이전으로 가는 명령어 reset commit을 지우면서 이전으로 돌아가는 명령어입니다. reset 명령어 option으로 hard, soft, mixed가 존재합니다. --hard 옵션은 해당하는 해시값을 가진 commit으로 HEAD를 이동시킨 후 해당 commit 이후에 commit 된 내용을 완전히 삭제합니다. 코드도 같이 삭제됩니다. 만약 복구하고 싶다면 다음과 같이 할 수 있습니다. --soft 옵션은 해당하는 해시값을 가진 commit으로 HEAD를 이동시킨 후 해당 commit 이후에 commit 된 내용을 삭제하고 Staging Area로..
2022.11.23
Git
no image
[Git] Git의 작업 영역 & Git 기본 명령어
Git이란? Git은 리눅스의 창시자 리누스 토발즈가 리눅스의 각 버전들을 관리하기 위해 만든 버전 관리 프로그램이다. Git 시작 git으로 버전 관리를 하기 위해서는 git에게 현재 디렉터리를 관리를 요청해줘야 한다. 이 명령어를 치면 .git이라는 디렉터리가 생기는데 이 디렉터리 현재 디렉터리의 파일들이 변화되는 과정을 저장하고 있다 만약 관리하고 싶지 않은 디렉토리나 파일이 있다면. gitignore 파일을 생성해준 다음 관리하고 싶지 않은 디렉터리나 파일명을 작성해주면 된다. Git의 작업 순서도 Git의 작업 영역 Git에는 크게 3가지 종류의 작업 영역이 존재한다. Git은 위에 작업 순서도 그림처럼 Working Directory에서 Staging Area를 거쳐 Repository에 반영..
2022.11.23
Git
no image
[Javascript] 게시판 구현하기 (CRUD) - 코드 수정
이전에 코드에서 새로고침을 할 때 조회수가 증가하는 문제가 생겼다.조회수 증가를 막아보자!추가적으로 수정 시 현재 날짜를 반영하도록 구현해보자!파일 목록📦Board┣ 📂board┃┣ 📜list.html┃ ┣ 📜modify.html┃ ┣ 📜view.html┃ ┗ 📜write.html┣ 📂public┃ ┗ 📂js┃ ┃ ┣ 📜list.js┃ ┃ ┣ 📜modify.js┃ ┃ ┣ 📜view.js┃ ┃ ┗ 📜write.js┗ 📜index.html구현view 페이지에서 새로고침을 할 때 조회수 증가 막기수정 시 현재 날짜 반영게시판 구현게시판 구조view 페이지에서 새로고침을 할 때 조회수 증가 막기나는 새로고침 증가 시 조회수를 막기 위해 boards 데이터에 refresh라는 속성을 추가해..
2022.11.22
no image
[Javascript] 게시판 구현하기 (CRUD) - Delete
이번에는 CRUD의 마지막 기능 Delete 기능을 구현해보자! 파일 목록 📦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) 게시판 구현하기 게시판 구조 게시물 삭제하기 (Delete) Delete 기능은 view.js 파일..
2022.11.21
no image
[Javascript] 게시판 구현하기 (CRUD) - Update
오늘은 게시물을 수정 기능을 구현해보자! 파일 목록 📦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) 게시판 구현하기 게시판 구조 게시물 수정하기 (Update) # public/js/view.js 먼저 view 페이지에서 ..
2022.11.21
no image
[Javascript] 게시판 구현하기 (CRUD) - Read
오늘은 이전에 만들었던 게시판에 list 페이지와 view 페이지를 만들어 보자! 파일 목록 📦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) 게시판 구현하기 게시판 구조 리스트로 게시물 보여주기 (Read) # public..
2022.11.20
no image
[Javascript] 게시판 구현하기 (CRUD) - Create
오늘은 게시판 만들어 보자! 이번에는 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) 리스트로..
2022.11.19

[Git] Git Branch

CloudCoke
|2022. 11. 24. 23:47

Git Branch

Git에서 Branch란 한 줄로 이어지던 커밋을 나무의 나뭇가지가 뻗어나가듯 커밋을 여러 줄로 만드는 것입니다.

Branch를 만드는 이유

잘 작동하는 코드를 작성하고 그 코드에 새로운 기능을 추가 싶어서 작업을 추가적으로 작업을 진행하다 보면 커밋이 계속 쌓이게 됩니다. 

그러다 만약 그 기능을 추가하지 않기로 한다면 어떻게 해야 할까요?

아마 revert를 이용해 되돌리거나 reset을 이용해 되돌리려 할 겁니다.

하지만 Branch를 이용해 작동하는 부분(main)은 그대로 두고 옆으로 가지를 만들어서 작업을 하면 어떨까요?

추가하고 싶지 않다면 가지를 지우거나 그대로 두어도 작동하는 부분(main)에는 영향을 미치지 않을 것입니다.

그렇다면 이제부터 Branch를 다루는 명령어를 알아봅시다!

Branch 확인하기

이 명령어를 사용하게 되면 내가 어떤 브랜치에 위치했는지 알 수 있습니다. (* 표시가 붙은 곳이 위치한 곳)

Branch 만들기

브랜치를 새로 만드는 명령어입니다.

Branch 바꾸기

새로 만든 브랜치로 이동하고 싶다면 다음과 같은 명령어를 사용하면 됩니다.

Branch 생성하면서 이동하기

브랜치를 생성하면서 이동하고 싶다면 다음 명령어를 사용하면 됩니다.

Branch 삭제

브랜치가 필요 없어졌다면 다음 명령어로 브랜치를 삭제할 수 있습니다.

주의할 점은 삭제하지 않는 브랜치로 이동한 뒤 브랜치를 삭제해야 한다는 점입니다.

Branch 이름 바꾸기

브랜치 이름이 마음에 들지 않아 바꾸고 싶다면 다음 명령어를 사용하면 됩니다.

Branch 합치기

여러 브랜치에서 작업을 하다 한 브랜치로 합치고 싶다면 다음 두 가지 명령어를 사용하면 됩니다.

두 가지 명령어의 차이점은 그래프가 그려지는 모습입니다.

merge

이 명령어는 기준이 되는 브랜치로 다른 브랜치를 끌어당겨서 하나로 합칩니다.

출처 : https://0314625.tistory.com/211

명령어를 사용하는 방법은 다음과 같습니다.

merge 취소하기

 

rebase

이 명령어는 선택한 브랜치를 이동시켜 원하는 브랜치에 정렬합니다.

출처 : https://giphy.com/gifs/synergic-BhjjlbtFCx8iObvLxt

명령어를 사용하는 방법은 다음과 같습니다.

rebase 취소하기

만약 정렬을 하다 충돌이 났을 때 이전 상태로 돌아가고 싶다면 아래 명령어로 취소를 할 수 있습니다.

rebase 진행하기

충돌이 났을 때 충돌이 난 부분을 수정해 충돌을 해결한 다음 아래 명령어를 사용하면 rebase가 다시 진행됩니다.

'Git' 카테고리의 다른 글

[Git] 이전 commit으로 돌아가는 명령어  (0) 2022.11.23
[Git] Git의 작업 영역 & Git 기본 명령어  (0) 2022.11.23

commit 기록 보기

만약 VS Code의 터미널에서 작업을 하고 있다면 VS Code Extention인 git graph를 설치해서 보면 편합니다.

이전으로 가는 명령어

reset

commit을 지우면서 이전으로 돌아가는 명령어입니다. 

reset 명령어 option으로 hard, soft, mixed가 존재합니다.

--hard 옵션은 해당하는 해시값을 가진 commit으로 HEAD를 이동시킨 후 해당 commit 이후에 commit 된 내용을 완전히 삭제합니다.

코드도 같이 삭제됩니다.

만약 복구하고 싶다면 다음과 같이 할 수 있습니다.

--soft 옵션은 해당하는 해시값을 가진 commit으로 HEAD를 이동시킨 후 해당 commit 이후에 commit 된 내용을 삭제하고 Staging Area로 이동시킵니다.

코드는 Staging Area에 add 된 상태입니다.

만약 commit을 하고 싶다면 add 명령어 필요 없이 바로 commit을 진행할 수 있습니다.

--mixed 옵션은 해당하는 해시값을 가진 commit으로 HEAD를 이동시킨 후 해당 commit 이후에 commit 된 내용을 삭제하고 Working Directory로 이동시킵니다.

코드는 Working Directory에 tracked 상태로 남아있게 됩니다.

만약 commit을 하고 싶다면 add 명령어를 이용해 Staging Area로 이동시킨 후 commit을 진행해야 합니다.

revert

지정한 commit을 복사해서 가장 마지막 commit 뒤에 붙여 넣는 형태의 명령어입니다.

revert 명령어를 사용할 때 파일의 충돌이 발생한다면 컴퓨터는 무엇이 우선순위인지 모르기 때문에 사람이 직접 명령을 해줘야 합니다.

 

'Git' 카테고리의 다른 글

[Git] Git Branch  (0) 2022.11.24
[Git] Git의 작업 영역 & Git 기본 명령어  (0) 2022.11.23

Git이란?

Git은 리눅스의 창시자 리누스 토발즈가 리눅스의 각 버전들을 관리하기 위해 만든 버전 관리 프로그램이다.

Git 시작

git으로 버전 관리를 하기 위해서는 git에게 현재 디렉터리를 관리를 요청해줘야 한다.

이 명령어를 치면 .git이라는 디렉터리가 생기는데 이 디렉터리 현재 디렉터리의 파일들이 변화되는 과정을 저장하고 있다

만약 관리하고 싶지 않은 디렉토리나 파일이 있다면. gitignore 파일을 생성해준 다음 관리하고 싶지 않은 디렉터리나 파일명을 작성해주면 된다.

Git의 작업 순서도

Git의 작업 영역

Git에는 크게 3가지 종류의 작업 영역이 존재한다.

Git은 위에 작업 순서도 그림처럼 Working Directory에서 Staging Area를 거쳐 Repository에 반영이 된다.

Working Directory (작업 디렉터리)

현재 코드를 작성하고 있는 공간 (작업 디렉터리)를 의미한다.

이 영역에는 두 가지 상태가 존재한다.

  • Tracked (추적)
    • 한 번이라도. git에 의해 관리된 적이 있는 파일
  • Untracked (추적되지 않음)
    • 한번이라도 .git에 의해 관리된 적이 없는 파일

Working Directory에서 작업한 내용을 Repository에 기록하고 싶다면 먼저 Staging Area에 등록을 해야 한다.

# 현재 위치에서 전체 파일 및 디렉터리 등록
git add .

Staging Area (대기소)

Repository에 기록하기 전 중간다리 역할을 한다.

Repository에 기록하기 전에 진짜 기록을 할 건지 마지막으로 물어보는 과정이라 볼 수 있다.

이 명령어를 치면 Editor가 뜨게 되는데 그곳에 commit 메시지를 작성해 주면 된다.

만약 Editor 없이 바로 commit 메시지를 작성하고 싶다면 다음과 같이 해주면 된다.

Repository (.git)

commit들이 저장되는 영역으로 Working Directory의 변경 이력들이 기록되어 있는 영역이다.

git init시 생성되는. git 디렉터리이기도 하다.

'Git' 카테고리의 다른 글

[Git] Git Branch  (0) 2022.11.24
[Git] 이전 commit으로 돌아가는 명령어  (0) 2022.11.23

이전에 코드에서 새로고침을 할 때 조회수가 증가하는 문제가 생겼다.

조회수 증가를 막아보자!

추가적으로 수정 시 현재 날짜를 반영하도록 구현해보자!

파일 목록

📦Board
┣ 📂board
┃┣ 📜list.html
┃ ┣ 📜modify.html
┃ ┣ 📜view.html
┃ ┗ 📜write.html
┣ 📂public
┃ ┗ 📂js
┃ ┃ ┣ 📜list.js
┃ ┃ ┣ 📜modify.js
┃ ┃ ┣ 📜view.js
┃ ┃ ┗ 📜write.js
┗ 📜index.html

구현

  • view 페이지에서 새로고침을 할 때 조회수 증가 막기
  • 수정 시 현재 날짜 반영

게시판 구현

게시판 구조

게시판 구조

view 페이지에서 새로고침을 할 때 조회수 증가 막기

나는 새로고침 증가 시 조회수를 막기 위해 boards 데이터에 refresh라는 속성을 추가해 주었다.

# 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 = -1;
    this.refresh = false; // 추가된 부분
  }

  // 값 설정시 빈 값 체크
  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;
  }
}

초기에 views속성을 처음에 -1로 설정해주고 refresh 속성을 false로 설정해주었다.

views 속성을 -1로 설정한 이유와 refresh 속성을 false로 설정한 이유는 view.js 코드에서 설명하겠다.

// 현재 날짜 반환 함수
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");

// localStorage 초기값 지정
if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

// 템플릿 생성
const template = (index, objValue) => {
  return `
  <tr>
  <td>${index + 1}</td>
  <td><a href="/board/view.html?index=${objValue.index}">${objValue.subject}</a></td>
  <td>${objValue.writer}</td>
  <td>${objValue.date}</td>
  <td>${objValue.views}</td>
  </tr>
  `;
};

// 템플릿 반영
const tbody = document.querySelector("tbody");

for (let i = 0; i < boardsObj.length; i++) {
  tbody.innerHTML += template(i, boardsObj[i]);
  // 추가된 부분
  boardsObj[i].refresh = false;
  const refreshStr = JSON.stringify(boardsObj);
  localStorage.setItem("boards", refreshStr);
}

 

list 페이지에서 게시글 목록을 출력해줄 때 boards 객체의 refresh 속성을 모두 false로 바꿔서 localStorage에 반영을 해준다.

그 이유는 view.js 부분에서 설명하도록 하겠다.

# public/js/view.js

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수 수정된 부분
if (!board.refresh) {
  board.views++;
  board.refresh = true;
  const viewCountStr = JSON.stringify(boardsObj);
  localStorage.setItem("boards", viewCountStr);
} else {
  if (beforeUrl === " ") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
}

만약 boards 데이터의 refresh가 false라면 조회수를 1 증가시키고 localStorage에 반영한다.

wirte.js에서 글을 작성하면 view 페이지로 넘어오게 되는데 이때 값이 1이 증가하게 되기 때문에 write.js에서 views의 초기값을 -1로 설정해주었다.

이렇게 하면 작성하고 view 페이지로 넘어오면 조회수가 0부터 시작한다.

만약 refresh 가 false라면 list 페이지에서 왔다고 보고 조회수를 1 증가시킨 뒤 boards 데이터의 refresh 속성을 true 바꾼다.

새로고침을 하게 되면 view.js 파일을 다시 로드하게 되는데 boards의 refresh 속성이 true이기 때문에 조회수가 증가하지 않는다. 

예외로 이전 url이 빈 값인 url 즉 url에 직접 입력해서 들어온 경우 조회수를 1 증가시킨다.

이 경우에도 새로고침을 해도 조회수가 증가하지 않는다.

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");

const deleteBtnHandler = (e) => {
  boardsObj.splice(index, 1);
  for (let i = 0; i < boardsObj.length; i++) {
    boardsObj[i].index = i;
  }

  const setBoardsStr = JSON.stringify(boardsObj);
  localStorage.setItem("boards", setBoardsStr);
  location.href = "/board/list.html";
};

deleteBtn.addEventListener("click", deleteBtnHandler);

수정 시 현재 날짜 반영

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

// 게시글의 데이터 값 출력
for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

// 작성한 입력 값이 빈 값인지 검사
const isEmpty = (subject, writer, content) => {
  if (subject.length === 0) throw new Error("제목을 입력해주세요");
  if (writer.length === 0) throw new Error("작성자를 입력해주세요");
  if (content.length === 0) throw new Error("내용을 입력해주세요");
};

// 현재 날짜 반환 함수
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("-");
};

이전에 write.js에서 사용한 현재 날짜를 반환해주는 함수인 recordDate를 사용하였다.

// 수정완료 버튼
const modifyHandler = (e) => {
  e.preventDefault();
  const subject = e.target.subject.value;
  const writer = e.target.writer.value;
  const content = e.target.content.value;

  try {
    isEmpty(subject, writer, content);
    board.subject = subject;
    board.writer = writer;
    board.content = content;
    board.date = recordDate();

    const boardsStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", boardsStr);
    location.href = "/board/view.html" + idx;
  } catch (e) {
    alert(e.message);
    console.error(e);
  }
};

수정 완료 버튼을  누르면 recordDate함수를 호출해 반환 값을 boards의 데이터에 덮어쓰도록 구현하였다.

const backBtn = document.querySelector("#back");

// 뒤로가기 버튼
const backBtnHandler = (e) => {
  location.href = document.referrer;
};

modifyFrm.addEventListener("submit", modifyHandler);
backBtn.addEventListener("click", backBtnHandler);

게시판 구경하기

최종 결과물  👈 클릭

이번에는 CRUD의 마지막 기능 Delete 기능을 구현해보자!

파일 목록

📦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)

게시판 구현하기

게시판 구조

게시판 구조

게시물 삭제하기 (Delete)

Delete 기능은 view.js 파일에서 구현할 예정이다.

delete.js 파일을 따로 만들지 않은 이유는 view 페이지에서 삭제하고 list 페이지로 넘어가기만 하면 되는데 따로 파일을 만들 이유가 없다고 개인적으로 생각을 했기 때문이다.

 

# public/js/view.js

제일 먼저 view 페이지의 삭제 버튼을 선택해 보자!

이전 코드에서 작업을 진행한다.

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수
const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");
console.log(deleteBtn);

삭제 버튼 요소를 선택한 결과

이제 삭제 버튼에 클릭 이벤트를 걸어보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수
const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");

const deleteBtnHandler = (e) => {
  console.log(e.target);
};

deleteBtn.addEventListener("click", deleteBtnHandler);

삭제 버튼을 클릭한 결과

쿼리 스트링으로 전달받은 인덱스를 이용해 버튼을 클릭하면 해당하는 데이터를 splice로 삭제해보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수
const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");

console.log(boardsObj);
const deleteBtnHandler = (e) => {
  boardsObj.splice(index, 1);
  console.log(boardsObj);
};

deleteBtn.addEventListener("click", deleteBtnHandler);

boards 데이터 삭제하기

0번째 인덱스에 해당하는 값이 잘 지워지는 걸 확인할 수 있다.

이번에는 인덱스 값을 앞으로 한 칸씩 당겨주자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수
const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");

console.log(boardsObj);
const deleteBtnHandler = (e) => {
  boardsObj.splice(index, 1);
  for (let i = 0; i < boardsObj.length; i++) {
    boardsObj[i].index = i;
  }
  console.log(boardsObj);
};

deleteBtn.addEventListener("click", deleteBtnHandler);

boards 데이터 삭제 후 인덱스 정렬

이제 이걸 localStorage에 반영하자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수
const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");

const deleteBtnHandler = (e) => {
  boardsObj.splice(index, 1);
  for (let i = 0; i < boardsObj.length; i++) {
    boardsObj[i].index = i;
  }

  const setBoardsStr = JSON.stringify(boardsObj);
  localStorage.setItem("boards", setBoardsStr);
};

deleteBtn.addEventListener("click", deleteBtnHandler);

삭제한 결과를 localStorage에 반영하기

마지막으로 삭제 버튼을 누르면 list 페이지로 이동하게 해 보자!

방금 데이터는 삭제되었기 때문에 list 페이지에서 새로운 게시글을 선택해서 view 페이지로 온 다음 작업을 진행하자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

// 조회수
const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

// 데이터 출력
const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

// 수정 버튼
const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

// 삭제 버튼
const deleteBtn = document.querySelector("#delete");

const deleteBtnHandler = (e) => {
  boardsObj.splice(index, 1);
  for (let i = 0; i < boardsObj.length; i++) {
    boardsObj[i].index = i;
  }

  const setBoardsStr = JSON.stringify(boardsObj);
  localStorage.setItem("boards", setBoardsStr);
  location.href = "/board/list.html";
};

deleteBtn.addEventListener("click", deleteBtnHandler);

게시물이 삭제되는 모습

이로써 게시판 구현하기가 끝이 났다!!!


실제 게시판은 데이터 베이스를 이용하겠지만 아직 데이터 베이스를 배우지 않아서 브라우저의 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)

게시판 구현하기

게시판 구조

게시판 구조

게시물 수정하기 (Update)

# public/js/view.js

먼저 view 페이지에서 수정 버튼을 누르면 modify 페이지로 이동하도록 만들어 보자!

클릭 이벤트를 걸 요소를 선택해보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

const modifyBtn = document.querySelector("#modify");
console.log(modifyBtn);

view 페이지에서 수정 버튼을 선택한 결과

수정 버튼에 클릭 이벤트를 걸어주고 확인을 해보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  console.log("수정");
};

modifyBtn.addEventListener("click", modifyBtnHandler);

수정 버튼에 이벤트가 잘 걸린지 확인

다음과 같이 modify 페이지로 이동시켜줄 때 매개변수로 index라는 키와 게시글의 index 번호를 넘겨준다.

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

const modifyBtn = document.querySelector("#modify");

const modifyBtnHandler = (e) => {
  location = "/board/modify.html" + idx;
};

modifyBtn.addEventListener("click", modifyBtnHandler);

view 페이지에서 modify 페이지로 넘어가는 모습

이제 modify 페이지로 이동해서 작업을 진행한다.

 

# public/js/modify.js

modify 페이지는 write 페이지와 view 페이지가 합쳐진 페이지라 볼 수 있다.

submit 이벤트를 걸기 위한 요소를 선택한다.

그리고 데이터가 들어갈 위치인 modifyFrm > div 요소들 선택해 준다.

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
console.log(modifyFrm);
console.log(modifyFrmList);

boards의 데이터가 들어갈 위치 확인

modify 페이지에 수정 완료 버튼에 submit 이벤트를 걸어준다.

submit의 기본 이벤트를 막아주고 이벤트가 잘 작동하는지 출력해본다.

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");

const modifyHandler = (e) => {
  e.preventDefault();
  console.log("Hello");
};

modifyFrm.addEventListener("submit", modifyHandler);

submit 이벤트 작동확인

boards 데이터를 가져온다.

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const boardsObj = JSON.parse(localStorage.getItem("boards"));

console.log(boardsObj);

const modifyHandler = (e) => {
  e.preventDefault();
};

modifyFrm.addEventListener("submit", modifyHandler);

boards에 저장된 데이터 확인

location.search를 이용해 이전 페이지에서 넘겨준 인덱스를 받아온다.

인덱스를 이용해 수정하려는 데이터를 확인해보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
console.log(idx);
const index = location.search.split("=")[1];
console.log(index);
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];
console.log(board);

const modifyHandler = (e) => {
  e.preventDefault();
};

modifyFrm.addEventListener("submit", modifyHandler);

넘겨받은 인덱스 확인 및 수정할 데이터 확인

수정하기 전 데이터가 미리 들어가야 할 위치를 찾아보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes;
  console.log(element);
}

const modifyHandler = (e) => {
  e.preventDefault();
};

modifyFrm.addEventListener("submit", modifyHandler);

저장되어 있던 boards의 데이터가 들어갈 공간

찾은 위치에 수정하기 전 데이터를 넣어보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

console.log(boardsObj[index]);
for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

const modifyHandler = (e) => {
  e.preventDefault();
};

modifyFrm.addEventListener("submit", modifyHandler);

boards의 데이터가 위치에 맞게 들어간 모습

글을 작성하고 수정완료를 눌렀을 때 input 박스와 textarea의 값들을 가져와보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

const modifyHandler = (e) => {
  e.preventDefault();
  const subject = e.target.subject.value;
  const writer = e.target.writer.value;
  const content = e.target.content.value;

  console.log(subject, writer, content);
};

modifyFrm.addEventListener("submit", modifyHandler);

수정된 데이터를 저장하기전 값을 확인하는 모습

값들을 저장하기 이전에 빈 값이 있는지 체크하는 함수를 만들어 보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

const isEmpty = (subject, writer, content) => {
  if (subject.length === 0) throw new Error("제목을 입력해주세요");
  if (writer.length === 0) throw new Error("작성자를 입력해주세요");
  if (content.length === 0) throw new Error("내용을 입력해주세요");
};

const modifyHandler = (e) => {
  e.preventDefault();
  const subject = e.target.subject.value;
  const writer = e.target.writer.value;
  const content = e.target.content.value;

  try {
    isEmpty(subject, writer, content);
  } catch (e) {
    alert(e.message);
    console.error(e);
  }
};

modifyFrm.addEventListener("submit", modifyHandler);

빈 값을 체크하는 함수 실행 결과

이제 수정한 값을 boards 데이터에 저장하고 view 페이지로 이동할 수 있도록 만들어 보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

const isEmpty = (subject, writer, content) => {
  if (subject.length === 0) throw new Error("제목을 입력해주세요");
  if (writer.length === 0) throw new Error("작성자를 입력해주세요");
  if (content.length === 0) throw new Error("내용을 입력해주세요");
};

const modifyHandler = (e) => {
  e.preventDefault();
  const subject = e.target.subject.value;
  const writer = e.target.writer.value;
  const content = e.target.content.value;

  try {
    isEmpty(subject, writer, content);
    board.subject = subject;
    board.writer = writer;
    board.content = content;

    const boardsStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", boardsStr);
    location.href = "/board/view.html" + idx;
  } catch (e) {
    alert(e.message);
    console.error(e);
  }
};

modifyFrm.addEventListener("submit", modifyHandler);

수정된 값이 반영이 되어 view 페이지에 반영이 된 모습

뒤로가기뒤로 가기 버튼을 구현하기 전에 뒤로 가기 부분부터 선택해보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

const isEmpty = (subject, writer, content) => {
  if (subject.length === 0) throw new Error("제목을 입력해주세요");
  if (writer.length === 0) throw new Error("작성자를 입력해주세요");
  if (content.length === 0) throw new Error("내용을 입력해주세요");
};

const modifyHandler = (e) => {
  e.preventDefault();
  const subject = e.target.subject.value;
  const writer = e.target.writer.value;
  const content = e.target.content.value;

  try {
    isEmpty(subject, writer, content);
    board.subject = subject;
    board.writer = writer;
    board.content = content;

    const boardsStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", boardsStr);
    location.href = "/board/view.html" + idx;
  } catch (e) {
    alert(e.message);
    console.error(e);
  }
};

const backBtn = document.querySelector("#back");
console.log(backBtn);

modifyFrm.addEventListener("submit", modifyHandler);

뒤로가기 버튼을 선택한 결과

선택한 뒤로가기 버튼에 이벤트를 걸어서 이전 페이지로 이동할 수 있도록 해보자!

const modifyFrm = document.querySelector("#modifyFrm");
const modifyFrmList = document.querySelectorAll("#modifyFrm > div");
const idx = location.search;
const index = location.search.split("=")[1];
const boardsObj = JSON.parse(localStorage.getItem("boards"));
const board = boardsObj[index];

for (let i = 0; i < modifyFrmList.length; i++) {
  const element = modifyFrmList[i].childNodes[1];
  const id = element.name;
  element.value = board[id];
}

const isEmpty = (subject, writer, content) => {
  if (subject.length === 0) throw new Error("제목을 입력해주세요");
  if (writer.length === 0) throw new Error("작성자를 입력해주세요");
  if (content.length === 0) throw new Error("내용을 입력해주세요");
};

const modifyHandler = (e) => {
  e.preventDefault();
  const subject = e.target.subject.value;
  const writer = e.target.writer.value;
  const content = e.target.content.value;

  try {
    isEmpty(subject, writer, content);
    board.subject = subject;
    board.writer = writer;
    board.content = content;

    const boardsStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", boardsStr);
    location.href = "/board/view.html" + idx;
  } catch (e) {
    alert(e.message);
    console.error(e);
  }
};

const backBtn = document.querySelector("#back");

const backBtnHandler = (e) => {
  location.href = document.referrer;
};

modifyFrm.addEventListener("submit", modifyHandler);
backBtn.addEventListener("click", backBtnHandler);

뒤로가기 버튼이 잘 작동하는 모습

오늘은 이전에 만들었던 게시판에 list 페이지와 view 페이지를 만들어 보자!

파일 목록

📦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)

게시판 구현하기

게시판 구조

게시판 구조

리스트로 게시물 보여주기 (Read)

# public/js/list.js

현재 리스트 파일의 코드는 다음과 같다.

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

localStrorage에서 boards라는 키를 가진 데이터를 불러오는데 없으면 빈 배열을 boards에 담아 저장하고 있다.

list 페이지는 write 페이지에서 저장한 데이터를 보여주는 페이지이다.

write 페이지에 데이터를 생성한 다음 list.js 파일에서 데이터를 불러오는 작업을 한번 진행해보자!

write 페이지에서 데이터 생성하기

데이터를 생성해 주었다면 list.js 파일에서 localStorage에 저장된 데이터를 가져와보자!

# public/js/list.js

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

console.log(boardsObj);

list.js 파일에서 localStorage의 데이터를 불러온 모습

가져온 데이터를 아래 형태에 맞게 tbody에 넣어 주어야 한다.

<tr>
  <td>번호</td>
  <td>글제목</td>
  <td>작성자</td>
  <td>등록일</td>
  <td>조회수</td>
</tr>

위 형식에 맞는 템플릿을 만드는 함수를 생성해 보자!

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

const template = () => {
  return `
  <tr>
    <td>번호</td>
    <td>글제목</td>
    <td>작성자</td>
    <td>등록일</td>
    <td>조회수</td>
  </tr>
  `;
};

console.log(template());

template함수 실행 결과

template 함수에 두 개의 매개변수를 생성해서 boards의 데이터에 있는 값들이 들어갈 수 있도록 해보자!

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

const template = (index, objValue) => {
  return `
  <tr>
    <td>${index + 1}</td>
    <td>${objValue.subject}</td>
    <td>${objValue.writer}</td>
    <td>${objValue.date}</td>
    <td>${objValue.views}</td>
  </tr>
  `;
};

for (let i = 0; i < boardsObj.length; i++) {
  console.log(template(i, boardsObj[i]));
}

template 함수에서 index와 objValue를 받을 수 있도록 했다.

index는 list 페이지에서 보여줄 데이터의 순서로 실제 boards 안의 index를 의미하지는 않는다.

objValue는 i 값에 해당하는 index에 위치한 boards안에 있는 데이터를 의미한다.

매개변수를 추가한 templage 함수 실행 결과

이제 tbody 요소를 선택한 다음 이 값을 innerHTML을 이용하여 tbody 안에 넣어주면 된다.

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

const template = (index, objValue) => {
  return `
  <tr>
    <td>${index + 1}</td>
    <td>${objValue.subject}</td>
    <td>${objValue.writer}</td>
    <td>${objValue.date}</td>
    <td>${objValue.views}</td>
  </tr>
  `;
};

const tbody = document.querySelector("tbody");

for (let i = 0; i < boardsObj.length; i++) {
  tbody.innerHTML += template(i, boardsObj[i]);
}

list 페이지에 데이터가 잘 출력되는 모습

list 페이지에 원하는 결과가 잘 출력되고 있다.

이번에는 글제목을 클릭하면 해당하는 게시물로 이동할 수 있도록 해보자!

그리고 url에 이게 몇 번째 게시물인지 알 수 있도록 매개변수로 데이터의 인덱스를 넘겨주자!

url에 매개변수를 넘겨주는 이유는 view 페이지에서는 몇 번째 게시물을 클릭해서 들어왔는지 알 수 없기 때문에 url에 매개변수를 넘겨주는 것이다. 

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

const template = (index, objValue) => {
  return `
  <tr>
  <td>${index + 1}</td>
  <td><a href="/board/view.html?index=${objValue.index}">${objValue.subject}</a></td>
  <td>${objValue.writer}</td>
  <td>${objValue.date}</td>
  <td>${objValue.views}</td>
  </tr>
  `;
};

const tbody = document.querySelector("tbody");

for (let i = 0; i < boardsObj.length; i++) {
  tbody.innerHTML += template(i, boardsObj[i]);
}

a 태그의 href 속성을 이용해 페이지를 이동할 수 있도록 하였다. 그리고 해당하는 데이터의 index값도 같이 넘겨주었다.

해당 게시물을 선택하면 url이 바뀌는 모습

쿼리 스트링 (Query String)
쿼리 스트링은 url 부분에 ? 뒤에 붙은 매개변수들로 구성된다.
? 뒤에 붙은 매개변수들은 key=value 형태로 이루어진다. 
자바스크립트에서 전달한 파라미터를 이용하기 위해 window.location 객체를 이용한다.

위에 그림을 보면 해당 게시물을 클릭했을 때 url 부분이 바뀌는 걸 볼 수 있다. 

형태를 살펴보면 다음과 같다.

쿼리 스트링 형태

이제 view 페이지에서 넘겨받은 인덱스 값을 이용해 해당하는 데이터를 보여주는 작업을 해보자!

게시물 클릭 시 해당 게시물 보여주기 (Read)

# public/js/view.js

데이터를 가져오는 작업부터 하자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

console.log(boardsStr);
console.log(boardsObj);

localStorage에서 데이터를 가져와서 원본 형태로 변환한 모습

데이터를 잘 가져왔다면 이번에는 url의 쿼리 스트링을 가져와서 필요한 형태로 만들어보자!

쿼리스트링을 가져오려면 location 객체의 search 속성을 이용하면 된다.

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
console.dir(location);
console.log(idx);

쿼리스트링 가져오기

우리가 필요한 건 index=0에서 0 부분이다.

0 부분을 split 메서드를 사용해 잘라내자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];

console.log(idx);
console.log(idx.split("="));
console.log(index);

필요한 부분을 잘라낸 결과

잘라낸 인덱스를 이용해 원하는 데이터를 출력해보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

console.log(board);

boards 데이터를 출력한 결과

이제 이 데이터를 위치에 맞게 뿌려주면 된다. 

뿌려줄 위치를 선택해보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const viewFrm = document.querySelectorAll("#viewFrm > div");
console.log(viewFrm);

데이터를 출력할 위치를 선택한 모습

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

데이터가 잘 출력되는 모습

for 문을 사용해 div 태그의 i번째 id 값을 가져온다.

boards 데이터에 id와 같은 이름을 가진 속성을 선택해 해당하는 값을 i번째 div에 넣어주었다.

[+추가] 조회수 구현

게시물을 클릭하면 조회수가 올라가도록 해보자!

주의해야 할 점은 현재 글을 작성하면 작성한 글을 바로 보여주게 되어있기 때문에 글 작성 직후에는 조회수가 올라가지 않도록 해줘야 한다.

그래서 나는 document.referrer를 사용했다.

document.referrer에는 이전 페이지의 url이 담겨 있기 때문이다. 

직접 한번 확인해 보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

console.log(document.referrer);

이전 페이지인 list.html이 출력되는 모습

이전 페이지의 url을 원하는 형태로 잘라보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

const beforeUrl = document.referrer;

console.log(beforeUrl);
console.log(beforeUrl.split("/"));
console.log(beforeUrl.split("/").pop());

이전 url을 원하는 형태로 자른 모습

이제 이전 페이지가 list.html 일 때만 조회수가 올라갈 수 있도록 코드를 구현해 보자!

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

조회수가 잘 작동하는 모습

Javascript 코드

# public/js/list.js

let boardsStr = localStorage.getItem("boards");

if (boardsStr === null) {
  const listStr = JSON.stringify([]);
  localStorage.setItem("boards", listStr);
  boardsStr = listStr;
}

const boardsObj = JSON.parse(boardsStr);

const template = (index, objValue) => {
  return `
  <tr>
  <td>${index + 1}</td>
  <td><a href="/board/view.html?index=${objValue.index}">${objValue.subject}</a></td>
  <td>${objValue.writer}</td>
  <td>${objValue.date}</td>
  <td>${objValue.views}</td>
  </tr>
  `;
};

const tbody = document.querySelector("tbody");

for (let i = 0; i < boardsObj.length; i++) {
  tbody.innerHTML += template(i, boardsObj[i]);
}

# public/js/view.js

const boardsStr = localStorage.getItem("boards");
const boardsObj = JSON.parse(boardsStr);

const idx = location.search;
const index = idx.split("=")[1];
const board = boardsObj[index];

const beforeUrl = document.referrer;

const viewCount = (beforeUrl) => {
  if (beforeUrl.split("/").pop() === "list.html") {
    board.views++;
    const viewCountStr = JSON.stringify(boardsObj);
    localStorage.setItem("boards", viewCountStr);
  }
};

viewCount(beforeUrl);

const viewFrm = document.querySelectorAll("#viewFrm > div");

for (let i = 0; i < viewFrm.length; i++) {
  const id = viewFrm[i].id;
  viewFrm[i].innerHTML += " " + board[id];
}

오늘은 게시판 만들어 보자!

이번에는 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;
}