지난 포스팅까지 두개의 화면을 만들었다.데이터를 불러오게 되면 많은 게시글이 있어 페이지네이션이 생기게 되는데 이번에는 페이징을 구현하고자 한다.
게시글 상세에서 목록보기를 클릭할 때 전에 보고 있던 페이지가 예를들어 2페이지라면 그대로 2페이지로 목록을 보여줘야 사용자가 원할하게 사용 할 것이다. (만약 1페이로 초기화가 된다면 좀 화날지도 😡 ) 그래서 그 페이지를 담는 변수가 필요한 데 여러 군데에서 사용되는 변수는 따로 객체를 만들어 담아 놓는 것이 좋다. (공유되는 자원들은 하나로 묶어놓자)
// 공유되는 자원이란 의미로 store 라고 명명
const store = {
currentPage: 1,
};
function newsFeed() {
const newsFeed = getData(NEWS_URL);
const newsList = [];
newsList.push('<ul>');
// 10개씩 게시글이 보여진다고 할 때 리스트가 10개씩 한 묶음으로 만들어 져야 하니까 10을 곱해주었다.
for(let i = (store.currentPage - 1) * 10; i < store.currentPage * 10; i++) {
newsList.push(`
<li>
<a href="#/show/${newsFeed[i].id}">
${newsFeed[i].title} (${newsFeed[i].comments_count})
</a>
</li>
`);
}
newsList.push('</ul>');
newsList.push(`
<div>
<a href="#/page/${store.currentPage - 1}">이전 페이지</a>
<a href="#/page/${store.currentPage + 1}">다음 페이지</a>
</div>
`);
container.innerHTML = newsList.join('');
}
주솟값이 #/page/currentPage
, #/show/currentPage
이런식으로 바뀌었다. 그 이유인 즉슨 페이징을 구현하기 전에는 #뒤에 무조건 id 값이 온다고 생각해서 빈칸이면 목록을 구현하고 그렇지 않으면 상세를 보여주기로 했었다. 그런데 페이지네이션을 구현하게 되면 뒤에 무조건 뭔가가 올 테니까 구분할 인자가 필요 했다.
그래서 page 를 보여줄 항목에는 /page/
를
상세페이지는 /show/
를 오게 처리해주고 주소 url 에 해당 단어가 들어 있으면 indexOf
목록 또는 상세페이지로 처리해 주었다.
function router() {
const routePath = location.hash;
if (routePath === '') {
newsFeed();
} else if (routePath.indexOf('#/page/') >= 0) {
store.currentPage = Number(routePath.substr(7));
newsFeed();
} else {
newsDetail()
}
}
이전에 작성 해 놓았던 코드를 살짝 수정 해보자
function newsDetail() {
//substr(1) 이 아니라 이제는 7이 되어야 한다. /page/ 등으로 구분을 해 놓았기 때문!
const id = location.hash.substr(7);
const newsContent = getData(CONTENT_URL.replace('@id', id))
container.innerHTML = `
<h1>${newsContent.title}</h1>
<div>
<a href="#/page/${store.currentPage}">목록으로</a>
</div>
`;
}
방어코드는 이런 것이다. 예를들면 이전 페이지를 눌렀을 때 currentPage -1 을 했는데 만약 현재페이지가 0 이었다면???
url 이 없는 페이지를 나타내게 하면 안되므로 조건문을 걸어서 처리 해 줘야 한다.
newsList.push(`
<div>
<a href="#/page/${store.currentPage > 1 ? store.currentPage - 1 : 1}">이전 페이지</a>
<a href="#/page/${store.currentPage + 1}">다음 페이지</a>
</div>
`);
const container = document.getElementById('root');
const ajax = new XMLHttpRequest();
const content = document.createElement('div');
const NEWS_URL = 'https://api.hnpwa.com/v0/news/1.json';
const CONTENT_URL = 'https://api.hnpwa.com/v0/item/@id.json';
const store = {
currentPage: 1,
};
function getData(url) {
ajax.open('GET', url, false);
ajax.send();
return JSON.parse(ajax.response);
}
function newsFeed() {
const newsFeed = getData(NEWS_URL);
const newsList = [];
newsList.push('<ul>');
for(let i = (store.currentPage - 1) * 10; i < store.currentPage * 10; i++) {
newsList.push(`
<li>
<a href="#/show/${newsFeed[i].id}">
${newsFeed[i].title} (${newsFeed[i].comments_count})
</a>
</li>
`);
}
newsList.push('</ul>');
newsList.push(`
<div>
<a href="#/page/${store.currentPage > 1 ? store.currentPage - 1 : 1}">이전 페이지</a>
<a href="#/page/${store.currentPage + 1}">다음 페이지</a>
</div>
`);
container.innerHTML = newsList.join('');
}
function newsDetail() {
const id = location.hash.substr(7);
const newsContent = getData(CONTENT_URL.replace('@id', id))
container.innerHTML = `
<h1>${newsContent.title}</h1>
<div>
<a href="#/page/${store.currentPage}">목록으로</a>
</div>
`;
}
function router() {
const routePath = location.hash;
if (routePath === '') {
newsFeed();
} else if (routePath.indexOf('#/page/') >= 0) {
store.currentPage = Number(routePath.substr(7));
newsFeed();
} else {
newsDetail()
}
}
window.addEventListener('hashchange', router);
router();
이번에 배운 점은 공유되는 자원은 따로 빼 놓는 게 코드를 효율적으로 관리 하는 것 이다.
여담으로 .. 저 store 객체는 나중에 정~~ 말 어려워지는 코드로 변환하고야 마는데 ..ㅜㅜ 사실 store 부분에 이것저것 코드가 추가되고 하면서 따라가기 어려워서 처음으로 돌아오게 되었다. 이 부분은 정말 중요하다! !