본문 바로가기

Java

JavaScript 게시판 만들기 - board-list 기능 만들기


디자인 시안 확인
board-list.html

 

 

 

 

 

header.js
// DOMContentLoaded 이벤트를 사용해 보자.
document.addEventListener("DOMContentLoaded", function () {
  // DOM 요소를 가져오기
  const boardMenu = document.getElementById("board");
  const signInMenu = document.getElementById("signIn");
  const signUpMenu = document.getElementById("signUp");
  const authLinks = document.getElementById("authLinks");

  // 로그인 여부 확인
  const user = localStorage.getItem("user");
  if (user !== null) {
    if (authLinks) {
      // 로그인, 회원가입 링크를 로그아웃 링크로 변경
      authLinks.innerHTML = '<span class="menu-link" id="logoutLink">로그아웃</span>';

      // 로그아웃 클릭 시 처리
      document.getElementById("logoutLink").addEventListener("click", function () {
        localStorage.removeItem("user");

        // 로그아웃 후 페이지를 새로고침 해야 렌더링이 된다.
        location.reload();
      });
    }
  }

  // 각 메뉴에 클릭 이벤트를 추가합니다.
  if (boardMenu) {
    boardMenu.addEventListener("click", function () {
      window.location.href = "board-list.html";
    });
  }

  if (signInMenu) {
    signInMenu.addEventListener("click", function () {
        window.location.href = "sign-in.html";
    });
  }

  if (signUpMenu) {
    signUpMenu.addEventListener("click", function () {
        window.location.href = "sign-up.html";
    });
  }
});

 

 

사전기반 지식

자바스크립트의 if문 조건식의 평가 방식

if (조건) {
    // 조건이 true일 때 실행되는 코드
}
자바스크립트에서 if 문 안에 들어가는 조건식은 Boolean 값으로 평가됩니다. 
즉, 조건식이 true로 평가되면 코드 블록이 실행되고, false로 평가되면 실행되지 않습니다.

 

 

  • 자바 스크립트 "truthy"와 "falsy”
  • 다음은 자바스크립트에서 거짓(false)으로 평가되는 "falsy" 값
false: Boolean 값 false
0: 숫자 0
"": 빈 문자열 (길이가 0인 문자열)
null: 값이 없음을 나타내는 값
undefined: 정의되지 않은 상태를 나타내는 값
NaN: 숫자가 아님을 나타내는 값 (Not-a-Number)

if (0) {
    // 이 코드는 실행되지 않습니다. (0은 falsy 값이므로)
}

 

 

위에 나열된 "falsy" 값들을 제외한 모든 값은 "truthy" 값으로 간주됩니다. 즉, if 문에서 참(true)으로 평가됩니다.

true
1, -1, 또는 다른 모든 숫자
"hello"와 같은 길이가 1 이상인 문자열
객체 ({}, [] 등)

if ("둘리") {
    // 이 코드는 실행됩니다. ("둘리"는 truthy 값이므로)
}

if (1) {
    // 이 코드는 실행됩니다. ("둘리"는 truthy 값이므로)
}

if (-1) {
    // 이 코드는 실행됩니다. ("둘리"는 truthy 값이므로)
}

 

 

board-list.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>게시글목록</title>
    <link rel="stylesheet" href="../css/common.css" />
    <link rel="stylesheet" href="../css/header.css" />
    <link rel="stylesheet" href="../css/boardList.css" />
  </head>
  <body>
    <!-- 헤더 섹션과 네비 영역 -->
    <header>
      <nav class="nav-container">
        <div class="nav-item">
          <span class="menu-link" id="board">게시판</span>
        </div>
        <div class="nav-item" id="authLinks">
          <span class="menu-link" id="signIn">로그인</span>
          <span class="menu-link" id="signUp">회원가입</span>
        </div>
      </nav>
    </header>

    <main class="content-wrapper">
      <section class="title-box">
        <h1>게시글 상세보기 by JS</h1>
      </section>
      
      <div class="content-box">
      <div class="board-box">
        <div class="head">
          <div class="head-1">번호</div>
          <div class="head-2">제목</div>
          <div class="head-3">작성자</div>
          <div class="head-4">작성일</div>
          <div class="head-5">조회수</div>
        </div>
        <div class="board-content-box">
          <!-- 삭제 예정 : js 코드로 반복문 활용으로 동적으로 컨텐트 만들기 -->
          <div class="board">
            <div class="board-1">1</div>
            <div class="board-2">후미진 어느 언덕에서 JS</div>
            <div class="board-3">둘리</div>
            <div class="board-4">2024.08.26</div>
            <div class="board-5">11</div>
          </div>
        </div>
        <!-- 페이징 영역 -->
        <div class="botton-box">
          <div class="page-box">
            <span class="left">◀</span>
            <div class="num-box">
              <span class="nul">1</span>
            </div>
            <span class="right">▶</span>
          </div>

          <div class="write-button-box">
            <button type="button" class="btn">글쓰기</button>
          </div>
        </div>
      </div>
    </div>
    </main>

    <script src="../js/header.js"></script>
    <script src="../js/boardList.js"></script>
  </body>
