자바스크립트

[JavaScript] 무한 스크롤 만들기

코딩하는둥이 2025. 3. 19. 18:27

스크롤이 바닥에 닿게 되면 

 

맨 처음에 데이터를 불러오는 함수를 만들어줍니다.

 const getPosts = async (page, limit) => {
    const API_URL = `https://jsonplaceholder.typicode.com/posts`
    const response = await fetch(API_URL)
    if (!response.ok) {
      throw new Error('에러가 발생했습니다.')
    }
    return await response.json()
  }

 

console.log를 통해 가져온 데이터를 확인합니다.

  const loadPosts = async () => {
    const response = await getPosts(page, limit)
    console.log(response);
  }

 

포스트 데이터를 HTML 요소로 렌더링하는 함수를 만들어줍니다.

const $posts = get('.posts')
const showPosts = (posts) => {
    posts.forEach((post) => {
      const $post = document.createElement('div')
      $post.classList.add('post')
      $post.innerHTML = `
          <div class="header">
            <div class="id">${post.id}.</div>
            <div class="title">${post.title}</div>
          </div>
          <div class="body">${post.body}</div>
      `
      $posts.appendChild($post)
    })
}

 

그러면 데이터를 잘 가지고 온 걸 확인할 수 있습니다.

 

 

이제 스크롤이 바닥에 닿았을 때 데이터를 불러오도록 변경하겠습니다. 

한번에 보여주는 데이터를 10개로 지정해주고 url를 변경해줍니다. 

  const limit = 10
  
  const getPosts = async (page, limit) => {
    const API_URL = `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
    const response = await fetch(API_URL)
    if (!response.ok) {
      throw new Error('에러가 발생했습니다.')
    }
    return await response.json()
  }

 

 

스크롤 끝을 감지하도록 스크롤 이벤트를 생성합니다. 

  window.addEventListener('DOMContentLoaded', () => {
    loadPosts()
    window.addEventListener('scroll', handleScroll)
  })

 

5px 정도 닿으면 새로운 데이터를 불러오도록 5px를 빼줍니다.

  const onScroll = () => {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement

    if (scrollTop + clientHeight >= scrollHeight - 5) {
      loadPosts(currentPage, limit)
    }
  }

 

이 페이지를 추가할건데 데이터를 10개 들어가 있는 페이지를 말합니다.

페이지 변수에 1를 넣어주고 url 주소에도 page를 추가해줍니다. 

let page = 1

const getPosts = async (page, limit) => {
    const API_URL = `https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${limit}`
    const response = await fetch(API_URL)
        if (!response.ok) {
          throw new Error('에러가 발생했습니다.')
        }
    return await response.json()
}

 

바닥에 닿을 떄마다 페이지를 추가해줍니다.

  const onScroll = () => {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement

    if (scrollTop + clientHeight >= scrollHeight - 5) {
      loadPosts(currentPage, limit)
      page++
    }
  }

 

여기서 문제가 발생할 수 있는 건 페이지를 다 가지고 왔는데도 page는 무한대로 증가합니다. 제한을 주기 위해서 총개수를 변수에 저장해줍니다.

total는 여태까지 불러온 데이터의 개수이고 end는 데이터의 총개수를 저장해줍니다.

  let total = 10
  const end = 100

 

그리고 total의 개수와 end의 개수가 같으면 리스트는 다 나온거지 때문에 증가되지 않도록 스크롤 이벤트를 없애줍니다.

  const handleScroll = () => {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement

    if (total === end) {
      window.removeEventListener('scroll', handleScroll)
      return
    }

    if (scrollTop + clientHeight >= scrollHeight - 5) {
      currentPage++
      total += 10
      loadPosts(currentPage, limit)
      return
    }
  }

 

지금까지 무한 스크롤을 완성했고 디테일한 부분을 만들겠습니다.

리스트가 로드될 때 로딩 이미지가 나와야 합니다.

 

응답을 받기 전까지 로딩이 나오도록하고 응답을 받으면 로딩을 없지도록 코드를 짰습니다.

  const loadPosts = async (page, limit) => {
    showLoader()
    try {
      const response = await getPosts(page, limit)
      showPosts(response)
    } catch (error) {
      console.error(error.message)
    } finally {
      hideLoader()
    }
  }

이제 loader라는 함수를 선언하고 hideLoader일 때는 css를 없애고 showLoader일 때는 css를 추가해줍니다. 

  const $loader = get('.loader')

  const hideLoader = () => {
    $loader.classList.remove('show')
  }

  const showLoader = () => {
    $loader.classList.add('show')
  }

 

 

무한스크롤을 완성했습니다!!!

아래는 총 코드입니다.

;(function () {
  'use strict'

  const get = function (target) {
    return document.querySelector(target)
  }

  let currentPage = 1
  let total = 10
  const limit = 10
  const end = 100

  const $posts = get('.posts')
  const $loader = get('.loader')

  const hideLoader = () => {
    $loader.classList.remove('show')
  }

  const showLoader = () => {
    $loader.classList.add('show')
  }

  const showPosts = (posts) => {
    posts.forEach((post) => {
      const $post = document.createElement('div')
      $post.classList.add('post')
      $post.innerHTML = `
          <div class="header">
            <div class="id">${post.id}.</div>
            <div class="title">${post.title}</div>
          </div>
          <div class="body">${post.body}</div>
      `
      $posts.appendChild($post)
    })
  }

  const getPosts = async (page, limit) => {
    const API_URL = `https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${limit}`
    const response = await fetch(API_URL)
    if (!response.ok) {
      throw new Error('에러가 발생했습니다.')
    }
    return await response.json()
  }

  const loadPosts = async (page, limit) => {
    showLoader()
    try {
      const response = await getPosts(page, limit)
      showPosts(response)
    } catch (error) {
      console.error(error.message)
    } finally {
      hideLoader()
    }
  }

  const handleScroll = () => {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement

    if (total === end) {
      window.removeEventListener('scroll', handleScroll)
      return
    }

    if (scrollTop + clientHeight >= scrollHeight - 5) {
      currentPage++
      total += 10
      loadPosts(currentPage, limit)
      return
    }
  }

  window.addEventListener('DOMContentLoaded', () => {
    loadPosts(currentPage, limit)
    window.addEventListener('scroll', handleScroll)
  })
})()

 

'자바스크립트' 카테고리의 다른 글

[JavaScript] 모달 띄우기  (0) 2025.03.24
[JavaScript] 이미지 슬라이드  (0) 2025.03.20
[JavaScript] Scroll에 따른 progressbar 만들기  (0) 2025.03.18
[JavaScript] 클래스  (0) 2025.03.17
[JavaScript] 프로토타입  (0) 2025.03.14