- DOM API 복습
- infinite scroll 기능 구현
평화로운 주말 오전부터 Vanilla JS 복습을 시작했다!! 기본기를 더 다지기 위해 fetch
로 데이터를 받아오고, 이 데이터를 통해 DOM 조작하는 법을 복습하고 싶었다.
그리고 infinite scroll 기능을 React로는 해봤지만 JS로는 해본 경험이 없어서 Vanilla JS로 구현을 해보면서 다시 intersectionObserver
에 대해 복습할 수 있어서 좋았다.
그래서 기능들을 다 구현하고 기록을 하고 싶어서 블로깅까지 하기로 했다!
(블로깅하면서 코드 다시 점검하기..)
먼저 나는 무료로 데이터를 요청하고 사용할 수 있는 사이트(jsonplaceholder.typicode.com)를 통해 이미지와 제목, 아이디등이 있는 데이터를 받아오고, 이 데이터를 화면에 뿌려 줄 예정이다. 수도 코드를 간략히 적어보고 시작했다.
createElement
를 통해 만든다.classList.add
를 통해 Class를 추가해준다.textContent
를 통해서 값을 할당한다.appendChild
를 통해 container div에 자식 노드를 추가해준다.observer
를 통해 관찰 대상이 되면 데이터를 10개씩 추가로 불러온다. (infinite scroll)// 데이터 구조
console.log(DATABASE);
[
{
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "https://via.placeholder.com/600/92c952",
"thumbnailUrl": "https://via.placeholder.com/150/92c952"
},
{
"albumId": 1,
"id": 2,
"title": "reprehenderit est deserunt velit ipsam",
"url": "https://via.placeholder.com/600/771796",
"thumbnailUrl": "https://via.placeholder.com/150/771796"
},
...
]
// index.html
<body>
<div id="post-list"></div>
</body>
// script.js
const DATABASE = [];
// TODO: 데이터 외부 변수에 저장하기
async function getData() {
const res = await fetch('https://jsonplaceholder.typicode.com/photos');
const data = await res.json();
DATABASE.push(...data);
}
// TODO: infinite scroll를 위해 외부 변수에 저장
let start = 0;
let end = 10;
// TODO: 10개씩 post 불러오기
function postRequest(data, start, end) {
for (let i = start; i < end; i++) {
const { title, thumbnailUrl, id } = data[i];
makePost(title, thumbnailUrl, id);
}
};
// TODO: document에 한 번만 접근한다.
let doc = document;
const postLists = doc.getElementById('post-list');
// TODO: add post tag to post list
function makePost(postTitle, postThumbnailUrl, postId) {
const wrap = doc.createElement('div');
wrap.classList.add('post-wrap');
const thumbnailUrl = doc.createElement('img');
thumbnailUrl.classList.add('post-thumbnailUrl');
thumbnailUrl.src = postThumbnailUrl;
const title = doc.createElement('div');
title.classList.add('post-title');
title.textContent = postTitle;
const id = doc.createElement('div');
id.classList.add('post-id');
id.textContent = postId;
wrap.append(thumbnailUrl, title, id);
postLists.appendChild(wrap);
};
function render() {
getData()
setTimeout(() => {
postRequest(DATABASE, start, end);
}, 500);
}
render();
먼저 DOM을 조작해서 화면에 데이터들을 보여줄 수 있게 구현을 해 보았다. 한 가지 특이점은 render
함수에서 setTimeout을 사용해서 데이터를 뿌려주었다. 처음에는 그냥 뿌려주었었는데, 오류가 났었다. 조금만 생각을 해보니, 데이터를 요청하고 받아오는데 시간이 필요했다. 이 부분은 이벤트 루프에 대한 이해도가 있으면 금방 생각이 날 수 있었던 문제였다.
다음으로는 이제 list가 끝나는 지점에 div 태그를 하나 더 추가해놓고, intersectionObserver API를 통해 무한 스크롤 기능을 구현을 하면 된다.
맨 밑에 추가한 div 태그가 관찰 대상이 될 때마다, 데이터를 10개씩 추가로 불러오면 된다. 이 코드들은 render
함수안에 작성을 해보았다.
// index.html
<body>
<div id="post-list"></div>
<div class="list-end"></div>
</body>
// script.js
// ... 중간 생략 ... //
function render() {
getData();
const listEnd = doc.querySelector('.list-end');
setTimeout(() => {
postRequest(DATABASE, start, end);
}, 500);
const observerCallback = (entries) => {
entries.forEach(entry => {
const { target } = entry;
// TODO: 해당 요소가 교차되었을 때
if (entry.isIntersecting) {
if (end <= DATABASE.length
&& target.getBoundingClientRect().y > 362) {
start = end;
end += 10;
// start, end 변수 재할당
// 그리고 10개를 추가로 불러온다.
postRequest(DATABASE, start, end);
observer.unobserve(target);
}
} else {
return;
}
observer.observe(target);
});
};
const observerOptions = {
root: null,
rootMargin: "0px",
threshold: 0.3,
}
const observer = new IntersectionObserver(
observerCallback,
observerOptions,
);
// list-end를 관찰 대상으로 등록한다.
observer.observe(listEnd);
};
render();
intersectionObserve API를 잘 활용하면 손쉽게(?) 무한 스크롤 기능을 구현할 수 있다. 저 데이터가 총 5000개가 있는데, 데이터를 한 번에 렌더링하지 않고 10개씩 불러오기 때문에 성능은 물론 유리하고, 활용도가 높은 기술이다. React로 구현했을 때와 큰 차이는 없지만, JS로 구현을 해보니 또 다른 느낌이 들었다. 어렵지 않으면서, 활용도가 많은 기술이라 자주 사용해 보는 것이 중요한 것 같다.
오늘 Vanilla JS로 복습한 내용은 여기까지 블로깅을 하고, 다음에 추가적으로 기능들을 구현하고 다시 블로깅을 해야겠다! 차근 차근 React의 의존도를 낮추면서 아주 근본인 Javacript를 공부하다보니, 기본기가 쌓여가는 기분이 든다~~ 아주 좋은 주말을 보낸 것 같다~!!
다음에는 이 코드에 이어서 post 추가 기능과 post 필터링 기능을 구현하고 블로깅할 예정이다.