</html>

 

 

boardList.css
.content-box {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 1000px;
}

.board-box {
    display: flex;
    flex-direction: column;
}

.head, 
.board {
    border-bottom: 1px solid black;
    display: flex;
    justify-content: space-between;
    padding: 10px;
    align-items: center;
}

/* 제목, content 공각 영역 맞추기  */
.head-1, 
.board-1 {
    /* flex 속성 (고정 너비) */
    flex: 0 0 80px;
}

.head-2,
.board-2 {
    /* 가변 너비 */
    flex: 1;  
    text-align: left;
    padding-left: 10px;
    padding-right: 10px;
}

.head-3,
.board-3,
.head-4,
.board-4,
.head-5, 
.board-5
{
    /* 고정 너비 */
    flex: 0 0 100px;
}

/* 하단 영역 */
.button-box{
    display: flex;
    flex-direction: column;
    margin-top: 20px;
} 

.page-box {
    display: flex;
    justify-content: center;
    padding: 20px;
}

.write-button-box {
    display: flex;
    justify-content: flex-end;
}

 

 

boardList.js
// 샘플 데이터 입력
const sampleBoardList = [
  {
    id: 1,
    title: "첫번째 게시글",
    content: "첫번째 게시글에 내용 입니다.",
    username: "홍길동",
    today: "2024.08.25",
    count: 5,
  },
  {
    id: 2,
    title: "두번째 게시글",
    content: "두번째 게시글에 내용 입니다.",
    username: "이몽룡",
    today: "2024.08.25",
    count: 5,
  },
  {
    id: 3,
    title: "세번째 게시글",
    content: "세번째 게시글에 내용 입니다.",
    username: "성춘향",
    today: "2024.08.25",
    count: 14,
  },
  {
    id: 4,
    title: "네번째 게시글",
    content: "네번째 게시글에 내용 입니다.",
    username: "변학도",
    today: "2024.08.25",
    count: 21,
  },
  {
    id: 5,
    title: "다번째 게시글",
    content: "다번째 게시글에 내용 입니다.",
    username: "심청",
    today: "2024.08.25",
    count: 51,
  },
];
// localStorage.setItem('boardList', JSON.stringify(sampleBoardList));

document.addEventListener("DOMContentLoaded", function () {
  // DOM 접근
  const boardContainer = document.querySelector(".board-content-box"); // 컨텐트를 넣을 Element 선택
  const writeButton = document.querySelector(".btn"); // 글쓰기 버튼 Element 선택
  const paginationContainer = document.querySelector(".num-box");

  // 로컬 스토리지에서 게시글 목록 가져오기
  const storedBoardList = JSON.parse(localStorage.getItem("boardList"));
  // 게시글 목록을 내림차수능로 정렬하기
  if (storedBoardList) {
    storedBoardList.reverse();
  }

  // 페이징 처리 필요한 변수
  let currentPage = 0;
  const limit = 2; // 한 페이지당 게시글 수

  loadPosts(currentPage);

  // 게시글을 로드 하는 함수
  function loadPosts(page) {
    const offset = page * limit;
    const end = offset + limit;

    let postElements = ""; // 게시긁 HTML 요소을 저장할 변수

    // 방어적 코드 작성
    if (storedBoardList != null && storedBoardList.length > 0) {
      // 반복문을 사용 ()
      for (let i = offset; i < end && i < storedBoardList.length; i++) {
        postElements += `<div class="board" data-id=${storedBoardList[i].id}>
                <div class="board-1">${i + 1}</div>
                <div class="board-2">${storedBoardList[i].title}</div>
                <div class="board-3">${storedBoardList[i].username}</div>
                <div class="board-4">${storedBoardList[i].today}</div>
                <div class="board-5">${storedBoardList[i].count}</div>
              </div>`;
      }

      boardContainer.innerHTML = postElements; // 게시글 컨테이너에 HTML 추가

      // 페이지 네이션 생성 하기
      createPagination(storedBoardList, page);
    } else {
      // 게시글이 없는 경우 메세지 표시
      boardContainer.innerHTML = '<div class="no-list"  style="text-align:center; margin-top:20px"> 조회된 게시글이 없습니다 </div>';
    }
  }

  // 페이지 네이션 생성 함수 선언
  function createPagination(boardList, currentPage) {
    // 전체 게시글 수 , 한 페이장 보여질 게시글 수
    const totalPosts = boardList.length; // 전체 게시글 수
    const totalPages = Math.ceil(totalPosts / limit); // 전체 페이지 수
    // 페이지 번호 HTML 저장할 변수
    let paginationHTML = "";
    for (let i = 0; i < totalPages; i++) {
      paginationHTML += `<span class="num" data-page="${i}">${i + 1}</span>`;
    }
    
    paginationContainer.innerHTML = paginationHTML;

    // 생성된 페이지 번호의 요소 접근 (동적 할당)
    const pageNumbers = document.querySelectorAll('.num');
    
    // 현재 페이지 번호에 스타일 적용 
    pageNumbers[currentPage].style.backgroundColor = 'grey';
    pageNumbers[currentPage].style.fontWeight = 600;

    pageNumbers.forEach( (pageNumber) => {
        pageNumber.addEventListener('click', (event) => {
            // console.log('event', event);
            // console.log('event.target', event.target);
            // console.log('event.target.dataset', event.target.dataset);
            // console.log('event.target.dataset.page', event.target.dataset.page);
            // 해당하는 번호를 가지고 와서 다시 렌더링 
            const targetPageNumber =  parseInt(event.target.dataset.page); // 문자열 --> number 변환 
            loadPosts(targetPageNumber);
        })
    }); 

    // 글쓰기 버튼 눌렀을 경우 -> 글쓰기 페이지 이동 처리 
    writeButton.onclick = function() {
        location.href = "board-write.html";
    }

    // 해당 row 게시글을 눌렀을 경우 -> 상세보기 화면 이동 처리 

  }
});

 

 

