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