Claude 자유 디자인 계약
한국어 번역본
이 문서는 원문(영어)의 한국어 번역이다. 정본은 원문이며, 계약·게이트 판단이 갈리면 원문을 따른다. 원문: docs/process/claude-free-design-contract.md
Updated: 2026-07-23
이 프로젝트는 백엔드/기능 작업과 디자인 표면(surface)을 분리한다.
Claude Design이 전달한 표면이 시각 UI의 단일 진실 원천(source of truth)이다. 기능적 상태 전이와 프로덕션 동작은 여전히 props/컨트롤러/도메인 계약이 관장한다. Codex는 Claude의 표면을 제거된 바닐라 DOM 구조나 이전 CSS 시스템에 억지로 끼워 맞춰서는 안 된다.
현재 프런트엔드의 진실 원천은 src/react/다.
Codex가 소유하는 것:
- 인증/세션 메커니즘
- 데이터 라우팅과 서비스 액션
- Claude의 UI를 저장(persist)하고 복원(hydrate)하는 데 필요한 앱 상태
- 런타임 상호작용 의미론과 프로덕션 배선
- 제어 입력(controlled input), 검색/필터/IME 동작, 정규 ID(canonical ID), 내비게이션
- 운동 세션, 종목, 세트 계층
- Supabase 읽기/쓰기
- 회복, PR, 길드 계산
- 이벤트 커맨드와 렌더 데이터
- Claude 앱이 필요로 하는 백엔드/서비스 함수
- 기능 통합 및 회귀 테스트.
src/react/ui/**내부의 좁게 한정된 배선(binding)도 포함한다.
Claude가 소유하는 것:
- 시각적 흐름과 화면 연출(choreography)
- 레이아웃
- 시각적 위계
- 여백
- 타이포그래피
- 컴포넌트 형태
- 모바일·데스크톱 구성
- CSS 클래스 이름
- DOM 중첩
- 반응형 동작
- 디자인 표면의 프레임워크 선택. Claude가 React 프로토타입을 전달하면 현재 앱 표면은 React여야 한다.
R&R: 시각 디자인 대 기능 통합
경계는 코드가 들어 있는 디렉터리만이 아니라 책임을 기준으로 정해진다. src/react/ui/**는 디자인 표면이지만, 그 표면 안의 프로덕션 동작은 여전히 Codex의 몫이다.
| 관심사 | Claude Design | Codex |
|---|---|---|
| 화면 표면 | 레이아웃, 마크업 구성, CSS, 타이포그래피, 여백, 반응형 구조, 애니메이션 | 승인된 표면을 보존한 채로 통합한다 |
| 시각 상태 | loading, pending, empty, error, selected, disabled 상태가 어떻게 보이는지 설계한다 | 그 상태가 언제 발생하는지 정의하고 앱/컨트롤러 상태로 구동한다 |
| 입력과 검색 | 컨트롤, 결과 목록, 포커스 처리, 시각적 피드백을 설계한다 | 제어 값, IME, 디바운스, 정규화, 매칭, 정규 ID, 결과 데이터를 소유한다 |
| 액션과 내비게이션 | 컨트롤이 어디에 나타나는지, 상호작용을 시각적으로 어떻게 전달하는지 설계한다 | 콜백 바인딩, 라우트 전이, 뒤로가기 동작, 동시성, 실패, 재시도, 낙관적 업데이트를 담당한다 |
| 데이터와 도메인 규칙 | 공급된 계약을 렌더한다. 픽스처 예시도 포함한다 | 인증, RPC, 저장, 복원, 병합, 검증, 계산, 계정 격리를 소유한다 |
| 테스트 | 시각 상태, 반응형 동작, 오버플로, 컨트롤이 문서화된 의도(intent)를 방출하는지 확인한다 | 의미론적 결과, 통합, 저장, 경쟁 조건, 회귀를 확인한다 |
Codex는 프로덕션 배선을 src/react/ui/** 바깥에서 완결할 수 없을 때 그 안에서 좁게 한정된 기능 통합 변경을 할 수 있다. 여기에는 합의된 prop을 받는 것, 기존 컨트롤을 콜백에 바인딩하는 것, 조합(composition)/키보드 이벤트를 전달하는 것, 중복된 비즈니스 로직을 제거하는 것, 그리고 계약이 요구하는 의미론적 마커·접근성 속성·타입을 추가하는 것이 포함된다. Codex는 이 예외를 이용해 레이아웃, 스타일링, 타이포그래피, 여백, 시각적 위계, 컴포넌트 구성을 재설계해서는 안 된다.
Claude는 합의된 props/콜백 또는 의미론적 마커를 보존하는 한 어떤 시각 구현이든 바꿀 수 있다. 디자인을 완성하기 위해 Claude가 실제 컨트롤러, 서비스, 저장소, 도메인 알고리즘을 구현할 필요는 없다.
프리뷰 경계
Claude의 프리뷰는 시각 및 상호작용 계약을 위한 하네스이지 두 번째 앱 런타임이 아니다. 승인된 모든 시각 상태를 보여주고 컨트롤이 기대되는 의도를 방출함을 증명하기 위해 픽스처, 로컬 표시 상태, 목(mock) 콜백을 사용할 수 있다.
2026-08-19부터 픽스처와 프리뷰는 Claude Design의 로컬 환경에만 존재한다. 이 레포지토리의 일부도, 어떤 전달물의 일부도 아니다. 동작의 전달 채널은 props 계약뿐이다 (docs/process/design-codex-transfer-contract.md).
- 프리뷰 전용 픽스처와 어댑터는 Claude Design의 로컬 환경에 머무르며, 이 레포지토리나 프로덕션 앱의 일부가 되는 일은 결코 없다.
- 목은
onSearchInput("바벨로우")를 기록할 수 있다. 그러나 그것이 프로덕션 검색 구현이 되어서는 안 된다. - 프리뷰나 화면 코드는 검색 매칭, 정규화(canonicalization), 카탈로그 병합, 저장, 인증, RPC 동작, 도메인 계산을 재현해서는 안 된다.
- 프리뷰 통과가 뜻하는 것은 표면이 렌더되고 계약을 방출한다는 것뿐이다. 종단 간 프로덕션 통합과 의미론적 테스트는 여전히 Codex의 소유다.
이 원칙 덕분에 Claude는 모든 프로덕션 기능을 시뮬레이션하도록 강요받지 않고도 모든 시각 상태를 시험할 수 있다. 프리뷰에 현실적인 결과가 필요하면, 실제 알고리즘을 화면에 복제하는 대신 Codex가 픽스처 출력이나 목 컨트롤러 계약을 공급한다.
핵심 규칙
Claude는 앱 표면을 밑바닥부터 교체할 수 있다.
Claude가 완결된 디자인 프로토타입이나 React 표면을 전달하면, 그 산출물이 이전의 앱 HTML, CSS, 시각 흐름 가정보다 우선한다. 프리뷰 목은 프로덕션 기능 계약을 대체하지 않는다. Codex는 인터페이스를 재설계하지 않은 채 데이터, 인증, 저장, 계산, 이벤트 배선을 덧붙인다.
의미론적 data-lg-* 계약은 Codex가 DOM 수준 배선을 해야 할 때 유용하다. 이것을 Claude의 레이아웃, 시각 구조, 선택한 컴포넌트 모델을 제약하는 데 사용해서는 안 된다.
현재 앱 HTML, 현재 앱 CSS, 스크린샷, 이전 프로토타입은 디자인 제약이 아니다. Codex가 통합 과정에서 기존 동작의 위치를 찾는 데 쓸 수는 있지만, Claude는 그것을 필수 레이아웃, 컴포넌트 위계, 여백 체계, 시각 방향으로 취급해서는 안 된다.
이 문서의 어떤 HTML 조각이든 마커 목록일 뿐이다. 어떤 의미론적 속성이 존재하는지를 보여줄 뿐, 표시된 태그 이름, DOM 중첩, 순서, 래퍼 개수, 여백, 시각적 묶음을 요구하지 않는다.
지원 속성
data-lg-hook: 유일한 기능 요소 하나data-lg-list: 반복되는 기능 요소data-lg-action: 사용자 커맨드 트리거data-lg-field: 사용자 입력 필드data-lg-slot: 동적 렌더 대상data-lg-view: 뷰 또는 화면 루트data-lg-panel: 페이지 레지스트리가 제어하는 대시보드/모바일 패널data-lg-value: 태그에 의존하지 않는 필드 컴포넌트의 현재 의미론적 값data-lg-state:active,selected,completed,locked,resume,empty,editing같은 기능 상태 토큰data-lg-status:completed,missed,planned같은 도메인 상태 값data-lg-option-value: 필드 컴포넌트 내부 커스텀 옵션의 값
앱은 data-lg-*만 찾는다.
기능 상태는 CSS 클래스로 표현하지 않는다. Claude는 표현용 클래스를 자유롭게 이름 바꾸거나 제거하거나 교체할 수 있다. 재설계된 요소가 동작이나 스타일링을 위해 상태를 필요로 한다면, 의미론적 data-lg-state나 data-lg-status 속성을 유지하고 그 속성에 스타일을 건다.
생성되는 카드와 템플릿도 같은 규칙을 따른다. Codex 렌더러는 복제된 카드 내부를 채울 수 있지만, 복제본 안에서 data-lg-slot과 data-lg-action만을 대상으로 한다. Claude는 이 의미론적 내부 슬롯/액션이 남아 있는 한 템플릿 내부의 태그, 중첩, 클래스 이름을 바꿀 수 있다.
선택 필드는 태그에 의존하지 않는다. Claude는 네이티브 <select>, 히든 입력, 버튼 그룹, 세그먼티드 컨트롤, 커스텀 피커 어느 것이든 쓸 수 있다. 앱은 data-lg-field와 data-lg-value를 통해 의미론적 필드 값을 읽고 쓴다. 커스텀 옵션에는 data-lg-option-value를 달아야 한다.
마커 전용 예시:
<input data-lg-field="workout.set.reps" type="number" />
<input data-lg-field="workout.set.load" type="number" />
<div data-lg-hook="conditionButtons" data-lg-field="workout.session.condition" data-lg-value="보통">
<button type="button" data-lg-option-value="좋음">좋음</button>
<button type="button" data-lg-option-value="보통">보통</button>
</div>
<button data-lg-action="workout.picker.select">
Select
</button>
<div data-lg-hook="draftList"></div>
<template>
<article>
<span data-lg-slot="workout.set.entryLabel"></span>
</article>
</template>Claude가 자유롭게 바꿀 수 있는 것
index.html의 레이아웃 구조를 교체한다.- 디자인 표면의 프레임워크나 컴포넌트 구조를 교체한다.
- CSS 클래스의 이름을 바꾸거나 제거한다.
- 어떤 표현용 클래스 이름과 DOM 구조든 사용한다.
- 모바일과 데스크톱을 완전히 다른 DOM 형태로 분리한다.
- 버튼과 슬롯을 뷰 안 어디로든 옮긴다.
- 모든 시각 래퍼, 카드, 패널, 그리드, 내비게이션 레이아웃을 바꾼다.
- 필드와 컨트롤의 태그 종류를 바꾼다.
- 표현 전용 래퍼를 추가·제거·재배치한다.
- 이전 레이아웃 CSS를 보존하지 않고 화면의 CSS 구조를 새로 만든다.
Claude가 반드시 보존해야 하는 것
- 사용자가 승인한 기능적 의도:
- 로그인/프로필 게이트
- 대시보드 페이지
- 운동 시작
- 계획 확인
- 운동 기록
- 종목 피커
- 종목 종료 노트
- 세션 종료
- 세션 계층:
- 일(day)
- 세션
- 종목
- 세트
- Codex가 인증, Supabase, 계산을 연결할 수 있는 명확한 기능 경계.
- 선택한 디자인 표면이 DOM 수준 의미론적 배선을 쓰는 경우에 한해
data-lg-*마커. - 선택한 디자인 표면이 React인 경우 그에 상응하는 React props/콜백 또는 서비스 경계.
React Props 계약
화면 단위 React props 계약은 docs/contracts/ 아래에 있다.
현재 동결된 파일럿:
docs/contracts/session-screen-props.mddocs/contracts/workout-screen-props.md
화면에 동결된 props 계약이 있으면, Claude는 그 인터페이스 안에서 화면을 자유롭게 재설계할 수 있다. Codex는 컨테이너, 서비스 호출, 저장, 그리고 그 인터페이스가 요구하는 최소한의 컴포넌트 수준 이벤트 바인딩을 배선한다.
실측 PR 표현 불변식
PR 표면은 반복 목표 1부터 20까지에 대한 정확한 실측 NRM 상태를 전달받는다. 이 값들은 추정 근력보다 엄격한 의미를 가진다:
- 정확한 NRM이 없으면 없는 것으로 표시한다(
-또는 그에 준하는 빈 상태). 다른 반복 목표로부터 역 Epley나 그 밖의 어떤 공식으로도 유도해서는 안 된다. - e1RM은 별도의, 명시적으로 라벨된 추정치다. 실측 1RM 히어로, 실측 NRM 셀, 근력 등급 입력, PR 델타, PR 개수를 절대 채우지 않는다.
current1RMIsBaseline: true는 사용자가 날짜를 모르는 과거 1RM을 제공했다는 뜻이다. 날짜 없는 기준선(baseline)으로 표시한다. 가입 시각, 원본 행 생성 시각, 오늘 날짜를 달성일로 표시해서는 안 된다.current1RMSourceKind와 이벤트의sourceKind는session_set과historical_1rm을 구분한다. 이벤트 델타는 엄격한 실측 전이에 대해서만 존재한다.- 과거 1RM 입력은 선택적 달성일과 함께 "날짜 모름"을 명시적으로 고를 수 있는 선택지를 노출해야 한다. 아는 날짜는
achievedOn으로 보내고, 모르는 경우에는null을 보내며, 이를 가입 시각·생성 시각· 오늘로 대체하는 일은 결코 없어야 한다. repMaxes는 실제 수행한 반복 횟수를 키로 한다.100 kg x 10세트는 10RM만 채울 수 있으며, 1RM, 3RM, 5RM, 8RM은 결코 채우지 않는다.
이것들은 기능적 데이터 불변식이지 레이아웃 제약이 아니다.
모바일 PR 종목 검색 핸드오프
src/react/ui/mobile/screens/PrTools.tsx의 모바일 주요 종목 리스트 관리와 1RM 직접 입력 표면을 대상으로 한다. 승인된 시각 구성은 보존한다. 이 핸드오프가 바꾸는 것은 검색 동작과 그 props 경계뿐이다.
Codex는 컨트롤러가 소유하는 search prop 하나를 공급한다:
type MobilePrExerciseSearch = {
input: string;
query: string;
items: PrExerciseSearchItem[];
results: PrExerciseSearchItem[];
hasQuery: boolean;
isPending: boolean;
onInput(value: string, isComposing?: boolean): void;
onCompositionStart(): void;
onCompositionEnd(value: string): void;
onCommit(value?: string): void;
onClear(): void;
};Codex는 검색 로직을 다시 만들거나 승인된 구성을 바꾸지 않은 채 그 prop을 기존 시각 표면에 연결한다. Claude Design은 입력, 결과 목록, pending, empty, 지우기, 닫기, 선택 표면과 합의된 props/마커를 보존한다:
- 입력은
search.input이 제어한다. onChange는search.onInput(value, nativeEvent.isComposing)을 호출한다.- 조합 시작/종료는 대응하는 컨트롤러 콜백을 호출한다. Enter는 네이티브 이벤트가 조합 중이 아닐 때에만
search.onCommit(value)를 호출한다. - 빈 질의는
search.items를 렌더하고, 커밋된 질의는search.results를 렌더한다.search.isPending동안에는 거짓 빈 상태를 번쩍이는 대신 직전에 커밋된 결과를 유지한다. - 시트를 닫거나 종목을 고르면 닫기 전에
search.onClear()를 호출한다. 선택은 여전히 기존onOpenExercise(id)를 통해 라우팅된다. - 버전이 매겨진 카탈로그가 복원되는 동안에도 목록은 계속 살아 있어야 한다. 시트가 열릴 때
items를 복사하거나 스냅숏하지 않는다. 카탈로그 항목이 도착하면 같은 질의를 다시 평가해야 한다. - 기존
pr.favEdit.search,pr.manual.search,prFavAddInput,prManualAddInput마커를 유지한다.
검색 정규화, 토큰 매칭, 정규 ID 중복 제거, 한글 IME 디바운스는 오직 exerciseSearch.ts와 searchController.ts에만 존재한다. UI 코드는 자체적인 includes, toLowerCase, 공백 치환, 별칭 병합, 카탈로그 병합, 정규 ID 변환을 추가해서는 안 된다. Codex는 별칭, 장비, 부위, 기록 없는 종목까지 검색 가능하도록 컨트롤러에 활성 카탈로그 전체를 공급해야 한다. DB나 RPC 변경은 필요 없다.
요구 동작:
바벨로우와바벨 로우는 동일한 정규 항목barbell-row를 반환한다.Barbell Row, 뒤집힌 영어 토큰, 카탈로그 별칭 모두 그 동일한 단일 항목으로 해석된다.- 진행 중인 한글 조합은 낡은 중간 질의를 결코 커밋하지 않는다.
- 지우기, 닫기, 선택, 탭 이탈, 계정 변경은 대기 중인 질의를 되살릴 수 없다.
- 카탈로그 복원 전에 입력된 질의는 카탈로그가 도착하면 자동으로 갱신되며, 성급하게 "결과 없음"으로 확정되어서는 안 된다.
- PR 기록이 없는 종목도 계속 검색되며 빈 상세 화면을 연다.
이것들은 기능적 검색 불변식이지 레이아웃이나 스타일링 지시가 아니다.
변경이 잦은 운동 훅
새 디자인에서 가장 많이 쓰일 훅들이다:
아래 조각들은 레이아웃 처방이 아니다. Claude는 이 마커들을 다른 태그에 붙이거나, 흐름 안 어디로든 옮기거나, 다르게 감싸거나, 모바일/데스크톱 마크업을 완전히 분리할 수 있다.
<section data-lg-hook="workoutView" data-lg-view="workout.flow"></section>
<section data-lg-hook="sessionStartStep" data-lg-view="workout.start"></section>
<section data-lg-hook="sessionRecordStep" data-lg-view="workout.record"></section>
<section data-lg-hook="sessionFinishStep" data-lg-view="workout.finish"></section>
<input data-lg-field="workout.session.date" />
<input data-lg-field="workout.session.startTime" />
<div data-lg-hook="conditionButtons" data-lg-field="workout.session.condition" data-lg-value="보통">
<button type="button" data-lg-option-value="좋음"></button>
<button type="button" data-lg-option-value="보통"></button>
</div>
<textarea data-lg-field="workout.session.preNote"></textarea>
<button data-lg-action="workout.usePlan"></button>
<button data-lg-action="workout.skipPlan"></button>
<button data-lg-action="workout.finishStep"></button>
<button data-lg-action="workout.finish.home"></button>
<button data-lg-hook="addExerciseButton" data-lg-action="workout.exercise.add"></button>
<div data-lg-hook="exerciseTabs" data-lg-slot="workout.exercise.tabs"></div>
<div data-lg-hook="draftList" data-lg-slot="workout.draft.list"></div>
<div data-lg-slot="workout.set.entryLabel"></div>
<input data-lg-field="workout.set.load" />
<input data-lg-field="workout.set.reps" />
<button data-lg-action="workout.exercise.finish"></button>
<section data-lg-hook="exercisePickerView" data-lg-view="workout.picker"></section>
<input data-lg-field="workout.picker.search" />
<div data-lg-hook="exerciseSearchResults"></div>
<button data-lg-action="workout.picker.select"></button>
<button data-lg-action="workout.picker.cancel"></button>카드 내부 슬롯
Claude가 템플릿이나 생성 카드 컨테이너를 재설계할 때, 해당 카드가 존재하는 한 다음 내부 의미론을 보존한다:
아래에 나오는 <template>, <article>, <span>, <button> 태그는 예시일 뿐이다. Codex가 동일한 data-lg-slot과 data-lg-action 마커를 여전히 찾을 수 있는 한, Claude는 내부 태그와 중첩을 바꿀 수 있다.
<template data-lg-hook="feedTemplate">
<article>
<span data-lg-slot="feed.avatar"></span>
<span data-lg-slot="feed.title"></span>
<span data-lg-slot="feed.meta"></span>
<button data-lg-action="feed.kudos"></button>
</article>
</template>
<template>
<article>
<span data-lg-slot="workout.set.entryLabel"></span>
</article>
</template>
<button data-lg-action="workout.set.add"></button>
<span data-lg-slot="workout.set.entryLabel"></span>Codex 핸드오프 방식
Codex가 Claude에게 디자인을 요청할 때:
- 기능 요구사항과 상태만 서술한다.
- 사용자가 명시적으로 결정한 경우가 아니라면 여백, 정확한 레이아웃, 카드 형태, 팔레트, 위계를 처방하지 않는다.
- 데이터 형태, 상태, 스트레스 데이터를 포함한다.
- 그 화면이 필요로 하는
data-lg-*훅을 포함한다. - 그 화면의 완전한 교체용 HTML/CSS를 Claude에게 요청한다.
- 사용자가 바로 그 시각 방향을 명시적으로 요구한 경우가 아니라면, 정적 픽스처 HTML/CSS나 현재 앱 레이아웃을 참조로 포함하지 않는다.
구현 원천
- 현재 React 앱 소스:
src/react/ - 기능 셀렉터 계약:
src/react/contracts/designContract.js - 현재 통합 진입점:
index.html - Supabase/데이터 브리지:
src/react/services/barbelicApi.js - 웹 앱 매니페스트:
public/manifest.webmanifest - 오프라인 서비스 워커 지원: 현재로서는 제거됨. 필요하면 별도로 복원한다
옛 바닐라 프런트엔드 폴더들은 제거되었다. src/app, src/core, src/features, src/mobile, src/desktop, src/dom, 최상위 css, 최상위 app.js를 디자인이나 구현 참조로 사용하지 않는다.