사전기반 지식

data- 속성 및 dataset 개념

1. data-* 속성

data-* 속성은 HTML5에서 제공하는 사용자 정의 데이터 속성입니다. 이 속성을 사용하면 HTML 요소에 데이터를 저장할 수 있으며, 이 데이터는 JavaScript로 쉽게 접근할 수 있습니다.

  • data-* 속성의 이름은 반드시 data-로 시작하고, 그 뒤에 사용자 정의 키를 붙일 수 있습니다. 예를 들어, data-id, data-user, data-index 등이 가능합니다.
<div data-id="123" data-name="둘리1"></div>

 

 

2. dataset 객체

dataset 객체는 JavaScript에서 HTML 요소의 data-* 속성에 접근할 수 있도록 해주는 특수한 객체입니다. dataset 객체를 사용하면 data-*로 시작하는 모든 속성에 접근할 수 있으며, 이를 통해 데이터 값을 가져오거나 변경할 수 있습니다.
const myDiv = document.getElementById('myDiv');

// data-id 속성에 접근
console.log(myDiv.dataset.id); // 출력: 123

// data-name 속성에 접근
console.log(myDiv.dataset.name); // 출력: 둘리

// data-name 속성의 값을 변경
myDiv.dataset.name = "고길동";
console.log(myDiv.dataset.name); // 출력: 고길동

 

 

샘플 게시 글 확인

 

 

 

