# reference-method.md — 레퍼런스 해체와 합성

레퍼런스를 **원리로 분해**해서 **다른 결과물로 재조합**하는 절차다.

**원칙 한 줄**: 레퍼런스를 언어로 설명했을 때 그 설명이 여러 결과물로 구현될 수 있으면 원리다. 하나로만 이어지면 표현이다. **원리만 가져간다.**

| 가져간다 (원리) | 안 가져간다 (표현) |
|---|---|
| 그리드 비율 (5:7 비대칭) | 섹션 배치 그대로 |
| 타입 스케일 비율 (1.333) | 폰트 조합 그대로 |
| 색의 역할 배분 (60/30/10) | 색상값 |
| 여백 리듬 (섹션 120 / 요소 24) | 섹션 순서 |
| 모션의 의도 (스크롤=시선 유도) | 애니메이션 시퀀스 |

---

## 1. 3-소스 합성 규칙

레퍼런스 1개는 모방, 2개는 절충, **3개부터 합성**이다. 단 **층위를 나눠서** 고른다.

| 슬롯 | 가져올 것 | 어디서 |
|---|---|---|
| **R1 · 구조** | 그리드, 섹션 순서, 정보 위계, 여백 리듬 | 같은 업종·같은 목적 |
| **R2 · 톤** | 색 역할, 타입 페어링, 형태 언어, 질감 | **반드시 다른 업종** (또는 웹이 아닌 것) |
| **R3 · 디테일** | 모션 1개, 호버 1개, 전환 1개 | Codrops / Eyecandy / Design Spells |

**R2가 R1과 같은 업종이면 합성이 아니다.**
SaaS 구조 + SaaS 톤 = 또 하나의 SaaS 슬롭.
SaaS 구조 + 독립 출판사 톤 = 차별화.

**충돌 해결 순위**
1. 가독성·접근성이 언제나 이긴다. R2의 톤이 대비를 깨면 톤을 조정한다.
2. R1 구조가 R3 모션을 이긴다. 모션 때문에 구조를 바꾸지 않는다.
3. 브리프가 모든 레퍼런스를 이긴다. 충돌하면 레퍼런스를 버린다.
4. **한 축은 한 소스에서만.** 그리드는 R1에서만, 팔레트는 R2에서만. 섞으면 이유 없는 절충이 된다.

---

## 2. 6축 해체 프레임워크

각 레퍼런스를 6축으로 뜯는다. **형용사는 기록이 아니다. 숫자나 규칙으로 적는다.**

**R3 예외**: R3는 사이트가 아니라 기법이다. 축 5(모션 언어) 하나만 채우고 나머지는 비운다.
대신 R3에는 **구현 제약**(브라우저 지원, 성능 비용, 보간 조건)을 반드시 적는다.

### 축 1 — 레이아웃 그리드
컬럼 수·거터 / 콘텐츠 최대폭과 뷰포트 비 / 대칭·비대칭(비율) / 그리드를 깨는 요소 / 본문 한 줄 글자 수(영문 45–75, 국문 25–40)
→ *"무엇을 정렬시키고 무엇을 일부러 어긋나게 했나?"*

### 축 2 — 타입 스케일
실제 사용된 크기 목록 / 그 사이 비율(1.25 또는 1.333이 표준) / 단계 수(3–5가 건강, 8+ 는 통제 실패) / 패밀리 수(2개 표준) / 굵기 대비 / line-height / letter-spacing
→ *"최대 글자와 본문의 비는 몇 배인가?"* (4배 미만이면 위계 약함)

### 축 3 — 컬러 역할
색상값이 아니라 **역할**로 적는다. surface 단계 수 / text 3단계 유무 / accent 면적 %(10% 이하가 정상) / border 존재 여부 / state 색 분리 여부
→ *"강조색을 지우면 여전히 읽히는가?"* (읽혀야 정상)

### 축 4 — 여백 리듬
기본 단위(4 또는 8px) / 섹션 간 수직 여백(80–160px) / 요소 간 : 섹션 간 비율(1:4~1:6) / 여백 값 종류 수(4–6종이 건강) / 좌우 패딩
→ *"가장 큰 빈 공간은 어디이고 왜 거기인가?"*

### 축 5 — 모션 언어
트리거(로드/스크롤/호버/커서) / duration / easing / 정보 전달인가 장식인가 / reduced-motion 대응
→ *"모션을 전부 끄면 작동하는가?"* (작동해야 정상)

**어떤 CSS 속성을 애니메이션하는지 반드시 적어라.** 하드 게이트 #8은 `transform`·`opacity` 만 허용한다.
`stroke-dashoffset`·`height`·`clip-path`·`filter` 로 만든 기법을 R3로 골라놓고 4단계에서 발견하면
그때는 이미 늦다. **1단계에서 걸러라.** 그 기법이 좋다면 `motion.md` 의 우회법 표에서
같은 인상을 `transform`/`opacity` 로 내는 방법을 찾은 뒤에 채택해라.

