์์ ฏ 10๊ฐ์์ ํ์ด์ง๊ฐ ๋ฒ๋ฒ ์๋ค โ ๊ฐ์ reflow๋ฅผ ์ ํด ์๊ฐ์ผ๋ก ๋ฐ์ด๋ด 95% ๊ฐ์ ํ ์ด์ผ๊ธฐ
์์ธ ์์ ฏ ํ์ฅ์๋ ์์ง๋ง ์์ฌ๋ธ ๊ธฐ๋ฅ์ด ์๋ค. ์์ ฏ ํ ์คํธ ์์ ๋ฐฐ๊ฒฝ์ ๋ง์ถฐ ์๋์ผ๋ก ์กฐ์ ํ๋ ๊ฒ. ํฐ ํ์ด์ง ์์์ ๊ฒ์ ๊ธ์จ, ์ด๋์ด ํ์ด์ง ์์์ ํฐ ๊ธ์จ. ๊ทธ๋ฐ๋ฐ ์ด ์์ ๊ธฐ๋ฅ์ด ์ฑ๋ฅ์ ๋ฌด๋๋จ๋ ธ๋ค.
์ฆ์: ์์ ฏ์ ์ฌ๋ฌ ๊ฐ ๋์ฐ๋ฉด ํ์ด์ง๊ฐ ๋๋ ค์ง๋ค
์์ ฏ์ 10๊ฐ์ฏค ๋์ฐ๋, ๋ด๊ฐ ๋ณด๊ณ ์๋ ํ์ด์ง ์์ฒด์ ๋ฐ์์ฑ์ด ๊ธ๊ฒฉํ ๋๋น ์ก๋ค. ์คํฌ๋กค๋, ํด๋ฆญ๋ ๊ตผ๋ด๋ค. ์งํ๋ก๋ INP(Interaction to Next Paint)๊ฐ ํฌ๊ฒ ํ์๋ค.
Chrome DevTools Performance ํญ์ ์ด์ด๋ณด๋ ๋ฒ์ธ์ด ๋ช
ํํ๋ค.
getBackgroundColorAtPoint๋ผ๋ ํจ์๊ฐ **์ ์ฒด ์คํ ์๊ฐ์ 65~71%**๋ฅผ ๋จน๊ณ
์์๋ค.
์์ธ: elementsFromPoint๋ ๊ณต์ง๊ฐ ์๋๋ค
๋ฐฐ๊ฒฝ์์ ์์๋ด๋ ค๊ณ document.elementsFromPoint(x, y)๋ฅผ ์ผ๋ค. "์ด ์ขํ์
์ด๋ค ์์๋ค์ด ์์ฌ ์๋"๋ฅผ ์๋ ค์ฃผ๋ API๋ค. ์ด๋ฐ ๋ ์ด์์์ ์ฝ๋ API๋
๋ ์ด์์์ด dirty ์ํ์ผ ๋ ํธ์ถํ๋ฉด ๊ฐ์ ์ฌ๊ณ์ฐ(reflow) ์ ์ ๋ฐํ๋ค.
ํธ์ถ ์์ฒด๊ฐ ํญ์ ๋น์ผ ๊ฑด ์๋๊ณ , "๊ทธ ์ฌ์ด์ ๋ญ๊ฐ ์คํ์ผ์ ๊ฑด๋๋ ธ๋ค๋ฉด"
๋น์ธ์ง๋ค.
๊ทธ๋ฐ๋ฐ ๋ด ์ฝ๋๊ฐ ์ ํํ ๊ทธ ์ง์ ํ๊ณ ์์๋ค. ์ํ๋งํ ๋ ์์ ฏ ์์ ์ด
์ฐํ์ง ์๋๋ก, ์ํ ์ฌ์ด์ฌ์ด์ ์ค๋ฒ๋ ์ด ๋ฃจํธ์ pointer-events๋ฅผ
๊ป๋ค ์ผฐ๋ค ํ๋ ๊ฒ.
const previousPointerEvents = root.style.pointerEvents;
root.style.pointerEvents = "none"; // โ ์คํ์ผ ๋ณ๊ฒฝ = ๋ ์ด์์ invalidate
const sampled = samplePoints.map(({ point }) =>
getBackgroundColorAtPoint(point.x, point.y) // โ ๋ค์ ๋ ์ด์์์ ์ฝ์ = ๊ฐ์ reflow
);
root.style.pointerEvents = previousPointerEvents; // โ ๋ invalidate
"์คํ์ผ ์ฐ๊ธฐ โ ๋ ์ด์์ ์ฝ๊ธฐ"๋ฅผ ๋ฐ๋ณตํ๋, elementsFromPoint๊ฐ ์บ์๋
๋ ์ด์์์ ์ฌ์ฌ์ฉํ์ง ๋ชปํ๊ณ ๋งค๋ฒ ์ฌ๊ณ์ฐํ๊ฒ ๋๋ค.
ํธ์ถ ๊ตฌ์กฐ๊ฐ ์ด๋ฌ๋ค.
renderWidgets() [RAF โ ํ์ธํธ ์ง์ ์ ์คํ]
โ getCachedTheme() ร ์์ ฏ N๊ฐ
โ sampleTheme()
โ getBackgroundColorAtPoint() ร 9 ์ํํฌ์ธํธ
โ document.elementsFromPoint() ร 7 ํ๋ก๋ธ
์์ ฏ ํ๋๋น 9 ร 7 = 63๋ฒ. ์์ ฏ 10๊ฐ๋ฉด ํ ๋ฒ ๋ ๋์ 630๋ฒ์ ๊ฐ์ reflow.
๊ทธ๊ฒ๋ ํํ requestAnimationFrame ์ฝ๋ฐฑ ์์์ ๋๊ธฐ์ ์ผ๋ก ๋์๋ค. RAF๋
"ํ์ธํธ ๋ฐ๋ก ์ง์ "์ด๋ผ, ์ฌ๊ธฐ์ 630๋ฒ reflow๋ฅผ ํ๋ฉด ํ์ธํธ๊ฐ ์ ์ด๊ฐ
๋ธ๋กํน๋๋ค. ์ธก์ ํ๋ ์ด ๊ฒฝ๋ก๊ฐ ์ฝ 6,993ms๋ฅผ ์ฐจ์งํ๋ค.
ํด๊ฒฐ 1: ๋ฌด๊ฑฐ์ด ์ผ์ RAF ๋ฐ์ผ๋ก โ requestIdleCallback
์ฒซ ๋ฒ์งธ ์์น: ํ์ธํธ ๊ฒฝ๋ก์์ ๋ฌด๊ฑฐ์ด ๊ณ์ฐ์ ๊ฑท์ด๋ธ๋ค. ํ
๋ง ์ํ๋ง์ RAF
์์์ ์ฆ์ ํ์ง ์๊ณ , ๋ธ๋ผ์ฐ์ ๊ฐ ํ๊ฐํ ๋(requestIdleCallback) ์ฒ๋ฆฌํ๋๋ก
ํ๋ก ๋บ๋ค.
function getCachedTheme(widget) {
const cached = themeCache.get(widget.id);
if (cached?.theme) return cached.theme; // ์บ์ ์์ผ๋ฉด ์ฆ์ ๋ฐํ
// ์บ์ ๋ฏธ์ค: ์ง๊ธ ๊ณ์ฐํ์ง ๋ง๊ณ ํ์ ๋ฃ๊ณ , ์ผ๋จ ๊ธฐ๋ณธ ํ
๋ง ๋ฐํ
themeSampleQueue.add(widget.id);
scheduleIdleThemeSampling();
return DEFAULT_THEME; // โ RAF๋ฅผ ๋ธ๋กํนํ์ง ์๋๋ค
}
function scheduleIdleThemeSampling() {
if (themeSampleIdleHandle) return;
themeSampleIdleHandle = requestIdleCallback((deadline) => {
themeSampleIdleHandle = null;
// ๋ธ๋ผ์ฐ์ ๊ฐ ์ค ์ฌ์ ์๊ฐ(deadline) ์์์๋ง ์กฐ๊ธ์ฉ ์ฒ๋ฆฌ
while (themeSampleQueue.size > 0 && deadline.timeRemaining() > 1) {
const id = themeSampleQueue.values().next().value;
themeSampleQueue.delete(id);
const widget = widgets.find((w) => w.id === id);
if (widget) themeCache.set(id, { key: id, theme: sampleTheme(widget) });
}
requestRender(); // ๋ค ๋๋๋ฉด ๊ทธ๋ ํ ๋ฒ ๋ ๋
});
}
๋ ๋๋ง์ "์ผ๋จ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๊ทธ๋ฆฌ๊ณ ", ๋ฌด๊ฑฐ์ด ์ํ๋ง์ ์ ํด ์๊ฐ์ ๋๋ ์ ํ๋ค. ํ์ธํธ๊ฐ ๋๋ ๋งํ์ง ์๋๋ค.
ํด๊ฒฐ 2: ์ ์ด์ ๊ณ์ฐ๋์ ์ค์ธ๋ค
requestIdleCallback์ผ๋ก ์ฎ๊ฒจ๋, ๊ทผ๋ณธ์ ์ผ๋ก ํธ์ถ ์๊ฐ ๋ง์ผ๋ฉด ์ข์ ๊ฒ ์๋ค.
๊ทธ๋์ ์ํ ํฌ์ธํธ๋ฅผ 9๊ฐ โ 3๊ฐ, ํ๋ก๋ธ๋ฅผ 7๊ฐ โ 1๊ฐ๋ก ์ค์๋ค. ์์ ฏ๋น
63๋ฒ โ 3๋ฒ. ์ค์ธกํด๋ณด๋ 3๊ฐ ํฌ์ธํธ๋ง์ผ๋ก๋ ๋ฐฐ๊ฒฝ์ ํ์ ์ ํ๋๊ฐ ์ถฉ๋ถํ๋ค.
ํด๊ฒฐ 3: ๊น๋นก์ ์์ ๊ธฐ โ stale ์บ์ ์ ์ง
๋ค์ ์ํ๋งํ๋ ๋์ ์บ์๋ฅผ ๋น์๋ฒ๋ฆฌ๋ฉด, ๊ทธ ์ฐฐ๋์ ์์ ฏ์ด ๊ธฐ๋ณธ์์ผ๋ก ๋ฒ์ฉ์ธ๋ค. ๊ทธ๋์ "์ฌ์ํ๋ง ์ค"์ด์ด๋ ์ด์ ํ ๋ง๋ฅผ ๊ทธ๋๋ก ๋ฐํํ๊ณ , ์ ๊ฐ์ด ๋์ค๋ฉด ์กฐ์ฉํ ๊ต์ฒดํ๋ค. cache invalidation์ ๊ณ ์ ์ ํจ์ ์, "์ง์ฐ์ง ๋ง๊ณ ๊ฐฑ์ ๋ ๋๊น์ง ๋ค๊ณ ์๊ธฐ"๋ก ํผํ๋ค.
๊ฒฐ๊ณผ
| ๋จ๊ณ | ์กฐ์น | ์์ |
|---|---|---|
| ์ | RAF ์ ๋๊ธฐ ์ํ๋ง (630 calls) | ~6,993ms |
| 1 | requestIdleCallback๋ก ๋ถ๋ฆฌ |
~1,593ms |
| 2 | ์ํ ํฌ์ธํธ 9โ3, ํ๋ก๋ธ 7โ1 (630โ30) | ~133ms |
์ฝ 95% ๊ฐ์ . ์์ ฏ์ ์๋ฉ ๋์๋ ํ์ด์ง๊ฐ ๋๋ ๋ฒ๋ฒ ์ด์ง ์๋๋ค.
๋ฐฐ์ด ๊ฒ
-
์ถ์ธกํ์ง ๋ง๊ณ ์ธก์ ํ๋ค. DevTools๊ฐ
getBackgroundColorAtPoint๋ฅผ ์ฝ ์ง์ด์ฃผ์ง ์์๋ค๋ฉด ์๋ฑํ ๋ฐ๋ฅผ ๊ณ ์ณค์ ๊ฑฐ๋ค. -
elementsFromPoint/getBoundingClientRect๊ฐ์ ๋ ์ด์์์ ์ฝ๋ API๋ ๊ทธ ์์ฒด๋ก ํญ์ ๋น์ผ ๊ฒ ์๋๋ผ, ์คํ์ผ ์ฐ๊ธฐ์ ๋ฒ๊ฐ์ ๋ถ๋ฅด๋ฉด ๋งค๋ฒ ๊ฐ์ reflow๊ฐ ๋๋ค (layout thrashing). ํนํ RAFยท์คํฌ๋กค ํธ๋ค๋ฌ ์์์ ๋ฐ๋ณตํ๋ฉด ์น๋ช ์ . -
๋ฌด๊ฑฐ์ด ์์ ์ ํ์ธํธ ๊ฒฝ๋ก ๋ฐ(idle) ์ผ๋ก, ๊ทธ๋ฆฌ๊ณ ์ ์ด์ ์์ ์ค์ด๋ ๊ฒ ๋ ๋ค ํ์ํ๋ค. ์ฌ์ฉ์ ๋์ ์ ๋ณด์ด๋ ์ด ์ต์ ํ๊ฐ ์ค์ ์ฒด๊ฐ์ ๊ฐ์ฅ ํฌ๊ฒ ๋ฐ๊ฟจ๋ค.
-
๊ด๋ จ ํ๋ก์ ํธ: Stock Price Widget