[FRONT] 인터섹션 옵저버로 인터섹션 여부 감지하기

인터섹션 여부 감지하기

Posted by lim.Chuck on November 12, 2024

[FRONT]

  1. [FRONT] 프론트엔드 쿠키 이슈 해결하기
  2. [FRONT] Nuxt Proxy 설정과 활용
  3. [FRONT] 웹 캐시 전략과 구현
  4. [FRONT] Next.js와 Nuxt.js 비교 분석
  5. [FRONT] Monorepo vs Multi-repo vs Monolith 아키텍처
  6. [FRONT] mitmproxy를 활용한 디버깅
  7. [FRONT] Storybook 활용 가이드
  8. [FRONT] Vercel Turbopack 소개
  9. [FRONT] 캐시와 캐싱 전략
  10. [FRONT] SWC 컴파일러 이해하기
  11. [FRONT] 인터섹션 옵저버로 인터섹션 여부 감지하기
  12. [FRONT] BroadcastChannel 사용해서 같은 도메인 브라우저 간 통신하기
  13. [FRONT] DOM이벤트 버블링(Bubbling)과 캡처링(Capturing)
  14. [FRONT] XSS와 CSRF
  15. [FRONT] 웹 성능 최적화
  16. [FRONT] 브라우저 렌더링 과정
  17. [FRONT] 웹 접근성
  18. [FRONT] URL과 Domain 정확히 이해하자 (url구조)
  19. [FRONT] HTTP 헤더 이해하기

참고문서 MDN

IntersectionObserver 란?

Intersection Observer API는 성능 문제를 해결하기 위해 2016년 4월 구글 개발자 페이지를 통해 소개된 API로, 브라우저의 뷰포트와 특정 요소가 겹치는지를 감지하는 기능을 제공합니다. 이를 통해 특정 요소가 사용자의 화면에 보이는지 여부를 효율적으로 구분할 수 있습니다.

이 API는 비동기적으로 실행되기 때문에, 요소의 변화를 감지하면서도 메인 스레드의 성능에 영향을 주지 않습니다. 즉, 스크롤과 같은 이벤트 기반 요소를 감지할 때 발생할 수 있는 렌더링 성능 저하나 불필요한 이벤트 호출 문제를 해결해 줍니다. 또한, IntersectionObserverEntry의 속성을 사용하여 요소의 위치를 확인할 수 있기 때문에 getBoundingClientRect()를 사용하는 것보다 리플로우(reflow) 현상을 줄이는 데 도움이 됩니다.

Intersection Observer의 주요 사용 사례

MDN에서는 Intersection Observer를 다음과 같은 상황에서 활용할 수 있다고 설명하고 있습니다:

  1. 페이지 스크롤 시 요소 감지: 특정 요소가 스크롤을 통해 화면에 등장하거나 사라지는 순간을 감지할 수 있습니다.
  2. 지연 로딩(Lazy Loading): 스크롤을 따라 화면에 표시될 때 이미지를 로드하거나, 콘텐츠를 로드하여 불필요한 리소스 로딩을 방지할 수 있습니다.
  3. 무한 스크롤(Infinite Scroll): 사용자가 페이지를 스크롤할 때 더 많은 콘텐츠를 동적으로 로드하고 렌더링하여, 페이지를 넘기지 않고도 연속적인 콘텐츠를 제공할 수 있습니다.
  4. 광고 노출도 분석: 광고가 실제로 사용자에게 노출된 횟수를 추적하여 광고 수익 계산에 활용할 수 있습니다.
  5. 조건부 애니메이션: 요소가 사용자 화면에 보일 때만 애니메이션을 실행하는 등, 화면 내 요소의 가시성 여부에 따라 특정 동작을 수행할 수 있습니다.

Intersection Observer API는 웹 성능 최적화와 사용성 개선을 위한 중요한 도구로, 특히 스크롤 기반 인터랙션이나 콘텐츠 로딩을 최적화할 때 매우 유용합니다.

간단예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import { useEffect } from "react";
import "./App.css";

function App() {
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            entry.target.classList.add("fade-in");
          } else {
            entry.target.classList.remove("fade-in");
          }
        });
      },
      { threshold: 0.5 } // 10%가 보일 때 트리거
    );

    document.querySelectorAll(".box").forEach((box) => {
      observer.observe(box);
    });

    return () => {
      document.querySelectorAll(".box").forEach((box) => {
        observer.unobserve(box);
      });
    };
  }, []);

  return (
    <>
      <h1>Vite + React</h1>
      <div style={{ height: "100vh" }}>스크롤 해주세요</div>
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      ></div
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div
        className="box"
        style={{
          width: "400px",
          height: "400px",
          opacity: 0,
          backgroundColor: "white",
          transition: "opacity 0.5s ease-in-out",
        }}
      />
      <div style={{ height: "100vh" }}>더 스크롤 해주세요</div>
    </>
  );
}

export default App;

설명

1
2
3
4
5
6
7
8
9
10
11
12
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add("fade-in");
      } else {
        entry.target.classList.remove("fade-in");
      }
    });
  },
  { threshold: 0.5 } // 50%가 보일 때 트리거
);

threshold를 입력해서 몇퍼센트가 보일 때 트리거 할지 설정할 수 있음