### 축 6 — 시선 흐름
첫 진입점 / 2·3번째 / 비즈니스 우선순위와 일치 여부 / Z·F·수직 낙하 중 무엇인가 / CTA가 경로 위에 있나

**회색조 위계 서술** (에이전트용 squint test 대체) — 자기가 만들 화면에 대해 문장으로 쓴다:
> 색을 전부 제거했을 때 가장 강한 덩어리는 ___, 두 번째는 ___, 세 번째는 ___.
> 이 순서는 비즈니스 우선순위 [1위 ___ / 2위 ___ / 3위 ___]와 일치한다 / 하지 않는다.

일치하지 않으면 크기·굵기·대비를 조정한다. **색으로 해결하지 마라.**

---

## 3. 실전 절차 체크리스트

- [ ] **1. 브리프를 한 문장으로 압축한다.** `[대상]이 [행동]하게. 느낌은 [형용사 2개].` 이 문장 없이 수집을 시작하지 않는다.
- [ ] **2. galleries.md 라우팅 표에서 R1/R2/R3 갤러리를 정한다.** R2가 R1과 다른 업종인지 확인한다.
- [ ] **3. 각 갤러리에서 원본 사이트 URL을 1개씩 확보한다.** 하위 페이지가 필요하면 **인덱스에서 `href`를 뽑아라. slug를 추측하지 마라** — 추측한 URL은 404가 난다.
- [ ] **3-b. 그 URL이 정본인지 검증한다.** 사용자가 특정 제품·브랜드를 지목했고 후보 도메인이 여럿이면 필수다. **HTTP 200은 살아있다는 뜻이 아니다.**
  - `curl -sS -o /dev/null -w "%{url_effective} %{http_code} %{size_download}\n" -L <url>` 로 **최종 URL·본문 크기**를 본다. 본문이 **1KB 미만이면 파킹 도메인을 의심하라** — 전형적 파킹 페이지는 `/lander` 로 보내는 JS 한 줄뿐이다.
  - 후보들의 본문이 **바이트 단위로 동일**하면 전부 같은 파킹 서비스다.
  - 정본 판정 근거는 **콘텐츠 안에서** 찾아라: 저장소 링크, 설치 명령에 적힌 패키지·스킬 이름, 소유자 계정, 문서 제목이 로컬 파일과 일치하는지.
- [ ] **4. 원본 사이트 3개를 §4 템플릿으로 WebFetch 한다.** 갤러리 페이지가 아니라 원본이다. → 축 6·정보 위계·카피 톤을 얻는다.
- [ ] **5. §5로 computed style을 추출한다.** → 축 1·2·3·4를 얻는다. **이 단계를 건너뛰면 6축의 4개가 빈칸으로 남는다.**
- [ ] **6. 텍스트 소스로 폰트 이름·무료 대체를 확정한다.** (Typewolf / Happy Hues / Eyecandy — galleries.md §3)
- [ ] **7. 6축 해체 시트를 작성한다.** 숫자로. 6축에 안 들어가는 관측은 `6축 밖의 관측`에 적는다.
- [ ] **8. 합성한다.** 축마다 어느 소스에서 가져왔는지 명시.
- [ ] **9. 의도적 변형을 각 레퍼런스마다 1개씩 넣고 이유를 적는다.**
- [ ] **10. antipatterns.md를 훑고 걸린 항목이 없는지 확인한다.**

---

## 4. WebFetch 프롬프트 템플릿 (그대로 복사해서 쓴다)

### 4-A. 원본 사이트 구조 분석 — 가장 자주 쓴다

> 이 페이지의 구조를 분석해줘. 다음을 순서대로 답해라.
> 1. 상단부터 순서대로 섹션 목록. 각 섹션의 역할을 한 단어로 붙여라.
> 2. h1의 정확한 문장, 그리고 h2 전체 목록.
> 3. CTA 문구 전부와 각각이 페이지 어디에 있는지.
> 4. 내비게이션 항목 목록.
> 5. 이 페이지가 방문자에게 시키려는 단 하나의 행동.
> 6. 카피의 톤 — 평균 문장 길이, 1인칭/2인칭 사용, 전문용어 밀도.
> 7. 숫자나 지표가 사용된 위치와 그 값.
> 추측하지 말고 페이지에 실제로 있는 것만 답해라. 없으면 "없음"이라고 써라.

### 4-B. 폰트 확인 (Typewolf 등)

