[JS ] ๐Ÿ”ญ Intersection Observer API ์•Œ์•„๋ณด๊ธฐ

23459 ๋‹จ์–ด JavaScriptJavaScript

๐Ÿ”ญ Intersection Observer API ๋ž€?

Intersection Observer API(๊ต์ฐจ ๊ด€์ž˜์ž) ๋Š” ํƒ€์ผ“ ์š”์†Œ๊ฐ€ ํ™”๋ฉด์— ๋ณด์ด์ง€๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ด€์ฐฐํ•˜๋Š” API ์ด๋‹ค.

React๋กœ Infinite Scroll ๊ตฌํ˜„์„ ์œ„ํ•ด ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ฐพ์•„๋ณด๋˜ ์ค‘ JavaScript API๋กœ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ ํ•ด์„œ ์ •๋ฆฌํ•ด๋ณด๊ณ ์žํ•œ๋‹ค.

์‚ฌ์šฉํ•ด์•ผ๋˜๋Š” ์ด์œ 

scroll event ๋กœ๋„ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กค์„ ๋ฐœ์ƒ์‹œํ‚ฌ๋•Œ ๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(Main Thread)์— ์˜ํ–ฅ์„ ์ค€๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋””๋ฐ”์šด์‹ฑ(Debouncing)๊ณผ ์“ฐ๋กคํ‹€๋ง(Throttling)์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๋˜ํ•œ getBoundingClientRect() ํ•จ๊ผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๋ฐ ์ด ํ•จ์ˆ˜๋Š” ๋ฆฌํ”Œ๋กœ์šฐ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๋””๋ฐ”์šด์‹ฑ / Debouncing : ์—ฐ์†์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋“ค ์ค‘์— ๋งˆ์ง€๋ง‰์— ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜(๋˜๋Š” ์ œ์ผ ์ฒ˜์Œ ํ•จ์ˆ˜)๋งŒ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ
์“ฐ๋กคํ‹€๋ง / Throttling : ๋งˆ์ง€๋ง‰ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ ํ›„ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๊ธฐ ์ „์— ๋‹ค์‹œ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ
๋ฆฌํ”Œ๋กœ์šฐ / reflow :๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์†Œ๋‚˜ ๋„ํ˜•์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜์—ฌ ๊ทธ๋ฆฌ๋Š” ํ˜„์ƒ

์‚ฌ์šฉ๋ฐฉ๋ฒ•

Intersection Observer ์ƒ์„ฑํ•˜๊ธฐ

๋จผ์ € IntersectionObeserver ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค.

const observer = new intersectionObserver(callback,options)

์ƒ์„ฑ์‹œ, ์ฝœ๋ฐฑ ํ•จ์ˆ˜์™€ ์˜ต์…˜์„ ํ•จ๊ป˜ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋จผ์ € ์˜ต์…˜๋ถ€ํ„ฐ ์•Œ์•„๋ณด์ž.

Options

์•„๋ž˜ ๊ทธ๋ฆผ์„ ์ฐธ๊ณ ํ•˜๋ฉด ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค.

  • root : ์ด ์š”์†Œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ด€์ฐฐํ•˜๊ณ ์žํ•˜๋Š” ์š”์†Œ์˜ ๊ฐ€์‹œ์„ฑ์„ ๊ฒฐ์ •ํ•œ๋‹ค. (default: ๋ธŒ๋ผ์šฐ์ € viewport)
  • rootMargin: ๋‹จ์–ด์˜ ๋œป ๊ทธ๋Œ€๋กœ root๊ฐ€ ๊ฐ–๋Š” margin ๊ฐ’์ด๋‹ค. (default: 0)
  • threshold : root๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋Œ€์ƒ์š”์†Œ๊ฐ€ ์–ผ๋งˆ๋งŒํผ ๋ณด์ผ๋•Œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ• ์ง€ 0~1 ์‚ฌ์ด์˜ ๊ฐ’์œผ๋กœ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋งŒ์•ฝ ์š”์†Œ๊ฐ€ 25%์”ฉ ๋ณด์ผ๋•Œ๋งˆ๋‹ค ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด [0, 0.25, 0.5, 0.75, 1] ์™€ ๊ฐ™์ด ๋ฐฐ์—ด๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค. (default: 1)

