
์คํ์์ค ์ฑ๋ฅ ์ต์ ํ, ๋์ฒด ๋ญ๊ฐ ๋ฌธ์ ์์๊น?
์คํ์์ค ์ฑ๋ฅ ์ต์ ํ, ๋์ฒด ๋ญ๊ฐ ๋ฌธ์ ์์๊น? ๊ด๋ จ
์คํ์์ค ์ฑ๋ฅ์ ์ต์ ํํ๋ฉฐ ๋ฐฐ์ด ๊ฒ๋ค
ํ๋ก ํธ์๋๋ฅผ ๊ฐ๋ฐํ๋ฉด์ ํ๋ฉด์ด ๊ฐ์๊ธฐ ๋ฒ๋ฒ ๋๊ฑฐ๋, ์ฌ๊ฐํ ์ฑ๋ฅ ์ ํ๋ฅผ ๊ฒช์ด ๋ณธ ์ ์์ผ์ ๊ฐ์? ํนํ ์ ํ๋ฆฌ์ผ์ด์ ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก ์ด๋ฐ ๋ฌธ์ ๋ฅผ ๋ง์ฃผ์น๊ธฐ ์ฌ์ด๋ฐ, ์ ์ ์ด๋์๋ถํฐ ์์ ๋์ผ ํ ์ง ๋ง๋งํ ๋๊ฐ ๋ง์ต๋๋ค. ๋ ๋๋ง์ ๊ด์ฌํ๋ ์์๊ฐ ๋ง์์ง์๋ก, ์์ ์ฝ๋ ๋ณ๊ฒฝ๋ง์ผ๋ก๋ ์ ์ฒด ์ฑ๋ฅ์ด ๊ธ๊ฒฉํ ๋จ์ด์ง๊ฑฐ๋, ์๋์น ์์ ๋ถ๋ถ์์ ๋ณ๋ชฉ์ด ๋ฐ์ํ๊ธฐ๋ ํฉ๋๋ค.
์ด๋ฒ ๊ธ์์ ์ ๊ฐ ๊ฒช์ ์ฑ๋ฅ ์ต์ ํ ์ฌ์ ์ ๊ณต์ ํ๊ณ ์ ํฉ๋๋ค. ๊ท๋ชจ๊ฐ ์ปค์ง๋ ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฑ๋ฅ ๋ฌธ์ ๋ก ๊ณ ๋ฏผํ๊ฑฐ๋, ์ต์ ํ ๊ณผ์ ์ ๊ด์ฌ์ด ์๋ ๋ถ๋ค๊ป ์กฐ๊ธ์ด๋๋ง ๋์์ด ๋๋ฉด ์ข๊ฒ ์ต๋๋ค. ์ฝ๊ฒ ์ ํ๊ธฐ ์ด๋ ค์ด ์ ์์ค ์ต์ ํ ์ด์ผ๊ธฐ๋ถํฐ, ์ค์ ์ ์ฉ ๊ณผ์ ์์ ์ป์ ์ธ์ฌ์ดํธ๊น์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
โ์ ์ด๋ ๊ฒ ๋๋ ค์ง ๊ฑฐ์ง?โ ์ฑ๋ฅ ๋ฌธ์ ์์ ์ฒซ ๋ง๋จ
์ ๋ ์ต๊ทผ โEasyrdโ๋ผ๋ ๋ค์ด์ด๊ทธ๋จ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๋ฉด์ ๋น์ทํ ๋ฌธ์ ๋ฅผ ๊ฒช์์ต๋๋ค. ์ด ์๋น์ค๋ ์ ๊ฐ ๋ง๋ โFlitterโ๋ผ๋ ๋ ๋๋ง ์์ง ํ๋ ์์ํฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋๋ฐ์. Flitter๋ SVG๋ Canvas๋ฅผ ์ ์ธ์ ์ผ๋ก ๋ค๋ฃจ๋ฉด์, Flutter์ ๋น์ทํ ์ธํฐํ์ด์ค๋ฅผ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๊ตฌํํ ์คํ์์ค ํ๋ก์ ํธ์ ๋๋ค.
์ฒ์์ ๊ทธ๋ญ์ ๋ญ ์ ๋์๊ฐ๋ ๊ฒ ๊ฐ์์ง๋ง, ๋ค์ด์ด๊ทธ๋จ์ ๋ ธ๋๋ฅผ ๋๋๊ทธํ๊ฑฐ๋ ์ด์ ์ ์ฎ๊ธธ ๋๋ง๋ค ํ๋ฉด์ด ๋ฒ๋ฒ ์ด๊ธฐ ์์ํ์ต๋๋ค. ๊ฒฐ๊ตญ ์ฌ์ฉ์ ๊ฒฝํ์ด ํฌ๊ฒ ์ ํ๋๋ ์ํฉ๊น์ง ๋ง๋ฅ๋จ๋ฆฌ๊ฒ ๋์์ฃ .
๋ ธ๋๊ฐ ๋ง์์ง์๋ก ํ ํ๋ ์(16ms)์ ํ์ฉ ๋๊ธฐ๋ ํจ์ ํธ์ถ์ด ์์์ต๋๋ค. ๋ ธ๋๋ฅผ ๋น ๋ฅด๊ฒ ์์ง์ด๊ฑฐ๋ ํ๋ยท์ถ์๋ฅผ ๋ฐ๋ณตํ๋ฉด, ์ด๋ฒคํธ๊ฐ ๋ชฐ๋ ค๋ค์ด ๋ธ๋ผ์ฐ์ ๊ฐ ๊ฒฌ๋์ง ๋ชปํ์ฃ . ๊ฒฐ๊ตญ ๋ ธ๋๊ฐ ํญํญ ๋๊ธฐ๊ฑฐ๋ ์์์น ๋ชปํ ๊ณณ์ผ๋ก ํ์ด๊ฐ๋ ํ์์ด ๋ฐ์ํ๊ณ , ์ด๋ ์๋น์ค์ ํต์ฌ ๊ฐ์น๋ฅผ ํผ์ํ๋ ์ฌ๊ฐํ ๋ฌธ์ ์์ต๋๋ค.
์ฒ์ Flitter๋ฅผ ์ค๊ณํ ๋ ๋จ์ํ SVG ์์๋ค์ ์ ์ธ์ ์ผ๋ก ๊ทธ๋ ค์ฃผ๋ฉด ๋ ๊ฑฐ๋ผ ์๊ฐํ์ต๋๋ค. ํ์ง๋ง โ์ ์ฒซ ๋ก๋ฉ์ด ์ด๋ ๊ฒ ๋๋ฆฌ์ง?โ๋ผ๋ ์๋ฌธ์ด ๋ค๋ฉด์ ๋ฌธ์ ์ ์ค์ฒด๊ฐ ๋๋ฌ๋ฌ์ต๋๋ค. ๋คํธ์ํฌ ์ง์ฐ์ด ์๋, ๋ธ๋ผ์ฐ์ ๊ฐ SVG๋ฅผ ๊ทธ๋ฆฌ๋ ๊ณผ์ ์์ฒด๊ฐ ๋ณ๋ชฉ์ด์๋ ๊ฑฐ์ฃ . ์์ธํ ๋ค์ฌ๋ค๋ณด๋ SVG ์์ ์์๋ง๋ค ๊ฐ๋ณ์ ์ผ๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ฌ๋ฉด์, DOM ์ ๊ทผ์ด ๊ณผ๋ํ๊ฒ ๋ฐ์ํ๊ณ ์์์ต๋๋ค.

