ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코로나19 백신 접종 현황 차트 페이지 만들기 - (10) search 페이지, datatable, 검색 기능
    Node.js/예제 2021. 6. 5. 15:35

     

    1. search 페이지

    테이블 라이브러리를 활용하여 엘라스틱서치 인덱스 데이터를 테이블로 시각화하여 제공하는 페이지다. country-vaccinations 인덱스(country_vaccinations.csv 데이터)를 사용하였고, 일자별 접종 기록을 국가별로 제공해준다. 상단 Country search 입력란에 국가명을 입력하면 해당 국가에 대한 데이터가 조회된다.

     

    table.ejs

    
    <%- include('header') %>
    <%- include('dt') %>
      </head>
      <body>
        <div class="container">
          <%- include('navbar') %>
          <div class="tablediv">
            <table id="table" class="display" style="width: 100%">
              <thead>
                <tr id="tableTr"></tr>
              </thead>
            </table>        
          </div>
        </div>
        <script src="js/datatable.js"></script>
        <script>
          
          const ulInnerHtml = document.getElementById("navBar").innerHTML;
          const newBtns = `
            <div class="country-search-area">
              <input class="country-input" type="input" id="countryFilter" onclick="clearInput(this);">
              <label class="label-text">Country search</label>
            </div>
          `;
          document.getElementById("navBar").innerHTML = ulInnerHtml + newBtns;      
          getCountryList();
          drawDataTable("South Korea");
        </script>
      </body>  
    </html>
    

    search 페이지에 대한 frontend 코드는 위와 같다. 공통 헤더와 datatable 모듈, 공통 내비게이션을 불러온다. 내비게이션에는 국가명 검색 기능을 위한 검색창을 추가해주었다. 페이지 시작 시 자동 완성을 위해 국가 리스트를 불러오는 함수인 getCountryList를 호출하고, 테이블을 생성해주는 drawDataTable 함수도 호출한다. drawDataTable은 국가명을 파라미터로 받는데, 첫 페이지 시작 시에는 기본적으로 한국에 대한 데이터가 로딩될 수 있게 'South Korea'를 인자로 넣었다.

     

    2. 서버 코드(table.js)

    router.post('/records', async (req, res) => {
      const { param } = req.body;
      const query = `
        SELECT 
          date,
          country,
          daily_vaccinations,
          daily_vaccinations_per_million,
          vaccines
        FROM "country-vaccinations"
        WHERE country='${param}'
      `;
      try {
        const data = await getDataUsingSql(query, 10000);
        res.json(data)
      } catch (err) {
        console.error(err);
      }
    });

    테이블을 그대로 보여주기 때문에 쿼리는 복잡하지 않다. 테이블 페이지에서 보여줄 컬럼인 날짜(date), 국가(country), 일일 백신 접종자수(daily_vaccinations), 일일 백신 접종률(daily_vaccinations_per_million)을 select절에서 지정하고, where절에서는 검색어로 입력되는 국가명을 받아서 필터링할 수 있게 하였다. 검색창에 입력된 키워드는 ajax에서 파라미터로 전달받는다.

    조회 대상이 많아질 수도 있기 때문에 getDataUsingSql 함수에 두 번째 파라미터인 개수 데이터를 지정하였다. 디폴트값이 1000이기 때문에 개수를 지정하지 않으면 최대 1000개만 조회된다. 그 이상의 양을 조회할 필요가 다면 별도로 최대 조회 데이터 수를 지정해주어야 한다.

     

    3. datatable

    datatable은 테이블 UI를 위한 라이브러리다. 데이터를 테이블 형태로 표현하는 데에 유용하며, 페이지네이션, 결과 내 검색, 정렬 기능 등을 기본적으로 제공해주기 때문에 사용하기에도 편리하다. datatable 홈페이지에서 자세한 사용법과 예제를 확인할 수 있다. 여기에서는 관련 cdn을 dt.ejs에서 불러와서 사용하였다.

     

    datatable.js - drwsDataTable()

    function drawDataTable(param) {
      $.ajax({
        url: "/table/records",
        type: "POST",
        data: { param },
        dataType: "json",
        success: function (result) {
          const { columns, rows } = result;
          let colHtml = "";
          const cols = columns.map(col => {
            const colStr = col.name;
            const colName = colStr.split("_").map(s => s.replace(s[0], s[0].toUpperCase())).join(" ");
            colHtml += `<th>${colName}</th>`;
            return { data: colName };
          });
          document.getElementById("tableTr").innerHTML = colHtml;
    
          const data = rows.map(row => {
            const obj = {};
            for (let i = 0; i < row.length; i++) {
              obj[cols[i].data] = i === 0 ? row[i].replace("T00:00:00.000Z", "") : row[i];
            }
            return obj;
          });
          $(document).ready(function () {
            $("#table").dataTable({
              data,
              columns: cols,
              destroy: true,
              columnDefs: [
                { width: "15%", "targets": 0, className: "text-center" },
                { width: "17%", "targets": 1, className: "text-center" },
                {
                  width: "15%", "targets": 2, className: "text-center", render: function (data) {
                    return data ? addComma(data) : data;
                  }
                },
                {
                  width: "18%", "targets": 3, className: "text-center", render: function (data) {
                    return data ? addComma(data) : data;
                  }
                },
                { width: "45%", "targets": 4 },
              ],
              order: [[0, "desc"]]
            });
          });
        },
        error: function (request, status, error) {
          console.error(error);
        }
      })
    }

    ajax 요청 시 함수의 인자로 받아온 param 값을 서버로 전달해준다. 이 값은 검색 입력창에 입력한 키워드며, 서버의 쿼리 where절에서 조건으로 처리된다. 요청에 대해 받아온 데이터 값을 datatable에서 요구하는 형태로 변환해준다. 테이블은 $("#table").dataTable({.... 내에서 생성해준다. 이 구절 내부 키의 값으로 각각의 값들을 입력해주면 된다. colomns는 컬럼명을 지정해준다. 컬럼으로 지정할 단어들이 담긴 배열을 값으로 지정해준다. 실제 데이터들은 data의 값으로 전달해주면 된다. { 컬럼명: 값, 컬럼명: 값, ... } 의 형태로 된 데이터들이 담긴 배열이다. 그리고 각 컬럼에 대한 정렬, width 등의 옵션은 columnDefs에서 지정해주면 된다. 그리고 order를 통해 기본 sorting 컬럼을 지정해줄 수 있다.

    destroy는 테이블을 재초기화할 필요가 있을 때 true로 지정해주어야 한다. 검색 결과를 표시할 때 페이지 리로딩 과정 없이 ajax의 요청 결과로 테이블 내용이 바뀌어야 하는데, destroy 과정 없이 기존 테이블이 있는 상태에서 테이블 초기화를 시도할 경우 datatable은 초기화 에러를 발생시킨다. destroy의 값을 true로 지정한다면 이 에러는 발생하지 않는다.

     

    4. 검색 기능과 검색어 자동 완성

    검색창에 국가명을 입력하면 그에 대한 데이터를 테이블에서 보여준다. 그러나 사용자 입장에서는 어떤 국가들이 어떤 이름으로 들어가있는지 알 수 없기 때문에 자동완성을 통해 입력 시 리스트를 띄워주면 사용하기에 더 편하다. 입력 input에 jQuery의 autocomplete를 지정해두면 위와 같이 글자 입력시 그 글자가 포함된 검색어 리스트를 보여주게 된다.

    과정은 다음과 같다. 1) 페이지 실행 시 getCountryList 함수 호출하고, 2) /countries 라우터로 ajax 요청한 후, 3) 요청에 대한 결과값 전달받은 국가 리스트를 autocomplete의 리스트에 적용한다.

    table.js - post /countries

    router.post('/countries', async (req, res) => {
      const query = `
        SELECT country, COUNT(*)
        FROM "country-vaccinations"
        GROUP BY country
      `;
      try {
        const data = await getDataUsingSql(query);
        const resultData = data.rows.map(el => el[0]);
        res.json(resultData)
      } catch (err) {
        console.error(err);
      }
    });

    datatable.js - getCountryList()

    function getCountryList() {
      $.ajax({
        url: "/table/countries",
        type: "POST",
        dataType: "json",
        success: function (result) {
          $("#countryFilter").autocomplete({
            source: result,
            minLength: 2,
            select: function (e, data) {
              drawDataTable(data.item.value);
            },
          })
        },
        error: function (request, status, error) {
          console.error(error);
        }
      })
    }

    getCountryList에서 ajax 요청을 하면 전체 국가 리스트를 서버에서 전달해준다. 이 리스트를 autocomplete의 source의 값으로 전달해준다. minLength를 2로 지정해주었는데, 배열의 길이가 190이 넘어가기 때문에 기본 1인 상태에서는 한 글자만 입력해도 그 글자가 포함된 모든 국가가 리스트에 뜨게 된다. 따라서 글자 2개를 입력했을 때 2개의 글자고 시작되는 국가명만 리스트에 뜰 수 있도록 제한을 두었다.

    그리고 select의 값으로 테이블을 생성하는 함수를 값으로 지정하여 리스트에서 값을 선택했을 때 검색 결과가 테이블에 바로 뜨드록 하였다.

     

     

Designed by Tistory.