Pryne 프론트엔드 디버깅 플레이북은(는) 무엇을 하나요?
Pryne의 vanilla-JS 데모 빌드를 위한 증상→트라이지 가이드: 단일 HTML 페이지로, window.Roll 모듈 코어(design-demos/js/roll-*.js), WebGL 그레인 셰이더(2D 폴백 포함), 하나의 rAF 렌더 루프, 벤더된 Lenis 부드러운 스크롤, 그리고 CDN에서 async로 로드되는 GSAP(점진적 향상)을 사용합니다.
필수 배경 지식: superpowers:systematic-debugging이 일반적인 근본 원인 규율(조사 없이 수정 금지)을 담당합니다. 이 지침을 따르세요. 이 스킬은 프로젝트별 증상 테이블과 함정만 제공합니다.
도구: 브라우저 구동, 콘솔 캡처, reduced-motion 에뮬레이션, 트레이스 수집 방법은 pryne-browser-lab을 사용하세요. 이 스킬은 이러한 명령어를 이미 실행할 수 있다고 가정합니다.
먼저 헤드 주석을 읽으세요
모든 v4 데모(design-demos/의 v4-a ~ v4-d)는 파일 최상단에 HTML 주석으로 감사 기록(무엇이 고장났고, 무엇이 수정되었으며, 그 이유)을 담고 있습니다. 2026-07-22 기준으로 네 파일 모두 확인되었습니다. 무언가를 다시 진단하기 전에, 디버깅 중인 데모의 head -30을 실행하세요. 발견한 여러 "버그"는 이미 해결된 회귀(regression)로 문서화되어 있습니다. 다시 나타난다면 새로운 원인을 찾는 것이 아니라 무엇이 되돌렸는지 찾아야 합니다.
또한 빌드 구조(2026-07-22 기준)를 알아야 합니다: v4-d-hybrid.html만 공유 roll-*.js 모듈을 사용합니다. v4-a/b/c는 범위가 축소된 비교 빌드로 자체 인라인 JS를 가지고 있습니다; 이 테이블의 Roll 관련 검사는 해당 파일들에 적용되지 않지만, 동일한 실패 클래스는 적용됩니다.
콘솔 에러 0개가 기본 상태입니다
표준은 콘솔 에러 0개입니다(design-demos/DECISIONS.md의 CRITIQUE 루브릭에서 요구함). v3는 프로젝트 상태 로그에 따라 0개로 확인되었습니다. v4 변종들은 방향 캠페인(pryne-design-direction-decision)이 진행 중이므로 인증되지 않았습니다. 모든 콘솔 에러는 트라이지 대상 결함이며, 지나쳐 넘길 수 있는 잡음이 아닙니다.
증상 → 트라이지 테이블
| 증상 | 가능한 원인 | 판별 검사 |
|---|---|---|
| 애니메이션/개발플레이트가 해당 섹션이 스크롤되어 보이기 전에 실행(또는 완료)됨 | 가시성 게이팅 회귀. v4-d 헤드 주석은 이것이 가장 높은 우선순위의 감사 수정사항임을 기록: 기능 플레이트가 "페이지 로드 시 무조건 틱(tick)하여 아무도 스크롤하기 전에 해소를 마침." 개발은 패널의 .in 클래스에 게이트되어야 함. | 아래 실험 1 참조 |
| 리빌 애니메이션이 잘못 클리핑되거나, 점프하거나, 절대 트리거되지 않음 | IntersectionObserver가 관찰하는 요소에 clip-path가 직접 적용됨(내부 래퍼에 적용되어야 함). concept-C 수정(v4-d에 반영됨, 헤드 주석 참조): clip-path 리빌은 내부 래퍼에 있으며, 관찰 대상 요소에는 절대 적용되지 않음. | 요소 검사: 관찰된 노드와 클리핑된 노드가 동일한가? |
| OS 커서가 사라지고, 커스텀 커서로 대체되지 않음 | 재발하지 않아야 함 — 2026-07-22 수정됨(콜드 테스트 감사에서 발견, 당일 수정): 이벤트 핸들러(roll-cursor.js:17-20)와 그 pointermove 호출 지점(roll-core.js:26-29)이 이제 원래 설정 시 캐치에 더해 모두 캐치 앤 복원(null Roll.cursorUpdate, .custom-cursor 제거); #cur/#ring z-index가 #flash(60) 위로 50→70 상향되어 클라이맥스 펄스가 커서 위에 칠해지는 것을 방지. 증상이 재발한다면, 이러한 가드 중 하나가 회귀했거나 새로운 코드가 이 계약 외부에서 커서를 숨기는 것임. | document.body.classList.contains('custom-cursor') vs Roll.cursorUpdate — true/null이 함께면 깨진 상태; 둘 다 정상이면 getComputedStyle(cur).opacity 확인 및 클라이맥스 플래시에서만 사라지는지 확인 |
| WebGL 고스팅/트레일, 또는 색상이 페이지와 잘못 블렌딩됨 | 프레임별 클리어 누락, 또는 프리멀티플라이드 알파 불일치. roll-shader.js는 매 프레임 gl.clear(gl.COLOR_BUFFER_BIT)를 호출하고 { alpha: true, premultipliedAlpha: false }로 컨텍스트를 생성함(둘 다 감사 수정 사항, v4-d 헤드 주석 참조). | roll-shader.js를 git과 비교: git diff HEAD -- design-demos/js/roll-shader.js |
| 셰이더 캔버스가 비어 있고, 그레인이 전혀 없음 | (a) Reduced motion — GL이 의도적으로 설정되지 않음(useGL = !Roll.reduced && setupGL()); (b) WebGL 컨텍스트 손실 — webglcontextlost 핸들러가 캔버스 불투명도를 0으로 설정하고 조용히 실패; (c) WebGL이 기회를 얻기 전에 2D 컨텍스트가 먼저 획득됨(캔버스당 하나의 컨텍스트만 가능, roll-shader.js 상단에 문서화됨). | 먼저 Roll.reduced 확인. 컨텍스트 손실의 경우, 핸들러의 관찰 가능한 효과 확인: getComputedStyle(document.getElementById('tone')).opacity === '0'. 주의: Roll.shader.useGL은 설정 시점 스냅샷(roll-shader.js:120-121) — webglcontextlost 핸들러는 클로저 변수만 변경(roll-shader.js:130-132)하므로, 컨텍스트 손실 후에도 Roll.shader.useGL은 true로 유지됨; 이를 사용하여 손실을 배제하지 말 것. |
| 스크롤 연출이 죽었지만 페이지는 렌더링되고 스크롤은 정상 작동 | 스크립트 로드 순서 또는 window.Roll 네임스페이스 손상. 스크립트 순서는 중요함 — 체인과 확인 명령어는 pryne-frontend-architecture에 있음. 모든 모듈은 window.Roll을 통해서만 상태를 공유하는 IIFE임; 초기 모듈의 단 하나의 오류로 이후 모든 모듈이 굶어 죽음. | 아래 실험 2 참조; 콘솔에 굶어 죽은 모듈을 가리키는 Roll.X is undefined가 표시됨 |
| 한 사용자에게 모든 모션이 죽었다고 보고됨; 내 기기에서는 정상 | 사용자에게 prefers-reduced-motion이 활성화됨. 이는 올바른 동작이며 버그가 아님: roll-core.js가 미디어 쿼리에서 Roll.reduced를 설정하고, 모든 모듈이 이를 기준으로 게이팅(커버 제거, 플레이트 즉시 완전히 렌더링, Lenis 건너뛰기, 셰이더 끄기). 이를 "수정"하지 마십시오. | 아래 실험 3 참조 |
| 트랙패드/휠/터치 간 스크롤 느낌이 일관되지 않음 | 원시 스크롤 델타 차이 — Lenis 벤더링의 문서화된 근거(design-demos/DECISIONS.md 참조). Lenis는 네이티브 스크롤을 래핑하므로 window.scrollY는 유효함; 느낌 향상일 뿐이며, reduced motion에서는 null임. | typeof Lenis(로드됨?), 데모의 스크롤 모듈에서 Lenis를 생성하는지 확인 |
| 베이스라인은 작동하지만 호버/키네틱/커서 폴리시가 누락됨 | GSAP CDN 실패 또는 차단됨. GSAP는 jsdelivr에서 async로 로드(SRI 고정)되며, 로드될 때만 Roll.enhanced = true로 Roll.upgradeMotion()을 통해 전환됨. 점진적 향상은 설계상: 기본 연출은 없어도 동작해야 함. 기본 연출도 깨졌다면 이것이 원인이 아님. | 아래 실험 4 참조 |
| 오디오가 조용함 | 음소거가 기본값: roll-audio.js는 음소거 상태로 시작하며, localStorage['ch001_muted']에 선택을 유지하고, #mute-toggle 클릭(사용자 제스처, 자동 재생 정책 충족) 시에만 앰비언트 허밍을 시작함. 모든 합성은 try/catch 래핑되고 에셋이 없음. 코드를 의심하기 전에 SND 토글 상태를 확인하세요. | Roll.audio.muted, 그리고 localStorage.getItem('ch001_muted') |
| 고정 HUD가 푸터를 겹침/가림 | 푸터 페이드 수정의 회귀: footer의 IntersectionObserver가 비트 미터와 음소거 버튼에 .faded를 토글(roll-scroll.js). | 푸터로 스크롤; #beatmeter에 .faded가 나타나는지 확인 |
판별 실험
브라우저 콘솔 또는 evaluate_script를 통해 실행하세요(pryne-browser-lab 참조). 각각 코드를 건드리기 전에 한 가지 질문에 답합니다.
1 — 플레이트 개발이 가시성에 올바르게 게이팅되었는가?
({ workVisible: document.getElementById('work').classList.contains('in'),
plate1: { d: Roll.featurePlate.d, done: !!Roll.featurePlate.done },
plate2: { d: Roll.featurePlate2.d, done: !!Roll.featurePlate2.done } })
새로 로드 후, work 섹션으로 스크롤하기 전: workVisible: false이고 두 d 값이 0 근처입니다. d가 증가 중이거나(done: true) workVisible이 false인 동안이라면 게이팅이 회귀한 것입니다.
2 — Roll 코어가 온전한가?
({ core: !!window.Roll, reduced: Roll?.reduced, enhanced: Roll?.enhanced,
shader: !!Roll?.shader, audio: !!Roll?.audio, plates: !!Roll?.plates,
cursorUpdate: Roll?.cursorUpdate === null ? 'null' : typeof Roll?.cursorUpdate })
cursorUpdate에 대한 참고: roll-core.js:23에서 null로 초기화되며, GSAP 로드 전과 pointer: fine이 아닌 기기에서도 null입니다 — null 자체로는 아무것도 증명하지 않습니다. "안전 실패"는 document.body.classList.contains('custom-cursor')가 true 이고 cursorUpdate가 null일 때만 나타납니다(트라이지 테이블의 커서 행 참조).
false/undefined는 완료되지 않은 모듈을 나타냅니다 — 콘솔에서 발생한 오류를 확인하고, HTML의 스크립트 순서가 위의 로드 순서와 일치하는지 확인하세요.
3 — "모든 모션 정지"가 단순히 reduced-motion인가?
({ mediaQuery: matchMedia('(prefers-reduced-motion: reduce)').matches, roll: Roll.reduced })
둘 다 true → 의도된 대로 작동 중; reduced-motion을 에뮬레이션하여(pryne-browser-lab) 보고를 재현하세요. mediaQuery: true지만 roll: false(또는 반대) → 모듈이 잘못된 진실 공급원을 읽고 있습니다.
4 — GSAP 향상 레이어가 로드되었는가?
({ gsapLoaded: !!window.gsap, enhanced: Roll.enhanced, reduced: Roll.reduced })
gsapLoaded: false → CDN 차단/실패(네트워크 패널에서 jsdelivr 요청 확인; SRI 불일치도 여기에 해당). gsapLoaded: true, enhanced: false, reduced: false → upgradeMotion()이 실행되지 않음 — 스크립트 태그의 onload 훅을 확인하세요.
실제 시간을 낭비하는 함정
| 함정 | 이야기 (한 줄) | 방지책 |
|---|---|---|
| "확인된 작동" ≠ 올바른 방향 | v3 NIGHT DRIVE는 완전히 빌드되고, 콘솔 에러 0개로 확인되었으며, 통합 개념이 잘못되어 폐기됨(전체 기록: pryne-failure-archaeology). | 개념이 현재 방향으로 확인되지 않은 빌드를 방어하기 위해 폴리시/디버그 노력을 낭비하지 마십시오. 먼저 DECISIONS.md와 저장소 상태를 확인하세요. |
| 문서화된 수정 사항을 재진단 | 모든 v4 헤드 주석은 이미 발견되고 수정된 결함의 변경 로그입니다. | devtools를 열기 전에 헤드 주석을 읽으십시오. 나열된 수정이 깨진 것처럼 보이면 증상이 아닌 되돌리기(revert)를 추적하십시오. |
| reduced-motion을 "수정"함 | 모든 모션 중단은 prefers-reduced-motion 사용자를 위한 설계된 동작이며, 한 곳(Roll.reduced)에서 게이팅됩니다. | 모션 없음 보고를 결함으로 처리하기 전에 실험 3을 실행하십시오. |
| "테스트하려고" 2D 컨텍스트 획득 | 캔버스는 한 유형의 컨텍스트만 가질 수 있음; 먼저 2D를 잡으면 해당 캔버스에서 WebGL이 영원히 불가능해짐(roll-shader.js에 문서화됨). | 콘솔 실험에서 #tone에 getContext('2d')를 절대 호출하지 마십시오. |
이 스킬을 사용하지 말아야 할 경우
- 일반 디버깅 규율 (가설, 최소 변경, 수정 전 근본 원인) → superpowers:systematic-debugging.
- 브라우저 구동/측정 방법 (MCP 도구 구조, 트레이스, Lighthouse, 스크린샷) → pryne-browser-lab.
- 동작이 설계 불변량을 위반하는지 결정 (reduced-motion 게이팅, 낮은 목소리 성격, 벤더링) → pryne-frontend-architecture.
- 무언가가 빌드/되돌려진 전체 역사 → pryne-failure-archaeology.
- 다른 스택 (React/프레임워크 빌드, Pryne 이외의 프로젝트): 위 테이블은
window.Roll데모 제품군에 특화됨; 일반 규율만 전이됩니다.
출처 및 유지보수
scripts/verify.sh(이 스킬의 디렉토리에서)를 실행하여 아래 모든 사항을 한 번에 확인하세요.
현재 정보는 2026-07-22 기준이며, 저장소 ~/Documents/Coding Folder/Portfolio (test) 커밋 457868a에서 확인됨. 신뢰하기 전에 재확인하세요:
- 모듈 목록 및 크기:
wc -l design-demos/js/*.js - 스크립트 로드 순서:
grep -n "<script" design-demos/v4-d-hybrid.html - 어떤 데모가 공유 모듈을 사용하는지:
grep -l "roll-core.js" design-demos/*.html - 헤드 주석 규칙 유지 확인:
for f in design-demos/v4-*.html; do echo "== $f"; head -5 "$f"; done - Reduced-motion 게이트:
grep -n "prefers-reduced-motion\|Roll.reduced" design-demos/js/*.js - 플레이트 가시성 게이팅:
grep -n "classList.contains('in')" design-demos/js/roll-scroll.js - Lenis 벤더링 및 연결:
ls design-demos/js/vendor/ && grep -n "new Lenis" design-demos/js/roll-scroll.js - GSAP 향상 훅:
grep -n "upgradeMotion" design-demos/js/roll-core.js design-demos/v4-d-hybrid.html - 오디오 음소거 기본값 및 저장소 키:
grep -n "ch001_muted" design-demos/js/roll-audio.js - 콘솔 에러 0개 기준:
grep -n "console errors" design-demos/DECISIONS.md