์ํ์ฐฉ์ค์ ์ฐ์
์ด๊ธฐ์๋ ์ฑ๋ฅ ๋ฌธ์ ์ ์ฌ๊ฐ์ฑ์ ์ ๋๋ก ํ์ ํ์ง ๋ชปํ ์ฑ, ์คํ๋ ค ์ํฉ์ ์ ํ์ํค๋ ๋ฐฉํฅ์ผ๋ก ๊ตฌ์กฐ๋ฅผ ์์ ํ๊ธฐ๋ ํ์ต๋๋ค. ๊ดํ ์๋ฑํ ๋ถ๋ถ์ ๊ณ ์น๋๋ผ ๋ ๋ณต์กํ ๋ก์ง์ ์ถ๊ฐํ๊ฒ ๋์๊ณ , ๊ฒฐ๊ตญ ๋ ๋๋ง ๋ถ๋ด๋ง ๊ฐ์ค๋๋ ์ ์ํ์ ๊ฒช์์ฃ . ์ด ๊ณผ์ ์์ ๊นจ๋ฌ์ ๊ฑด ์ฑ๋ฅ ์ต์ ํ๋ ๋ฌด์์ ์ฝ๋๋ฅผ ๊ณ ์น๋ ๊ฒ ์๋๋ผ, ์ ํํ ์ธก์ ๊ณผ ๋ถ์์ด ์ ํ๋์ด์ผ ํ๋ค๋ ์ ์ด์์ต๋๋ค.
SVG ์ด๋ฒคํธ ์ฒ๋ฆฌ์์ ๋ถํ์ํ DOM ์ ๊ทผ ์ค์ด๊ธฐ
์ด๊ธฐ ๋ฌธ์ : ๊ฐ ์์์ ์ง์ ํธ๋ค๋ฌ ๋ถ์ฐฉ์ ํ๊ณ
ํ๋ก์ ํธ ์ด๊ธฐ์ Flitter๊ฐ SVG๋ง ์ง์ํ๋ ์์ , ๊ฐ์ฅ ๋จผ์ ๋ง์ฃผํ ๋ฌธ์ ๋ ๋ชจ๋ SVG ์์์ ์ง์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ฐฉํ๋ ๋ฐฉ์์ด์์ต๋๋ค. ๋ํ์ ์ผ๋ก GestureDetector๋ผ๋ ์์ ฏ์ ํด๋ฆญ, ๋๋๊ทธ ๋ฑ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ฌ๋ฉด, ๋ด๋ถ์ ์ผ๋ก ์ค์ SVG ์์์ ํธ๋ค๋ฌ๊ฐ ์ถ๊ฐ๋๋ ๊ตฌ์กฐ์์ฃ . ์ด๋ ๊ฒ ์์ ์์๊ฐ ํ๋์ฉ ๋์ด๋ ๋๋ง๋ค DOM ์ ๊ทผ์ด ๊ธ๊ฒฉํ ์ฆ๊ฐํ๊ณ , ๊ฒฐ๊ตญ ์๋ง์ ์์์ ํธ๋ค๋ฌ๊ฐ ์ฐ๊ฒฐ๋๋ฉด์ ํ๋ฉด ์ด๊ธฐ ๋ก๋ฉ๋ถํฐ ์ธํฐ๋์ ๊น์ง ๋ฒ๋ฒ ๋์ด ๋๋๋ฌ์ก์ต๋๋ค.
๋ ธ๋๊ฐ ์ฌ๋ฌ ๊ฐ ์์ ๋๋ ํฌ๊ฒ ๋์ ๋์ง ์์์ง๋ง, ์ ์ฐจ ๋ค์ด์ด๊ทธ๋จ ๋ ธ๋๋ UI ์์ ฏ์ด ๋ณต์กํด์ง๋ฉด์ ํธ๋ฆฌ ๊ตฌ์กฐ๊ฐ ๊น์ด์ก์ต๋๋ค. ์ด๋๋ง๋ค SVG ์์๋ง๋ค ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ด๋ ์์ ์์ฒด๊ฐ ๋ณ๋ชฉ ์ง์ ์ด ๋์๊ณ ์. DOM ์ ๊ทผ์ด ๋์ ๋์ด ๋ธ๋ผ์ฐ์ ๊ฐ ํ ํ๋ฉด์ ์์ ํ ๊ตฌ์ฑํ๊ฑฐ๋ ๋ณ๊ฒฝํ๋ ๋ฐ ํ์ํ ์๊ฐ์ด ๋์ ๋๊ฒ ๊ธธ์ด์ก๊ณ , ์ฌ์ฉ์๋ โํ๋ฉด์ด ์๊ฐ๋ณด๋ค ๋ฆ๊ฒ ๋ฌ๋คโ๋ผ๊ณ ๋๋ผ๊ฒ ๋์์ฃ .
์ด๋ฒคํธ ์์ ํจํด์ ๋์
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋์ ๋ ๊ฒ์ด ์ด๋ฒคํธ ์์(Event Delegation) ํจํด์ ๋๋ค. ์ต์์ SVG ์์ ํ๋์๋ง ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ๊ณ , ์ค์ ๋ก๋ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ํตํด ๋ด๋ถ ์ด๋ฒคํธ๋ฅผ ์บ์นํ๋๋ก ์ค๊ณํ ๊ฒ์ด ํต์ฌ์ ๋๋ค.
์์ ์์ ฏ์ ์ฐ๊ฒฐ๋ ํธ๋ค๋ฌ๋ ์ค์ ๋ก๋ โ์์ ฏํธ๋ฆฌโ ๋ด๋ถ์์๋ง ๊ด๋ฆฌ๋๋ฉฐ, ์ต์์ SVG์์ ๋ฐ์ํ๋ ์ด๋ฒคํธ๊ฐ ์ ์ ํ ์ฝ๋ฐฑ ๋ชฉ๋ก์ผ๋ก ๋ถ๋ฐฐ๋๋ ๊ตฌ์กฐ๊ฐ ๋ง๋ค์ด์ก์ต๋๋ค. ์ด๋ ๊ฒ ๋ฃจํธ ์์ ํ๋์๋ง ์ด๋ฒคํธ๋ฅผ ์ง์ค์ํค๋ฉด DOM ์ ๊ทผ์ด ํ๊ธฐ์ ์ผ๋ก ์ค์ด๋ค์ด, ๋ ธ๋๊ฐ ์๋ฌด๋ฆฌ ๋ง์์ ธ๋ ์์๋ง๋ค ์ง์ ํธ๋ค๋ฌ๋ฅผ ๋ค๋ ๋ฐฉ์์ ๋นํด ํจ์ฌ ๊ฐ๋ฒผ์ด ์ธํฐ๋์ ์๋ต ์๋๋ฅผ ์ป์ ์ ์์ต๋๋ค.
์ด๋ฒคํธ ์์์ ์ฌ์ค ๋ฆฌ์กํธ ๋ฑ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๊ธฐ๋ฒ์ ๋๋ค. ๋ฆฌ์กํธ ์ญ์ โ๊ฐ์ DOMโ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๊ด๋ฆฌํ๊ณ , ์ค์ DOM์๋ ๋ฃจํธ ํ๋์๋ง ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํจ์ผ๋ก์จ ์ค๋ณต ์์ ์ ์ต์ํํฉ๋๋ค. Flitter ์ญ์ ์ด๋ฅผ ๋ฒค์น๋งํนํ์ฌ โ๋ถ๋ชจ SVG์๋ง ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ฐฉํ๋ ์ ์ฑ โ์ ์ฑํํ์ต๋๋ค. ๊ทธ ๊ฒฐ๊ณผ, ๋๊ท๋ชจ ๋ค์ด์ด๊ทธ๋จ์์๋ ์ผ๊ด๋ ์ธํฐ๋์ ์ฑ๋ฅ์ ์ ์งํ ์ ์์๊ณ , ์์ ๊ฐ์์ ๋น๋กํด ๋์ด๋ฌ๋ DOM ์ ๊ทผ์ด ํ์ ํ ์ค์ด๋ค์์ต๋๋ค.
ํ๋ ์๋น ํ ๋ฒ์ requestAnimationFrame
์ผ๋ก ์ต์ ํํ๊ธฐ
requestAnimationFrame
๋น์ฉ ๋ฌธ์
๋ ๋๋ง ์ฑ๋ฅ์ ๋ถ์ํ๋ฉด์ ์์ธ์ ์ฌ์ค์ ๋ฐ๊ฒฌํ์ต๋๋ค. requestAnimationFrame
ํจ์ ์์ฒด๊ฐ ๊ฝค ํฐ ๋น์ฉ์ ๋ฐ์์ํจ๋ค๋ ๊ฑฐ์ฃ . ๋ธ๋ผ์ฐ์ ๋ด๋ถ์์ ์ฝ๋ฐฑ์ ํน์ ํ์ ๋ฑ๋กํ๊ณ ๊ด๋ฆฌํ๋ ๊ณผ์ ์์, 30~60ฮผs ์ ๋์ ์๊ฐ์ด ์์๋๋ค๋ ์ ์ ํฌ๋กฌ DevTools๋ก ํ์ธํ ์ ์์์ต๋๋ค.
โ๊ฒจ์ฐ ๋ง์ดํฌ๋ก์ด ๋จ์์ธ๋ฐ ๋ญ๊ฐ ๋ฌธ์ ์ผ?โ๋ผ๊ณ ์๊ฐํ ์ ์์ง๋ง, ์ด ์์ ๋น์ฉ์ด ํ๋ ์๋ง๋ค ์์ญ ๋ฒ์ฉ ๋ฐ์ํ๋ฉด ์ด์ผ๊ธฐ๊ฐ ๋ฌ๋ผ์ง๋๋ค. ์ค์ ๋ก ํ๋ ์ ๋ถ์ ๊ฒฐ๊ณผ, ๋จ์ํ ๋๋๊ทธ ๋์์์๋ ์ฌ๋ฌ ์ปดํฌ๋ํธ๊ฐ ๊ฐ๋ณ์ ์ผ๋ก requestAnimationFrame
์ ํธ์ถํ๋ฉด์ ๋ถํ์ํ ์ค๋ฒํค๋๊ฐ ์์ฌ๊ฐ๊ณ ์์์ฃ .
Phase๋ก ๋๋์ด ์ฝ๋ฐฑ์ ๊ด๋ฆฌํ๋ค
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Flutter์ ํ๋ ์ ์ฒ๋ฆฌ ๋ฐฉ์์์ ์๊ฐ์ ์ป์์ต๋๋ค. ํ ํ๋ ์ ๋ด์์ ๋ชจ๋ ์
๋ฐ์ดํธ ์์ฒญ์ phase๋ณ๋ก ๊ตฌ๋ถํ๊ณ , requestAnimationFrame
์ ๋ฑ ํ ๋ฒ๋ง ํธ์ถํ๋ ๋ฐฉ์์ ๋์
ํ ๊ฑฐ์ฃ . ์ค์ ๊ตฌํํ ์ฝ๋๋ฅผ ํ๋ฒ ์ดํด๋ณผ๊น์?
enum SchedulerPhase {
idle,
persistenceCallbacks,
postFrameCallbacks,
}
class Scheduler {
phase: SchedulerPhase;
private persistenceCallbacks: (() => void)[];
private postFrameCallbacks: (() => void)[];
private renderFrameDispatcher: RenderFrameDispatcher;
constructor({
renderFrameDispatcher,
}: {
renderFrameDispatcher: RenderFrameDispatcher;
}) {
this.phase = SchedulerPhase.idle;
this.persistenceCallbacks = [];
this.postFrameCallbacks = [];
this.renderFrameDispatcher = renderFrameDispatcher;
renderFrameDispatcher.setOnFrame(() => this.handleDrawFrame());
}
private hasScheduledFrame = false;
ensureVisualUpdate() {
switch (this.phase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
this.schedule();
break;
case SchedulerPhase.persistenceCallbacks:
break;
}
}
private schedule() {
if (this.hasScheduledFrame) return; // ์ด๋ฏธ ์์ฝ๋ ํ๋ ์์ด ์๋ค๋ฉด ์ค๋ณต ํธ์ถ ๋ฐฉ์ง
this.renderFrameDispatcher.dispatch();
this.hasScheduledFrame = true;
}
}
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด, โhasScheduledFrameโ ํ๋๊ทธ๋ฅผ ํตํด ํ๋ ์๋น ๋จ ํ ๋ฒ๋ง requestAnimationFrame
์ด ํธ์ถ๋๋๋ก ๋ณด์ฅํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ถํ์ํ requestAnimationFrame
ํธ์ถ์ ๋ฐฉ์งํ ์ ์์์ฃ .
์ค์ ๋ก ์ฒด๊ฐ๋๋ ์ฐจ์ด
์ด๋ ๊ฒ ๋ณ๊ฒฝ ํ ์ฑ๋ฅ ์ธก์ ํ๋ ๋๋ผ์ด ๊ฒฐ๊ณผ๊ฐ ๋์์ต๋๋ค. ๋์ผํ ๋๋๊ทธ ๋์์์ requestAnimationFrame
ํธ์ถ ํ์๊ฐ ํ๋ ์๋น ํ๊ท 100ํ์์ 1ํ๋ก ์ค์ด๋ค์์ต๋๋ค. CPU ํ๋กํ์ผ๋ง ๊ฒฐ๊ณผ์์๋ ์ฝ๋ฐฑ ๋ฑ๋ก๊ณผ ๊ด๋ จ๋ ์ค๋ฒํค๋๊ฐ ํ์ฐํ ๊ฐ์ํ ๊ฒ์ ํ์ธํ ์ ์์์ฃ .
ํนํ ์ฌ๋ฌ ๋ ธ๋๋ฅผ ๋์์ ๋๋๊ทธํ๊ฑฐ๋, ๋ณต์กํ ์ ๋๋ฉ์ด์ ์ ์คํํ ๋ ์ฐจ์ด๊ฐ ๋ ๋๋๋ฌ์ก์ต๋๋ค. ์ด์ ์๋ ํ๋ ์ ๋๋์ด ๋ฐ์ํ๋ ์ํฉ์์๋ ์ด์ ๋ 60fps๋ฅผ ์์ ์ ์ผ๋ก ์ ์งํ ์ ์๊ฒ ๋์ต๋๋ค. ๋จ์ํ ์ฝ๋ฐฑ์ ๋ชจ์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ๋์ด, ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ํ์ดํ๋ผ์ธ๊ณผ ๋ ํจ์จ์ ์ผ๋ก ๋์ํ๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ด๋ธ ๊ฒ๋๋ค.
โ์ด๊ฒ ์ ๋ง ๋นจ๋ผ์ง ๊ฑฐ ๋ง๋์?โ ์ฑ๋ฅ ๊ฐ์ ์ ์์น๋ก ์ฆ๋ช ํ๊ธฐ
๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ์์ฃผ ๋ง์ฃผ์น๋ ์ํฉ์ด ์์ต๋๋ค.
โ์ฌ๊ธฐ ์ต์ ํํ๋๋ฐ ํ์คํ ๋นจ๋ผ์ง ๊ฒ ๊ฐ์์.โ
โ์... ์ ๋ ๋ณ๋ก ์ฐจ์ด๋ฅผ ๋ชป ๋๋ผ๊ฒ ๋๋ฐ์?โ
์ด๋ฐ ๋ชจํธํ ์ํฉ์ ๋ฒ์ด๋๊ธฐ ์ํด, Playwright์ ํฌ๋กฌ ๋ฐ๋ธํด ํ๋กํ ์ฝ(Chrome DevTools Protocol)์ ๊ฒฐํฉํ ์๋ํ๋ ์ฑ๋ฅ ์ธก์ ์์คํ ์ ๊ตฌ์ถํ์ต๋๋ค. ์ด ์์คํ ์ ํตํด ์ฝ๋์ ํน์ ๋ถ๋ถ์ด ์ผ๋ง๋ ์์ฃผ ์คํ๋๋์ง, ์ค์ ๋ก ์ผ๋ง๋ ์๊ฐ์ด ๊ฑธ๋ฆฌ๋์ง๋ฅผ ์ ํํ๊ฒ ํ์ ํ ์ ์์์ต๋๋ค.
์๋ํ๋ ์ฑ๋ฅ ์ธก์ , ์ด๋ป๊ฒ ๊ตฌํํ๋์?
test.describe("Performance Tracking", () => {
test("Capture performance traces ans save json file on diagram is rendered", async ({
page,
browser,
}) => {
await browser.startTracing(page, {
path: `./performance-history/${formatDate(new Date())}.json`,
});
await page.goto("http://localhost:4173/performance/diagram");
await page.evaluate(() => window.performance.mark("Perf:Started"));
await page.click("button");
await page.waitForSelector("svg");
await page.evaluate(() => window.performance.mark("Perf:Ended"));
await page.evaluate(() =>
window.performance.measure("overall", "Perf:Started", "Perf:Ended")
);
await browser.stopTracing();
});
test("Capture analyzed trace when diagram is rendered", async () => {
const COUNT = 10;
const duration = {
timestamp: Date.now(),
runApp: 0,
mount: 0,
draw: 0,
layout: 0,
paint: 0,
note: "",
};
for (let i = 0; i < COUNT; i++) {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto("http://localhost:4173/performance/diagram");
await browser.startTracing(page, {});
await page.evaluate(() => window.performance.mark("Perf:Started"));
await page.click("button");
await page.waitForSelector("svg");
await page.evaluate(() => window.performance.mark("Perf:Ended"));
await page.evaluate(() =>
window.performance.measure("overall", "Perf:Started", "Perf:Ended")
);
const buffer = await browser.stopTracing();
const jsonString = buffer.toString("utf8"); // buffer๋ฅผ UTF-8 ๋ฌธ์์ด๋ก ๋ณํ
const trace = JSON.parse(jsonString); // ๋ฌธ์์ด์ JSON ๊ฐ์ฒด๋ก ํ์ฑ
const analyzer = new ChromeTraceAnalyzer(trace);
duration.runApp += analyzer.getDurationMs("runApp") / COUNT;
duration.mount += analyzer.getDurationMs("mount") / COUNT;
duration.draw += analyzer.getDurationMs("draw") / COUNT;
duration.layout += analyzer.getDurationMs("layout") / COUNT;
duration.paint += analyzer.getDurationMs("paint") / COUNT;
browser.close();
}
console.log("****Execution Time****");
console.log(`runApp: ${duration.runApp}ms`);
console.log(`mount: ${duration.mount}ms`);
console.log(`draw: ${duration.draw}ms`);
console.log(`layout: ${duration.layout}ms`);
console.log(`paint: ${duration.paint}ms`);
console.log("********************");
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const filePath = path.join(__dirname, "../performance-history/duration.ts");
let fileContent = fs.readFileSync(filePath, { encoding: "utf8" });
fileContent += `histories.push(${JSON.stringify(duration)});\n`;
fs.writeFileSync(filePath, fileContent);
});
});
Playwright๋ ์ฌ๋ฌ ๋ธ๋ผ์ฐ์ ๋ฅผ ์๋์ผ๋ก ๊ตฌ๋ํ๊ณ ์ ์ดํ ์ ์์ด, ํ ์คํธ์ ์ธก์ ํ๊ฒฝ์ ์ผ๊ด์ฑ ์๊ฒ ์ ์งํ๊ธฐ ์ข์ต๋๋ค. ํฌ๋กฌ ๋ฐ๋ธํด ํ๋กํ ์ฝ์ ํตํด ์ป์ ๋ธ๋ผ์ฐ์ ๋ด๋ถ ์ฑ๋ฅ ์งํ์ ์คํ ์ค์ธ ์คํฌ๋ฆฝํธ ์ ๋ณด๋ฅผ ๋ถ์ํ๋ฉด, ์ด๋ ๋ถ๋ถ์ด CPU๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์๋ชจํ๋์ง ์ธ๋ฐํ๊ฒ ํ์ ํ ์ ์์ฃ .
์๋ํ ์์คํ ์ ํตํด ๋์ผํ ์๋๋ฆฌ์ค๋ฅผ ์ฌ๋ฌ ์ฐจ๋ก ๋ฐ๋ณต ์ธก์ ํ๋ฉด, ๋จ์ํ ํ๋ ๋ฒ์ ์ฒด๊ฐ ํ ์คํธ๋ก๋ ๋์น๊ธฐ ์ฌ์ด ๋ฏธ์ธํ ๋ณํ๋ ์ ํํ ํฌ์ฐฉํ ์ ์์ต๋๋ค. ๋ณ๋ชฉ ์ง์ ์ ์์ ํ ๋ค์๋ ๊ทธ ์ ํ ๋ฐ์ดํฐ๋ฅผ ๋์ ๊ด๋ฆฌํจ์ผ๋ก์จ, โ์ ๋ง ์ฑ๋ฅ์ด ๊ฐ์ ๋์๋๊ฐ?โ๋ฅผ ๋ช ํํ๊ฒ ์ฆ๋ช ํ ์ ์๊ฒ ๋ฉ๋๋ค.
์ด๋ฐ ๋ฐฉ์์ ํนํ ํ๋ก์ ํธ ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก ๋์ฑ ์ค์ํด์ง๋๋ค. ํ ๋จ์๋ก ํ์ ํ ๋ โ์ด๋ ๋ถ๋ถ์ ๋ ํฌ์ํด์ผ ํ๋๊ฐ?โ๋ฅผ ๊ฒฐ์ ํ๋ ๊ฐ๊ด์ ์ธ ๊ทผ๊ฑฐ๊ฐ ๋์ด์ฃผ์ฃ . ๊ฒ๋ค๊ฐ ๊ฐ๋ฐ ๊ณผ์ ์์ ์ฝ๊ฒ ๋์น๊ธฐ ์ฌ์ด โ์์ ํจ์โ๋ ํน์ ์ผ์ด์ค๊ฐ ๋ฐ๋ณต ํธ์ถ๋๋ ๋ถ๋ถ๋ ๋ฐ์ดํฐ๋ก ์ ํํ ๋ํ๋๋ฏ๋ก, ์ฌ์ํด ๋ณด์ด๋ ๋ณ๋ชฉ ์์๋ ์ฐพ์๋ผ ์ ์์ต๋๋ค.

