{
    "componentChunkName": "component---src-templates-blog-post-js",
    "path": "/21-12-07-intersection-observer/",
    "result": {"data":{"site":{"siteMetadata":{"title":"irrationnelle","author":"irrationnelle","siteUrl":"https://dev.rase.blog","comment":{"disqusShortName":"","utterances":"irrationnelle/devlog"},"sponsor":{"buyMeACoffeeId":""}}},"markdownRemark":{"id":"89cbd99f-a7ab-5026-966c-c73aa670bd4c","excerpt":"intersection observer api 공식문서 를 참조하면 일반적으로 페이지 스크롤로 이미지나 다른 요소를 지연 평가로 불러오고 싶은 경우 무한 스크롤 페이지를 구현하고 싶은 경우 이런 경우들을 다루기 위해 사용한다고 한다. intersection observer api 를 사용할 때, 브라우저 뷰포트나 대상 element 를 일컫어 root 혹은 root element 라고 부른다. 이 root 에  메서드의 인자로 전달하는 element 가 나타나면(intersect…","html":"<h2 id=\"intersection-observer-api\" style=\"position:relative;\"><a href=\"#intersection-observer-api\" aria-label=\"intersection observer api permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>intersection observer api</h2>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API\">공식문서</a> 를 참조하면 일반적으로</p>\n<ul>\n<li>페이지 스크롤로 이미지나 다른 요소를 지연 평가로 불러오고 싶은 경우</li>\n<li>무한 스크롤 페이지를 구현하고 싶은 경우</li>\n</ul>\n<p>이런 경우들을 다루기 위해 사용한다고 한다.</p>\n<p>intersection observer api 를 사용할 때,</p>\n<p>브라우저 뷰포트나 대상 element 를 일컫어 root 혹은 root element 라고 부른다.</p>\n<p>이 root 에 <code class=\"language-text\">observer.observe()</code> 메서드의 인자로 전달하는 element 가 나타나면(intersect)</p>\n<p><code class=\"language-text\">IntersectionObserver</code> 의 생성자의 첫번째 인자로 전달한 callback 을 실행시킨다.</p>\n<h2 id=\"코드-예제\" style=\"position:relative;\"><a href=\"#%EC%BD%94%EB%93%9C-%EC%98%88%EC%A0%9C\" aria-label=\"코드 예제 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>코드 예제</h2>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fun</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hello world'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> intersectionobserver <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntersectionObserver</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span>entries<span class=\"token punctuation\">,</span> observer<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    entries<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>entry<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>entry<span class=\"token punctuation\">.</span>isIntersecting<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">fun</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> el <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"#target\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\nintersectionobserver<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span>el<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>첫번째 인자로 callback 함수를 전달하고, 두번째 인자인 option 은 전달하지 않는다.</p>\n<p>따라서 root 역시 기본값인 브라우저의 뷰포트이다.</p>\n<p>callback 의 첫번째 인자인 entries 는 <code class=\"language-text\">IntersectionObserverEntry</code> 의 배열이다.</p>\n<p><code class=\"language-text\">IntersectionObserverEntry</code> 는 root 와 <code class=\"language-text\">observer.observe()</code> 메서드의 인자로 전달하는 element 간 intersect 상황에 따라 새로운 프로퍼티 값을 가진다.</p>\n<p>단일 인스턴스가 아니라 배열인 이유는</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> el <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"#target\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> secondEl <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"#second-target\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\nintersectionobserver<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span>el<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nintersectionobserver<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span>secondEl<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>이처럼 <code class=\"language-text\">observer.observe()</code> 을 여러번 호출해서 다수의 element 를 주시하는 경우 각 주시하는 element 에 대응하는 인스턴스를 배열 요소로 담기 때문이다.</p>\n<h2 id=\"무한-스크롤\" style=\"position:relative;\"><a href=\"#%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4\" aria-label=\"무한 스크롤 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>무한 스크롤</h2>\n<p>SPA 에서 무한 스크롤을 구현하는 경우 스크롤 이벤트에 페이지네이션 operation 을 추가하거나</p>\n<p>이 intersection observer 를 사용해서 마지막 element 가 뷰포트에 드러났을 때 페이지네이션 operation 을 실행시킬 수 있다.</p>\n<p>스크롤 이벤트로 다루는 경우, debounce 작업이 필요하고 마지막 페이지에 도달한 경우에는 또 조건문을 추가해서 api 요청 등을 하지 않도록 해야하는 번거로움이 존재한다.</p>\n<p>그래서 대부분의 경우 intersection observer 로 무한 스크롤을 구현하는 것이 깔끔하다.</p>\n<p>하지만 가끔은 각 item 들의 크기가 균등하지 않은 grid 레이아웃의 경우에는 마지막 row 가 뷰포트에 intersect 하는 것이 애매할 수 있어서, 이 경우에는 스크롤 이벤트에 무한 스크롤과 관련한 operation 을 추가하는 것이 더 나을 것이다.</p>\n<h2 id=\"react-custom-hook-으로-무한-스크롤에-사용하는-intersection-observer-재사용\" style=\"position:relative;\"><a href=\"#react-custom-hook-%EC%9C%BC%EB%A1%9C-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%97%90-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-intersection-observer-%EC%9E%AC%EC%82%AC%EC%9A%A9\" aria-label=\"react custom hook 으로 무한 스크롤에 사용하는 intersection observer 재사용 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>react custom hook 으로 무한 스크롤에 사용하는 intersection observer 재사용</h2>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useEffect<span class=\"token punctuation\">,</span> useRef <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"react\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> useDetectElement <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Dependencies <span class=\"token keyword\">extends</span> <span class=\"token class-name\"><span class=\"token builtin\">any</span></span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">(</span>\n  <span class=\"token function-variable function\">callback</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">,</span>\n  deps<span class=\"token operator\">:</span> Dependencies\n<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> displayElement <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useRef</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>HTMLElement<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>displayElement <span class=\"token operator\">&amp;&amp;</span> displayElement<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> intersectionobserver <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntersectionObserver</span><span class=\"token punctuation\">(</span>\n        <span class=\"token punctuation\">(</span>entries<span class=\"token punctuation\">,</span> observer<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n          entries<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>entry<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>entry<span class=\"token punctuation\">.</span>isIntersecting<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              observer<span class=\"token punctuation\">.</span><span class=\"token function\">unobserve</span><span class=\"token punctuation\">(</span>entry<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      intersectionobserver<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span>displayElement<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> intersectionobserver<span class=\"token punctuation\">.</span><span class=\"token function\">disconnect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>displayElement<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>deps<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> displayElement<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> useDetectElement<span class=\"token punctuation\">;</span></code></pre></div>\n<p>위와 같은 hook 을 생성한 뒤, <code class=\"language-text\">const el = useDetectElement(callback, [deps])</code> 로 MutableRefObject 을 초기화한다.</p>\n<p>이후 <code class=\"language-text\">el</code> 이라 이름붙인 MutableRefObject 를 대상 react component 의 <code class=\"language-text\">ref</code> props 에 전달하면</p>\n<p>대상 react component 가 뷰포트에 드러날 때 callback 함수를 호출한다.</p>\n<p>무한 스크롤 용도로 제작한 custom hook 이기 때문에(정확히는 마지막 element 에만 특정 callback 을 실행시키기 위한 것이기 때문에)</p>\n<p><code class=\"language-text\">observer.unobserve(entry.target);</code> 을 callback 호출 전에 호출한다.</p>\n<p>내 경우, 대상 react component 에 <code class=\"language-text\">ref</code> props 를 전달하는 것을, 조건에 따라 대상 react component 를 변경해주었기 때문에</p>\n<p><code class=\"language-text\">observer.unobserve(entry.target);</code> 을 따로 호출하지 않아도 뷰포트에 대상 컴포넌트가 드러난 이후에는 더 이상 observer 가 대상 컴포넌트를 주시하지 않도록 했다.</p>\n<p>여기서 좀 더 custom hook 을 일반화하고 싶은 경우에는, custom hook 의 매개변수를 하나 더 추가해서, option 매개변수에다가 root element 를 설정할 수 있을 것이다.</p>","frontmatter":{"title":"Custom hook 을 이용해서 intersection observer 재사용하기","date":"December 12, 2021"}}},"pageContext":{"slug":"/21-12-07-intersection-observer/","previous":{"fields":{"slug":"/21-11-17-sever-side-rendering-next-js/"},"frontmatter":{"title":"서버 사이드 렌더링에 대해","category":"dev","draft":false}},"next":null}},
    "staticQueryHashes": ["3128451518","3474900066"]}