designpaca/packages/skill/references/preflight.md
Yun Chan 9648d1d82a 소개 페이지를 스킬 전체 경로로 다시 만들고, 배운 것을 스킬에 반영
방향을 글래스로 바꿀 때 2단계 재결정을 하고도 전체 경로로 올리지 않았다.
레퍼런스 없이 재질만 갈아끼운 결과가 스위스 미니멀 뼈대에 유리를 바른
페이지였고, design.md 는 초판 결정을 그대로 담고 있었다. 0단계부터 다시 돌았다.

1단계 — 레퍼런스 3개를 computed style 까지 실측
  R1 zed.dev      컨테이너 1120px 과 전체폭이 교대 · radius 2px 621회
  R2 lusion.co    3D 는 액자 안, 텍스트는 액자 밖 · 배경 흰색 · h1 36px
  R3 Codrops      DOM 이 구조를 소유하고 WebGL 은 uniform 만 만진다

2단계 — 프리셋 swiss-minimal → dark-instrument, 컨셉 "검사대 위의 물체"
        유리는 액자에만 남긴다(헤더 · 검사대 · 명령창)

4단계 — 히어로 재구성. 캔버스를 배경에서 빼 액자 안으로, 텍스트는 밖으로
        판독기가 통과한 게이트를 표시한다(값이 바뀔 때만 DOM 을 쓴다)
        파이프라인을 2열로 열어 625px → 1113px

5단계에서 실제로 걸린 것
  게이트 3  radius 4종 → 2종 (3px / 14px, pill 제거)
  게이트 1  base.css 토큰 밖 rgb() 7곳 → 토큰
  게이트 7  헤더 English 2.59:1 → ink 로 상향
  카운트    히어로 h1 4줄 → 2줄
  셰이더    판이 정면이라 프레넬이 구조적으로 0. 법선을 굽히고 UV 로 테두리
  셰이더    gl_FragCoord 를 렌더 타겟 크기로 나눠 UV 가 1 을 넘고 있었다
  수치      페이지가 실은 "타입 단계 5단계"가 실측 9종이었다. lang 속성이
            스케일을 재정의해 링크 하나가 16px 로 새고 있었다
  측정      대비 스크립트가 반투명 배경을 불투명으로 계산했다. 알파 합성으로 교정

스킬 반영
  three.md    §0-A 배치 원리(3D 는 액자 안) · 프레넬 0 함정 · gl_FragCoord 함정
  preflight.md 대비 측정 시 알파 합성. 검사 도구가 틀리면 통과도 의미가 없다

최종: 하드 게이트 12/12, 대비 실패 0, 320px 오버플로 0, 테스트 22개 통과
2026-08-20 13:33:28 +09:00

8.4 KiB
Raw Permalink Blame History

preflight — 출시 전 감사

5단계에서 읽는다. 자기 결과물을 남의 것처럼 본다. 통과 못 한 항목은 4단계로 돌아가 고친다.

슬롭 지문 검출과 자가 채점표는 references/antipatterns.md 에 있다. 이 문서는 그 외 전부를 다룬다.


0. 먼저 기계로 검사한다

무엇에 돌리는가부터 정해라.

  • 정적 HTML/CSS 프로젝트 → 소스 그대로
  • React·Vue·Astro 등 프레임워크 프로젝트 → 프로덕션 빌드 결과에 돌린다. 소스만 보면 렌더된 DOM 이 없어 대비·가로 스크롤·LCP 를 잴 수 없다. pnpm build && pnpm preview 로 띄우고 그 URL 을 검사해라
  • 소스만 보고 "측정 불가"로 남기지 마라. 미측정은 통과가 아니다

grep 검사는 주석과 문자열을 제외해라. "100vh 금지"라고 쓴 주석이 게이트 #9 에 걸리는 오탐이 실제로 나왔다.

사람 눈보다 정확하고 빠르다. 셋 다 돌려라.

A. 이펙트 전부 끄기

/* 임시로 주입해서 확인한다 */
*, *::before, *::after {
  filter: none !important;
  backdrop-filter: none !important;
  animation: none !important;
  transition: none !important;
  transform: none !important;
}
canvas { display: none !important; }