โ์ด? ์ด์ ์ ๋ฒ๋ฒ ๋๋คโ ์ฌ์ฉ์๋ค์ ์ฒซ ๋ฐ์
์ด๋ฌํ ์ต์ ํ๋ฅผ ๊ฑฐ์น ํ ๋ณต์กํ SVG ๋ค์ด์ด๊ทธ๋จ์ ๋๋๊ทธํ๊ฑฐ๋, ํ๋ยท์ถ์ํ๋ ๊ณผ์ ์์ ๋ฐ์ํ๋ ํ๋ ์ ๋๋์ด ํฌ๊ฒ ์ค์์ต๋๋ค. ๋ง์ฐ์ค ํฌ์ธํฐ๊ฐ ๋ํ๋ณด๋ค ํจ์ฌ ์์๊ฐ๊ฑฐ๋, ๊ฐ์ฒด ์ด๋ ์ค๊ฐ์ ์๊ฐ ๋ฉ์ถค ์ฆ์์ด ๋ํ๋๋ ๋ฑ ์ด์ ์๋ ์์ฃผ ๋ณด์ด๋ ํ์๋ ๊ฑฐ์ ์ฌ๋ผ์ก์ต๋๋ค. ์ด๋ ์ฌ์ฉ์ ์ธํฐ๋์ ํ๋ฆ์ด ํ์ธต ๋งค๋๋ฌ์์ก๋ค๋ ์๋ฏธ๋ก, ์ค์ ์ฌ์ฉ์ ๋ง์กฑ๋๋ ์์นํ์ต๋๋ค.
โEasyrdโ์์๋ ๋ ธ๋๋ฅผ ๋๋๊ทธํ ๋ ๋ง์ฐ์ค ์์ง์์ ์์ฐ์ค๋ฝ๊ฒ ๋ฐ๋ผ๊ฐ๋ ๋ชจ์ต์ ์ฒด๊ฐํ ์ ์์ต๋๋ค. ๋ ธ๋๋ฅผ ์ฌ๋นจ๋ฆฌ ์์ง์ฌ๋ ์ง์ฐ ์์ด ์ซ์์ค๊ธฐ ๋๋ฌธ์, ์ฌ๋ฌ ๊ฐ ๋ ธ๋๋ฅผ ๋์์ ์ด๋ํ๊ฑฐ๋ ๋ณต์กํ ๊ตฌ์กฐ๋ฅผ ๋ฏธ์ธ ์กฐ์ ํ ๋๋ ํจ์ฌ ์์ ์ ์ธ ํ๊ฒฝ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฌํ ์๊ฐ์ ยท๊ธฐ๋ฅ์ ๊ฐ์ ์ ์์ง ๋ด๋ถ์์ ์ด๋ฃจ์ด์ง ์ต์ ํ๊ฐ ์ค์ ๋ก ์ด๋ค ๋ณํ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋์ง ๋ณด์ฌ์ค๋๋ค.

