[Observer API ํŒŒํ—ค์น˜๊ธฐ] 3. ResizeObserver

โฐ 2024-06-20 (๋ชฉ) 01:14:40

screener
์‹œ๋ฆฌ์ฆˆ ๋ชจ์•„๋ณด๊ธฐ
Observer API ํŒŒํ—ค์น˜๊ธฐ

3 / 6

Table of Contents

  • 1. ResizeObserver















ResizeObserver

ResizeObserver๋Š” ์š”์†Œ์˜ ํฌ๊ธฐ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ์˜ต์ €๋ฒ„๋‹ค. ์š”์†Œ์˜ width, height๊ฐ€ ๋ณ€ํ•  ๊ฒฝ์šฐ, ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์›ํ•˜๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

TYPESCRIPT

1
2
3
const ro = new ResizeObserver(callback);

ro.observe(tag, options);

์ด์ „ ์žฅ์˜ IntersectionObserver์™€ ๋‹ค๋ฅด๊ฒŒ ์˜ต์…˜์„ API์—์„œ ํ• ๋‹นํ•˜์ง€ ์•Š๋Š”๋‹ค. observe ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์š”์†Œ๋ฅผ ๋“ฑ๋กํ•  ๋•Œ, options๋ฅผ ๊ฐ™์ด ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

options

์š”์†Œ ๋“ฑ๋ก ์‹œ options๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์„ธ๋ถ€ ์˜ต์…˜์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ResizeObserverOptions ํƒ€์ž…์„ ๊ฐ€์ง„๋‹ค.

๋ฐ•์Šค ์ข…๋ฅ˜ (box)

์š”์†Œ์˜ ์ข…๋ฅ˜๋ฅผ ์ง€์ •ํ•œ๋‹ค.

  • content-box - CSS์— ์ •์˜๋œ ์ปจํ…์ธ  ์˜์—ญ์˜ ํฌ๊ธฐ
  • border-box - CSS์— ์ •์˜๋œ ๋ฐ•์Šค ์ „์ฒด ์˜์—ญ์˜ ํฌ๊ธฐ (margin, padding, border ํฌํ•จ)
  • device-pixel-content-box - ๊ธฐ๊ธฐ์˜ ํ”ฝ์…€ ๋‹จ์œ„ ๊ธฐ์ค€์œผ๋กœ ์ •์˜๋œ ์ปจํ…์ธ  ์˜์—ญ์˜ ํฌ๊ธฐ

callback

ResizeObserver์˜ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋Š” ์š”์†Œ์˜ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ•  ๊ฒฝ์šฐ ๋™์ž‘ํ•˜๋Š” ์ด๋ฒคํŠธ ๋ฉ”์„œ๋“œ๋‹ค.

ResizeObserverCallback ํƒ€์ž…์œผ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ์ฝ”๋“œ๋กœ ํ‘œ๊ธฐํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

TYPESCRIPT

1
const ro = new ResizeObserver((entries, observer) => {});

ResizeObserverEntry

entries๋Š” ResizeObserverEntry[] ํƒ€์ž…์„ ๊ฐ–๋Š”๋‹ค. ResizeObserverEntry์— ๋“ฑ๋ก๋œ ์š”์†Œ์˜ ์ด๋ฒคํŠธ ์ •๋ณด๋ฅผ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

ResizeObserverEntry์— ์—ฌ๋Ÿฌ DOM์„ ๋“ฑ๋กํ•˜์—ฌ, ์—ฌ๋Ÿฌ ๊ฐ์ฒด์— ์˜ต์ €๋ฒ„๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ธฐ๋ณธ ๋ฐ•์Šค

๊ฐ ์š”์†Œ์˜ ์˜๋ฏธ๋Š” ์œ„ ๋ฐ•์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์„ค๋ช…ํ•œ๋‹ค.

์ „์ฒด ์˜์—ญ์˜ ํฌ๊ธฐ ์ •๋ณด (borderBoxSize)

border-box

CSS์—์„œ ์ปจํ…์ธ ์˜ ํฌ๊ธฐ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

๊ฐ๊ฐ border, padding์œผ๋กœ ๋ ˆ์ด์•„์›ƒ์ด ๊ตฌ์„ฑ๋˜๋ฉฐ, ๋‚ด๋ถ€์˜ ์ปจํ…์ธ ์— ๋”ฐ๋ผ ํฌ๊ธฐ๊ฐ€ ์ •ํ•ด์ง„๋‹ค.

