페이징 기능 추가하기

각종 css와 shortcode들을 추가했던 이전 글 에 이어 이번에는 pagination을 추가해보려고 한다.

저는 각 카테고리의 메인 페이지에서는 해당 카테고리의 글들을 paging처리하여 모두 볼 수 있게 구성을 하고 싶었는 데, 제가 사용하고 있는 docport 테마는 paging처리가 들어있지 않은 테마였다. 그래서 hugo 공식사이트 를 참고하여 적용시킨 방법을 공유하려고 한다.


1. pagination.html작성

우선 **/latyouts/partials/**에 pagination.html을 생성해준다. 지난 번에 설명한것 처럼 이 경로에 파일을 추가해주면 해당 내용을 테마폴더의 내용을 오버라이딩하는 방식으로 적용이 된다. 현재 저는 theme파일에도 해당 이름의 파일은 존재하지 않기때문에 바로 적용이 된다.

파일을 생성했으면 다음과 같이 파일을 작성해주면 된다.

{{ $paginator := .Paginate (where .Data.Pages "Type" .Section ) }}
<div class = "content">
  <ul class="page">
{{ range $paginator.Pages }}
    <div class = "content-page">
      <a href="{{.RelPermalink}}" >{{ .Title }}</a>
      {{ if gt .WordCount 0 }}
      <p>{{ slicestr .Summary 0 100 }} ...</p>
      {{ end }}
  </div>
{{ end }}
</ul>

<!--  PAGE NUMBERS 그리는 부분 -->
{{ $paginator := .Paginator }}
{{ $adjacent_links := 2 }}
<!-- $최대 표시 가능한 페이지 개수 = ($adjacent_links * 2) + 1 -->
{{ $max_links := (add (mul $adjacent_links 2) 1) }}

<!-- 하한 페이지 = $adjacent_links + 1 -->
{{ $lower_limit := (add $adjacent_links 1) }}
<!-- 상한 페이지 = $paginator.TotalPages - $adjacent_links -->
{{ $upper_limit := (sub $paginator.TotalPages $adjacent_links) }}

<!-- 해당 카테고리의 글이 있을때만 페이지를 render -->
{{ if gt $paginator.TotalPages 1 }}
<div class = "pagination-container">
  <ul class="pagination">

    <!-- 이전 페이지로 이동 버튼 -->
    {{ if $paginator.HasPrev }}
    <li class="page-item">
      <a href="{{ $paginator.Prev.URL }}" class="page-link">
        «
      </a>
    </li>
    {{ end }}

    <!-- 페이지 숫자 표시 부분 -->
    {{ range $paginator.Pagers }}
      {{ $.Scratch.Set "page_number_flag" false }}
      <!-- 현재 표시한 페이지 번호보다 페이지수가  많을 경우 -->
      {{ if gt $paginator.TotalPages $max_links }}
        <!-- 하한 페이지 (1-3)보다 작을때 -->
        {{ if le $paginator.PageNumber $lower_limit }}
          <!-- hugo의 .Scratch를 이용해 렌더한 page번호가 max보다 작다면 기본설정이 false로 되어있는데 true로 변경 -->
          {{ if le .PageNumber $max_links }}
            {{ $.Scratch.Set "page_number_flag" true }}
          {{ end }}

        <!-- 상한 페이지수 보다   -->
        {{ else if ge $paginator.PageNumber $upper_limit }}
          {{ if gt .PageNumber (sub $paginator.TotalPages $max_links) }}
            {{ $.Scratch.Set "page_number_flag" true }}
          {{ end }}
       <!-- Middle pages. -->
        {{ else }}
          {{ if and ( ge .PageNumber (sub $paginator.PageNumber $adjacent_links) ) ( le .PageNumber (add $paginator.PageNumber $adjacent_links) ) }}
            {{ $.Scratch.Set "page_number_flag" true }}
          {{ end }}
        {{ end }}
      <!-- Simple page numbers. -->
      {{ else }}
        {{ $.Scratch.Set "page_number_flag" true }}
      {{ end }}


      <!-- 페이지 번호 출력 -->
      {{ if eq ($.Scratch.Get "page_number_flag") true }}
      <!-- 현재 페이지라면 page-item-active로 -->
      {{ if eq . $paginator }}
      <li class="page-item-active">
        <a href="{{ .URL }}" class="page-link">
          {{ .PageNumber }}
        </a>
      </li>
    {{ else }}
    <li class="page-item">
      <a href="{{ .URL }}" class="page-link">
        {{ .PageNumber }}
      </a>
    </li>
    {{ end }}
      {{ end }}

    {{ end }}

    <!-- 다음 page 버튼. -->
    {{ if $paginator.HasNext }}
    <li class="page-item">
      <a href="{{ $paginator.Next.URL }}" class="page-link">
        »
      </a>
    </li>
    {{ end }}
  </ul>
</div>
{{ end }}

1) 코드 설명

코드만 보면 굉장히 어려워 보일 수 있는데 지금부터 간단하게 핵심만 설명해보겠다.

{{ $paginator := .Paginate (where .Data.Pages "Type" .Section ) }}
<div class = "content">
  <ul class="page">
{{ range $paginator.Pages }}
    <div class = "content-page">
      <a href="{{.RelPermalink}}" >{{ .Title }}</a>
      {{ if gt .WordCount 0 }}
      <p>{{ slicestr .Summary 0 100 }} ...</p>
      {{ end }}
  </div>
{{ end }}
</ul>

위의 코드부분에서 .Section을 통해 현재 url을 통해 카테고리를 받아와 해당 폴더 밑에 있는 페이지들을 불러와 글 요약 표시를 div로 감싸 만들어 주었고, if gt .WordCount 0 부분은 내용이 없는 페이지를 그리려고 하면 그 밑의 .Summary가 null 이라 error가 발생하기 때문에 다음과 같이 조건문을 추가해 에러를 방지했다.

{{ slicestr .Summary 0 100 }} 부분을 통해 내용을 요약 하여 보여주도록 했다. 해당 코드 아래부분은 모두 pagination의 핵심 기능인 page버튼을 그리는 부분이며 최대 표시가능 한 페이지 개수를 설정해 조금 더 똑똑한 paging처리를 해주었다.


{{ $adjacent_links := 2 }}
<!-- $최대 표시 가능한 페이지 개수 = ($adjacent_links * 2) + 1 -->
{{ $max_links := (add (mul $adjacent_links 2) 1) }}

adjacent_links가 2이면 최대 표시가능한 페이지 개수가 5이다. 만약 더 늘리거나 줄이고 싶으시면 이를 변경하면 되고 3,5,7 같이 홀수로 바뀌게 됩니다.


  <!-- 페이지 번호 출력 -->
  {{ if eq ($.Scratch.Get "page_number_flag") true }}
    <!-- 현재 페이지라면 page-item-active로 -->
    {{ if eq . $paginator }}
      <li class="page-item-active">
        <a href="{{ .URL }}" class="page-link">
          {{ .PageNumber }}
        </a>
      </li>
    {{ else }}
      <li class="page-item">
        <a href="{{ .URL }}" class="page-link">
          {{ .PageNumber }}
        </a>
      </li>
    {{ end }}
  {{ end }}

이 부분은 실제로 페이지 번호를 그리는 부분으로 if eq . $paginator 를 통해 현재 페이지 번호와 그렇지 않은 페이지를 구분해 class 명을 작성해 주었다.

이렇게 해준 이유는 클래스명에 따라 현재 페이지의 css속성을 다르게 하여 가시성을 좋게 하기 위함이다.



2. paination.html을 layout내에 추가

이렇게 작성한 pagination.html을 실제 content를 그리는 layout에 추가해주어야 실제로 render가 되기 때문에 main content를 그리는 layout에 추가해주면 된다.

저의 테마는 body-article-content.html 이 실제 내용의 부분이기 때문에 해당 파일의 적절한 부분에 다음과 같이 추가해 주었다.

<!-- pagination -->
{{if (in .Params.page "true")}} {{ partial "pagination.html" . }} {{end}}

{{ partial “pagination.html” . }} 이 실제로 해당 html을 추가해 그리는 부분인데 그 앞에 if문을 추가해주어야 에러가 발생하지 않는다.

조건문이 없다면 페이징이 적용되지 않는 페이지들, 예를 들어 실제 포스팅하는 글들도 해당 레이아웃을 이용하고 이런 페이지에는 sub page가 존재하지 않기 때문이다. 그래서 글 작성시 파라미터로 paging을 처리할 페이지임을 명시하고 이 파리미터가 true라면 paging처리를 하도록 해주어야 한다. (이 부분때문에 에러가 발생했었는데 해결하는데 정말 많은 골머리를 앓았었다…)



3. paging처리할 페이지에 파라미터 추가해주기

md파일 상단에 active: - page의 파라미터를 추가해주면 쉽게 적용이 된다.

---
title: 페이징 예시 페이지
date: 2021-02-02T01:15:40+09:00
Description: '페이징 예시 페이지 입니다'
weight: 1
images:
hide:
  - breadcrumb
  - comment
  - nextpage
active:
  - page  //다음과 같이 파라미터를 추가 해주면 되고 page기능을 사용안한다면 생략하면 된다.
---



4. config.toml / config.yml 파일 수정

한 페이지에 최대 출력하는 글의 개수는 기본 10으로 설정되어있는데 이를 변경하고자 하면, 해당 설정파일에 paginate = 6과 같이 설정을 해주면 쉽게 변경이 가능하다.

그리고 이제 build를 해보면 페이징이 추가된 것을 볼 수 있을 것이다.

alter-text

하지만 기대했던과는 다른 모양인 리스트형태의 페이징이 되었는 데 이는 css 설정을 안해 주었기 때문에 css만 추가해주면 간단하게 해결이 된다.



5. css 추가

위의 코드를 그대로 이용하여 만들었다면 제가 설정한 클래스이름대로이기 때문에 아래의 css 파일을 /static/css 밑에 하나 만들어 추가해주고 커스텀해서 사용하면 된다.

article section.page div.content div.pagination-container {
  text-align: center;
  margin: 25px auto;
  width: 100%;
}
article section.page div.content div.pagination-container ul.pagination {
  display: inline-flex;
  width: auto;
  list-style: none;
  border-radius: 0.25rem;
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item
  a.page-link {
  position: relative;
  display: block;
  padding: 0.5rem 0.75rem;
  margin-left: -1px;
  line-height: 1.25;
  color: #027f51;
  background-color: #fff;
  border: 1px solid #dee2e6;
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item
  .page-link:hover {
  z-index: 2;
  color: #0056b3;
  text-decoration: none;
  background-color: #e9ecef;
  border-color: #dee2e6;
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item-active
  a.page-link {
  position: relative;
  display: block;
  padding: 0.5rem 0.75rem;
  margin-left: -1px;
  line-height: 1.25;
  color: #fff;
  background-color: #027f51;
  border: 1px solid #dee2e6;
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item-active
  .page-link:hover {
  z-index: 2;
  color: #0056b3;
  text-decoration: none;
  background-color: #dee2e6;
  border-color: #e9ecef;
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item
  .page-link:focus {
  z-index: 3;
  outline: 0;
  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item:first-child
  .page-link {
  margin-left: 0;
  border-top-left-radius: 0.25rem;
  border-bottom-left-radius: 0.25rem;
}

article
  section.page
  div.content
  div.pagination-container
  ul.pagination
  li.page-item:last-child
  .page-link {
  border-top-right-radius: 0.25rem;
  border-bottom-right-radius: 0.25rem;
}

그리고 해당 css파일을 헤더에서 load해주면 되는데 페이지네이션 기능이 없는데 굳이 css파일을 로드할 필요가 없으므로 /layouts/partials/head.html 에 아래와 같이 추가해주면 적용이 된다.

<!-- pagination -->
{{if (in .Params.active "page")}}
<link rel="stylesheet" href='{{"/css/pagination.css?ver=1" | relURL}}' />
{{end}}

6. 결과

alter-text
결과

위처럼 정상적인 페이지 버튼의 모습의 페이징이 추가된 것을 볼 수 있다.

다음과 같이 적용했는데 css모습이 그대로인 경우가 있는데, css 캐싱기능 때문에 서버에서 가져오는 것이 아니라 웹캐시에서 이전에 저장된 css데이터를 불러와 css적용이 안될 수가 있다. 이때는 인터넷 설정에서 쿠키 및 사이트 데이터를 초기화 시켜주면 간단하게 해결이 된다.



7) 처음, 마지막 페이지 이동 버튼 추가하기

<!-- First page. -->
{{ if ne $paginator.PageNumber 1 }}
  <li class="page-item">
    <a class="page-link" href="{{ $paginator.First.URL }}">
      ««
    </a>
  </li>
{{ end }}

<!-- Last page. -->
{{ if ne $paginator.PageNumber $paginator.TotalPages }}
  <li class="page-item">
    <a  href="{{ $paginator.Last.URL }}" class="page-link">
      »»
    </a>
  </li>
{{ end }}

다음과 같이 두개의 코드 부분을 페이지 버튼 그리는 가장 윗 부분, 마지막 부분에 추가해주면 처음/마지막 페이지로 이동하는 버튼이 쉽게 추가가 가능하다.

설명은 여기까지이며 마음에 드는 테마를 고르고 봤더니 페이징기능이 없어 다른 테마로 옮겨야 하나 고민했던 나와 비슷한 분들에게 많은 도움이 되면 좋겠다.





Reference

https://gohugo.io/variables/page/

https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/

Related Posts

PointRee 프로젝트 1 - 설계와 환경 구성

PointRee 프로젝트 1 - 설계와 환경 구성

웹 전반적인 흐름도 익히고 프레임워크들도 공부하기 위해 토이 프로젝트로 간단한 고객 정보를 관리하고 포인트 적립을 하는 웹을 구현해보고자 한다. 사용할 스택으로는 크게 React와 Spring Boot를 사용해서 개발해보려고 한다. React는 사용을 해본적이 있고 JS에 관심이 많기 때문에 선택을 하였고, 백을 JS 프레임워크가 아닌 굳이 Spring Boot를 사...

Read More
Packet Capture Program 만들기

Packet Capture Program 만들기

Linux환경에서 C를 이용해 raw socket을 이용한 tcpdump의 interface를 모방하는 패킷 캡쳐 프로그램 작성하는 것을 목표로 시작했습니다. 캡쳐할 정보는 IPv4(이더넷 타입이 0x0800 (ip헤더의 버전이 4)를 기반으로 2계층인 Ethernet 정보부터 패킷을 수집하여 앞에서부터 잘라내면서, Ethernet header | ip header | TCP/UDP/ICMP header | data(payload) 캡쳐 하는 프로그램...

Read More
리눅스 Timezone 설정하기

리눅스 Timezone 설정하기

1. 현재 서버 시간확인 $ date 리눅스를 설치할 때 timezone을 따로 설정하지 않으면 UTC 타임존으로 설치가 되고, date명령어로 현재 서버의 시간을 확인할 수 있다. 2. /etc/localtime 심볼릭 링크파일 수정 /usr/share/zoneinfo/에 여러 국가들의 정보가 존재하는데 바꾸고자 하는 지역을 /etc/localtime라는 이름으로 기존의...

Read More