- Puppeteer๋ DevTools Protocol์ ํตํด Chrome ๋ธ๋ผ์ฐ์ ๋ฅผ ํ๋ก๊ทธ๋๋งคํฑํ๊ฒ ์ ์ดํ๋ Node.js ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- Headless Chrome์ Headless ๋ชจ๋๋ก ์คํ๋๋, GUI ์์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋์ํ๋ Chrome ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค
- ์คํฌ๋ฆฐ์ท ์บก์ฒ, PDF ์์ฑ, DOM ์กฐ์, ๋คํธ์ํฌ ๊ฐ์ ๋ฑ์ ์๋ํํ๋ ๋ธ๋ผ์ฐ์ ์๋ํ ๋๊ตฌ
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- Hyperframes์ ๋ ๋๋ง ์์ง์ Puppeteer๋ก Headless Chrome์ ๊ตฌ๋ํ์ฌ ๊ฐ ํ๋ ์์ ์บก์ฒํ๋ค
- HTML Composition์ Chrome์ ๋ก๋ โ ํ๋ ์๋ณ seek โ ํ๋ฉด ์บก์ฒ โ FFmpeg ์ธ์ฝ๋ฉ์ด๋ผ๋ ํ์ดํ๋ผ์ธ์ ํต์ฌ ์ถ
- ์ผ๋ฐ ์คํฌ๋ฆฐ์ท๊ณผ ๋ฌ๋ฆฌ, Chrome์
BeginFrameAPI๋ฅผ ํ์ฉํด ๊ฒฐ์ ๋ก ์ (deterministic) ํ๋ ์ ์บก์ฒ๊ฐ ๊ฐ๋ฅํ๋ค
AS-IS โ ์ ํต์ ๋น๋์ค ๋ ๋๋ง
sequenceDiagram autonumber participant ํธ์ง๊ธฐ as ๋น๋์ค ํธ์ง SW participant GPU participant ์ถ๋ ฅ as MP4 ํธ์ง๊ธฐ->>GPU: ์ค์๊ฐ ๋ ๋๋ง (wall-clock ์์กด) GPU->>์ถ๋ ฅ: ํ๋ ์ ์ธ์ฝ๋ฉ Note over ํธ์ง๊ธฐ,์ถ๋ ฅ: ์คํ ํ๊ฒฝ(GPU, OS)์ ๋ฐ๋ผ ๊ฒฐ๊ณผ ๋ฌ๋ผ์ง ์ ์์
TO-BE โ Hyperframes์ Puppeteer ๊ธฐ๋ฐ ๋ ๋๋ง
sequenceDiagram autonumber participant Engine as @hyperframes/engine participant Puppeteer participant Chrome as Headless Chrome participant Page as Composition HTML Engine->>Puppeteer: ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์์ฒญ Puppeteer->>Chrome: DevTools Protocol๋ก Headless Chrome ์คํ Engine->>Chrome: Composition HTML ๋ก๋ Chrome->>Page: window.__hf (seek protocol) ๋๊ธฐ loop ๊ฐ ํ๋ ์ (0 ~ totalFrames) Engine->>Page: window.__hf.seek(frame / fps) Page->>Page: GSAP timeline.totalTime() โ DOM ์ ๋ฐ์ดํธ Engine->>Chrome: BeginFrame API๋ก ํ๋ ์ ์บก์ฒ Chrome-->>Engine: PNG ๋ฒํผ ๋ฐํ end Engine->>Engine: FFmpeg๋ก ์ธ์ฝ๋ฉ โ MP4
Headless Chrome์ด๋
์ผ๋ฐ Chrome ๋ธ๋ผ์ฐ์ ์์ ํ๋ฉด ํ์(GUI) ๋ถ๋ถ๋ง ์ ๊ฑฐํ ๊ฒ. ๋ ๋๋ง ์์ง(Blink), JavaScript ์์ง(V8), ๋คํธ์ํฌ ์คํ ๋ฑ์ ๋ชจ๋ ๋์ผํ๊ฒ ๋์ํ๋ค. Chrome 112๋ถํฐ headless ๋ชจ๋๋ headful Chrome๊ณผ ๋์ผํ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๊ณต์ ํ๋ค.
๋ ๊ฐ์ง ๋ฐ์ด๋๋ฆฌ๊ฐ ์กด์ฌํ๋ค:
| ๋ฐ์ด๋๋ฆฌ | ์ฉ๋ |
|---|---|
chrome --headless | ์ผ๋ฐ Chrome์ headless ๋ชจ๋๋ก ์คํ |
chrome-headless-shell | ๊ฒฝ๋ ์ ์ฉ ๋ฐ์ด๋๋ฆฌ โ Hyperframes Docker ๋ชจ๋์์ ์ฌ์ฉ |
Puppeteer์ ์ญํ
Puppeteer๋ Chrome๊ณผ ์ฌ๋ ์ฌ์ด์ ๋ฆฌ๋ชจ์ปจ ์ญํ ์ ํ๋ค:
Node.js ์ฝ๋ โ Puppeteer API โ DevTools Protocol (WebSocket) โ Chrome
์ฃผ์ ๊ธฐ๋ฅ:
| ๊ธฐ๋ฅ | ์ค๋ช |
|---|---|
| ํ์ด์ง ํ์ | URL ๋ก๋, ์ ํญ ์ด๊ธฐ |
| DOM ์กฐ์ | page.evaluate()๋ก ํ์ด์ง ๋ด JS ์คํ |
| ์คํฌ๋ฆฐ์ท | page.screenshot()์ผ๋ก ํ์ฌ ํ๋ฉด PNG ์บก์ฒ |
| CDP ์ธ์ | Chrome DevTools Protocol์ ์ง์ ๋ช ๋ น ์ ์ก |
BeginFrame API โ ๊ฒฐ์ ๋ก ์ ์บก์ฒ์ ํต์ฌ
์ผ๋ฐ ์คํฌ๋ฆฐ์ท(Page.captureScreenshot)์ Chrome์ด ์์ฒด ํ์ด๋ฐ์ผ๋ก ํ๋ฉด์ ๊ฐฑ์ ํ โ์ค๋
์ทโ์ ์ฐ๋ ๊ฒ์ด๋ค. ๋ฐ๋ฉด BeginFrame API๋ ์์ง์ด Chrome์ ๋ ๋๋ง ์ฌ์ดํด์ ์ง์ ์ ์ดํ๋ค:
| ๋ฐฉ์ | ํ์ด๋ฐ ์ ์ด | ๊ฒฐ์ ๋ก ์ฑ |
|---|---|---|
Page.captureScreenshot | Chrome ์์ฒด (wall-clock) | ๋น๊ฒฐ์ ๋ก ์ |
HeadlessExperimental.beginFrame | ์์ง์ด ์ง์ ์ ์ด | ๊ฒฐ์ ๋ก ์ |
Hyperframes ์์ง์ ์บก์ฒ ํ๋ฆ:
seek(time)ํธ์ถ โ DOM/์ ๋๋ฉ์ด์ ์ํ ์ ๋ฐ์ดํธbeginFrame(frameTimeTicks, interval)ํธ์ถ โ Chrome์ด ์ ํํ ์ด ์์ ์ ํ๋ ์์ ํฉ์ฑ(composite)- ํฉ์ฑ๋ ํ๋ ์ ๋ฐ์ดํฐ๋ฅผ PNG ๋ฒํผ๋ก ๋ฐํ
์ด ๋ฐฉ์ ๋๋ถ์ CPU ์๋, OS, ์์คํ ๋ถํ์ ๊ด๊ณ์์ด ๋์ผํ ์ ๋ ฅ โ ๋์ผํ ํฝ์ ์ถ๋ ฅ์ด ๋ณด์ฅ๋๋ค.
window.__hf โ Seek Protocol
Hyperframes ์์ง๊ณผ Composition HTML ์ฌ์ด์ ์ ์ผํ ๊ณ์ฝ:
interface HfProtocol {
duration: number; // ์ ์ฒด ๊ธธ์ด(์ด)
seek(time: number): void; // ํด๋น ์์ ์ผ๋ก ์ด๋
media?: HfMediaElement[]; // ๋ฏธ๋์ด ์์ ์ ์ธ
transitions?: HfTransitionMeta[]; // ํธ๋์ง์
๋ฉํ๋ฐ์ดํฐ
}
// ํ์ด์ง์์ ์ด ๊ฐ์ฒด๋ฅผ ๋
ธ์ถํ๋ฉด ์์ง์ด ์บก์ฒ๋ฅผ ์์ํ๋ค
window.__hf = { duration, seek, media };์์ง์ ์ ๋๋ฉ์ด์
ํ๋ ์์ํฌ๊ฐ ๋ฌด์์ธ์ง ์ ํ ๋ชจ๋ฅธ๋ค. GSAP์ด๋ CSS๋ Three.js๋ , seek()์ด ๊ฒฐ์ ๋ก ์ ์ถ๋ ฅ์ ๋ณด์ฅํ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
Hyperframes์์์ ๋ธ๋ผ์ฐ์ ๊ด๋ฆฌ
์์ง์ Puppeteer ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค๋ฅผ **ํ๋ง(pooling)**ํ์ฌ ์ฌ์ฌ์ฉํ๋ค:
acquireBrowser()โ ํ์์ ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์ด (์์ผ๋ฉด ์๋ก ์คํ)releaseBrowser()โ ์ฌ์ฉ ํ ํ์ ๋ฐํ- ์บก์ฒ ๋ชจ๋๋ฅผ ์๋ ๊ฐ์ง: Linux์์
chrome-headless-shell์ด ์์ผ๋ฉดbeginframe, ์๋๋ฉดscreenshotํด๋ฐฑ