borderBoxSize๋Š” ์ด ๋ชจ๋“  ์š”์†Œ๋ฅผ ์•„์šฐ๋ฅด๋Š” ๋ฐ•์Šค์˜ ํฌ๊ธฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋ฐฐ์—ด๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์œผ๋ฉฐ, ๊ฐ ์•„์ดํ…œ์€ blockSize, inlineSize๋ฅผ ๊ฐ€์ง„๋‹ค.

  • blockSize - ๋†’์ด (height)
  • inlineSize - ๋„ˆ๋น„ (width)

์ˆœ์ˆ˜ ์ปจํ…์ธ  ์˜์—ญ์˜ ํฌ๊ธฐ ์ •๋ณด (contentBoxSize)

content-box

CSS์—์„œ ๋ฐ•์Šค ๋ ˆ์ด์•„์›ƒ์„ ๊ตฌ์„ฑํ•˜๋Š” border, padding์„ ์ œ์™ธํ•œ ์ˆœ์ˆ˜ ์ปจํ…์ธ ์˜ ํฌ๊ธฐ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

borderBoxSize์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฐฐ์—ด๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์œผ๋ฉฐ, ๊ฐ ์•„์ดํ…œ์˜ ์ •๋ณด ์—ญ์‹œ ๋™์ผํ•˜๋‹ค.

  • blockSize - ๋†’์ด (height)
  • inlineSize - ๋„ˆ๋น„ (width)

๊ธฐ๊ธฐ ํ”ฝ์…€ ๊ธฐ์ค€ ์ปจํ…์ธ  ์˜์—ญ์˜ ํฌ๊ธฐ ์ •๋ณด (devicePixelContentBoxSize)

๊ธฐ๊ธฐ ํ”ฝ์…€ ๊ธฐ์ค€์˜ ์ปจํ…์ธ  ์˜์—ญ ํฌ๊ธฐ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

borderBoxSize์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฐฐ์—ด๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์œผ๋ฉฐ, ๊ฐ ์•„์ดํ…œ์˜ ์ •๋ณด ์—ญ์‹œ ๋™์ผํ•˜๋‹ค.

  • blockSize - ๋†’์ด (height)
  • inlineSize - ๋„ˆ๋น„ (width)

์š”์†Œ์˜ ํฌ๊ธฐ ์ •๋ณด (contentRect)

contentRect

์š”์†Œ๋“ค์˜ ๋‹ค์–‘ํ•œ ํฌ๊ธฐ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด๋กœ, ๋‹ค๋ฅธ ๊ฐ์ฒด์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๊ฐ€ ์ผ๋ถ€ ์ค‘๋ณต๋˜์–ด ์žˆ๋‹ค.

ํ•ด๋‹น ๊ฐ์ฒด๋Š” ๋ ˆ๊ฑฐ์‹œ ์†์„ฑ์œผ๋กœ, ํ˜ธํ™˜์„ฑ ์œ ์ง€๋กœ ์ธํ•ด ์ง€์›๋˜๊ณ  ์žˆ์œผ๋ฉฐ, ์–ธ์  ๊ฐ€ ์ง€์› ์ข…๋ฃŒ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค. ๐Ÿ”— (์ฐธ๊ณ )

  • width - ์ˆœ์ˆ˜ ์ปจํ…์ธ  ์˜์—ญ์˜ ๋„ˆ๋น„ (contentBoxSize์™€ ๋™์ผ)
  • height - ์ˆœ์ˆ˜ ์ปจํ…์ธ  ์˜์—ญ์˜ ๋†’์ด (contentBoxSize์™€ ๋™์ผ)
  • x - ๋ฐ•์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์œ„์น˜ํ•œ ์ปจํ…์ธ ์˜ x ์ขŒํ‘œ
  • y - ๋ฐ•์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์œ„์น˜ํ•œ ์ปจํ…์ธ ์˜ y ์ขŒํ‘œ
  • left - ์ปจํ…์ธ ์˜ ์ขŒ์ธก ๊ธฐ์ค€ ์œ„์น˜. ํ†ต์ƒ x์™€ ๋™์ผ
  • top - ์ปจํ…์ธ ์˜ ์ƒ๋‹จ ๊ธฐ์ค€ ์œ„์น˜. ํ†ต์ƒ y์™€ ๋™์ผ
  • right - ์ปจํ…์ธ  ์šฐ์ธก ๊ธฐ์ค€ ์œ„์น˜.
  • bottom - ์ปจํ…์ธ  ํ•˜๋‹จ ๊ธฐ์ค€ ์œ„์น˜.