이 상태에서 페이지가 여전히 읽히고 구조가 살아 있어야 한다. 무너지면 이펙트가 구조를 대신하고 있는 것이다. 4-1로 돌아가라.

B. 슬롭 지문 grep

references/antipatterns.md 의 grep 목록을 소스에 돌린다. 검출률 상위 항목(보라 CTA, 전체 대문자 헤드라인, 번호 매긴 1·2·3 단계)부터.

C. 카피 경쟁사 치환

제품명을 경쟁사 이름으로 바꿔 읽는다. 문장이 그대로 성립하면 그 카피는 아무것도 말하지 않았다. 히어로 문구부터 검사한다.


1. 접근성 — 타협 없음

항목 확인 방법 기준
키보드 도달 Tab 만으로 순회 모든 인터랙티브 요소에 도달, 순서가 시각 순서와 일치
포커스 표시 Tab 하며 눈으로 항상 보인다. outline: none 만 있고 대체가 없으면 실패
대비 측정 도구 (아래 주의) 본문 4.5:1, 큰 글자·UI 3:1
이미지 대체텍스트 소스 확인 의미 있는 이미지에 alt, 장식은 alt=""
폼 레이블 소스 확인 모든 입력에 연결된 <label>. placeholder 는 레이블이 아니다
제목 구조 개요 확인 h1 하나, 건너뛰지 않음
모션 감소 prefers-reduced-motion 켜고 확인 전부 끄는 게 정답이 아니다 — 위치 이동·시차·자동재생을 죽이고, 페이드·상태 변화는 남긴다
자동재생 확인 5초 이상 반복되는 애니메이션에는 정지 수단이 있다
캔버스/WebGL 실패 JS 끄고 확인 콘텐츠가 여전히 보인다

대비를 스크립트로 잴 때 — 반투명 배경을 불투명으로 계산하지 마라. getComputedStyle(el).backgroundColor 로 조상을 거슬러 올라가며 "투명이 아닌 첫 값"을 배경으로 쓰면, rgb(255 255 255 / 0.085) 같은 유리 틴트를 순백으로 취급한다. 흰 글자가 1.15:1 로 나온다. 알파가 1 미만인 층은 계속 쌓고, 아래에서 위로 합성한 뒤에 재라:

// 알파 < 1 이면 멈추지 말고 계속 쌓는다
const stack = []; let e = el;
while (e && e !== document.documentElement) {
  const p = parse(getComputedStyle(e).backgroundColor);
  if (p && p[3] > 0) { stack.push(p); if (p[3] >= 1) break; }
  e = e.parentElement;
}
let [r, g, b] = rootBg;
for (let i = stack.length - 1; i >= 0; i--) {
  const [sr, sg, sb, sa] = stack[i];
  r = sr*sa + r*(1-sa); g = sg*sa + g*(1-sa); b = sb*sa + b*(1-sa);
}

유리·오버레이·틴트를 쓰는 페이지에서는 이 차이가 통과와 실패를 뒤집는다. 검사 도구가 틀리면 통과도 실패도 의미가 없다. 실패가 나오면 값을 고치기 전에 측정을 먼저 의심해라.

스크린리더를 실제로 켜볼 수 없다면 최소한 소스의 읽기 순서를 확인해라. order·position: absolute 로 시각 순서를 바꿨다면 읽기 순서가 어긋난다.


2. 성능 — 3단계에서 정한 예산과 대조

예산 값은 tokens.md §5 를 보라. 여기서 다시 적지 않는다 — 두 곳에 적으면 갈라진다. 이 문서가 하는 일은 실측을 채우는 것이다.

항목 실측 판정
히어로까지 JS (gzip)
WebGL/3D 추가분
총 전송량 (첫 화면)
첫 인터랙션 (모바일 4G)
LCP
CLS
폰트 파일 수

측정하지 않았으면 통과가 아니다. 예산을 넘겼다면 둘 중 하나다: 이펙트를 빼거나, 예산을 올린 이유를 명시하거나.