> 이 페이지에서 언급된 폰트 이름을 전부 나열해라. 각각에 대해 헤드라인용인지 본문용인지, 유료인지 무료인지, 제시된 무료 대체 폰트가 있으면 그 이름까지 적어라. 페이지에 없는 정보는 지어내지 마라.

### 4-C. 모션 기법 확인 (Eyecandy · Codrops)

> 이 페이지에서 설명하는 애니메이션 기법의 이름, 그 기법이 시각적으로 무엇을 하는지, 구현에 쓰인 CSS 속성이나 JS 라이브러리를 나열해라. 코드 예시가 있으면 핵심 부분을 그대로 옮겨라.

### 4-D. 디자인 시스템 토큰 확보 (DesignSystems.one · 공개 시스템 문서)

> 이 문서에서 다음을 추출해라. 값이 명시된 것만 적고 없으면 "미공개"라고 써라.
> 1. 컬러 토큰 이름과 값 (전체 스케일)
> 2. 타입 스케일 — 크기 목록, line-height, 굵기
> 3. 여백 스케일 — 기본 단위와 전체 단계
> 4. border-radius 값 목록
> 5. 그림자 정의
> 6. 모션 duration과 easing

### 4-E. 리디자인 — 현행 사이트 감사

> 이 페이지를 감사해줘. 다음을 답해라.
> 1. 섹션 순서와 각 섹션이 실제로 전달하는 정보
> 2. 정보 위계상 가장 강조된 것과, 비즈니스적으로 가장 중요해 보이는 것 — 둘이 일치하는가
> 3. 카피에서 구체적 사실(숫자, 고유명사, 조건)이 들어간 문장과 그렇지 않은 문장의 비율
> 4. 내비게이션 구조와 항목 수
> 5. 페이지에서 명백히 불필요하거나 중복인 섹션
> 추측하지 말고 페이지에 있는 것만 답해라.

---

## 5. computed style 추출 (축 1·2·3·4를 얻는 유일한 방법)

WebFetch는 마크다운으로 변환하면서 CSS를 버린다. **그리드·타입스케일·컬러·여백은 WebFetch로 절대 나오지 않는다.**
브라우저 도구(Chrome DevTools MCP / Playwright)로 원본 사이트를 열고 아래를 실행한다.

```js
() => {
  const cs = (el, p) => getComputedStyle(el).getPropertyValue(p);
  const vis = el => { const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; };
  const all = [...document.querySelectorAll('body *')].filter(vis);
  const textEls = all.filter(el => [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 1));
  const tally = a => { const m={}; a.forEach(v=>{m[v]=(m[v]||0)+1;}); return Object.entries(m).sort((x,y)=>y[1]-x[1]).slice(0,12); };
  const h1 = document.querySelector('h1');
  const p  = [...document.querySelectorAll('p')].filter(vis).find(el => el.innerText.trim().length > 200);
  return {
    // 반드시 첫 줄에 둔다. 페이지가 실행 중 이탈하면 엉뚱한 사이트의 값이 돌아온다.
    url: location.href,
    title: document.title,
    viewport: innerWidth + 'x' + innerHeight,
    heading: h1 && { size: cs(h1,'font-size'), lh: cs(h1,'line-height'), ls: cs(h1,'letter-spacing'),
                     weight: cs(h1,'font-weight'), family: cs(h1,'font-family').split(',')[0] },
    body: p && { size: cs(p,'font-size'), lh: cs(p,'line-height'), color: cs(p,'color'),
                 family: cs(p,'font-family').split(',')[0], widthPx: Math.round(p.getBoundingClientRect().width) },
    fontSizes:  tally(textEls.map(el => cs(el,'font-size'))),
    families:   tally(textEls.map(el => cs(el,'font-family').split(',')[0].replace(/["']/g,''))),
    textColors: tally(textEls.map(el => cs(el,'color'))),
    backgrounds:tally(all.map(el => cs(el,'background-color')).filter(c => c !== 'rgba(0, 0, 0, 0)')),
    radii:      tally(all.map(el => cs(el,'border-radius')).filter(r => r !== '0px')),
    widths:     tally(all.filter(el => el.getBoundingClientRect().width > 500)
                         .map(el => Math.round(el.getBoundingClientRect().width) + 'px')),
    sectionPadding: [...new Set([...document.querySelectorAll('section, main > div')].filter(vis).slice(0,20)
                       .map(el => cs(el,'padding-top') + ' / ' + cs(el,'padding-bottom')))]
  };
}
```

**결과 읽는 법**

> **먼저 `url` 을 봐라. 열려고 한 사이트가 아니면 나머지 값은 전부 버려라.**
> 페이지가 실행 중 이탈하거나 리다이렉트되면 스크립트는 **다른 사이트의 값을 조용히 반환한다.**
> 실측에서 2회 연속 발생했다. 이건 실패가 아니라 **오염**이다 — 숫자가 6축 시트에 들어가고,
> 3단계 토큰의 근거가 되고, `design.md` 에 남고, 어디서도 검출되지 않는다.