์ด๋ฒคํŠธ ๋Œ€์ƒ (target)

ResizeObserver ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ DOM ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

observer

ResizeObserver ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ๋„ ResizeObserver๋ฅผ ์—ฐ์‡„์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.

React์—์„œ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ

ResizeObserver๋ฅผ ์ปค์Šคํ…€ ํ›…์„ ํ†ตํ•ด ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด๋ณด์ž.

TYPESCRIPT

1
2
3
4
export function useResizeObserver(): void
{
  //
}

์œ„์™€ ๊ฐ™์ด useResizeObserver ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.

useResizeObserver๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  ์•„๋ž˜์˜ ์„ธ ์š”์†Œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

  1. ๋Œ€์ƒ DOM
  2. ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
  3. ์˜ต์…˜

์œ„ ์„ธ ์š”์†Œ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ •์˜ํ•˜์ž. 1๋ฒˆ์˜ ๊ฒฝ์šฐ, ์ผ๋ฐ˜์ ์œผ๋กœ HTMLElement๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, ํ•ด๋‹น ํ›…์—์„œ๋Š” HTMLElement ํƒ€์ž… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, string์„ ๋ฐ›์•„ #id, .class์™€ ๊ฐ™์€ ์„ ํƒ์ž๋กœ ํƒœ๊ทธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useEffect } from "react";

export type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void;

/**
 * ResizeObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseResizeObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {ResizeObserverOptions} options: ์˜ต์…˜
 */
export function useResizeObserver(ref: Element | string | null, callback: UseResizeObserverCallback, options?: ResizeObserverOptions): void
{
	//
}

ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์œ„์™€ ๊ฐ™๋‹ค. ์ดํ›„ ResizeObserver๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ• ๋‹นํ•œ๋‹ค.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { useEffect } from "react";

export type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void;

/**
 * ResizeObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseResizeObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {ResizeObserverOptions} options: ์˜ต์…˜
 */
export function useResizeObserver(ref: Element | string | null, callback: UseResizeObserverCallback, options?: ResizeObserverOptions): void
{
	useEffect(() =>
	{
		const ro = new ResizeObserver((entries) =>
		{
			entries.forEach(callback);
		});
	}, [ ref, callback, options ]);
}

ResizeObserver๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ro ๋ณ€์ˆ˜๋กœ ํ• ๋‹นํ•œ๋‹ค.

์ดํ›„ ref๋กœ ์ง€์ •ํ•œ DOM์„ ํ• ๋‹นํ•œ๋‹ค.

ref๋Š” ์„ธ ๊ฐ€์ง€ ์ƒํƒœ์— ๋”ฐ๋ผ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ถ„๊ธฐ๋ฅผ ๊ฑฐ์นœ๋‹ค.

  • null์ผ ๊ฒฝ์šฐ -> ํŒจ์Šคํ•œ๋‹ค.
  • string์ผ ๊ฒฝ์šฐ -> document.querySelector ๋ฉ”์„œ๋“œ๋กœ ํƒœ๊ทธ๋ฅผ ์„ ํƒํ•œ ๋’ค, ํ•ด๋‹น ํƒœ๊ทธ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
  • HTMLElement์ผ ๊ฒฝ์šฐ -> ์ด๋ฏธ Element์ด๋ฏ€๋กœ, ์ฆ‰์‹œ ๋“ฑ๋กํ•œ๋‹ค.

typeof ref === "string" ๊ตฌ๋ฌธ์„ ํ†ตํ•ด ref๊ฐ€ ๋ฌธ์ž์—ด์ธ์ง€๋ฅผ ํŒ๋ณ„ํ•˜์—ฌ ๊ตฌํ˜„ํ–ˆ๋‹ค.

ro.observe() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํƒœ๊ทธ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

