Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/DevooKim/my-gallery --skill verify명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | verify |
| description | 3D 갤러리 앱을 실행하고 브라우저로 전체 관람 플로우(입장→이동→감상→복귀)를 검증하는 레시피 |
npm run dev # 기본 5173 포트, 백그라운드 실행
Chrome DevTools MCP로 접속.
콘솔에서 import('/src/store.ts')는 앱과 다른 모듈 인스턴스를 만든다 — HMR을 거친 앱 그래프는 ?t=타임스탬프가 붙은 URL을 쓰기 때문. 앱 스토어를 진단하려면 변환된 소스에서 실제 URL을 추출해 import하라: const url = (await fetch('/src/ui/Hud.tsx').then(r=>r.text())).match(/"([^"]*store\.ts[^"]*)"/)[1]. 이걸 모르면 "스토어가 안 바뀐다"는 가짜 진단이 나온다.
initScript에서 document.documentElement에 MutationObserver를 붙이면 조용히 실패한다 (그 시점엔 documentElement가 없음). DOMContentLoaded 이후 document.body에 붙일 것.
로딩 진행률 검증: 로컬 자산은 순식간이라 문구가 안 보인다. emulate로 Slow 3G + exhibitions.json 포스터를 임시로 큰 원격 URL(picsum)로 바꿔 검증 후 원복.
포인터 락 요청은 브라우저 창이 OS 포커스를 가질 때만 성공한다. CDP 클릭이 락을 못 걸고 콘솔에 Unable to use Pointer Lock API가 찍히면 select_page를 bringToFront: true로 호출한 뒤 다시 클릭하라. 그래도 불안정하면 아래 락 스텁을 써라.
스크린샷 캡처는 실제 포인터 락을 강제로 풀어버린다 (일시정지 트리거와 동일). 실제 락으로 테스트할 때는 조작→상태확인(evaluate)→스크린샷 순서를 지켜라.
락 스텁: 앱 상태 머신만 검증할 때는 실제 락 대신 주입한다. 스크린샷이 락을 깨는 문제도 사라진다.
let fakeLock = null
Object.defineProperty(Document.prototype, 'pointerLockElement', { configurable: true, get: () => fakeLock })
window.__setLock = (on) => { fakeLock = on ? document.querySelector('canvas') : null; document.dispatchEvent(new Event('pointerlockchange')) }
키 입력: window.dispatchEvent(new KeyboardEvent('keydown', {code:'KeyW'})) 후 setTimeout으로 홀드, keyup으로 종료. 이동 속도 3m/s. 락(또는 스텁)이 켜져 있어야 이동한다.
감상 진입/종료 클릭: canvas.dispatchEvent(new MouseEvent('click', {bubbles:true})) — isTrusted 체크 없음.
캔버스 CDP 클릭: 캔버스는 a11y 트리에 없으므로 canvas.setAttribute('role','button') + aria-label 후 snapshot→click.
canvas.__r3f 없음). Player가 포인터 모드에서 매 프레임 Raycaster.setFromCamera(coords, camera)를 호출하므로, 앱과 같은 three 모듈 인스턴스의 프로토타입을 훅하면 카메라를 얻는다 (점프 y 궤적·yaw 측정에 사용; 터치 모드는 탭 시점에만 레이캐스트하므로 매 프레임 샘플이 없다):
const threeUrl = (await fetch('/src/scene/Player.tsx').then(r=>r.text())).match(/import \* as THREE from "([^"]+)"/)[1]
const THREE = await import(threeUrl)
const orig = THREE.Raycaster.prototype.setFromCamera
THREE.Raycaster.prototype.setFromCamera = function (c, cam) { window.__cam = cam; return orig.call(this, c, cam) }
document.pointerLockElement !== null (스텁 사용 시 스텁 값)합성 mousemove는 Chrome에서 movementX를 지원하므로 회전을 실제로 검증할 수 있다:
const moveMouse = (dx) => document.dispatchEvent(new MouseEvent('mousemove', { movementX: dx, movementY: 0, bubbles: true }))
파랑새 앞에서 조준 힌트가 켜진 상태를 기준으로:
moveMouse(40) ×20) → 힌트 꺼짐 (회전 동작 확인)하나라도 실패하면 락 상태가 어딘가에 복제/캐시되고 있다는 뜻이다. CLAUDE.md 금지 목록 참조.
window.matchMedia = (q) => q === '(pointer: coarse)' ? { matches: true, media: q, addEventListener(){}, removeEventListener(){}, ... } : orig(q). inputMode는 마운트 시 1회 판정.style.touchAction === 'none' && style.inset === '0px'인 div)에 합성 PointerEvent(pointerType 'touch') dispatch:
THREE.Clock: This module has been deprecated — R3F 내부, 무시.