- `fontSizes` 상위 항목 = 타입 스케일. 최다 크기가 본문이다. 최대÷본문이 **4배 미만이면 위계가 약한 레퍼런스**다.
- `textColors` 상위 3~4개 = 텍스트 위계 단계. 3단계 이상이면 잘 설계된 것.
- `backgrounds` 최다값 = 지배 배경. 등장 횟수가 적은 채도 높은 색이 **강조색**이고, 그 횟수가 곧 면적 감각이다.
- `radii` 종류 수 = 형태 어휘. 1~3개면 통제된 것, 그 이상이면 우연히 결정된 것.
- `widths` 최다값 = 컨테이너 폭. `body.widthPx` = 텍스트 컬럼 폭.

브라우저 도구를 못 쓰는 환경이면 **6축 중 4개가 빈칸이라는 사실을 산출물에 명시**하고 넘어간다. 채운 척하지 마라.

---

## 6. 하지 말아야 할 것

- **갤러리 홈페이지만 fetch하고 "레퍼런스를 봤다"고 말하기.** 갤러리 홈은 카드 이미지뿐이라 정보가 0이다. **반드시 원본 사이트를 연다.**
- 레퍼런스 이름만 나열하고 넘어가기. 6축 수치가 없으면 참고한 게 아니다.
- **WebFetch만 하고 6축을 채웠다고 하기.** WebFetch는 축 6과 카피 톤만 준다. 나머지는 §5가 필요하다.
- 하위 페이지 URL의 slug를 추측하기. 인덱스에서 `href`를 뽑아라.
- R1·R2·R3를 같은 갤러리에서 고르기.
- 6축 시트에 "세련됐다", "깔끔하다" 같은 형용사 적기.

---

## 6. 산출물 형식 (필수)

레퍼런스 조사 단계는 **아래 블록을 출력하기 전까지 완료되지 않는다.** 다음 단계로 넘어가지 마라.

```markdown
## 브리프
[대상]이 [행동]하게. 느낌은 [형용사 2개].

## R1 · 구조 — <이름> <원본 URL>
- 가져올 축: 그리드, 여백 리듬
- 그리드: 최대폭 1200 / 비대칭 7:5 / 거터 32
- 여백: 8px 단위 / 섹션 간 120 / 요소 간 24 (1:5)
- 6축 밖의 관측: (있으면) 카피 규범, 증거 제시 방식, 반복 노출 규칙 등
- 변형: 최대폭을 1120으로 축소 — 본문 한 줄이 국문 40자를 넘었기 때문

## R2 · 톤 — <이름> <원본 URL>   [업종: R1과 다름 ✓]
- 가져올 축: 컬러 역할, 타입 스케일
- 컬러: 배경 1단계 / 텍스트 3단계 / 강조 1색 면적 6%
- 타입: 세리프 디스플레이 + 그로테스크 본문, 비율 1.333, 최대/본문 4.2배
- 6축 밖의 관측: (있으면) 신뢰를 만드는 방식, 제품을 보여주는 방식
- 변형: 비율을 1.25로 조정 — 정보량이 많아 중간 단계가 필요

## R3 · 디테일 — <이름> <출처 URL>
- 가져올 축: 모션 언어 (R3는 이 축만 채운다)
- 모션: 스크롤 진입 시 8px 상승 + 페이드, 320ms, ease-out
- 구현 제약: 브라우저 지원 / JS 비용 / 보간 조건
- 변형: 280ms로 단축 — 페이지가 길어 반복 노출이 잦음

## 회색조 위계
가장 강한 덩어리는 ___, 두 번째 ___, 세 번째 ___.
비즈니스 우선순위 [1 ___ / 2 ___ / 3 ___]와 일치한다.

## 안 할 것
- (antipatterns.md에서 이 브리프에 특히 위험한 항목 3개 이상 명시)
```

**검증 조건** — 하나라도 빠지면 되돌아간다.
- R1/R2/R3 각각 **원본 사이트 URL**이 있다 (갤러리 URL 아님)
- R2의 업종이 R1과 다르다
- 각 슬롯에 **가져올 축**이 명시되어 있다
- 각 슬롯에 **변형 1개와 그 이유**가 있다
- 6축 값에 숫자가 들어 있다 — **§5를 실행했거나, 못 했다면 어느 축이 빈칸인지 명시했다**
- R3에 **구현 제약**(지원 범위·JS 비용)이 적혀 있다
- 회색조 위계가 비즈니스 우선순위와 일치한다

> 근거: research/references/02-methodology.md (조사일 2026-08-20)
