개발이야기/JavaScript

[VanilaJs] 게시판 만들기 (1) - 글 목록 조회 및 페이징 처리

dev.josh 2022. 6. 28. 17:00
반응형

1. 글 목록 조회

 

1-1. json 데이터를 localstorage에 저장

//data.json 내용을 로컬스토리지에 문자형식으로 넣는다.
async function dataSet() {
  const data = await fetch("./data.json")
  .then(response => {
    return response.json();
  });
  window.localStorage.setItem('storageList',JSON.stringify(data));
}

개발자도구 -> Application -> Storage -> Local Storage 에 storageList이름의 키로 데이터가 저장된것을 확인 할 수 있다.

 

 

//data.json 내용을 로컬스토리지에 문자형식으로 넣는다.
async function dataSet() {
  let check = () => {
    _storageData = JSON.parse(window.localStorage.getItem('storageList'));
    return _storageData == null || _storageData.length < 0 ? true : false;
  }
  if(check()) { //로컬스토리지 storageList 내용이 없을때만 .jon 데이터를 읽어들여 로컬스토리지에 넣는다.
    const data = await fetch("./data.json")
    .then(response => {
      return response.json();
    });
    window.localStorage.setItem('storageList',JSON.stringify(data));
  }
}

로컬 스토리지 데이터를 항상읽어 들여서 게시물 목록을 초기화 할 수는 없으니

로컬스토리지에 데이터가 0개일때만 data.json을 읽어들여 로컬스토리지에 저장시키도록 예외처리를 해주었다.

 

 

1-2. localstorage에 있는 데이터를 읽어 테이블에 출력

  <section class="main">
    <table class="table">
      <thead>
        <tr>
          <th scope="col">#</th>
          <th scope="col">제목</th>
          <th scope="col">내용</th>
          <th scope="col">작성일</th>
          <th scope="col"></th>
        </tr>
      </thead>
      <tbody id="dataTable">
      	<tr>
          <th scope="row">1</th>
          <td>Mark</td>
          <td>Otto</td>
          <td>@mdo</td>
        </tr>
        <tr>
          <th scope="row">2</th>
          <td>Mark</td>
          <td>Otto</td>
          <td>@mdo</td>
        </tr>
        <tr>
          <th scope="row">3</th>
          <td>Mark</td>
          <td>Otto</td>
          <td>@mdo</td>
        </tr>
      </tbody>
    </table>
  </section>

Bootstrap의 Table 형식을 가져왔고 목록 데이터 바인딩을 진행하기 위해 tbody 태그에 dataTable id를 추가해 주었다.

 

<tr>
  <th scope="row">1</th>
  <td>Mark</td>
  <td>Otto</td>
  <td>@mdo</td>
</tr>

tbody 태그에 자식으로 삽입되어야될 row 1줄의 HTML 형식이다.

해당 형식을 아래 소스에서 만들어 보겠다.

 

//로컬스토리지의 내용을 읽어서 테이블에 데이터를 바인딩시킨다. 
function boardListAction() {
  const dataTable = document.getElementById('dataTable');
  boardListRemove();

  if(_storageData) {
    for(let i=_start; i<_limit; i++) {
      if(_storageData[i]) {
        let tr = document.createElement("tr");
        let th_no = document.createElement("th");
        let td_title = document.createElement("td");
        let td_content = document.createElement("td");
        let td_date = document.createElement("td");

        th_no.setAttribute('scope', 'row');
        th_no.innerText = _storageData[i].no;
        td_title.innerText = _storageData[i].title;
        td_content.innerText = _storageData[i].content.length > 20 ? _storageData[i].content.substr(0, 20) + '...' : _storageData[i].content; //20글자 뒤에는 ... 으로 처리
        td_date.innerText = _storageData[i].date;

        tr.appendChild(th_no);
        tr.appendChild(td_title);
        tr.appendChild(td_content);
        tr.appendChild(td_date);

        dataTable.appendChild(tr);
      }
    }
  }
}

목록 부분을 그리기 전에는 HTML으로 이미 그려져있는 테이블을 지우는 boardListRemove() 함수 사용 후에

localstorage에 있는 데이터를 forEach로 돌며 HTML형식을 만들어 주었다.

내용 부분에서는 글자가 너무 길어지면 모양세가 좋지 않으니,

20글자가 넘아가는 부분을 ... 으로 처리 하였다.

 

결과 화면은 아래와 같다.

...

1000개의 row가 한 화면에 출력되는 모습..



 

2. 페이징처리

 

