HTML,CSS, JS/JavaScript

[JavaScript] 29. JavaScript를 사용한 글쓰기 기능 만들기

Song hyun 2024. 8. 28. 10:09
728x90
반응형

[JavaScript] 29. JavaScript를 사용한 글쓰기 기능 만들기

 

 

 

1. 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);
          })
      }); 
    }

      // 하나의 게시글 클릭 시 상세보기 화면 이동 처리
      function postClickListeners(){
        
      }
      
      // 글쓰기 버튼 눌렀을 경우 -> 글쓰기 페이지 이동 처리 
      writeButton.onclick = function() {
        location.href = "board-write.html";
    }
  });

 

2. board-write.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/boardWrite.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="form-title">
            <h1>글쓰기 by JS</h1>
        </section>

        <section>
            <table>
                <tr>
                    <th>제목</th>
                    <td>
                        <input type="text" class="inputs title">
                    </td>
                </tr>
                <tr>
                    <th>작성자</th>
                    <td>
                        <input type="text" class="inputs username" readonly>
                    </td>
                </tr>
                <tr>
                    <th>첨부파일</th>
                    <td>
                        <input type="file" class="file" accept="image/*">
                    </td>
                </tr>
                <tr>
                    <th>내용</th>
                    <td>
                        <div class="img-box" >이미지 미리보기</div>
                        <textarea name="" id="" class="content"></textarea>
                    </td>
                </tr>
            </table>

            <div class="btn-area">
                <button type="button" class="btn">글쓰기</button>
            </div>

        </section>
      
    </main>
      
      <script src="../js/header.js"></script>
      <script src="../js/boardWrite.js"></script>
</body>
</html>

 

3. boardWrite.css

.content {
    width: 95%;
    height: 300px;
    font-size: 18px;
    border: none;
    resize: none;
    margin-top: 5px;
}

.img-box {
    width: 100%;
    display: flex;
    justify-content: center;
}

.img-box img {
    width: 100%; 
    max-width: 300px;
}

 

4. boardWrite.js

// 로그인 상태 여부 
redirectToPageIfNotLoggedIn('sign-in');

document.addEventListener('DOMContentLoaded', function() {
    
    // 사용 할 요소에 접근 
    const title = document.querySelector('.title');
    const username = document.querySelector('.username');
    const fileInput = document.querySelector('.file');
    const imgViewBox = document.querySelector('.img-box');
    const content = document.querySelector('.content');
    const button = document.querySelector('button');
    const day = new Date(); 

    // 사용자 선택한 이미지를 저장할 공간이 필요 
    let imageData = null; 
    // 사용자 정보 가져 오기 
    const getUser = JSON.parse(localStorage.getItem('user'));
    username.value = getUser.username;

    // 파일 미리보기 기능 만들기
    function fileUpload(event) {
        
        const file = event.target.files[0];
        //console.log('file', file);
        // 파일 크기 유효성 검사 
        // 1024 * 1024  * 5 =  (5MB) 이하만 허용 
        if(file.size >= 5242880) { 
            alert('첨부 파일은 5MB 이하만 가능 합니다');
            event.target.value = "";
            return; 
        }
        // 파일 타입 유효성 검사 
        const validFileTypes = ['image/jpeg', 'image/png', 'image/gif'];
        if(!validFileTypes.includes(file.type))   {
            alert('유효한 파일 타입이 아닙니다.(jpeg, png, gif만 허용');
            return; 
        } 

        // 파일 미리보기 기능 
        const reader = new FileReader();
        reader.readAsDataURL(file); // Base64 인코딩 바이트 단위 데이터를 -- 64개 문자로 표현하는 인코딩 방식
        reader.onload = function (e) {
            console.log('Base64', e.target.result);
            imgViewBox.innerHTML = `<img src="${e.target.result}" alt="Upload Image">`;
            // 글 저장시에 로컬스토리지에 바이너리 데이터를 -- 64 인코딩 저장 
            imageData = reader.result;
        }
    }

    // 글 저장하는 기능 만들기 
    function saveBoard() {
        // 유효성 검사 
        if(title.value === "") {
            alert('제목을 입력하시오');
            return;
        }
        
        if(content.value === "") {
            alert('내용을 입력하시오');
            return;
        }

        // 로컬 스토리지에 게시글 전체 목록 가져오기 
        let boardList = JSON.parse(localStorage.getItem('boardList') || '[]' );

        // 고유 ID 생성 
        const newId = generateUniqueId(boardList);

        // 객체 리터럴 표기법 사용 
        const board = {
            id: newId, 
            title : title.value,
            content : content.value, 
            username: username.value, 
            today:  `${day.getFullYear()}.${day.getMonth() + 1}.${day.getDate()}`,
            count: 0, 
            imgData: imageData
        };
        
        // 배열에다가 생성한 객체 추가 
        boardList.push(board);

        // 로컬 스토리지에 저장 (배열 전체)
        localStorage.setItem("boardList", JSON.stringify(boardList));
        // 페이지 이동 처리 
        location.href = 'board-list.html';
    }

    // 고유 아이디 생성 함수 
    function generateUniqueId(boardList) {
        return boardList.length > 0  ? boardList[boardList.length - 1].id + 1 :  1 ;
    }
    
    // 이벤트 리스너 등록 처리 - file change 
    fileInput.addEventListener('change', fileUpload);
    button.addEventListener('click', saveBoard);
});
728x90
반응형