규칙

클라이언트

모든 앱이 같이 따르는 것. 앱마다의 구조 · 명령 · 서버 부르는 방식은 그 앱의 AGENTS.md(clients/apps/<앱>/).

clients/폴더 구조
  • React · Vue · Next · 순수 TS · Expo · Tauri
  • 각 앱이 서버의 타입으로 API 를 호출하는 공통 클라이언트입니다.
  • 클라이언트 앱과 패키지가 공유하는 린트 · 포맷 설정입니다.
  • 정적 웹 파일을 제공하고 /api 요청을 서버로 전달합니다.

서버 부르기

TS 앱은 부를 서버마다 클라이언트를 하나 만든다. 모든 서버의 타입이 열려 있지만 경로마다 생기는 코드는 없어서 API 가 늘어도 번들은 그대로다. 다른 언어 앱은 contract/openapi/<서버>.json 에서 클라이언트를 생성한다 (contracts.md).

import { createClient, type notes } from '@app/client'
const api = createClient<notes.paths>({ name: 'notes', base: 'http://127.0.0.1:8003' }) // name · base(gateway) · fetch 는 선택
await api.get('/api/notes') // 경로 · 응답 타입은 notes 서버 문서에서
await api.post('/api/notes', { text }) // 201 본문(만든 메모)을 돌려준다. put 은 (경로, 본문, { params })
await api.delete('/api/notes/{id}', { params: { id } }) // 경로 변수가 있으면 params 가 필수(빼면 타입 오류), 204 는 void
useQuery(api.query('/api/notes')) // TanStack Query (React · Vue). 키 = [name, 경로, params, query]
  • name: 쿼리 키의 첫 칸. 경로가 같은 서버가 둘이어도 캐시가 섞이지 않게. 주소가 아니라 이름이라 서버 렌더링(절대 주소)과 브라우저(상대 경로)의 키가 같다.
  • base: 같은 출처(프록시)면 생략, 네이티브 앱 · 서버 렌더링은 서버 주소. fetch: CORS 없는 fetch 가 필요할 때(Tauri).
  • 주소는 gateway 하나다(servers/gateway). 같은 출처 프록시 · nginx 는 /api 를 API_URL(gateway)로 넘기고, gateway 가 경로 접두사로 서버를 고른다. 네이티브 앱의 base 도 gateway 주소다.
  • 서버를 둘 이상 부르면 서버마다 클라이언트를 하나씩 만든다(타입이 서버마다라서). admin 은 createClient<tags.paths>({ name: 'tags' }) · createClient<activity.paths>({ name: 'activity' }).

서버 데이터

  • TanStack Query (@tanstack/react-query · @tanstack/vue-query). 쿼리 옵션은 손으로 짜지 않고 api.query() 를 쓴다.
  • 만들기 · 고치기 · 지우기 뒤에는 invalidateQueries({ queryKey: api.query('/api/notes').queryKey }) 로 목록을 다시 받는다.
  • 프레임워크가 없는 앱(lite · desktop)은 api.get · post · put · delete 그대로.
  • API 응답 문자열은 텍스트로만 렌더한다 (innerHTML · v-html · dangerouslySetInnerHTML 금지, hook 이 막는다).

의존성 · 도구

  • 버전은 pnpm-workspace.yaml 카탈로그 한 곳에서 ("react": "catalog:"). Expo 가 정하는 버전은 catalog:expo (올릴 때 npx expo install --fix), TS 의 JS API 가 필요한 도구(admin 의 vue-tsc · site 의 next build)는 catalog:ts6. 서버는 카탈로그를 쓰지 않는다.
  • react · react-dom 은 워크스페이스 전체가 정확히 같은 한 버전(Expo 가 정한 것)이다. ^ 범위로 두면 pnpm 이 둘을 따로 골라, 테스트 · 빌드는 통과하고 브라우저에서만 화면이 빈다.
  • 모든 패키지에 lint(oxlint && oxfmt --check) · format, 앱에는 typecheck. 기본은 TS 7(tsc -p .). TS 의 JS API 를 쓰는 도구가 있으면 그 앱만 자기 TS 를 둔다(TS 7 에는 아직 안정된 JS API 가 없다).
  • 웹 앱(web · admin · site · lite)은 Dockerfile 이 있다. 정적 앱은 nginx 가 /api 를 gateway 로 넘기고(nginx.conf.template), site(Next)는 standalone. mobile · desktop 은 네이티브 빌드라 이미지가 없다 (development.md 의 “이미지”).
  • 새 앱은 guides/add-app.md.

UI

색 · 글자 · 간격 · 둥글기 · 컴포넌트와 그 쓰임은 저장소 루트의 DESIGN.md 가 기준이다 (DESIGN.md 형식: 앞의 YAML 이 값, 본문이 규칙). 새 값이 필요하면 거기 먼저 더하고, pnpm dlx @google/design.md@0.4.0 lint DESIGN.md 로 확인한다.

GitHub 에서 고치기