TYPESCRIPT

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
import { useEffect } from "react";

export type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void;

/**
 * ResizeObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseResizeObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {ResizeObserverOptions} options: ์˜ต์…˜
 */
export function useResizeObserver(ref: Element | string | null, callback: UseResizeObserverCallback, options?: ResizeObserverOptions): void
{
	useEffect(() =>
	{
		const ro = new ResizeObserver((entries) =>
		{
			entries.forEach(callback);
		});

		// DOM์ด ์œ ํšจํ•  ๊ฒฝ์šฐ
		if (ref)
		{
			// ref๊ฐ€ ๋ฌธ์ž์—ด์ผ ๊ฒฝ์šฐ
			if (typeof ref === 'string')
			{
				const tag = document.querySelector(ref);

				// ํƒœ๊ทธ๊ฐ€ ์œ ํšจํ•  ๊ฒฝ์šฐ
				if (tag)
				{
					ro.observe(tag, options);
				}
			}

			// DOM์ผ ๊ฒฝ์šฐ
			else
			{
				ro.observe(ref, options);
			}
		}
	}, [ ref, callback, options ]);
}

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ResizeObserver์˜ ๋“ฑ๋ก์ด ์ค‘์ฒฉ๋˜์ง€ ์•Š๋„๋ก, ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.

ro.disconnect() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ResizeObserver๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์ „์ฒด ์ฝ”๋“œ

TYPESCRIPT

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
import { useEffect } from "react";

export type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void;

/**
 * ResizeObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseResizeObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {ResizeObserverOptions} options: ์˜ต์…˜
 */
export function useResizeObserver(ref: Element | string | null, callback: UseResizeObserverCallback, options?: ResizeObserverOptions): void
{
	useEffect(() =>
	{
		const ro = new ResizeObserver((entries) =>
		{
			entries.forEach(callback);
		});

		// DOM์ด ์œ ํšจํ•  ๊ฒฝ์šฐ
		if (ref)
		{
			// ref๊ฐ€ ๋ฌธ์ž์—ด์ผ ๊ฒฝ์šฐ
			if (typeof ref === 'string')
			{
				const tag = document.querySelector(ref);

				// ํƒœ๊ทธ๊ฐ€ ์œ ํšจํ•  ๊ฒฝ์šฐ
				if (tag)
				{
					ro.observe(tag, options);
				}
			}

			// DOM์ผ ๊ฒฝ์šฐ
			else
			{
				ro.observe(ref, options);
			}
		}

		return () =>
		{
			ro.disconnect();
		};
	}, [ ref, callback, options ]);
}

์ „์ฒด ์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™๋‹ค.

CodeSandbox๋ฅผ ํ†ตํ•œ ์˜ˆ์‹œ

CodeSandbox๋กœ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

์ด์™€ ๊ฐ™์ด ResizeObserver๋ฅผ ํ™œ์šฉํ•˜๋ฉด DOM์˜ ๋ทฐํฌํŠธ ๊ด€๋ จ ์š”์†Œ๋ฅผ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๋‹น API๋Š” resize ์ด๋ฒคํŠธ๋ฅผ ๋Œ€์ฒดํ•˜์—ฌ ์ข€ ๋” ํšจ์œจ์ ์ธ ์ด๋ฒคํŠธ ๋™์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ ์žฅ์—์„  MutationObserver์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณด์ž.


๐Ÿท๏ธ ํƒœ๊ทธ
# JavaScript
# TypeScript
# React
# Observer API
# ResizeObserver

์ฝ์–ด์ฃผ์…”์„œ ๊ณ ๋งˆ์›Œ์š”!

๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด, ๊ณต๊ฐ์ด๋‚˜ ๋Œ“๊ธ€์„ ๋‹ฌ์•„์ฃผ์‹œ๋Š” ๊ฑด ์–ด๋–ค๊ฐ€์š”?

๋ธ”๋กœ๊ทธ ์šด์˜์— ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค.

https://hits.seeyoufarm.com/api/count/incr/badge.svg?count_bg=%23484848&icon=react.svg&icon_color=dodgerblue&title=view&title_bg=%23242424&url=https%3A%2F%2Fblog.itcode.dev%2Fposts%2F2024%2F06%2F20%2Fobserver-api-3