๐Ÿ”ฎ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ -10 (lazyloading)

ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ, ์ด๋ฏธ์ง€๋‚˜ ๋™์˜์ƒ ๊ฐ™์€ ์šฉ๋Ÿ‰์ด ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž์˜ ๋ทฐ ์˜์—ญ์— ๋„๋‹ฌ ํ–ˆ์„ ๋•Œ, ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๋Š” ์ง€์—ฐ๋กœ๋”ฉ (lazy loading)์„ ์ด์šฉํ•ด๋ณด์•˜๋‹ค.


#. Project Map


์ œ์ž‘๋…ธํŠธ ํ•œ๋ˆˆ์—๋ณด๊ธฐ[์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ]

1. ๋ ˆ์ด์•„์›ƒ

1-1. ๋ฉ”์ธํ™”๋ฉด

lazyloadingmain

์šฐ์—ฐํžˆ ์›๋ณธ ์ด๋ฏธ์ง€์™€ blur๋œ ์ด๋ฏธ์ง€ ๋ชจ๋‘ ์ฐํ˜”๋‹คโ€ฆ!

1-2. ์ปดํฌ๋„ŒํŠธ

<Contents style={{ marginTop: '100px' }}>
  <p>1์ดˆ์˜ ์ง€์—ฐ๋กœ๋”ฉ(lazy loading) ๊ตฌํ˜„</p>
  <div>ํ๋ฆฐ ์ด๋ฏธ์ง€๋กœ ์ง€์—ฐ</div>
  {obj.map(item => {
    return item
  })}
  <div>๋‹จ์ƒ‰ ์ด๋ฏธ์ง€๋กœ ์ง€์—ฐ</div>
  {obj2.map(item => {
    return item
  })}
</Contents>

Contents ๋ผ๋Š” styled-component๋ฅผ ์‚ฌ์šฉํ•ด์„œ, Container ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‚ด๋ถ€ p๋‚˜ div์˜ ์Šคํƒ€์ผ๋„ ์กฐ์ •ํ•ด์ฃผ์—ˆ๋‹ค.

2. ์ง€์—ฐ๋กœ๋”ฉ

2-1. โญ src, data-src

<img className="lazy" src="๋กœ๋”ฉ์ „์ด๋ฏธ์ง€URL" data-src="URL" alt="Some tacos." />

data-src ์†์„ฑ ๊ฐ’์—๋Š” ์›๋ณธ ์ด๋ฏธ์ง€ URL์„ ๋„ฃ๊ณ , src ์†์„ฑ ๊ฐ’์—๋Š” ๋น„๊ต์  ์šฉ๋Ÿ‰์ด ๋‚ฎ์€ ๋Œ€์ฒด ์ด๋ฏธ์ง€ URL์„ ๋„ฃ์–ด์ค€๋‹ค.

2-2. โญ nodeList -> Array

let lazyImages = [].slice.call(document.querySelectorAll('img.lazy'))
// or
let lazyImages = Array.slice.call(document.querySelectorAll('img.lazy'))

querySelectorAll ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด DOM ์—˜๋ฆฌ๋จผํŠธ๋“ค์ด ๋ฐฐ์—ดํ˜•ํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ ๋…ธ๋“œ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ๋ถˆ๋Ÿฌ์™€์ง„๋‹ค.

๊ทธ๋ž˜์„œ ๋ฐฐ์—ด์˜ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผํ•จ.

๋ฐฐ์—ด ๋ฉ”์†Œ๋“œ์˜ slice๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  call()์„ ํ†ตํ•ด ๋ฐ”์ธ๋”ฉ ํ•ด์ฃผ๋ฉด ๋จ.

2-3. scroll ์ด๋ฒคํŠธ

const lazyLoad = () => {
  if (active === false) {
    active = true

    setTimeout(() => {
      // image lazy loading ๋ถ€๋ถ„
      // ...

      active = false
    }, 1000)
  }
}

document.addEventListener('scroll', lazyLoad)

์Šคํฌ๋กค ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๊ณ , ๋‚ด๋ถ€์— image lazy loading ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ๋Š”๋ฐ,

lazy loading์ด ์ง๊ด€์ ์œผ๋กœ ๋ณด์ด์ง€ ์•Š์•„์„œ setTimeout() ํ•จ์ˆ˜๋กœ 1์ดˆ๊ฐ„ ๊ฐ•์ œ๋กœ ์ง€์—ฐ์‹œ์ผœ์ฃผ์—ˆ๋‹ค. ๐Ÿ˜‚

2-4. โญ ์ง€์—ฐ๋กœ๋”ฉ : getBoundingClientRect()

lazyImages.map(lazyImage => {
  if (
    lazyImage.getBoundingClientRect().top <= window.innerHeight &&
    lazyImage.getBoundingClientRect().bottom >= 0 &&
    getComputedStyle(lazyImage).display !== 'none'
  ) {
    lazyImage.src = lazyImage.dataset.src
    lazyImage.classList.remove('lazy')

    lazyImages = lazyImages.filter(function(image) {
      return image !== lazyImage
    })

    if (lazyImages.length === 0) {
      document.removeEventListener('scroll', lazyLoad)
    }
  }
})

๋จผ์ € getBoundingClientRect()๋ฅผ ํ†ตํ•˜์—ฌ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ์š”์†Œ๋ฅผ ๊ตฌํ•˜๊ณ , ์กฐ๊ฑด๋ฌธ์„ ํ†ตํ•ด ์Šคํฌ๋ฆฐ์— ๋“ฑ์žฅํ•  ๋•Œ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

getRec

getComputedStyle() ๋Š” DOM element์˜ style ์†์„ฑ๊ณผ ์†์„ฑ๊ฐ’์„ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๊ฐ€์ ธ์˜ค๋Š” window ๋‚ด๋ถ€ ๋ฉ”์†Œ๋“œ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์กฐ๊ฑด๋ฌธ์„ ํ†ต๊ณผํ•˜๋ฉด src๊ฐ’์„ ๋ฐ”๊พธ๋„๋ก ํ•˜์˜€๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๋ฉด class๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋ฐฐ์—ด์„ ๋น„์› ๊ณ , ๋ชจ๋“  ๋ฐฐ์—ด์ด ๋น„์›Œ์ง€๋ฉด ์Šคํฌ๋กค ์ด๋ฒคํŠธ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๋“ฑ ์ตœ์ ํ™” ์ž‘์—…๋„ ํ•ด์ฃผ์—ˆ๋‹ค.

3. ๊ฐœ์ธ์ ์ธ ํ”ผ๋“œ๋ฐฑ

3-1. Chrome Native lazy loading

<img src="cat.jpg" loading="lazy" />

์ด๋Ÿฐ ๋ฐฉ์‹๋„ ์“ธ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค..!

4. ์ฐธ๊ณ 


Written by@taenyKim
์›น ํ”„๋ก ํŠธ์—”๋“œ ๊ณต๋ถ€ ๋ธ”๋กœ๊ทธ / Learn in Public

GitHubFacebook