2-1.페이징 목록 출력

  <section>
    <nav aria-label="..." id="page_area" style="display: flex; justify-content: center;">
      <ul id="page_ul" class="pagination">
        <li class="page-item disabled">
          <span class="page-link">Previous</span>
        </li> 
        <li class="page-item"><a class="page-link" href="#">1</a></li>
        <li class="page-item active"><span class="page-link">2</span></li>
        <li class="page-item"><a class="page-link" href="#">3</a></li>
        <li class="page-item"><a class="page-link" href="#">4</a></li>
        <li class="page-item"><a class="page-link" href="#">5</a></li>
        <li class="page-item"><a class="page-link" href="#">6</a></li>
        <li class="page-item"><a class="page-link" href="#">7</a></li>
        <li class="page-item"><a class="page-link" href="#">8</a></li>
        <li class="page-item"><a class="page-link" href="#">9</a></li>
        <li class="page-item"><a class="page-link" href="#">10</a></li>
        <li class="page-item">
          <a class="page-link" href="#">Next</a>
        </li>
      </ul>
    </nav>
  </section>

 

페이징 목록을 그리기위한 HTML 형식이다. 

nav하위에 ul li 태그들은 지워준 상태에서 ul 태그의 page_ul 부분 부터는 스크립트로 처리해 보겠다.

 

 

let _page = 1; //현재 페이지 번호
let _start = 0; //조회 시작 데이터 배열 번호
let _limit = 10; //조회 마지막 데이터 배열 번호

해당 변수를 글로벌 변수로 지정해놓고,

 

//페이지 컨트롤 영역 그리기
function renderPagination(totalCount, currentPage) {
  if (totalCount.length <= 10) return; //게시물 데이터가 10개 이하이면 페이지 영역을 그리지 않는다.
  
  let totalPage = Math.ceil(totalCount.length / 10); //페이징 목록의 총 개수를 구한다.
  let pageGroup = Math.ceil(currentPage / 10); //한 페이징 목록은 10개씩 구성한다.

  let last = pageGroup * 10;
  if (last > totalPage) last = totalPage;
  let first = last - (10 - 1) <= 0 ? 1 : last - (10 - 1);
  let next = last + 1;
  let prev = first - 1;

  let pageList = document.createElement('ul');
  pageList.id = 'page_ul';
  pageList.className = 'pagination';

  if (prev > 0) { //이전페이징 버튼
    const preli = document.createElement('li');
    preli.className = 'page-item';
    preli.onclick = () => prePage(first);
    preli.insertAdjacentHTML("beforeend", `<span class="page-link">Previous</span>`);
    pageList.appendChild(preli);
  }
	
  for (let i = first; i <= last; i++) { //페이징목록 숫자 버튼
    const li = document.createElement("li");
    li.className = 'page-item-' + i;
    li.onclick = () => movePage(i);
    li.insertAdjacentHTML("beforeend", `<a class="page-link" href="#">${i}</a>`);
    pageList.appendChild(li);
  }

  if (last < totalPage) { //다음페이징 버튼
    const endli = document.createElement('li');
    endli.className = 'page-item';
    endli.onclick = () => nextPage(last);
    endli.insertAdjacentHTML("beforeend", `<a class="page-link" href="#">Next</a>`);
    pageList.appendChild(endli);
  }

  document.getElementById('page_area').appendChild(pageList); //최종 페이징을 HTML에 바인딩

  let pageItem = document.getElementsByClassName(`page-item-${currentPage}`);
  pageItem[0].classList.add("active"); //페이징 번호 active 스타일 적용 처리
};
renderPagination() 함수 에서는 총 개수와, 현재 페이지 를 받아서 페이지를 그려줬다.
 
//data.json 내용을 로컬스토리지에 문자형식으로 넣는다.
async function dataSet() {
  let check = () => {
    _storageData = JSON.parse(window.localStorage.getItem('storageList'));
    return _storageData == null || _storageData.length < 0 ? true : false;
  }
  if(check()) { //로컬스토리지 storageList 내용이 없을때만 .jon 데이터를 읽어들여 로컬스토리지에 넣는다.
    const data = await fetch("./data.json")
    .then(response => {
      return response.json();
    });
    window.localStorage.setItem('storageList',JSON.stringify(data));
  }
  boardListAction(); //게시물 목록 바인딩
  renderPagination(_storageData, _page); //페이징 바인딩
}

최초 실행되는 dataSet() 함수에서

1. boardListAction() 게시물을 불러오고

2.renderPagination(로컬스토리지 데이터, 현재페이지:디폴트 1)

순서대로 실행 시켜주었다.

 

//페이지 이동
function movePage(page) {
  _page = page;
  _start = Number(String(page) + '0') - 10;
  _limit = Number(String(page) + '0');

  let activeItem = document.getElementsByClassName('active'); 
  activeItem[0].classList.remove('active') //기존 active 스타일 제거
  let pageItem = document.getElementsByClassName(`page-item-${page}`);
  pageItem[0].classList.add("active"); //페이징 번호 active 스타일 적용 처리

  boardListAction();
}