boardList.js 코드 추가
조회수 증가 로직, 비동기 처리 개념 잡아 보기
document.addEventListener("DOMContentLoaded", function () {
  // DOM 접근
  const boardContainer = document.querySelector(".board-content-box"); // 컨텐트를 넣을 Element 선택
  const writeButton = document.querySelector(".btn"); // 글쓰기 버튼 Element 선택
  const paginationContainer = document.querySelector(".num-box");

  // 로컬 스토리지에서 게시글 목록 가져오기
  const storedBoardList = JSON.parse(localStorage.getItem("boardList"));
  
  // 게시글 목록을 내림차수능로 정렬하기
  if (storedBoardList) {
    storedBoardList.reverse();
  }

  // 페이징 처리 필요한 변수
  let currentPage = 0;
  const limit = 2; // 한 페이지당 게시글 수

  loadPosts(currentPage);

  // 게시글을 로드 하는 함수
  function loadPosts(page) {
    const offset = page * limit;
    const end = offset + limit;

    let postElements = ""; // 게시긁 HTML 요소을 저장할 변수

    // 방어적 코드 작성
    if (storedBoardList != null && storedBoardList.length > 0) {
      // 반복문을 사용 ()
      for (let i = offset; i < end && i < storedBoardList.length; i++) {
        postElements += `<div class="board" data-id=${storedBoardList[i].id}>
                <div class="board-1">${i + 1}</div>
                <div class="board-2">${storedBoardList[i].title}</div>
                <div class="board-3">${storedBoardList[i].username}</div>
                <div class="board-4">${storedBoardList[i].today}</div>
                <div class="board-5">${storedBoardList[i].count}</div>
              </div>`;
      }

      boardContainer.innerHTML = postElements; // 게시글 컨테이너에 HTML 추가
      
      // 동적으로 생성한 rows 들 전체를 함수로 전달 시킴 
      const postElementsCollection = document.querySelectorAll('.board');
      postClickListeners(postElementsCollection);
      

      // 페이지 네이션 생성 하기
      createPagination(storedBoardList, page);
    } else {
      // 게시글이 없는 경우 메세지 표시
      boardContainer.innerHTML = '<div class="no-list"  style="text-align:center; margin-top:20px"> 조회된 게시글이 없습니다 </div>';
    }
  }

  // 페이지 네이션 생성 함수 선언
  function createPagination(boardList, currentPage) {
    // 전체 게시글 수 , 한 페이장 보여질 게시글 수
    const totalPosts = boardList.length; // 전체 게시글 수
    const totalPages = Math.ceil(totalPosts / limit); // 전체 페이지 수
    // 페이지 번호 HTML 저장할 변수
    let paginationHTML = "";
    for (let i = 0; i < totalPages; i++) {
      paginationHTML += `<span class="num" data-page="${i}">${i + 1}</span>`;
    }
    
    paginationContainer.innerHTML = paginationHTML;

    // 생성된 페이지 번호의 요소 접근 (동적 할당)
    const pageNumbers = document.querySelectorAll('.num');
    
    // 현재 페이지 번호에 스타일 적용 
    pageNumbers[currentPage].style.backgroundColor = 'grey';
    pageNumbers[currentPage].style.fontWeight = 600;

    pageNumbers.forEach( (pageNumber) => {
        pageNumber.addEventListener('click', (event) => {
            // console.log('event', event);
            // console.log('event.target', event.target);
            // console.log('event.target.dataset', event.target.dataset);
            // console.log('event.target.dataset.page', event.target.dataset.page);
            // 해당하는 번호를 가지고 와서 다시 렌더링 
            const targetPageNumber =  parseInt(event.target.dataset.page); // 문자열 --> number 변환 
            loadPosts(targetPageNumber);
        })
    }); 
  }

    // 하나의 게시글 클릭 시 상세보기 화면 이동 처리 
    function postClickListeners(postElements) {
        for(let i = 0; i < postElements.length; i++) {
            postElements[i].onclick = async function() {
                const postId =  postElements[i].getAttribute('data-id');  
                await increaseViewCount(storedBoardList,  postId);
                // 상세 보기 화면 
                location.href = `board-detail.html?id=${postId}`;
            }        
        }            
    }

    // 조회수 증가 로직 만들어 보기  -- 1 단계
    // function increaseViewCount(boardList, postId) {
    //     for(let i = 0; i < boardList.length; i++) {
    //         if(boardList[i].id === parseInt(postId)) {
    //             boardList[i].count += 1; 
    //             break;
    //         }
    //     }
    //     // 스파케티 코드 발생 유발할 수 있음 
    //     localStorage.setItem("boardList", JSON.stringify(boardList.reverse()));
    // }

    // 2단계 - 통신을 통한 로직이라고 가정 
    function increaseViewCount(boardList, postId) {
        return new Promise((resolve) => {
            setTimeout( () => {
                for(let i = 0; i < boardList.length; i++) {
                    if(boardList[i].id === parseInt(postId)) {
                        boardList[i].count += 1; 
                        break;
                    }
                }
                // 스파게티 코드를 유발할 수 있음,  현재 메모리 상태는 reverse() - 다시 순서대로 넣어 주어여 함
                localStorage.setItem("boardList", JSON.stringify(boardList.reverse()));
                // 작업 완료 후 resolve(); 호출
                resolve();
                alert('조회수 증가 후 상세보기 화면으로 이동 됨');
             }, 2000 ); // 2초 딜레이 
        });
    }

    // 글쓰기 버튼 눌렀을 경우 -> 글쓰기 페이지 이동 처리 
    writeButton.onclick = function() {
        location.href = "board-write.html";
    }

});

 

 

 

해당 페이지 캐쉬 메모리 사용 안함
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">

 

 

💡 
자바 스크립트에서 비동치 처리를 프로미스 타입을 사용하면 된다.
(프로미스 타입 사용방법)
2. 자바스크립트로 통신을 하는 경우 (비동기 통신 처리를 많이 한다)
3. 소화 시키는 방법
-1. 동기적 방식으로 만들어 할 때 (async, awaite)
-2. 콜백 메서드 활용( then(), catch())

 

728x90

'Java' 카테고리의 다른 글

JDBC에서의 예외 처리  (1) 2024.06.18
JDBC 성능 최적화  (2) 2024.06.18
JDBC 트랜잭션 관리와 배치 처리  (0) 2024.06.13
래퍼 클래스 ( Wrapper Class )  (0) 2024.06.12
JDBC 기본 사용법  (0) 2024.06.11