Callback

์ฝœ๋ฐฑํ•จ์ˆ˜๋Š” ๋‘๊ฐ€์ง€์˜ ์ธ์ž๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, entries์™€ obeserver ์ด๋‹ค.

const observer = new intersectionObserver((entries,observer)=>{
  
    entries.forEach((entry) => {
      console.group('Intersection Observer Entry');
      console.log(entry.boundingClientRect);
      console.log(entry.intersectionRatio);
      console.log(entry.intersectionRect);
      console.log(entry.isIntersecting);
      console.log(entry.rootBounds);
      console.log(entry.target );
      console.log(entry.time);
      console.groupEnd()
    })
	
})
  • entries : Intersection Observer Entry ์˜ ๋ฐฐ์—ด๊ฐ’์ด๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด์„œ ๋“ฑ๋ก๋œ ๋Œ€์ƒ์š”์†Œ๋“ค์˜ ๋ฐฐ์—ด๊ฐ’ ์ด๋‹ค.
    Intersection Observer Entry ์ด ๊ฐ์ฒด๋Š” ์•„๋ž˜ ์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
    boundingClientRect : target์˜ getBoundingClientRect() ๊ฐ’
    intersectionRatio : target์ด ์–ผ๋งˆ๋งŒํผ ๋…ธ์ถœ๋ฌ๋Š”์ง€ 0~1 ์‚ฌ์ด์˜ ๊ฐ’์œผ๋กœ ๋‚˜ํƒ€๋‚ธ๋‹ค. (๋…ธ์ถœ๋น„์œจ)
    intersectionRect : ๋…ธ์ถœ๋œ ์˜์—ญ์— ๋Œ€ํ•œ ์‚ฌ๊ฐํ˜• ์†์„ฑ๊ฐ’
    isIntersecting : ๋…ธ์ถœ์—ฌ๋ถ€๋ฅผ true/false ๊ฐ’์œผ๋กœ ๋‚˜ํƒ€๋‚ธ๋‹ค.
    rootBounds: ๋ฃจํŠธ์˜ ์‚ฌ๊ฐํ˜• ์†์„ฑ๊ฐ’
    target : ๋Œ€์ƒ์š”์†Œ
    time : ๋Œ€์ƒ์ด ๊ต์ฐจ๋œ ์‹œ๊ฐ„
  • observer: ๋งŒ๋“ค์–ด์ง„ Intersection Observer ๊ฐ์ฒด์ด๋‹ค.

๋ฉ”์†Œ๋“œ ์‚ดํŽด๋ณด๊ธฐ

Intersection Observer ๊ฐ์ฒด๋Š” 4๊ฐ€์ง€์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

  • ๐Ÿ”ญ observe: ์š”์†Œ์— ๋Œ€ํ•œ ๊ด€์ฐฐ์„ ์‹œ์ž‘
  • โŒ disconnect: ๋ชจ๋“  ๋Œ€์ƒ์š”์†Œ๋“ค์— ๋Œ€ํ•œ ๊ด€์ฐฐ์„ ์ค‘๋‹จ (์—ฐ๊ฒฐ์„ ๋Š๋Š”๋‹ค.)
  • ๐Ÿ“ takeRecordes: ๋ชจ๋“  ๋Œ€์ƒ์š”์†Œ๋“ค์— ๋Œ€ํ•œ Intersection Observer Entry ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜
  • โœ‹๐Ÿป unobserve: ํŠน์ • ์š”์†Œ์— ๋Œ€ํ•œ ๊ด€์ฐฐ์„ ์ค‘๋‹จ

๋ฌดํ•œ ์Šคํฌ๋กค ๋งŒ๋“ค์–ด๋ณด๊ธฐ

์ž. ์ด์ œ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•ด๋ณด์ž.

๊ตฌํ˜„ ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค.

