# 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으로도 제공해야 한다
