# 노마드코더 「이게 진짜 HTML이라고?」 분석 - **URL**: https://www.youtube.com/watch?v=8ucoskmA1yg - **채널**: 노마드 코더 Nomad Coders - **주제**: Chrome 신규 **HTML in Canvas** API - 원문 자막: `transcript-ko.txt` / 압축본 `transcript-ko-compact.txt` / 영문 `nomad-html.en.srt` ## 목차 (영상 챕터) | 시간 | 내용 | |---|---| | 0:00–2:29 | 이게 진짜 HTML이라고? (데모 쇼케이스) | | 2:30–2:50 | 왜 지금까지 불가능했을까? | | 2:51–3:51 | HTML in Canvas는 어떻게 작동할까? | | 3:52–6:02 | 직접 만들어보자 (반사 버튼 구현) | | 6:03–6:37 | 아직 당장 쓸 수는 없다 | ## 핵심 주장 > "웹사이트와 비디오 게임의 경계를 허무는 새로운 API" > "2D 페이지라기보다는 비디오 게임이나 예술 설치 작품처럼 느껴지는 웹사이트" **기존의 딜레마**: 캔버스는 다른 HTML 요소를 전혀 알지 못한다. 캔버스 안에 버튼을 그리려면 사각형+글자를 직접 그리고, 클릭 좌표를 히트테스트하고, 호버 시 다시 그려야 한다. 그렇게 해도 접근성·키보드 지원·CSS 스타일링이 전부 없는 "버튼 그림"일 뿐이다. → **픽셀(canvas)이냐 HTML이냐 둘 중 하나만** 골라야 했다. **해결**: 같은 요소가 DOM(상호작용)과 캔버스(픽셀)에 **동시에 존재**한다. 셰이더가 그 픽셀을 붙잡아 비틀고, 왜곡하고, 혼합하고, 3D 형태에 매핑한다. 접근성과 폼 동작은 DOM이 그대로 유지한다. ## API 구조 > ⚠️ **영상이 쓰는 API 이름은 전부 구 명칭이다.** 조사 결과 확정된 현재 이름으로 아래를 정정했다. > 근거와 전체 스펙: `research/canvas/01-api-spec.md` 1. `` 속성 → 캔버스가 **직계 자식** 요소를 인식 (철자는 전부 소문자, IDL은 `layoutSubtree`) 2. 그리고 싶은 HTML을 캔버스 자식으로 넣는다 (평범한 HTML/CSS 그대로) 3. **`ctx.drawElementImage(el, x, y)`** — 요소 + 좌표를 주면 실시간 복사본을 그 자리에 그린다 - 이름 변천: `drawElement` → `drawHTMLElement` → `drawHTML` → **`drawElementImage`**(2025-09-11 확정). 영상은 첫 이름을 쓴다 4. **반환값이 핵심**: 그려진 위치를 나타내는 `DOMMatrix` 를 돌려준다 → 그것을 요소의 `style.transform` 에 넣으면 실제 DOM 요소가 그려진 위치로 이동한다 → **클릭이 맞아떨어진다** - 구 API의 `setHitTestRegions()` 는 2025-11-08 폐기되고 이 방식으로 대체됐다 - **매 프레임 반영하지 않으면 클릭이 전부 어긋난다** — 이 API의 필수 계약이다 5. WebGL **`gl.texElementImage2D(...)`** / WebGPU **`device.queue.copyElementImageToTexture(...)`** - 2026-04~06에 두 시그니처 모두 바뀌어 **Chrome 공식 블로그 예제조차 구버전**이다. WICG 공식 데모도 try/catch 로 신·구 양쪽을 지원한다 6. 캔버스 내부 HTML이 바뀔 때(포커스·호버 등) **`paint` 이벤트** 발생 → 듣고 다시 그린다 (중첩 시 역트리 순서) ### 보안 모델 — "read-back-allowed rendering" 캔버스 픽셀은 읽을 수 있으므로, **저자가 원래 볼 수 없던 것은 아예 그리지 않는다**: cross-origin 콘텐츠, 시스템 색상, 맞춤법 마커, **`:visited`(히스토리 스니핑 차단)**, 자동완성 미리보기, 서브픽셀 AA, IME 팝업. same-origin iframe은 그려진다. ### 데모 구현 흐름 (반사 버튼, JS 약 20줄) ``` 매 프레임(rAF): 캔버스 clear ctx.save() translate(아래로) + 상하 반전 + globalAlpha 0.3 drawElement(button) ← 흐릿한 뒤집힌 반사 ctx.restore() drawElement(button) ← 원본 반환된 변환값 → button.style.transform 에 적용 (히트박스 정렬) requestAnimationFrame(render) ``` 클릭 이벤트에서 클릭 좌표로 `ripple()` 호출 — 픽셀을 샘플링해 위치를 조정하는 건 기존 캔버스 연산이라 새로울 게 없다. **요점은 HTML이 일단 캔버스에 올라가면 무엇이든 할 수 있다는 것.** ## 영상에 나온 데모들 - 3D 씬의 **천 조각 위에 그려진 포트폴리오** — 찌르면 천이 실제처럼 반응하고 폼이 천에 녹아든다 - HTML `range` 입력 → **젤리 슬라이더** - 폼 제출 시 셰이더로 **HTML이 왜곡되는** 효과 - 라이트/다크 모드 전환 시 **페이지가 타들어가는** 애니메이션 - 웹에서 구현한 **Apple Liquid Glass** ## 한계 영상이 밝힌 것: - Chrome **Canary + 플래그** 뒤에 있다 (`chrome://flags/#canvas-draw-element`) - 아직 **제안(proposal) 단계**, 전부 초안 - **API 시그니처와 기본 형태조차 바뀔 수 있다** — 실제로 조사 기간에만 4번 바뀌었다 - → "당장 내일 프로덕션에 투입할 것은 아니지만, 오늘 실험을 시작할 가치는 충분하다" 조사로 보강한 것 (영상이 말하지 않은 것): - **Origin Trial이 진행 중이다** (M148~M150, M154까지 연장 승인). OT 토큰을 등록하면 **Chrome Stable 사용자에게도 동작한다** — 영상은 이 부분을 "플래그뿐"으로 축소해서 말한다. 다만 2026-10 초 만료 예정 - **Firefox·Safari 모두 "No signal"**(입장 미표명), WHATWG PR #11588은 2025-08 개설 후 여전히 미머지 - 실전 제약 둘: ① cross-origin iframe 미지원 ② **캔버스 안 콘텐츠는 컴포지터 스레드 스크롤/애니메이션을 잃고 JS에 묶인다** — Chrome은 캔버스 안에 스크롤 영역을 넣지 말고 캔버스 전체를 스크롤시키라고 명시 - **three.js는 이미 정식 지원한다**: `THREE.HTMLTexture(element)` + `InteractionManager`, **r184 포함**(2026-04-10 머지), WebGL/WebGPU 렌더러 양쪽 - **폴백이 존재한다**: `three-html-render` 폴리필(MIT)이 동일 API 표면을 `foreignObject`로 제공하고 네이티브가 있으면 자동으로 fast path를 탄다. three.js 공식 예제가 이걸 쓴다 ## designpaca 에의 함의 1. **이 API는 스킬의 "실험 레이어"에 넣는다** — 기본 경로가 되어선 안 된다. 프로덕션 기본값은 SVG 필터 + three.js. 단 조사 결과 위상이 조금 올라갔다: **three.js `HTMLTexture`(r184 정식) + `three-html-render` 폴리필** 조합이면 *점진적 향상*으로 오늘 쓸 수 있다. "쓰지 마라"가 아니라 **"폴백을 먼저 만들고, 그 위에 얹어라"**로 규칙을 정한다 2. **폴백 필수**: 미지원 브라우저에서 같은 인상을 주는 대안 경로를 항상 같이 설계한다 (`foreignObject` 폴리필, SVG displacement, backdrop-filter, CSS3DRenderer 등 — 비교표는 `research/canvas/04-fallbacks.md`) 3. **"DOM은 살려두고 픽셀만 왜곡한다"는 원칙**은 이 API 없이도 유효하다 — 접근성을 포기하지 않고 화려함을 얻는 designpaca의 기본 철학으로 삼는다 4. 영상의 문제의식("웹이 다시 재밌어질 수 있다", "플래시 시절처럼")이 곧 스킬의 방향성 — **안전하고 지루한 템플릿의 반대편**