From 17d04ee2bb01ae2dda0af7ed03c63c7b972fdb47 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Thu, 20 Aug 2026 12:32:09 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9E=88=EC=96=B4=EB=A1=9C=203D=20=EB=A5=BC=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B8=20=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=EB=A1=9C=20=E2=80=94=20=ED=98=95=ED=83=9C?= =?UTF-8?q?=EC=97=90=20=EC=9D=B4=EC=9C=A0=EB=A5=BC=20=EC=A4=80=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '3D 라고 그냥 큐브만 돌면 안 된다'는 지적에서 시작해 조사부터 했다. research/three/06~08 에 사례 32건, GLB 소스 17곳, 방향 제안 3개가 있다. 조사 결론: 추상이다. GLB 는 쓰지 않는다. - 구체 오브젝트의 성립 조건 3개(물리적으로 존재 / 각도가 정보 / 오브젝트가 하나로 정해짐)를 우리 브리프는 하나도 충족하지 않는다. 규칙 묶음은 돌려봐도 새 정보가 없다 - 실측: DamagedHelmet.glb 3,685KB 중 텍스처가 87%라 Draco 를 걸어도 -13%. 게다가 디코더만 200KB 로 예산의 67%를 먹는다 - Stripe Globe 는 모델 파일이 0바이트다. 6만 점을 코드로 생성한다. 지오메트리가 파일이 아니라 코드일 때 추상이 이긴다 - 2026 Awwwards Developer Award 수상작 중에는 3D 를 아예 안 쓴 것도 있다 채택안: 파이프라인 게이트 - 유리 판 여섯 장이 깊이 축에 정면으로 선다. 이 스킬의 여섯 단계다 - 스크롤하면 카메라가 문을 하나씩 통과하고 지나온 판은 뒤에 겹친다 - 판이 여섯인 이유가 생겼다. 아래 Pipeline 섹션과 같은 수다 - 순서는 이 제품의 본체다('순서를 바꾸지 마라'가 절대 규칙). 순서를 깊이로 옮기는 것은 은유가 아니라 직역이다 스크롤 연동은 이미 도는 렌더 루프에서 위치를 한 번 읽는다. addEventListener('scroll') 을 쓰지 않는다(하드 게이트 10). 핀도 없다. --- apps/site/src/lib/hero-scene.ts | 67 ++++++--- research/three/06-webgl-showcase.md | 127 +++++++++++++++++ research/three/07-glb-sources.md | 206 ++++++++++++++++++++++++++++ research/three/08-proposal.md | 190 +++++++++++++++++++++++++ 4 files changed, 572 insertions(+), 18 deletions(-) create mode 100644 research/three/06-webgl-showcase.md create mode 100644 research/three/07-glb-sources.md create mode 100644 research/three/08-proposal.md diff --git a/apps/site/src/lib/hero-scene.ts b/apps/site/src/lib/hero-scene.ts index dfd7ca5..f5f963e 100644 --- a/apps/site/src/lib/hero-scene.ts +++ b/apps/site/src/lib/hero-scene.ts @@ -1,7 +1,15 @@ /** - * 히어로 3D 씬. + * 히어로 3D 씬 — 파이프라인 게이트. * - * 유리 판 몇 장이 공간에 떠 있고, 그 뒤의 빛을 실제로 굴절시킨다. + * 유리 판 여섯 장이 깊이 축에 차례로 서 있다. 이 스킬의 여섯 단계다. + * 스크롤하면 카메라가 판을 하나씩 통과하고, 지나온 판은 뒤에 남아 겹친다. + * + * 형태에 이유가 있다. 이 제품이 파는 것은 순서이고("순서를 바꾸지 마라"가 + * 절대 규칙이다), 순서를 깊이로 옮기는 것은 은유가 아니라 직역이다. + * 판이 여섯인 이유도 여기서 나온다. 아래 Pipeline 섹션의 여섯 단계와 같은 수다. + * + * 조사 결론(research/three/08-proposal.md): 우리가 파는 것은 무형이라 + * 구체 오브젝트가 성립하지 않는다. GLB 는 쓰지 않는다. 지오메트리는 코드다. * * 구현 방식과 그 이유: * 배경 그라디언트를 렌더 타겟에 한 번 그리고, 유리 판이 그 텍스처를 @@ -96,6 +104,7 @@ async function boot({ canvas, accentVar, glowVar }: HeroSceneOptions): Promise 조사일 2026-08-20. 판정 기준 하나: **3D를 끄면 무엇을 잃는가.** 아무것도 안 잃으면 장식이다. +> 무게는 가능한 곳에서 실측했고, 아닌 것은 `추정`으로 표시했다. + +--- + +## 0. 먼저 — 3D 없이 이긴 사례들 + +사례집을 3D 사례로 시작하면 편향된다. 2026 Awwwards Developer Award 수상작 중 **3D를 전혀 쓰지 않은 것들**이 있다. + +| 사이트 | 무엇으로 이겼나 | +|---|---| +| **By-Kin** https://by-kin.com | GSAP + 스무스 스크롤. "전환이 스스로를 드러내지 않는데 전체가 하나의 연속된 표면으로 느껴진다" | +| **Mat Voyce** https://matvoyce.tv | 키네틱 타이포만. 작가가 키네틱 타입 아티스트라 **사이트가 그 작업처럼 움직여야** 했다 | +| **Uncommon Studio** https://uncommonstudio.com.au | GSAP 전환을 카메라 무브처럼. 그리드 규율 | + +**교훈**: "3D가 있어야 상을 받는다"는 전제가 틀렸다. 형태와 메시지가 맞을 때만 3D가 이긴다. +Mat Voyce의 논리 — *"작업이 움직임이니 사이트도 움직여야 한다"* — 가 이 문서 전체의 판정식이다. + +--- + +## 1. 구체 오브젝트 (제품·기계·캐릭터) + +파는 물건이 **물리적으로 존재할 때**의 문법이다. + +| # | 사례 | 3D가 무엇 | 왜 그 형태여야 했나 | 지오/모델 | 무게·전략 | 훔칠 것 | +|---|---|---|---|---|---|---| +| 1 | **Oryzo** oryzo.ai (Lusion, Awwwards SOTM 2026-04) | 코르크 코스터 하나 | 제품이 하나뿐이라 **하나만 제대로** 보여준다. 여러 각도 = 구매 확신 | GLB | 추정 1~3MB | **관성.** 물리를 흉내낸 이징 + 스크롤이 2D 레이어가 아니라 **진짜 Z축**으로 카메라를 민다 | +| 2 | **Cartier Watches & Wonders** cartier.com/watchesandwonders | 여섯 개의 독립된 3D 방(alcove) | 시계 여섯 종을 각각의 세계에 둔다. 그리드에 늘어놓으면 럭셔리가 죽는다 | GLB | 추정 수 MB/방, 방 단위 지연 로드 | **씬을 쪼개서 필요할 때만 로드.** GLSL+GSAP+Lenis+Web Audio를 서사 레이어로 | +| 3 | **Lacoste Ace Breaker** members-play.lacoste.com/ace-breaker-rg | 플레이 가능한 벽돌깨기 | 캠페인이 "참여"라서 **볼 것이 아니라 할 것**이어야 했다 | 프로시저럴 | — | 게임 메커닉 하나를 브랜드 월드로 감싼다. 기능이 곧 메시지 | +| 4 | **Porsche: Dream Machine** (Lusion) | 차 | 차는 형태 자체가 상품이다 | GLB | — | 3D 일러스트+모션그래픽을 웹이 아니라 **영상으로** 뺐다. 웹에 다 넣지 않는 선택 | +| 5 | **Bruno Simon portfolio** bruno-simon.com | 운전 가능한 차 + 물리 월드 | 포트폴리오 주인이 **three.js 교사**다. 사이트가 곧 실력 증명 | GLB + Cannon.js | 무겁다(수 MB). 로딩 화면 명시 | 어려운 아이디어 **하나**를 끝까지 판다. 여러 효과를 쌓지 않는다 | +| 6 | **Adventure Time: BMO** (Active Theory) | 캐릭터 | IP 자체가 캐릭터다 | GLB, 리깅 | — | 캐릭터는 **표정과 반응**이 전부. 폴리곤 수가 아니다 | +| 7 | **NASA JPL Open Source Rover** (Active Theory) | 실제 로버 | 오픈소스 하드웨어를 **분해해서 보여주는 것**이 목적 | GLB, 부품 분리 | — | 폭발도(exploded view). 조립 순서가 정보다 | +| 8 | **KODE Sports Club** (Merci Michel, SOTM) | 건축 공간 | 아직 안 지어진 건물을 판다 | GLB | — | 미완성 실물의 대체재로서의 3D — **가장 정당한 용도** | +| 9 | **Hubtown** hubtown.co.in | 풍경 위 빛나는 모놀리스 | 부동산. 랜드마크성이 상품 | 혼합 | — | 마우스로 **가려진 디테일을 벗겨내는** 인터랙션 | +| 10 | **Gunner Kennels** (AR 컨피규레이터) | 켄넬 실물 크기 | 개 옆에 놓아봐야 **사이즈 확신**이 생긴다 | GLB | — | **반품률 5% 감소**. 3D의 ROI가 숫자로 나온 드문 사례 | +| 11 | **Lundia** 모듈 선반 컨피규레이터 | 조립 결과물 | 조합 수가 많아 사진으로 불가능 | 프로시저럴 조립 | — | 실시간 가격 연동. 3D가 **견적서**가 된다 | +| 12 | **Explore Primland** explore.ownprimland.com | 블루리지 산맥 지형 | 리조트가 파는 것이 **입지**다 | 하이트맵(프로시저럴) | — | 스크롤=카메라 활공 + 대기 안개. 지형은 모델이 아니라 하이트맵으로 | +| 13 | **DeepSee Commerce** deepseecommerce.com | 빙산을 따라 하강 | "수면 아래를 본다"는 은유가 제품명 그 자체 | 혼합 | DPR 클램프, 에셋 압축, **보일 때만 렌더** | 은유가 **제품 이름과 일치**할 때만 은유를 쓴다 | + +### 구체 오브젝트의 공통 조건 +1. **파는 것이 물리적으로 존재한다** (또는 아직 존재하지 않아 대체가 필요하다) +2. **각도가 정보다** — 돌려봐야 알 수 있는 것이 있다 +3. **오브젝트는 하나** — Oryzo는 코스터 하나, Bruno Simon은 차 하나. 여러 개를 띄운 사례는 상위권에 없다 + +--- + +## 2. 추상 (데이터·재질·현상) + +파는 것이 **무형일 때**의 문법이다. designpaca가 여기 속한다. + +| # | 사례 | 3D가 무엇 | 왜 그 형태여야 했나 | 지오/모델 | 무게·전략 | 훔칠 것 | +|---|---|---|---|---|---|---| +| 14 | **Stripe Globe** stripe.com/blog/globe | 지구본 위 결제 흐름 | 평면 지도 대비 **화면 면적 20% 미만**으로 같은 정보. 국가 크기 왜곡도 적다 | **완전 프로시저럴** — 해바라기 나선으로 6만 점 생성, 실제 그리는 건 2만 점 | 모델 파일 **0바이트**. 국가 마스킹은 색으로 인코딩한 PNG 1장 | **이 문서 최고의 참조.** 점 하나하나가 데이터고, 지오메트리가 파일이 아니라 **코드**다. `antialias: false`가 전 기기에서 성능을 살렸다 | +| 15 | **Stripe Connect** stripe.com/blog/connect-front-end-experience | 결제 연결 구조 | 추상적 인프라를 공간으로 | 프로시저럴 | — | 무형 제품의 3D는 **구조도**여야 한다 | +| 16 | **fromanother** (FWA 수상) | 아이덴티티가 유리처럼 굴절·변형 | 스튜디오가 파는 것이 **아이덴티티 시스템**이다. 시스템이 변형되는 걸 보여준다 | 커스텀 GLSL | 모델 없음 | **"로고를 3D로"가 아니라 "정체성이 굴절한다"** — 우리와 가장 가까운 사례 | +| 17 | **Iventions** iventions.com | 프로젝트마다 스포트라이트 설치물 | 공간·이벤트를 만드는 회사라 사이트가 **공간처럼 느껴져야** 했다 | 조명 중심 | Next.js + Three + GSAP | **볼거리가 아니라 분위기로** WebGL을 쓴 가장 명확한 사례. 조명이 주인공 | +| 18 | **IVRESS** brand.ivress.co.jp | 브랜드 월드 | — | 프로시저럴 | **WebGPURenderer + WebGL 폴백**, TSL로 양쪽 컴파일 | 2026년 WebGPU 프로덕션 투입의 실사례 | +| 19 | **The Harmonic State** (Active Theory, SOTM) | 소리의 시각화 | 오디오가 주제라 **소리에 반응하는 형태**가 필수 | 프로시저럴 파티클 | — | 오디오 리액티브는 데이터 바인딩의 한 종류다 | +| 20 | **Spotify: Listening Together** (Active Theory) | 동시 청취 데이터 | 실제 재생 데이터가 형태를 만든다 | 프로시저럴 | — | **데이터가 없으면 형태가 없다** — 형태의 정당성이 데이터에서 나온다 | +| 21 | **Thorne: The Frontier Within** (Active Theory) | 체내 생물학 | 눈에 안 보이는 것을 보게 한다 | 혼합 | — | 비가시적 대상의 시각화 = 추상 3D의 정당한 자리 | +| 22 | **Worldcoin Globe** (Lusion) | 전 세계 분포 | Stripe Globe와 같은 논리 | 프로시저럴 | API 연동 | 지구본은 **분포 데이터가 있을 때만** 쓴다 | +| 23 | **Lusion Labs** lusion.co/projects/lusion_labs | 실험 모음 | 스튜디오의 R&D 자체가 상품 | 프로시저럴 | — | 기술 실험을 콘텐츠로 만드는 구조 | +| 24 | **Aave** aave.com | 금융 프로토콜 | 무형 금융의 추상 표현 | 추상 | — | 다크+연보라. **우리는 이 팔레트를 피한다**(슬롭 지문) | +| 25 | **Audion / Tavus / SISA / Tracebit** | AI·보안 SaaS의 추상 형태 | — | 추상 | — | 대부분 **의미 없는 추상**의 사례. 반면교사로 본다 | +| 26 | **Shopify Editions** shopify.com/editions/spring2026 | 기능 체인지로그를 제품 서사로 | 릴리스 노트를 읽게 만들어야 했다 | 씬 분할 | 섹션마다 자체 무대 | **스크롤 시퀀스 = 각 섹션이 독립된 무대.** 하나의 긴 씬이 아니다 | +| 27 | **Bellwoods / Audiograph** (Matt DesLauriers) | 제너러티브 지형/음악 | 작품 자체가 코드다 | 100% 프로시저럴 | 극도로 가벼움 | 모델 없이 **시드 하나**로 세계를 만든다 | +| 28 | **Mapbox 바람 지도** | 풍속 데이터 | 벡터장은 정지 이미지로 못 읽는다 | GPGPU 파티클 | 데이터 텍스처 | **움직임이 정보인** 유일한 정당화 | +| 29 | **IBM Technology Garden** (Variable) | 기술을 정원으로 | 은유가 브랜드 서사와 붙어 있다 | 프로시저럴 생성 | — | 은유형 추상. 성공 조건은 **은유가 카피에도 있는 것** | +| 30 | **Minh Pham** minhpham.design | 작업물의 틀 | 3D가 작업을 **압도하지 않도록** 설계 | 혼합 | — | 3D를 **액자**로 쓴다. 주인공이 되지 않는다 | +| 31 | **Sleep Well Creative** sleep-well-creatives.com | 손그림+3D 혼합 | 수면 안내라 부드러움이 요구됐다 | 혼합 | — | 3D에 손그림을 섞어 "CG 티"를 지운다 | +| 32 | **Taotajima.jp** (Akella 해부) | 이미지 전환 | — | 셰이더만 | 모델 0 | 셰이더 전환은 **모델 없이** 인상을 만드는 가장 싼 길 | + +### 추상이 성공하는 조건 — 셋 다 만족해야 한다 +1. **형태가 데이터에 묶여 있다.** Stripe의 점은 국가고, Spotify의 파티클은 재생이고, Mapbox의 흐름은 풍속이다. 데이터가 없으면 형태의 근거가 없다 +2. **은유가 카피에도 존재한다.** DeepSee는 제품명이 "수면 아래"고, fromanother는 "굴절하는 정체성"을 문장으로도 말한다 +3. **3D를 끄면 정보를 잃는다.** 분위기만 잃으면 그건 장식이다 + +--- + +## 3. 우리가 지금 실패한 이유 (진단) + +현재 히어로: 유리 판 6장이 렌더타겟에 구운 배경 그라디언트를 UV를 밀어 샘플링한다. + +| 성공 조건 | 현재 | +|---|---| +| 형태가 데이터에 묶여 있다 | ❌ **판이 6장인 이유가 없다.** 5장이나 8장이어도 똑같다 | +| 은유가 카피에 존재한다 | ❌ 페이지 어디에도 "유리"가 없다 | +| 3D를 끄면 정보를 잃는다 | ❌ CSS 그라디언트로 대체되고 아무도 모른다 | + +기술은 옳았다(렌더타겟 1장으로 transmission 비용 회피, 예산 준수, 폴백 완비). **형태 선택만 틀렸다.** +사용자 평 "3D라고 그냥 큐브만 돌면 안 됩니다"는 정확히 이 진단이다. + +--- + +## 4. 기법 카탈로그 — 무게 순 + +| 기법 | 추가 비용 | 언제 정당한가 | 출처 사례 | +|---|---|---|---| +| **프로시저럴 점/선 지오메트리** | **0KB** | 데이터가 점의 위치를 정할 때 | Stripe Globe (6만 점, 모델 0바이트) | +| 셰이더 전용 표면 (풀스크린) | 0KB | 재질이 메시지일 때 | Taotajima, fromanother | +| InstancedMesh 격자 | 0KB | 반복 단위가 데이터 항목일 때 | Codrops 큐브 그리드 | +| GPGPU 파티클 | 0KB | 움직임 자체가 정보일 때 | Mapbox 바람, Spotify | +| 하이트맵 지형 | 텍스처 1장(~50KB) | 지형이 상품일 때 | Explore Primland | +| 스크롤 카메라 리그 | +45KB (GSAP+Lenis) | 깊이가 서사일 때 | Oryzo, Shopify Editions | +| SDF 텍스트 (troika) | +55KB | WebGL 안에 대형 타이포가 필요할 때 | — | +| **GLB 모델 (무텍스처, Draco)** | **35~90KB** (실측 §07) | 오브젝트가 상품일 때 | Duck 34KB, Fox 90KB | +| **GLB 모델 (PBR 텍스처)** | **1~10MB** (실측 §07) | 사실감이 구매 결정에 필요할 때만 | DamagedHelmet 3.7MB | +| 포스트프로세싱 (bloom+CA) | +32KB | 발광이 형태의 일부일 때 | — | +| Spline 씬 임포트 | **800KB~2MB** | **거의 없음** | — | + +--- + +## 5. 출처 + +- awesome-casestudy (110건 아카이브) https://github.com/luruke/awesome-casestudy +- Stripe Globe 기술 해설 https://stripe.com/blog/globe +- Stripe Connect https://stripe.com/blog/connect-front-end-experience +- Best Three.js Websites 2026 https://www.utsubo.com/blog/best-threejs-websites-2026 +- Award-Winning Websites 2026 https://www.hontran.dev/blog/best-award-winning-websites-2026 +- WebGL Website Examples https://www.hontran.dev/blog/webgl-website-examples +- 3D Landing 갤러리 (24건) https://www.a1.gallery/websites/3d-landing +- Lusion 프로젝트 https://lusion.co/projects/ +- Active Theory 케이스 스터디 https://medium.com/active-theory +- 3D 컨피규레이터 ROI https://www.designrush.com/agency/ecommerce/trends/3d-product-configurators diff --git a/research/three/07-glb-sources.md b/research/three/07-glb-sources.md new file mode 100644 index 0000000..3310f09 --- /dev/null +++ b/research/three/07-glb-sources.md @@ -0,0 +1,206 @@ +# 07 — GLB 모델 소스 · 라이선스 · 실측 무게 + +> 조사일 2026-08-20. **`실측` 표기는 이 문서를 쓰면서 직접 다운로드/압축해 잰 값**이다. 나머지는 문서 기재값. +> 압축 실험은 `@gltf-transform/cli@4.4.2`로 수행했다. + +--- + +## 0. 먼저 알아야 할 것 — 텍스처가 예산이다 + +`DamagedHelmet.glb`(Khronos 표준 샘플)를 분해한 **실측**이다. + +| 구성 | 크기 | 비중 | +|---|---|---| +| 지오메트리 (14,556 vtx / 15,452 tri) | **558.5 KB** (GPU) | 13% | +| baseColor 2048² JPEG | 935.6 KB | | +| metallicRoughness 2048² JPEG | 1,300 KB | | +| normal 2048² JPEG | 517.8 KB | | +| occlusion 2048² JPEG | 361.7 KB | | +| emissive 2048² JPEG | 97.5 KB | | +| **텍스처 합계** | **≈ 3.2 MB** | **87%** | +| **VRAM** | 텍스처 5장 × 22.37 MB = **111.9 MB** | | + +압축 **실측**: + +| 처리 | 결과 | 감소 | +|---|---|---| +| 원본 | 3,685.5 KB | — | +| **Draco** | 3,216.3 KB | **−12.7%** | +| **meshopt** | 3,320.8 KB | −9.9% | +| gzip(원본) | 3,297.0 KB | −10.5% | + +> **Draco가 13%밖에 못 줄인다.** 지오메트리만 압축하는데 지오메트리가 파일의 13%뿐이기 때문이다. +> **"Draco 쓰면 90% 준다"는 말은 지오메트리가 payload일 때만 참이다.** + +지오메트리가 payload인 모델의 **실측**: + +| 모델 | 원본 | Draco | Draco+gzip | 감소 | +|---|---|---|---|---| +| **Duck.glb** (텍스처 1장, 저폴리) | 117.7 KB | **34.4 KB** | **33.1 KB** | **−71%** | +| **Fox.glb** (텍스처 없음, 애니메이션 3종) | 159.0 KB | **89.8 KB** | **68.1 KB** | **−44%** | +| Box.glb (최소 큐브) | 1.6 KB | — | — | — | + +### 결론 — 300KB 예산에 들어오는 것 + +| 시나리오 | 실현 가능? | +|---|---| +| 무텍스처 저폴리 GLB 1~3개 (Draco) | ✅ 35~90KB × 3 = **105~270KB** | +| 텍스처 1장(512² WebP ~40KB) + Draco 지오메트리 | ✅ **~120KB** | +| 애니메이션 캐릭터 1체 (무텍스처, Fox급) | ✅ **90KB** | +| PBR 텍스처 세트(2048²) 모델 1개 | ❌ **3.7MB — 예산의 12배** | +| PBR 세트를 512²로 줄이고 WebP | ⚠️ ~400~600KB **추정** — 여전히 초과 | +| Sketchfab/Poly Haven 쇼케이스 모델 그대로 | ❌ 대부분 2~24MB | + +**규칙: 300KB 예산에서는 텍스처를 포기하거나 모델을 포기해야 한다. 둘 다는 안 된다.** + +--- + +## 1. Khronos 표준 샘플 (실측 완료) + +`https://github.com/KhronosGroup/glTF-Sample-Assets` · GitHub raw 직링크 = **자동화 가능** +라이선스는 **모델마다 다르다**(CC0 / CC-BY 4.0 / Khronos 전용). `Models//README.md`에 명시. 반드시 개별 확인. + +| 모델 | **실측 크기** | 성격 | +|---|---|---| +| Box.glb | **1.6 KB** | 최소 | +| Duck.glb | **117.7 KB** | 저폴리 + 512 텍스처 1장 | +| Fox.glb | **159.0 KB** | 무텍스처 + 애니 3종 | +| BrainStem.glb | **3,120 KB** | 리깅 애니메이션 | +| DamagedHelmet.glb | **3,685 KB** | PBR 표준 레퍼런스 | +| SheenChair.glb | **4,029 KB** | 패브릭(sheen) | +| ToyCar.glb | **5,295 KB** | 클리어코트 | +| IridescentDishWithOlives.glb | **5,608 KB** | 이리데선스 | +| DragonAttenuation.glb | **6,252 KB** | 투과·감쇠 | +| ChronographWatch.glb | **7,272 KB** | 정밀 제품 | +| Avocado.glb | **7,920 KB** | 소품 + 4K 텍스처 | +| WaterBottle.glb | **8,757 KB** | 제품 | +| Lantern.glb | **9,340 KB** | 발광 | +| BoomBox.glb | **10,365 KB** | 제품 | +| AntiqueCamera.glb | **17,129 KB** | 고해상 | +| MosquitoInAmber.glb | **23,662 KB** | 투과 극한 | +| GlassHurricaneCandleHolder.glb | **2,642 KB** | **유리 — 우리 미학과 가장 근접** | + +**용도**: 렌더러/머티리얼 검증용이지 프로덕션 에셋이 아니다. 예산 안에 드는 것은 Box·Duck·Fox뿐. + +--- + +## 2. CC0 (상업 이용 자유 · 크레딧 불요) — 안전 등급 A + +| # | 소스 | URL | 라이선스 | 폴리곤 규모 | 크기대 | 자동화 | +|---|---|---|---|---|---|---| +| 1 | **Poly Haven** | polyhaven.com/models | **CC0** 전부 | 중~고 (스캔 기반 소품) | gltf 1k/2k/4k 분리 제공. **실측** `potted_plant_02_1k.gltf` 6.5KB(+ 별도 bin/텍스처) | ✅ 공개 API + `dl.polyhaven.org` 직링크 | +| 2 | **Quaternius** | quaternius.com | **CC0** 전부 | **저폴리** (수백~수천 tri) | 팩 단위 수 MB, 개별은 수십 KB | ⚠️ 팩 zip 다운로드 | +| 3 | **Kenney** | kenney.nl/assets | **CC0** 전부 | **극저폴리** | 개별 수 KB~수십 KB | ⚠️ 팩 zip | +| 4 | **KayKit** (Kay Lousberg) | kaylousberg.itch.io | **CC0** (대부분) | 저폴리, 캐릭터 리깅 포함 | 팩 단위 | ⚠️ itch.io zip | +| 5 | **ambientCG** | ambientcg.com | **CC0** | 텍스처·HDRI 중심, 모델 소수 | — | ✅ API | +| 6 | **Smithsonian 3D** | 3d.si.edu | **CC0** (2,000+) | **고폴리 스캔** | 수 MB~수십 MB | ⚠️ 개별 다운로드. glTF/OBJ 제공 | +| 7 | **Threedscans** | threedscans.com | **퍼블릭 도메인** | **초고폴리** 조각 스캔 | 수십 MB (STL/OBJ) | ⚠️ 수동. **GLB 변환 + 데시메이션 필수** | +| 8 | **NASA 3D Resources** | nasa3d.arc.nasa.gov | **퍼블릭 도메인** (일부 CC-BY, 개별 확인) | 중~고 | 수 MB | ⚠️ 수동 | +| 9 | **Blender Studio / demo files** | studio.blender.org, blender.org/download/demo-files | **CC0 / CC-BY** 혼재 | 고 | 수십 MB (.blend) | ⚠️ 변환 필요 | +| 10 | **awesome-cc0** (메타 목록) | github.com/madjin/awesome-cc0 | 링크 모음 | — | — | — | +| 11 | **Open Source 3D Assets** | opensource3dassets.com | CC0 중심 GLB 집계 | 저~중 | — | ⚠️ | + +--- + +## 3. 크레딧 필요 또는 제한 있음 — 안전 등급 B/C + +| # | 소스 | URL | 라이선스 | 주의 | +|---|---|---|---|---| +| 12 | **Sketchfab (CC0 필터)** | sketchfab.com/3d-models?licenses=cc0 | 필터로 CC0만 | ✅ 자동 glTF 변환 제공. **CC-BY/NC/ND 모델은 Sketchfab 밖으로 못 나가는 경우가 있다** — 필터 없이 쓰지 마라 | +| 13 | **Poly Pizza** (Google Poly 후속) | poly.pizza | **대부분 CC-BY** (크레딧 필수) | 로그인 불요, 저폴리 대량. **CC0가 아니다** | +| 14 | **Scan the World** (MyMiniFactory) | myminifactory.com/scantheworld | **혼재 — CC-BY-NC 다수** | ⛔ **상업 사이트에 그대로 쓰면 위반.** 개별 확인 필수 | +| 15 | **Sketchfab / Smithsonian 컬렉션** | sketchfab.com/Smithsonian | CC0 | ✅ 안전 | +| 16 | **Mixamo** | mixamo.com | Adobe 계정 필요, 사용 조건 있음 | 캐릭터 리깅·애니메이션. **재배포 제한** | +| 17 | **three.js examples 모델** | threejs.org/examples/models | 개별 상이 | **실측**: Soldier.glb 2,110KB · LittlestTokyo.glb 4,036KB · Flower.glb 4.7KB | + +--- + +## 4. 300KB 안에서 실제로 가능한 파이프라인 + +```bash +# 1) 지오메트리만 남긴다 — 텍스처를 버리고 정점 색/머티리얼 상수로 간다 +npx @gltf-transform/cli prune in.glb s1.glb +npx @gltf-transform/cli dedup s1.glb s2.glb +npx @gltf-transform/cli weld s2.glb s3.glb + +# 2) 폴리곤을 깎는다 (error 0.001~0.01 사이에서 육안 확인하며 조정) +npx @gltf-transform/cli simplify s3.glb s4.glb --ratio 0.5 --error 0.005 + +# 3) 지오메트리 압축 — 여기서 비로소 Draco 가 값을 한다 +npx @gltf-transform/cli draco s4.glb final.glb +# 또는 디코드가 5~10배 빠른 meshopt +npx @gltf-transform/cli meshopt s4.glb final.glb + +# 4) 텍스처를 꼭 써야 한다면 딱 한 장, 512 이하, WebP +npx @gltf-transform/cli resize final.glb r.glb --width 512 --height 512 +npx @gltf-transform/cli webp r.glb out.glb + +# 5) 검증 — 텍스처 비중과 VRAM 을 눈으로 본다 +npx @gltf-transform/cli inspect out.glb +``` + +> ⚠️ 이 환경에서 `--texture-compress` / `resize` 는 `sharp` 의 `colourspace: parameter space not set` 오류로 실패했다. +> 텍스처 처리는 별도 환경(또는 squoosh-cli)에서 하고 GLB에 되넣는 편이 안전하다. + +### 디코더 비용도 예산이다 + +| 디코더 | 크기 | 비고 | +|---|---|---| +| DRACO WASM | **~200 KB** | 자체 호스팅 필수. 한 번 받으면 캐시 | +| KTX2/basis 트랜스코더 | **~250 KB** | | +| meshopt 디코더 | **~25 KB** | **훨씬 작다.** 300KB 예산에서는 meshopt이 유일하게 현실적 | + +> **300KB 예산에서 Draco를 쓰면 디코더만으로 200KB(67%)가 날아간다.** +> 모델 하나를 위해 Draco를 도입하지 마라. **meshopt(25KB) 또는 무압축 + gzip**이 맞다. + +--- + +## 5. 압축 판단표 + +| 상황 | 선택 | +|---|---| +| 지오메트리 > 텍스처 (저폴리·무텍스처) | **meshopt** (디코더 25KB). Draco는 디코더가 더 크다 | +| 텍스처 > 지오메트리 (PBR) | **텍스처를 먼저 줄여라.** 지오메트리 압축은 무의미 | +| 다운로드 크기가 문제 | WebP/AVIF (VRAM은 안 줄어든다) | +| **VRAM**이 문제 (모바일) | **KTX2만이 답** (ETC1S ≈ 1/8). 단 이미 JPEG인 텍스처는 **파일이 커질 수 있다**(1.51MB → 1.98MB 사례) | +| 모델 1개, 예산 300KB | 압축하지 말고 **모델을 다시 만들어라**. 저폴리로 | + +--- + +## 6. 우리 브리프에 대한 판정 + +| 후보 | 예산 적합 | 컨셉 적합 | +|---|---|---| +| Khronos `GlassHurricaneCandleHolder.glb` (유리, 2.6MB) | ❌ 8배 초과 | ⚠️ 미학은 맞지만 촛대가 designpaca와 무관 | +| Khronos `Duck.glb` (34KB Draco) | ✅ | ❌ 오리가 왜 | +| Quaternius/KayKit 저폴리 팩 | ✅ (수십 KB) | ❌ 게임 에셋 톤 | +| Poly Haven 스캔 소품 | ⚠️ 1k도 수백 KB | ❌ 사실적 소품이 컨셉과 무관 | +| Threedscans 조각 (데시메이션 후) | ⚠️ 가능하나 수작업 | ⚠️ "고전 조각" 은유는 카피에 없다 | + +**어느 것도 컨셉과 연결되지 않는다.** 상세 판단은 `08-proposal.md`. + +**GLB가 정당해지는 유일한 경로**: designpaca가 **브랜드 캐릭터(알파카)**를 아이덴티티로 채택하는 경우. +그때는 CC0 팩에서 가져오지 말고 저폴리로 **직접 만들어야** 한다 — 남의 알파카는 우리 아이덴티티가 아니다. +비용 추정: 무텍스처 저폴리 + 정점 색 + meshopt = **40~80KB**, 디코더 25KB, 합 **65~105KB**. + +--- + +## 7. 출처 + +- Khronos glTF-Sample-Assets https://github.com/KhronosGroup/glTF-Sample-Assets +- Models 목록 https://github.com/KhronosGroup/glTF-Sample-Assets/blob/main/Models/Models.md +- glTF Transform https://gltf-transform.dev/ +- Draco 압축률 논의 https://github.com/donmccurdy/glTF-Transform/discussions/347 +- KTX2 크기 논의 https://github.com/donmccurdy/glTF-Transform/discussions/443 +- Poly Haven https://polyhaven.com/models +- Quaternius https://quaternius.com +- Kenney https://kenney.nl/assets +- KayKit https://kaylousberg.itch.io +- ambientCG https://ambientcg.com +- Smithsonian 3D https://3d.si.edu +- Threedscans https://threedscans.com +- NASA 3D Resources https://nasa3d.arc.nasa.gov +- Sketchfab CC0 https://sketchfab.com/3d-models?licenses=cc0 +- Poly Pizza https://poly.pizza +- Scan the World 라이선스 https://www.myminifactory.com/object-licensing +- awesome-cc0 https://github.com/madjin/awesome-cc0 diff --git a/research/three/08-proposal.md b/research/three/08-proposal.md new file mode 100644 index 0000000..13e24bb --- /dev/null +++ b/research/three/08-proposal.md @@ -0,0 +1,190 @@ +# 08 — designpaca 소개 페이지 3D 방향 제안 + +> 근거: `06-webgl-showcase.md`(사례 32건), `07-glb-sources.md`(실측 압축), 현재 코드 `apps/site/src/lib/hero-scene.ts` + +--- + +## 0. 결론 먼저 — 추상이다. GLB는 쓰지 않는다. + +`06` §2에서 뽑은 **추상 3D 성공 조건 3개**를 브리프에 대입한 결과다. + +| 조건 | designpaca | +|---|---| +| 형태가 **데이터**에 묶여 있다 | ✅ 가능. 페이지가 이미 파이프라인 6단계·토큰 값·자기 성능 수치를 **텍스트로** 공개하고 있다. 그 숫자를 형태로 옮기면 된다 | +| 은유가 **카피에도** 존재한다 | ✅ 가능. "게이트", "통과 조건", "예산" 은 이미 카피의 언어다 | +| 3D를 끄면 **정보를 잃는다** | ⚠️ 설계하기 나름. 아래 세 안은 전부 이 조건을 만족하도록 짰다 | + +**구체 오브젝트를 배제하는 이유** — `06` §1의 공통 조건 3개 중 하나도 만족하지 않는다. +1. 파는 것이 물리적으로 존재하지 않는다. 규칙 묶음이다 +2. 각도가 정보가 아니다. 규칙은 돌려봐도 새 정보가 없다 +3. 오브젝트가 하나로 정해지지 않는다 — 무엇을 놓아도 **은유**고, 은유가 카피에 없으면 장식이다 + +**예산이 이 판단을 다시 확인해준다** (`07` 실측): PBR 텍스처 GLB는 3.7MB로 300KB 예산의 12배다. +Draco는 텍스처를 못 줄여서 −13%에 그치고, Draco 디코더 자체가 **200KB(예산의 67%)** 다. +GLB를 쓰려면 무텍스처 저폴리(35~90KB) + meshopt(디코더 25KB)뿐인데, 그 급의 모델로는 의미를 나를 수 없다. + +--- + +## 제안 A — 파이프라인 게이트 (권장) + +### 무엇을 보여주나 +현재 떠 있는 **유리 판 여섯 장에 이름을 준다.** Z축을 따라 순서대로 세우고, 각 판이 파이프라인 한 단계가 된다. + +``` +카메라 ──→ [0 브리프 게이트] [1 레퍼런스] [2 방향] [3 토큰] [4 구현] [5 프리플라이트] ──→ 페이지 본문 +``` + +- 스크롤하면 카메라가 판을 **하나씩 통과**한다. 통과한 판은 뒤에 남아 겹쳐 굴절된다 +- 각 판 위에는 그 단계의 **통과 조건 한 줄**이 DOM 텍스트로 겹쳐 있다 (WebGL 안에 텍스트를 넣지 않는다 — 검색·선택·스크린리더) +- 아직 통과하지 않은 판은 흐리고, 통과한 판은 선명하다. **깊이 = 진행도** + +### 왜 이 브리프에 맞나 +1. **개수 문제가 이미 풀려 있다.** 히어로는 지금도 판이 6장이고 `Pipeline.astro`는 정확히 6단계다. "왜 6장인가"에 답이 생긴다 — 지금은 답이 없다 +2. **순서가 이 제품의 본체다.** SKILL.md의 절대 규칙이 "순서를 바꾸지 마라"다. 순서를 **공간의 깊이**로 만드는 것은 은유가 아니라 직역이다 +3. `06` §1의 Oryzo 교훈("스크롤이 2D 레이어가 아니라 진짜 Z축으로 카메라를 민다")과 Shopify Editions 교훈("각 섹션이 독립된 무대")을 동시에 쓴다 +4. **기존 코드를 버리지 않는다.** 렌더타겟 굴절 기법, 예산 준수, 폴백이 그대로 산다. 판의 배치와 라벨만 바뀐다 + +### GLB 필요 여부 +**불필요.** `PlaneGeometry` 6장 + 기존 굴절 셰이더. 새 에셋 0개. + +### 예산 추정 +| 항목 | 추가 | +|---|---| +| three (이미 있음) | ±0 | +| 판 배치·카메라 리그 코드 | **+2~3KB** | +| 스크롤 연동 | **+0KB** — `scroll` 리스너 대신 rAF에서 `scrollY`를 읽는다 (하드게이트 #10) | +| 라벨 | **+0KB** — DOM 오버레이 | +| **합계** | **+3KB 이내** | + +### 실패 위험 +| 위험 | 대응 | +|---|---| +| **스크롤 하이재킹.** 게이트 통과를 강제하면 이탈한다 | 전체 스크롤 길이를 **2뷰포트 이내**로 묶고, 핀 고정을 쓰지 않는다. 스크롤을 멈춰도 페이지가 진행된다 | +| 판이 텍스트 가독성을 해친다 | 라벨은 판 **옆**에 두고 판 위에 겹치지 않는다. 대비는 별도 측정 | +| 여전히 "떠다니는 사각형"으로 읽힌다 | 판을 **정면으로 세우고 정렬**한다. 지금의 자유 부유를 버린다. 계기판은 정렬돼 있다 | +| reduced-motion에서 의미가 사라진다 | 판 6장을 **정지 상태로 한 장 렌더**해서 순서가 보이게 한다. 현재처럼 아예 로드하지 않으면 정보를 잃는다 → 이 안은 폴백 정책을 바꿔야 한다 | + +> ⚠️ **이 안의 유일한 구조적 대가**: 3D가 정보를 나르게 되는 순간, "reduced-motion이면 아예 로드하지 않는다"는 현재 정책이 **정보 손실**이 된다. +> 해결: reduced-motion에서는 CSS만으로 6단계를 **세로 목록**으로 보여준다(이미 `Pipeline.astro`가 한다). 3D는 같은 정보의 다른 표현이 된다. + +--- + +## 제안 B — 토큰 격자 + +### 무엇을 보여주나 +페이지가 실제로 쓰는 토큰을 **3차원 좌표계**에 세운다. 축이 곧 토큰 종류다. + +- **X축** = 간격 스케일 `--space-1`~`--space-8` (8단계) +- **Y축** = 타입 스케일 (본문 단계 + 디스플레이) +- **Z축** = 색 역할 (`--surface` / `--surface-raised` / `--ink` / `--ink-muted` / `--line` / `--accent`) +- 각 노드의 **위치와 크기가 실제 CSS 변수 값에서 읽힌다**. `getComputedStyle`로 뽑아 그대로 스케일에 넣는다 +- 다크/라이트를 토글하면 **격자가 실제로 재배치된다** — 색 축의 좌표가 바뀌기 때문 +- 노드에 마우스를 올리면 그 토큰이 페이지 어디에 쓰였는지 DOM 요소가 하이라이트된다 + +### 왜 이 브리프에 맞나 +1. 페이지 컨셉이 *"그 절차와 **형태 수치**를 전부 공개한다"* 이다. **수치가 그대로 형태가 된다.** 은유가 0단계다 +2. `06` §2의 Stripe Globe와 같은 논리 — 점 하나하나가 데이터고, 지오메트리가 파일이 아니라 **코드**다 +3. **3D를 끄면 진짜로 정보를 잃는다.** 세 축을 2D로 접으면 축 하나가 사라진다. 이 조건을 가장 확실히 만족하는 안이다 +4. 토큰이 바뀌면 격자가 따라 바뀐다 — **하드코딩이 불가능한 구조**라 스킬의 "토큰은 계약이다" 원칙을 코드가 증명한다 + +### GLB 필요 여부 +**불필요.** `InstancedMesh` 노드 + `LineSegments` 축. Stripe가 6만 점을 모델 없이 만든 것과 같다. + +### 예산 추정 +| 항목 | 추가 | +|---|---| +| InstancedMesh + 라인 축 | **+3KB** | +| `getComputedStyle` 토큰 리더 | +1KB | +| 호버 하이라이트 연동 | +2KB | +| **합계** | **+6KB 이내** | + +### 실패 위험 +| 위험 | 대응 | +|---|---| +| **"도표를 왜 3D로?"** — 가장 큰 위험. 2D 표가 더 읽기 쉬우면 3D는 진 것이다 | 축 세 개가 **동시에** 필요하다는 걸 보여줘야 한다. 축 하나를 접었을 때 잃는 것이 눈에 보여야 정당화된다 | +| 격자가 차갑고 예쁘지 않다 | Ethereal Glass 미학과 충돌할 수 있다. 노드를 유리 재질로 하고 축을 시안 틸로 — 미학은 유지 가능 | +| 정보 과다 | 축당 표시 개수를 제한한다. 전부 보여주려 하지 마라 | +| 히어로에 두기엔 무겁다 | **히어로가 아니라 `Metrics`/`Typography` 섹션 옆**에 두는 것이 맞을 수 있다. 히어로는 A안, 본문은 B안 조합이 가능하다 | + +--- + +## 제안 C — 실측 계기판 + +### 무엇을 보여주나 +페이지가 **지금 이 순간의 자기 수치**를 3D로 그린다. 값은 전부 실제 API에서 읽는다. + +| 축 | 데이터 소스 | +|---|---| +| 번들 전송량 | `performance.getEntriesByType('resource')` | +| LCP | `PerformanceObserver` | +| 드로우콜 / 삼각형 | `renderer.info.render` | +| 현재 DPR | `renderer.getPixelRatio()` | +| 프레임 시간 분포 | rAF 델타 히스토그램 | + +- 막대그래프가 아니라 **공간에 뜬 눈금과 축**. 값이 예산선을 넘으면 그 축만 색이 바뀐다 +- **관찰자 효과를 숨기지 않는다**: 3D를 켜면 드로우콜과 프레임 시간이 올라가고, 그게 화면에 그대로 보인다 + +### 왜 이 브리프에 맞나 +1. `Metrics.astro`가 이미 *"이 페이지의 수치"*를 **텍스트로** 공개하고 있다(JS 0.6%, three 추가분 185KB, 슬롭 지문 0건). 이 안은 그 섹션의 3D 판이다 — **새 컨셉이 아니라 기존 컨셉의 연장** +2. 스킬의 5단계가 *"측정하지 않았으면 통과가 아니다"* 라고 말한다. 페이지가 스스로를 측정하는 화면은 **제품 주장의 증명**이다 +3. 3D가 자기 비용을 스스로 고백하는 구조라, "3D를 왜 넣었나"는 질문에 페이지가 직접 답한다 + +### GLB 필요 여부 +**불필요.** 라인 + 인스턴스 + 텍스트는 DOM. + +### 예산 추정 +| 항목 | 추가 | +|---|---| +| 계측 코드 (PerformanceObserver 등) | **+2KB** | +| 축·눈금 지오메트리 | +2KB | +| **합계** | **+4KB 이내** | + +### 실패 위험 +| 위험 | 대응 | +|---|---| +| **수치가 나쁘면 자폭한다.** 저사양 기기에서 프레임이 무너지면 그 화면이 증거로 남는다 | 이게 정직함이라 판단하면 그대로 두고, 아니면 이 안을 버려라. **어중간하게 감추면 최악** | +| 계기판 자체가 진부하다 | `antipatterns.md` §4 「펄스 애니메이션 상태 점」에 인접하다. 값이 **실제로 변할 때만** 움직여야 한다 | +| 개발자 아닌 방문자에게 무의미하다 | 청중이 에이전트·개발자라 큰 문제는 아니지만, 히어로에 두면 첫인상이 좁아진다. **본문 섹션용** | +| 측정 자체가 프레임을 먹는다 | 샘플링 주기를 1초로. 매 프레임 계측하지 않는다 | + +--- + +## 1. 조합 권장안 + +셋은 배타적이지 않다. **A를 히어로에, C를 Metrics 섹션에** 두는 조합이 가장 안전하다. + +| 위치 | 안 | 이유 | +|---|---|---| +| 히어로 | **A 파이프라인 게이트** | 첫 화면에서 제품이 무엇인지(6단계 순서) 말한다. 기존 코드 재사용 | +| Metrics 섹션 | **C 실측 계기판** | 이미 그 자리에 있는 텍스트 수치의 3D 판 | +| (선택) Typography/Tokens 섹션 | B 토큰 격자 | 여력이 있을 때만. 셋 다 넣으면 페이지가 3D 전시장이 된다 | + +**셋 다 하지 마라.** `06` §1이 반복해서 말하는 것은 *"어려운 아이디어 하나를 끝까지 판다"* 다. + +--- + +## 2. 하지 말아야 할 것 (진부해진 웹 3D) + +| 금지 | 왜 | +|---|---| +| **회전하는 큐브 / 토러스넛** | three.js "hello world" 그 자체. 18줄 튜토리얼의 결과물이 그대로 프로덕션에 올라간 신호 | +| **이유 없는 파티클 구(sphere)** | 파티클이 데이터에 묶이지 않으면 스크린세이버다. `06` §2 조건 1 위반 | +| **마우스를 따라다니는 구체 / 커서 오브** | 인터랙션을 설계하지 않았다는 신호. 커서는 이미 커서다 | +| **Spline 기본 씬 임포트** | 800KB~2MB. 예산의 3~7배. 게다가 Spline 특유의 매끄러운 블롭은 즉시 식별된다 | +| **떠다니는 3D 추상 블롭·기하 도형** | `antipatterns.md` §7이 명시적으로 금지 — "콘텐츠 부재를 덮는 장식" | +| **히어로 뒤 방사형 그라디언트 오브·헤일로** | `antipatterns.md` §1 — "구성을 못 잡았을 때의 회피 수단" | +| **네온 온 다크 발광 와이어프레임** | `antipatterns.md` §1 「네온 온 다크」. 개발자 툴 브리프라도 관성이다 | +| **아무 데나 떠 있는 유리 판** | **지금 우리다.** 형태에 근거가 없으면 유리도 블롭과 같다 | +| **3D 텍스트로 만든 로고 회전** | 텍스트는 DOM에 있어야 한다. 3D 로고는 검색·선택·스크린리더를 전부 잃고 얻는 게 없다 | +| **모든 섹션에 3D** | 스크롤할 때마다 캔버스가 뜨면 컨텍스트 상한(8~16)과 VRAM을 동시에 친다 | +| **로딩할 게 없는데 3D 프리로더** | `antipatterns.md` §5 — 없는 대기를 만든다 | + +--- + +## 3. 어느 안을 택하든 지켜야 할 것 + +- **텍스트는 DOM에.** 라벨·수치·통과 조건 전부. WebGL 안에 넣지 않는다 +- **판정 질문 하나**: *"3D를 끄면 무엇을 잃는가?"* 답이 "분위기"면 그 안은 실패다 +- **형태의 개수에 근거가 있어야 한다.** 6장인 이유, 8개인 이유를 한 줄로 못 대면 개수를 바꿔라 +- **예산**: 세 안 모두 **+6KB 이내**다. 300KB 상한은 GLB를 전제한 값이고, 여기서는 쓸 일이 없다 +- **폴백 재설계**: 3D가 정보를 나르게 되면 "reduced-motion이면 로드하지 않는다"가 정보 손실이 된다. 같은 정보를 CSS/DOM으로도 제공해야 한다