๋จผ์ €, html๋กœ scroll์ด ์ƒ๊ธธ๋งŒํ•œ ํฌ๊ธฐ์˜ ์š”์†Œ๋ฅผ ๋งŒ๋“ ๋‹ค.
๋Œ€์ƒ ์š”์†Œ๋ฅผ intersection Observer ๊ฐ์ฒด์— observe ํ•œ๋‹ค.
๊ทธ ํ›„ ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ ๊ฐ€์ƒ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋Œ๋ฉฐ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ์š”์†Œ๋ฅผ ๋‹ค์‹œ observe ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ด์‹œํ‚จ๋‹ค.

API ํŒŒ์•…ํ•˜๋Š” ์ •๋„๋กœ๋งŒ ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค๊บผ๋ผ CSS๋ถ€ํ„ฐ ๊ฐ„๋‹จํ•˜๊ฒŒ ์งฐ๋‹ค.

CSS ์ฝ”๋”ฉ

   body {
            width: 100%;
            height: 100%;
        }
        
		/* ๋„ฃ์ง€ ์•Š๊ณ  div์˜ ํฌ๊ธฐ๋ฅผ ๋Š˜๋ ค๋„ ๋œ๋‹ค. */
        header {
            width: 500px;
            height: auto;
            padding: 10px;
            background-color:  #f9f8ed;
            color: #6a9c78;
            text-align: center;
            margin: 10px auto 20px auto;
            border-radius: 20px;
        }

        div {
            width: 400px;
            height: 400px;
            border: 10px solid #c4e3cb;
            border-radius: 200px;
            margin: auto;
            text-align: center;
            line-height: 400px;
            font-size: 2em;
            color:forestgreen;
        }



HTML ์ฝ”๋”ฉ

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>๋ฌดํ•œ์Šคํฌ๋กค ๊ตฌํ˜„ํ•˜๊ธฐ</title>
</head>
<body>
    <div></div>

    <div></div>

    <div id="target">target</div>
  
</body>

</html>


JavaScript

  1. Ajax๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ฉด ์ •๋ง ์ข‹๊ฒ ์ง€๋งŒ, ๊ท€์ฐฎ์œผ๋‹ˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ ๋‹ค.
// ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ ๋‹ค.
let data = [];
const count = 10


for(let i = 0;i < 10;i++){
    data.push('์ƒˆ๋กœ์ƒ๊ธด Element' + (i+1));
}
  1. ๊ด€์ฐฐํ•  ๋Œ€์ƒ(target)๊ณผ body ํƒœ๊ทธ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
const target = document.querySelector('#target');
const body = document.querySelector('body');
  1. ๋ฐ์ดํ„ฐ์•ˆ์—์„œ ๋ฐ˜๋ณตํ•˜๋ฉฐ div ์š”์†Œ๋ฅผ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค. ์ด ๋•Œ ๋งˆ์ง€๋ง‰ div ๋ฅผ ๊ด€์ฐฐ๋Œ€์ƒ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.
const createElement = (data,observer) => {
    data.forEach((item,index)=>{
        let div = document.createElement('div');
        // ๋ฐ์ดํ„ฐ์˜ ๋งˆ์ง€๋ง‰ ์š”์†Œ๋ฅผ ๊ด€์ฐฐ๋Œ€์ƒ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.
        if(index === data.length -1){
            div.innerText = "target";
            observer.observe(div);
        } else {
            div.innerText = item;
        }
       body.appendChild(div);
    })
}
  1. Insersection Obeserver ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๋Œ€์ƒ์š”์†Œ๋ฅผ observe ํ•œ๋‹ค.
const option = {
    rootMargin: '0px',
    threshold: 1.0
}

const observer = new IntersectionObserver((entries,observer)=>{
    console.log(entries);
    entries.forEach((entry,index)=>{
        console.log(entries[index]);
        if(entry.isIntersecting){
           return createElement(data,observer);
        } else {
            return;
        }
       
    })  
},option);

observer.observe(target);

์™„์„ฑ!









์ฐธ๊ณ ํ•œ ๋ฌธ์„œ

http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial
https://velog.io/@meganatc7/Intersection-Observer-%EB%9E%80

https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API
https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry
https://developer.mozilla.org/ko/docs/Web/API/IntersectionObserver/IntersectionObserver

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