์์ธ๋ก ๋ณ๊ฑฐ ์๋ ์์ ๋ณํ์ ์์
ํ๋ ์์ํฌ ์ฐจ์์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ ๋๋ง ํ์ดํ๋ผ์ธ์ ์ผ๋ถ ์๋ณธ ๊ฒ๋ง์ผ๋ก๋, ์ด๋ ๊ฒ ์ง๊ด์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ ๋ณํ๋ฅผ ๋์ด๋ผ ์ ์๋ค๋ ์ ์ด ๋๋ผ์ ๋๋ฐ์. ์ค๋ฌด์์ ์ ์์ค ์ต์ ํ๋ฅผ ์๋ํ๋ฉด ์ด๊ธฐ ๋จ๊ณ์์ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ๊ธฐ๋ ํ์ง๋ง, ์ ๋๋ก ์ ์ฉ๋ง ๋๋ค๋ฉด ํ๋ก์ ํธ ์ ์ฒด ํผํฌ๋จผ์ค๊ฐ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค. ํนํ ์๋์ ์ผ๋ก ๊ฐ๋จํด ๋ณด์ด๋ ํน์ ํจ์ ํธ์ถ ํ์๋ฅผ ์ค์ด๊ฑฐ๋, ์ด๋ฒคํธ ๋ก์ง์ ์ฌ์ค๊ณํ๋ ๊ฒ๋ง์ผ๋ก๋ ์์ธ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์์ฃ .
๋ฌผ๋ก ์ค์ ํ๋ก์ ํธ์์ ์ต์ ํ ์์ ์ ์๊ฐ์ฒ๋ผ ๋จ์ํ์ง ์์ต๋๋ค. ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐํธํ๋ค ๋ณด๋ฉด, ๊ธฐ์กด ๊ธฐ๋ฅ๊ณผ ์ถฉ๋์ ์ผ์ผํค๊ฑฐ๋ ์์์น ๋ชปํ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ ์์ฃ . ์ด๋ฒคํธ ํธ๋ค๋ง ๋ก์ง์ ๋ฐ๊พธ๋ฉด ๋ฐ๋ก ๋ฐ์ ์๋๊ฐ ๋นจ๋ผ์ง๋ ๋์ , ์ด์ ์ ๋ฌ์๋์๋ ์ข ์๋ ๋ก์ง๋ค์ด ์ ์์ ์ผ๋ก ์๋ํ์ง ์๋ ์์ธก ๋ถ๊ฐ๋ฅํ ์ํฉ์ ๋ง๋๊ธฐ๋ ํ์ต๋๋ค.
๋ง์ฝ ์ฌ๋ฌ๋ถ๋ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด ๋ ํนํ ๊ธฐ๋ฒ์ ์ ์ฉํ๊ฑฐ๋, ์์์น ๋ชปํ ์ด๋ ค์์ ๊ฒช์ ์ ์ด ์๋ค๋ฉด ์ด๋ค ์ํ์ฐฉ์ค๋ผ๋ ์ข์ผ๋, GitHub Discussions์์ ์ฌ๋ฌ๋ถ์ ๊ฒฝํ์ ๋๋ ์ฃผ์๋ฉด ์ข๊ฒ ์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก ์ค์ ๊ตฌํ ์ฝ๋๊ฐ ๊ถ๊ธํ๋ค๋ฉด, GitHub ๋ฆฌํฌ์งํ ๋ฆฌ (meursyphus/flitter
)์์ ํ์ธํด ๋ณด์ธ์. ํ๋ ์์ํฌ ๋ด๋ถ ๊ตฌ์กฐ์ ๊ฐ์ข
์ต์ ํ ์์ด๋์ด๊ฐ ์ด๋ป๊ฒ ๋ฐ์๋์๋์ง ๋ณผ ์ ์์ต๋๋ค. ์์ง ๋ถ์กฑํ ์ ์ด ๋ง์ง๋ง, ์์ผ๋ก๋ ํผ๋๋ฐฑ์ ํตํด ๋์ฑ ๋ฐ์ ํด ๋๊ฐ๊ณ ์ ํฉ๋๋ค.