ㆍ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 속성을 이용한 동적 계산, 메서드를 이용한 페이지 전환 처리 등을 통해 뉴스 목록을 효율적으로 표시할 수 있었습니당