//페이징 다음 목록 이동
function nextPage(last) {
  let page_ul = document.getElementById('page_ul');
  while (page_ul.hasChildNodes() ) {
    page_ul.removeChild(page_ul.firstChild); 
  }
  page_ul.remove();

  renderPagination(_storageData, last+1); //다음 페이지는 10의배수+1에서 시작
  movePage(last+1)
}

//페이징 이전 목록 이동
function prePage(first) {
  let page_ul = document.getElementById('page_ul');
  while (page_ul.hasChildNodes() ) {
    page_ul.removeChild(page_ul.firstChild); 
  }
  page_ul.remove();

  renderPagination(_storageData, first-10); //이전 페이지는 현재의 시작페이지의 -10에서 시작
  movePage(first-10)
}

renderPagination()함수가 그려준 페이징에서 사용될 함수이다. 페이지를 그려줄때마다.

first, last 변수를 새로 대입시키며 해당 변수를 인자로 받는 페이지이동 movePage() , 다음 페이지 nextPage(), 이전 페이지 prePage()함수이다.

 

 

2-2. 페이지에 맞게 게시물 목록 수정

1-2. 에서 게시물 1000개를 한번에 출력했는데 이부분을 이제 페이지에 맞게 수정해 줘야 한다.

boardListAction() 함수를 수정해주었다.

//로컬스토리지의 내용을 읽어서 테이블에 데이터를 바인딩시킨다. 
function boardListAction() {
  const dataTable = document.getElementById('dataTable');
  boardListRemove();

  if(_storageData) {
    for(let i=_start; i<_limit; i++) {
      if(_storageData[i]) {
        let tr = document.createElement("tr");
        let th_no = document.createElement("th");
        let td_title = document.createElement("td");
        let td_content = document.createElement("td");
        let td_date = document.createElement("td");

        th_no.setAttribute('scope', 'row');
        th_no.innerText = _storageData[i].no;
        td_title.innerText = _storageData[i].title;
        td_content.innerText = _storageData[i].content.length > 20 ? _storageData[i].content.substr(0, 20) + '...' : _storageData[i].content; //20글자 뒤에는 ... 으로 처리
        td_date.innerText = _storageData[i].date;

        tr.appendChild(th_no);
        tr.appendChild(td_title);
        tr.appendChild(td_content);
        tr.appendChild(td_date);

        dataTable.appendChild(tr);
      }
    }
  }
}

위에서 선언한 _start와 _limit 변수를 가지고와서 해당 페이지에서 보여줄 부분을 10개씩 조회하도록 처리 하였다.

 

console.log.&nbsp; &nbsp; _start, _limit

1페이지  _start = 0, _limit = 10

2페이지 _start = 10, _limit = 20

3페이지 _start = 20, _limit = 30

4페이지 _start = 30, _limit = 40

...

이런식으로 _start와 _limit 번호가 부여되게 되고, boardListAction()에서 게시물 데이터를 10개씩 바인딩 시키도록 작업 하였다.

 

 

 

 

참고자료

https://getbootstrap.com/docs/4.1/content/tables/#examples

 

Tables

Documentation and examples for opt-in styling of tables (given their prevalent use in JavaScript plugins) with Bootstrap.

getbootstrap.com

 

https://getbootstrap.com/docs/4.0/components/pagination/#disabled-and-active-states

 

Pagination

Documentation and examples for showing pagination to indicate a series of related content exists across multiple pages.

getbootstrap.com

 

https://www.mockaroo.com/

 

Mockaroo - Random Data Generator and API Mocking Tool | JSON / CSV / SQL / Excel

Mock your back-end API and start coding your UI today. It's hard to put together a meaningful UI prototype without making real requests to an API. By making real requests, you'll uncover problems with application flow, timing, and API design early, improvi

www.mockaroo.com

 

https://velog.io/@eunoia/JS%EB%A1%9C-Pagination-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

 

JS로 Pagination 구현하기

출처페이지네이션이란 콘텐츠를 여러 페이지고 나누고, 이전 혹은 다음 페이지로 넘어가거나 특정 페이지로 넘어갈 수 있는 일련의 링크를 페이지 상단이나 하단에 배치하는 방법입니다.게시

velog.io

 

 

전체소스

https://github.com/Jo-App/vanillaJs_Board/tree/board-1

 

GitHub - Jo-App/vanillaJs_Board

Contribute to Jo-App/vanillaJs_Board development by creating an account on GitHub.

github.com

 

반응형