웹 앱 — coui_web(Jaspr)
coui_web 을 쓰는 Jaspr 앱에 적용한다. **coui_flutter 를 웹 타깃으로 빌드하는 앱은 Flutter 앱이다** — SKILL.md 절차와 [golden-gate.md](golden-gate.md) 1~6절을 그대로 따른다. 한 저장소에 둘 다 있으면(혼합) 공통 절차는 한 번 밟고, 플랫폼별 게이트는 양쪽 다 돌린다.
coui_web 을 쓰는 Jaspr 앱에 적용한다. coui_flutter 를 웹 타깃으로 빌드하는 앱은 Flutter 앱이다 — SKILL.md 절차와 golden-gate.md 1~6절을 그대로 따른다. 한 저장소에 둘 다 있으면(혼합) 공통 절차는 한 번 밟고, 플랫폼별 게이트는 양쪽 다 돌린다.
버전이 붙은 사실은 coui_web 0.130~0.150 에서 확인한 것이다. 다른 버전에서는 목표 태그의 소스로 다시 확인한다.
1. 같은 것과 다른 것#
| 항목 | Flutter 앱 | 웹 앱 |
|---|---|---|
| 불변식 | 형제 ref 전부 같은 커밋 | 같음 |
| 주 패키지 | coui_flutter | coui_web |
| override 할 형제 | coui_core + coui_flutter | coui_core + coui_web (둘 다 쓰면 셋) |
| 목표 커밋 | 주 패키지 태그 — pin-model.md §2 | 같음 |
| 툴체인 요구 | environment.flutter |
environment.sdk + dependencies.jaspr |
| 크기 배율 | 테마 배율을 호출부 값에도 곱함 | 없음(§3) |
| 시각 게이트 | flutter_test 골든 | SSR 마크업 스냅샷 + 브라우저 캡처( golden-gate.md §7) |
| 파생 산출물 | 아이콘 번들 | 앱이 미리 컴파일한 CSS(그렇게 하는 앱만) |
-
coui_web 도 coui_core 를
any로 참조하고 coui_core 는publish_to: none이다. coui_core override 를 빼면 pub 이 호스트 저장소에서 찾으려 해 해석이 실패하거나 다른 패키지로 풀린다.coui_pin.py plan이 출력하는 블록을 쓴다(형태는coui-getting-started의 Jaspr 예시와 같다). - coui_web 은 coui_flutter 에 의존하지 않는다. 웹 전용 앱은 coui_flutter 를 넣지 않는다.
-
coui_pin.py plan은 앱 pubspec 의 git 의존성으로 주 패키지를 고른다. 강제하려면--package coui_web.
2. 사전 점검 — 툴체인#
| 확인 | 방법 |
|---|---|
| Dart SDK |
목표 커밋
packages/coui_web/pubspec.yaml
의
environment.sdk
를 로컬·CI·빌드 이미지의 Dart 와 대조
|
| jaspr | 같은 파일의 dependencies.jaspr 제약을 앱의 jaspr 선언·override·lock 모두와 대조 |
| jaspr 연쇄 | jaspr_builder 등을 정확 버전·fork 로 고정했으면 함께 올리고, 추적 중인 생성물(jaspr 옵션 파일 등)을 다시 만든다 |
| jaspr_cli |
앱 lock 의 jaspr 와 같은 버전으로 맞추기를 권장(CLI 가 강제하지 않는다). jaspr 를 올릴 때
jaspr migrate
는 lock 기준 현재 버전을 읽는다
|
-
override 는 제약을 무시한다. jaspr 를 override 로 낮게 고정해 두면
pub get은 통과하고 제약 위반이 숨겨진다 — 새 jaspr API 를 쓰는 coui 버전이면 컴파일·런타임에서 깨진다. - Flutter 의 "SDK 분리 상향"(절차 4단계)은 웹에서 "jaspr 계열 분리 상향"이다. 단 coui_web 의 jaspr 제약은 캐럿(0.x 의 같은 minor 안)이라, 옛 핀의 제약이 새 jaspr 를 허용할 때만 먼저 따로 올릴 수 있다. 허용하지 않으면 coui 와 같은 홉에서 올리고 차이를 한꺼번에 기록한다. override 로 제약을 우회해 분리하지 않는다.
3. 단위와 배율#
-
웹에는 Flutter
theme.scaling(AdaptiveScaling)에 해당하는 크기·간격 배율이 없다. 호출부 값은 px/16 rem 으로 그대로 나간다. SKILL.md 의 "옛 px 는 px/배율"과 scaling-and-defaults.md §1·§2 는 웹에 적용하지 않는다. -
예외는 반경이다. 테마의
radiusScaling(테마 컨트롤러·프리셋)이 반경 토큰 변수에 곱해질 수 있다. 토큰이 아닌 임의 반경은 px 리터럴로 나간다.
Flutter 쪽 floor 조언을 옮길 때 주의할 노브:
| 노브 | 웹 동작 | Flutter 조언을 옮길 때 |
|---|---|---|
Button.height |
미지정이면 크기 토큰의 고정 높이 클래스, 지정하면 min-height |
height: 0 이 통한다 |
Tabs.itemHeight |
항상 고정 height(기본은 크기 토큰 값) |
itemHeight: 0 을 옮기면 높이가 0 이 된다 — 옮기지 않는다 |
Card.height·TextField.height |
고정 height. TextField 는 minHeight·maxHeight 가 따로 있다 |
최소 높이는 min 노브로 |
4. 테마 CSS 와 생성 산출물#
-
coui_web 은 테마 토큰과 Tailwind 설정을 싣지 않는다. 앱이
<head>에 공급한다(방법은coui-theme·coui-getting-started). 다만CoUIWeb루트가 소량의 기본 CSS(누르기·비율·forced-colors 포커스)를 넣고, 일부 컴포넌트는 인스턴스별<style>을 본문에 낸다 — SSR 스냅샷 정규화와 CSPstyle-src정책에 반영한다. -
Tailwind 설정 형식은 목표 태그의
ThemeConfig.tailwindConfig소스를 따른다(0.130·0.150 은preflight: false가 든 전역 설정 문자열). 패키지 문서의 Tailwind 버전 표기와 다를 수 있으니 소스로 확인한다. 다른 Tailwind 설정·테마 시트와 한 페이지에 섞지 않는다 — 간격·색 스케일 키가 충돌한다.preflight: false라 CSS 리셋은 앱이 넣는다. -
핀을 올리면 테마 출력(
tailwindConfig·cssVariables)이 바뀔 수 있다(0.130→0.150 에서 색 키·레이아웃 변수가 추가됐다).- 런타임에 주입하는 앱: 다시 만들 파일은 없다. 공급물이 모두 head 에 있는지만 본다.
- 빌드 시점에 CSS 를 미리 컴파일하는 앱: 프로필의 재생성 명령을 돌리고, 신선도 테스트(코드에서 다시 도출한 결과와 커밋본 비교)를 통과시킨다. 없으면 이번에 만든다 — 렌더 분기 하나가 클래스 수집에서 빠지면 그 분기만 스타일 없이 나온다.
- 워크스페이스 밖에서 빌드하는 경로(컨테이너 이미지 등)가 override 를 따로 만든다면 coui_web·coui_core 를 같은 커밋으로 넣고, 비공개 저장소 인증도 주입한다.
5. 게이트#
홉마다 공통 검사(coui_pin.py check, pub get) 뒤에 돈다. 명령과 범위는 프로젝트 프로필에 적는다.
| 게이트 | 명령 | 기준 |
|---|---|---|
| 분석 | 웹 패키지에서 dart analyze | 0 error, 새 warning 0 |
| 테스트 | dart test(jaspr_test 의 testComponents·testServer) |
알려진 실패와 일치. 웹 테스트가 CI 에 연결돼 있는지도 확인 |
| 컴파일 | 배포와 같은 jaspr build(클라이언트 번들 포함) |
성공.
analyze 는 의존 패키지 내부 컴파일 실패를 못 잡는다.
dart compile kernel
은 서버 엔트리 보조 검사일 뿐, 조건부 import 의 브라우저 분기는 검사하지 않는다
|
| 파생 산출물 | §4 | 재생성·신선도 테스트 통과 |
| 시각 | golden-gate.md §7 | 차이 전부 승인 기록 |
하이드레이션(@client 섬이 있을 때) |
서버 HTML 과 로드 뒤 DOM 비교 | 생성 id 를 정규화하고 색 모드를 고정한 뒤 차이 0, 또는 "하이드레이션 불일치 경고·섬 재마운트 0" |
하이드레이션 비교가 흔들리는 이유: 상태별 스타일을 가진 일부 컴포넌트는 co-<컴포넌트>-<해시> 형태의 id 와 그 id 를 쓰는 <style>
을 내는데, 해시가 실행마다·서버와 클라이언트에서 다르다. 시스템 색 모드를 따르는 테마는 서버가 라이트로 그리고 다크 설정 브라우저에서 로드 뒤 뒤집힌다.
6. 기본값·텍스트·아이콘·BREAKING 읽기#
- 기본값 — 컴포넌트 기본값(Card 변형별 배경·테두리, Button 크기별 반경·패딩 등)은 coui_core 계약에서 Flutter 와 함께 온다. core 기본값이 바뀌면 웹에도 온다. 같은 값이 다르게 그려질 수는 있다 — Flutter 는 폭 0 테두리도 헤어라인으로 그리지만, 웹 Card 는 폭 0 이면 테두리 클래스를 내지 않는다.
-
텍스트 — 웹
Text는<span>하나이고 색·크기·굵기·글꼴은 CSS 상속이다(시작점은CoUIWeb루트). Flutter 0.150 의 "역할 없는 체인이 주변 스타일 상속" BREAKING 은 웹에서는 원래 동작이라 대응할 일이 없다. 역할·색·굵기 체인(.bodyMedium·.bold)은 클래스로, 원시 크기(.s14)·줄 높이는 inline 으로 나간다. -
아이콘 — 웹
Icon은 자식 없는<iconify-icon>이다. SSR 결과는 빈 요소이고 브라우저에서 스크립트가 SVG 를 받아 그린다. 기본으로 뷰포트에 들어온 뒤 렌더하므로, 오프라인 환경·캡처에서는 쓰는 아이콘 데이터를 미리 싣는다. Flutter 쪽 아이콘 번들 이야기는 coui_flutter 전용이다. -
BREAKING 읽기 — coui_web CHANGELOG 에는
core·flutter가 섞인 스코프 항목도 실린다.web·core·deps가 들어간 스코프는 항상 읽는다(deps에 jaspr·SDK 하한 변경이 실린다 → §2).flutter만 있는 항목은 건너뛰고, 컴포넌트 스코프는 본문으로 판정한다. 스코프가core라도 본문에 플랫폼 차이가 적힐 수 있다 — Flutter 는 deprecated 로 넘어가도 웹은 별칭이 없어 바로 컴파일 에러인 경우가 있다. -
E2E 셀렉터 — coui 버전에 따라 DOM 태그·role 이 바뀔 수 있다(
jaspr-web-guide의 버전별 DOM 표).
7. 관련 스킬#
| 스킬 | 여기서 쓰는 곳 |
|---|---|
coui-getting-started | Jaspr 앱 override 블록, 웹 import |
coui-theme | CoUIWeb 배선, 섬 루트, 테마 공급 |
coui-icon | 웹 Icon·아이콘 스크립트 |
jaspr-patterns | 섬·하이드레이션 규칙 |
jaspr-web-guide | 운영 빌드 대 개발 서버, 하이드레이션 불일치 진단, 셀렉터 |
jaspr-state-management | jaspr 를 올릴 때 연쇄 제약 |
/cc-jaspr-web:regression | 브라우저 캡처 비교(결정성 보강은 golden-gate §7) |
/cc-jaspr-web:audit | 콘솔 에러·접근성 보조 게이트 |
parallel-test-env | 브라우저 세션 격리 |