폰트 (typography.md §8 이 전체 목록)

  • 폴백 메트릭을 보정했거나 font-display: optional 을 골랐다. 둘 다 안 했으면 폰트가 교체되는 순간 레이아웃이 튄다
  • preload 한 파일이 첫 화면에 실제로 쓰인다. crossorigin 이 있다
  • 수치를 나열하는 곳에 tabular-nums 가 있다
  • 한글 폰트를 명시했고 서브셋을 쓴다
  • 웹폰트 임베딩 라이선스를 확인해 design.md 에 적었다
  • 폰트를 못 받은 상태로 열어봤다. 그 상태로도 읽힌다

자주 걸리는 것

  • 폰트 서브셋 안 함 (한글 전체 폰트는 수 MB)
  • 첫 화면 밖 이미지에 loading="lazy" 누락
  • 3D/WebGL을 초기 번들에 포함 (뷰포트 진입 시 동적 import 해야 한다)
  • 애니메이션이 top/left/width 를 건드림 → transform 으로 바꿔라
  • will-change 남발 → 레이어가 늘어 오히려 느려진다

3. 반응형

  • 320px 폭에서 가로 스크롤 없음 (하드 게이트 #5 는 320~1920px 전 구간을 요구한다. 375px 만 보면 그 아래가 뚫린다)
  • 1920px 이상에서 콘텐츠가 늘어지지 않음(최대 폭 제한)
  • 중간 뷰포트(768~1024px)에서 레이아웃이 깨지지 않음 — 가장 자주 빠뜨리는 구간
  • 터치 타깃: 버튼·아이콘·카드 등 독립 컨트롤은 44×44px 이상(Apple/Google 권고)
  • 터치 타깃: 본문 안 인라인 텍스트 링크는 24×24px 이상(WCAG 2.5.8 AA). 여기에 44 를 요구하면 정상적인 내비 링크가 오탐된다
  • 호버로만 접근되는 기능 없음
  • 긴 단어/URL이 넘치지 않음 (overflow-wrap: anywhere)
  • 가로 모드에서 첫 화면이 잠기지 않음

4. 콘텐츠

  • 히어로가 무엇인지 말한다. 형용사만 있고 명사가 없으면 실패
  • 로렘입숨이 남아 있지 않다
  • 실제 콘텐츠 길이로 테스트했다 (짧은 제목만으로 맞춘 레이아웃은 긴 제목에서 깨진다)
  • 빈 상태·오류 상태·로딩 상태가 있다
  • 링크가 어디로 가는지 텍스트만 보고 알 수 있다 ("여기를 클릭" 금지)
  • 이미지가 없거나 실패했을 때 레이아웃이 무너지지 않는다(aspect-ratio 지정)

5. 디자인 자체

기계로 못 잡는 부분이다. 정직하게 답해라.

  1. 이 사이트를 다른 회사의 것으로 바꿔도 그대로 쓸 수 있는가? → 그렇다면 브리프에 맞춘 것이 없다
  2. 2단계에서 정한 한 문장 컨셉이 화면에서 보이는가? → 안 보이면 컨셉이 문서에만 있는 것이다
  3. 감수하기로 한 리스크가 실제로 들어갔는가? → 구현 중에 안전한 쪽으로 후퇴하지 않았는지
  4. 1단계 레퍼런스 R2(다른 업종의 톤)의 흔적이 있는가? → 없으면 결국 R1을 베낀 것이다
  5. 강조색이 하나인가?
  6. 여백이 의도적인가, 남은 것인가?

6. 최종 게이트

전부 통과해야 끝난다.

  • 0장 기계 검사 3종 통과
  • 접근성 표 전 항목 통과
  • 성능 예산 내 (또는 초과 이유 명시)
  • 반응형 체크리스트 통과
  • 콘텐츠 체크리스트 통과
  • 5장 여섯 질문에 정직하게 답했고, 실패 항목을 고쳤다
  • antipatterns.md 자가 채점표 통과

하나라도 실패하면 출시가 아니라 4단계 복귀다.

근거: research/references/04-ai-slop-signatures.md, research/motion/01-principles.md, research/three/04-performance.md (조사일 2026-08-20)