페이지네이션 구현(뉴스, q&a 게시판)

Project Diary/Vue (Roblox WebSite)

 Vue.js를 이용해 페이지네이션을 구현하는 방법 기록


 

1. 프로젝트 구조

 

  • News.vue: 메인 뉴스 페이지 컴포넌트
  • NewsMain.vue: 뉴스 데이터를 표시하는 컴포넌트
  • Pagination.vue: 페이지네이션 컴포넌트

Pagination.vue 컴포넌트를 분리한 이유는 페이지네이션 기능을 다른 페이지에서도 재사용할 수 있도록 하기 위함입니다. 컴포넌트로 분리함으로써 코드의 재사용성이 높아지고 유지보수가 용이해집니다. 예를 들어, 다른 페이지에서도 동일한 방식으로 페이지네이션을 구현하고자 할 때, 단순히 Pagination.vue 컴포넌트를 불러와 사용하면 됩니다

 

 

2. News.vue: 메인 뉴스 페이지 컴포넌트


News.vue는 뉴스 데이터를 API로부터 받아와 NewsMain과 Pagination 컴포넌트에 전달합니다.

 

2-1 템플릿 구조

<template>
  <div id="news">
    <news-main
      :newsData="newsData"
      :headerNews="headerNews"
      :pageNews="pageNews"
      :currentPage="currentPage"
    />
    <pagination
      :totalItems="totalItems"
      :currentPage.sync="currentPage"
      :itemPerPage="itemPerPage"
    />
  </div>
</template>

여기서 NewsMain과 Pagination 컴포넌트에 여러 속성을 전달하고 있습니다. 특히 currentPage는 sync를 이용해 양방향 바인딩을 하고 있습니다.

 

2-2  스크립트: 데이터 정의 및 API 호출

<script>
import NewsMain from "@/components/news/NewsMain.vue";
import Pagination from "@/components/layout/Pagination.vue";

export default {
  name: "News",
  data() {
    return {
      newsData: [],
      totalItems: 1,
      currentPage: 1,
      itemPerPage: 10,
      headerNews: [],
      pageNews: [],
    };
  },
  components: {
    NewsMain,
    Pagination,
  },
  computed: {
    countryCode() {
      return this.$store.getters.fnGetLocale === "ko" ? "kr" : "us";
    },
  },
  watch: {
    countryCode: {
      async handler(value, oldValue) {
        try {
          let response = await fetch(
            `https://newsapi.org/v2/top-headlines?country=${value}&pageSize=100&apiKey=bc66937ca43749cebbeddf8b4ce04df2`
          );
          let result = await response.json();
          this.newsData = result.articles;
          this.totalItems = result.totalResults;
          this.pageNews = [];
          this.headerNews = [];
          for (let i = 0; i < Math.ceil(this.totalItems / this.itemPerPage); i++) {
            this.pageNews.push(this.newsData.splice(0, 10));
            this.headerNews.push(this.pageNews[i].splice(0, 1));
          }
        } catch (err) {
          console.log(err);
        }
      },
      immediate: true,
    },
  },
};
</script>

 

위 코드에서 currentPage와 관련된 속성은 Pagination 컴포넌트에서 제어합니다.

 


3. Pagination.vue: 페이지네이션 컴포넌트

Pagination.vue는 페이지네이션을 구현하는 컴포넌트로, totalItems, currentPage, itemPerPage 데이터를 받아와 페이지네이션을 구현합니다.

 

3-1 템플릿 구조

<template>
  <div class="pagination">
    <button class="goend" :disabled="currentPage === 1" @click="prevPage">
      이전
    </button>
    <span v-for="(page, idx) in displayedPages" :key="idx">
      <button :class="{ active: currentPage === page }" @click="goToPage(page)">
        {{ page }}
      </button>
    </span>
    <button
      class="goend"
      :disabled="currentPage === pageCount"
      @click="nextPage"
    >
      다음
    </button>
  </div>
</template>

 

3-2 스크립트: 데이터 및 메서드 정의

<script>
export default {
  name: "Pagination",
  props: {
    totalItems: {
      type: Number,
      required: true,
    },
    currentPage: {
      type: Number,
      required: true,
    },
    itemPerPage: {
      type: Number,
      required: true,
    },
  },
  computed: {
    pageCount() {
      return Math.ceil(this.totalItems / this.itemPerPage);
    },
    displayedPages() {
      const pages = [];
      const startPage = Math.max(1, this.currentPage - 5);
      const endPage = Math.min(this.pageCount, startPage + 10);
      for (let i = startPage; i <= endPage; i++) {
        pages.push(i);
      }
      return pages;
    },
  },
  methods: {
    goToPage(page) {
      this.$emit("update:currentPage", page);
      window.scrollTo({
        top: 0,
        behavior: "smooth",
      });
    },
    prevPage() {
      if (this.currentPage > 1) {
        this.$emit("update:currentPage", --this.currentPage);
      }
      window.scrollTo({
        top: 0,
        behavior: "smooth",
      });
    },
    nextPage() {
      if (this.currentPage < this.pageCount) {
        this.$emit("update:currentPage", ++this.currentPage);
      }
      window.scrollTo({
        top: 0,
        behavior: "smooth",
      });
    },
  },
};
</script>

 

4. 페이지네이션 구현 설명

 

4-1 props를 이용한 데이터 전달

Pagination.vue 컴포넌트는 부모 컴포넌트 (News.vue)로부터 totalItems, currentPage, itemPerPage 데이터를 props를 통해 전달받습니다. 

 

4-2 computed 속성을 이용한 페이지 계산

pageCount와 displayedPages는 computed 속성을 이용해 동적으로 계산됩니다. 

pageCount는 전체 페이지 수를 계산하고, displayedPages는 현재 페이지 기준으로 표시할 페이지 목록을 생성합니다.

 

4-3 메서드 정의

goToPage, prevPage, nextPage 메서드는 페이지 이동을 처리합니다. 

페이지를 이동할 때마다 update:currentPage 이벤트를 발생시켜 부모 컴포넌트에서 currentPage를 업데이트합니다. 또한, 부드러운 스크롤 효과를 주기 위해 window.scrollTo 메서드를 사용했습니다.

 

methods: {
  goToPage(page) {
    this.$emit("update:currentPage", page);
    window.scrollTo({
      top: 0,
      behavior: "smooth", // 부드러운 스크롤 적용
    });
  },
  prevPage() {
    if (this.currentPage > 1) {
      this.$emit("update:currentPage", --this.currentPage);
    }
    window.scrollTo({
      top: 0,
      behavior: "smooth", // 부드러운 스크롤 적용
    });
  },
  nextPage() {
    if (this.currentPage < this.pageCount) {
      this.$emit("update:currentPage", ++this.currentPage);
    }
    window.scrollTo({
      top: 0,
      behavior: "smooth", // 부드러운 스크롤 적용
    });
  },
}

 

이 포스팅에서는  props를 이용한 데이터 전달, computed 속성을 이용한 동적 계산, 메서드를 이용한 페이지 전환 처리 등을 통해 뉴스 목록을 효율적으로 표시할 수 있었습니당

 

01
페이지네이션 (뉴스)
01
페이지네이션(q&a 게시판)