Pryne 프론트엔드 아키텍처 계약
Pryne의 바닐라 JS 빌드에서 구조적 결정 사항, 그 이유, 그리고 절대 깨뜨리면 안 되는 불변 규칙들입니다. 참조 구현: Portfolio (test) 저장소의 design-demos/ (~/Documents/Coding Folder/Portfolio (test)), 활성 빌드 v4-d-hybrid.html + js/roll-*.js, 기준일: 2026-07-22.
용어(한 번 정의): IIFE = 즉시 실행 함수 표현식, (function () { ... })(); — 파일 범위 클로저이므로 파일이 명시적으로 공유 네임스페이스에 넣는 것 외에는 아무것도 유출되지 않습니다. 점진적 향상 = 페이지가 제로 JS 향상으로도 완전히 사용 가능하며, 더 풍부한 동작은 해당 의존성이 실제로 로드될 때에만 계층화됩니다. 개발-판 = 이 프로젝트의 캔버스 아트워크로, 사용자가 참여함에 따라 "개발"(노이즈에서 해소)됩니다.
이 스킬을 사용하지 말아야 할 경우
- 애니메이션 타이밍, 이징, 안무 이론 → pryne-motion-reference
- 변경이 허용되는지 여부와 게이팅 방식 → pryne-change-control
- 무언가 고장 나서 진단 중 → pryne-frontend-debugging
- 변경 사항이 작동함을 증명(스크린샷, Lighthouse, 루브릭) → pryne-validation-and-qa
- React/Next.js 또는 번들러 프로젝트 — 이 계약은 빌드 없는 바닐라 사이트 전용입니다.
모듈 계약
파일당 하나의 관심사. 파일당 하나의 IIFE. 번들러 없음, 임포트 없음.
모든 모듈 간 상태는 단일 전역 네임스페이스 객체인 window.Roll에 저장됩니다. roll-core.js가 이를 생성하고 가장 먼저 로드되며, 다른 모든 모듈은 const Roll = window.Roll;로 시작하여 그 위의 프로퍼티를 읽고 씁니다(roll-core.js:1-7에 명시됨). 서로를 알 필요가 없는 모듈은 대신 DOM 이벤트를 통해 통신합니다(예: roll-scroll.js는 roll:launch를 디스패치하고, roll-contact-sheet.js는 수신 — roll-scroll.js:35-37 참조).
HTML의 스크립트 순서가 의존성 그래프입니다. v4-d-hybrid.html:429-437에서:
roll-core.js → roll-shader.js → roll-field.js → roll-audio.js → roll-cursor.js
→ vendor/lenis.min.js → roll-scroll.js → roll-contact-sheet.js → roll-eggs.js
불변 규칙: 모듈이 Roll에서 읽는 것을 먼저 확인하고 모든 프로듀서가 더 일찍 로드됨을 확인하지 않고서는 <script>를 절대 재정렬하거나 삽입하지 마십시오. 당신을 구원할 임포트 시스템은 없습니다. 재정렬은 조용히 실패하거나 로드 시점에 오류를 던집니다.
grep -n "Roll\.\w*" design-demos/js/<module>.js # 읽고 쓰는 것
GSAP은 의도적으로 그 체인에 없습니다 — 불변 규칙 2 참조.
모듈 맵 (2026-07-22 기준)
| 파일 | 하나의 관심사 | 라인 수 |
|---|---|---|
roll-core.js | 네임스페이스, 공유 상태, 개발-판 엔진, upgradeMotion | 192 |
roll-scroll.js | 척추: 진입, 스크롤 안무, HUD, 메인 rAF(requestAnimationFrame) 루프, Lenis | 226 |
roll-field.js | 배경 캔버스 필드 | 136 |
roll-shader.js | WebGL 그레인/라이트 셰이더 + canvas-2D 폴백 | 133 |
roll-eggs.js | 숨겨진 성격 트리거(속삭임 수준 전용) | 83 |
roll-audio.js | 스크롤 속도에 연결된 조용한 앰비언트 사운드 | 79 |
roll-contact-sheet.js | 런치 시 연락처 시트 공개 | 55 |
roll-cursor.js | 사용자 정의 커서(데스크톱 전용, GSAP 게이팅됨) | 36 |
새로운 인터랙티브 기능? 그것은 새로운 roll-*.js 파일(또는 해당 관심사를 소유한 모듈의 명확히 구분된 섹션)이며, 의존성 위치의 스크립트 체인에 추가됩니다 — HTML에 인라인 <script>가 아닙니다.
불변 규칙 (코드에서 검증됨 — 참으로 유지)
- 모든 모션 모듈은 reduced motion 게이트를 통과합니다.
Roll.reduced = matchMedia('(prefers-reduced-motion: reduce)').matches(roll-core.js:9). 모든 모듈이 이를 참고합니다: 셰이더는 WebGL을 건너뜀(roll-shader.js:90,124), 필드는 rAF 루프를 건너뜀(roll-field.js:125,135), 연락처 시트는 조기 반환(roll-contact-sheet.js:12), 스크롤은 hold-to-develop 진입과 Lenis를 건너뜀(roll-scroll.js:39-40,152), CSS는 clip-path 전환을 해제함(v4-d-hybrid.html:293-295). 새로운 모션은 반드시Roll.reduced(JS)를 확인하거나 reduced motion 미디어 쿼리(CSS) 아래에 위치해야 합니다. - GSAP은 점진적 향상이며, 절대 의존성이 아닙니다. CDN에서 SRI 해시와 함께
async로 로드됩니다(v4-d-hybrid.html:440). 로드 시Roll.upgradeMotion()을 호출하며, 이는Roll.enhanced를 설정하고Roll.onMotionReady에 등록된 콜백을 실행합니다(roll-core.js:10-15,186-191). GSAP이 절대 도착하지 않더라도 사이트는 완전히 기능해야 합니다 — 커서, 히어로 타입, 스큐는 단순히 기준선에 남습니다(roll-cursor.js:34-35,roll-scroll.js:32-33,89,196). GSAP 의존 코드는Roll.onMotionReady(fn)으로 등록하십시오. 절대gsap.*을 조건 없이 호출하지 마십시오. - 위험한 시각적 점유는 방어적으로 래핑됩니다 — 설정뿐만 아니라 반복 경로에서도. 사용자 정의 커서는 세 계층에서 보호되며, 각 계층에는
Roll.cursorUpdate를 null로 설정하고.custom-cursor(OS 커서를 숨김)를 제거하는 복원 catch가 있습니다: 설정(roll-cursor.js:9-32, catch at 32), 이벤트별 핸들러 본문(roll-cursor.js:17-20),pointermove호출 사이트(roll-core.js:26-29). 어느 지점에서든 실패하면 — 설정 시점이든 이후의 mousemove든 — OS 커서를 복원합니다(2026-07-22 수정됨; 파일 헤더의 fail-safe 계약,roll-cursor.js:1-5). 네이티브 어포던스를 숨기거나 대체하는 모든 것에 이 패턴을 적용할 때는 설정뿐만 아니라 반복 경로도 보호하십시오. - clip-path 공개는 내부 래퍼에서 이루어지며, IntersectionObserver가 감시하는 요소에서는 절대 이루어지지 않습니다 ("concept-C 수정" — v4 concept C,
v4-c-ch001.html에서 명명되었으며, v4-d로 이어지기 전에 시작됨). IO는.panel을 감시합니다. clip-path는.panel-inner를 애니메이션합니다(v4-d-hybrid.html:121-122,185-186). 근거: 관찰된 요소를 클리핑하면 교차 지오메트리가 변경되어 공개와 트리거가 충돌합니다. - 참여 기반 시각 효과는 페이지 로드가 아닌 자체 가시성에 게이트됩니다. 기능 판(plate)은 해당 패널이
.in을 가질 때만 개발됩니다(roll-scroll.js:204-219). 코드 주석은 원래 버그를 기록합니다: 판이 아무도 스크롤하지 않았는데 해소를 마쳐 "참여할수록 발전하는" 상호작용을 조용히 무산시켰습니다.v4-d-hybrid.html의 헤드 주석은 이 수정을 기술 감사에서 가장 높은 우선순위로 부릅니다. - WebGL은 명시적이며 폴백에 안전합니다. 컨텍스트 생성 시 non-premultiplied 알파(
roll-shader.js:17), 매 프레임마다 명시적gl.clear(roll-shader.js:95), 2D 컨텍스트는 WebGL이 기회를 가진 후에만 획득 — 캔버스는 항상 하나의 컨텍스트 타입만 가질 수 있음(roll-shader.js:9-12),webglcontextlost는 렌더 루프가 오류를 던지는 대신 조용히 실패합니다(roll-shader.js:129-132). 호출자는 단일Roll.shader.render(t)진입점을 사용하며 GL인지 2D인지 알 필요가 없습니다. - 스크롤은 Lenis 아래에서도 네이티브 상태를 유지합니다. Lenis는 벤더링됩니다(
js/vendor/lenis.min.js, CDN 아님),!Roll.reduced일 때만 인스턴스화되며window.scrollY를 유효하게 유지합니다 — 느낌 업그레이드이지 스크롤 하이재킹이 아닙니다(roll-scroll.js:148-152,167). 렌더 루프는 이전과 같이scrollY를 읽습니다.
단일 파일 데모 철학 + 디자인 토큰
각 디자인 변형은 하나의 자체 포함 HTML 파일(인라인 CSS, 공유 js/ 모듈)이며, 변형을 전체 아티팩트로서 diff, 스크린샷, A/B 테스트할 수 있습니다 — pryne-design-direction-decision 참조. 각 파일의 긴 헤드 주석은 변형이 무엇인지와 어떤 감사 결과를 담고 있는지 기록합니다(편집 전에 읽으십시오; 그것이 파일의 로컬 변경 로그입니다).
모든 색상, 간격, 이징은 :root의 CSS 커스텀 프로퍼티입니다(v4-d-hybrid.html:50-63):
:root {
--bg: #0a0a0b; --accent: #d2232e; --ink: #f4f3f1; --dim: #86868c;
--pad-x: clamp(1.25rem, 6vw, 6rem);
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--ease-snap: cubic-bezier(0.16, 1, 0.3, 1);
}
Pryne의 글로벌 규칙에 따라: 토큰이 존재하는 곳에 색상/크기를 하드코딩하지 마십시오. 유동 값은 clamp()를 사용합니다. 새로운 모션은 --ease-out/--ease-snap을 재사용해야 하며, pryne-motion-reference에서 달리 명시하지 않는 한 — 빌드당 하나의 이징 언어.
체크리스트: 모듈 추가
- 새
roll-*.js(또는 올바른 기존 소유자 모듈), 최상위 IIFE,const Roll = window.Roll; -
<script>는Roll에서 읽는 모든 것 뒤, 그것을 읽는 모든 것 앞에 삽입 -
Roll.reduced확인; GSAP 사용은Roll.onMotionReady내부에서만 - 네이티브 어포던스를 숨기는 모든 것은 try/catch로 래핑하고 복원 catch 포함
- 참여 기반 시각 효과는 가시성 게이팅됨; IO 대상 ≠ clip-path 대상
- 하드코딩된 값 대신 토큰 사용
- 그런 다음 pryne-validation-and-qa에 따라 검증
알려진 약점 (공개, 명확히 명시됨)
- 빌드 단계 없음: 축소 없음, 번들링 없음; 데모에는 적합하지만 프로덕션 배포에는 해결되지 않음.
- 폰트가 Google Fonts CDN에서 로드됨(
v4-d-hybrid.html:47-48) 프로젝트의 벤더 우선 의도에도 불구하고(자체 호스팅은design-demos/DECISIONS.md에서 Lenis 항목 내부에서만 간략히 언급됨; 결정 기록 자체가 파일보다 앞서며 아직 수행되지 않음). GSAP도 여전히 CDN(고정 + SRI). 어느 하나를 "수정"하기 전에 pryne-change-control을 참조하십시오. - 자동화된 테스트 없음 — 검증은 브라우저 기반 증거만으로 이루어짐(pryne-validation-and-qa).
- 플레이스홀더 콘텐츠: 사례 연구/바이오는 최종본이 아님; 마치 그런 것처럼 카피를 다듬지 마십시오.
출처 및 유지 관리
scripts/verify.sh(이 스킬의 디렉토리 내)를 실행하여 아래 모든 사항을 한 번에 확인하십시오.
커밋 457868a, 2026-07-22의 Portfolio (test) 저장소에 기반합니다. 변동 가능한 사실을 신뢰하기 전에 재확인하십시오:
- 스크립트 순서:
grep -n "<script" design-demos/v4-d-hybrid.html - 라인 수 / 모듈 목록:
wc -l design-demos/js/*.js - Reduced motion 커버리지:
grep -rn "Roll.reduced\|prefers-reduced-motion" design-demos/ - GSAP이 여전히 CDN+SRI, 폰트가 여전히 CDN:
grep -n "cdn.jsdelivr\|fonts.googleapis" design-demos/v4-d-hybrid.html - Lenis가 여전히 벤더링됨:
ls design-demos/js/vendor/ - 활성 빌드가 여전히 v4-d:
design-demos/DECISIONS.md및 최신git log --oneline확인