YAPP Plus의 웹, 모바일 셸, 관리자 앱을 관리하는 Turborepo 기반 모노레포입니다.
제품의 목적, 대상 사용자와 핵심 경험은 YAPP+ 제품 목적과 범위를 참고합니다.
웹 화면의 폴더 구조와 퍼블리싱 절차는 웹 작업 가이드를 참고합니다.
mise를 설치합니다. 이 저장소는 mise.toml에서 Node.js 버전을
고정하고 package.json에서 정확한 pnpm 버전을 읽습니다. Corepack은 사용하지 않습니다.
mise install
pnpm install| 경로 | 역할 |
|---|---|
apps/web |
모바일 WebView에서 렌더링되는 Next.js 앱 |
apps/mobile |
Expo/React Native 기반 WebView 셸 |
apps/admin |
TanStack Start 기반 관리자 앱 |
apps/storybook |
공용 UI를 독립적으로 확인하는 Storybook |
packages/ui |
vanilla-extract로 구현한 웹/관리자 공용 UI |
packages/api |
Hey API, Ky, Zod, TanStack Query 연동 |
packages/app-bridge |
타입이 지정된 WebView 브리지 계약 |
packages/config |
공용 TypeScript 및 Oxlint 설정 |
앱과 내부 패키지의 관계는 각 package.json의 workspace:* 의존성으로 정의합니다. Turbo의
^build, ^lint, ^test, ^typecheck가 이 관계를 따라 의존 패키지의 태스크를 먼저
처리합니다. 공통 외부 의존성 버전은 pnpm-workspace.yaml의 catalog에서 관리합니다.
| 명령어 | 설명 |
|---|---|
pnpm dev |
웹, 관리자, 모바일 개발 서버 시작 |
pnpm dev:web |
웹 앱 개발 서버만 시작 |
pnpm build |
워크스페이스 패키지와 배포 가능한 앱 빌드 |
pnpm commitlint |
커밋 메시지 규칙 검사 |
pnpm format |
Oxfmt가 지원하는 파일 포맷팅 |
pnpm lint |
Oxlint로 앱과 패키지 검사 |
pnpm typecheck |
워크스페이스 TypeScript 타입 검사 |
pnpm test |
워크스페이스 테스트 실행 |
pnpm expo:doctor |
Expo 앱 설정 검증 |
pnpm storybook |
공용 UI Storybook 개발 서버 시작 |
pnpm check |
포맷, 린트, 타입 검사, 테스트 품질 게이트 실행 |
Lefthook은 커밋 전에 스테이징된 소스 파일을 포맷팅하고 린트합니다. 커밋 메시지는
<type>(<scope>): <subject> 형식으로 검사하며 scope는 생략할 수 있습니다. 예를 들어
feat(web): 로그인 화면 추가, ci: GitHub Actions 설치 안정화처럼 작성합니다. 타입 검사와
테스트는 푸시 전에 실행합니다.
커밋 메시지 검사는 로컬 commit-msg 훅에서만 실행됩니다. --no-verify를 사용하거나 Git
훅이 설치되지 않은 환경에서 만든 커밋은 자동으로 검사되지 않습니다.
Oxlint는 oxlint-tsgolint의 type-aware 규칙과 React Compiler 검증 규칙을 함께 사용합니다.
Next.js 규칙은 웹 앱에만 적용하고, 프레임워크가 default export를 요구하는 파일은 별도
예외로 관리합니다.
Storybook은 packages/ui의 웹 컴포넌트만 다룹니다. 제품 앱 개발 서버와 항상 함께 실행하지
않도록 pnpm dev와 분리했으며, 정적 Storybook 빌드는 pnpm build에 포함합니다.
Storybook 정적 문서는 GitHub Pages에서 확인할 수
있습니다. main에 Storybook, packages/ui 또는 관련 빌드 설정 변경이 반영되면
Deploy Storybook workflow가 정적 산출물을 빌드해 Pages에 게시합니다. 코드 변경 없이 다시
게시해야 할 때는 GitHub Actions에서 workflow_dispatch로 수동 실행합니다.
최초 사용 시 저장소 Settings → Pages → Source를 GitHub Actions로 설정해야 합니다. Storybook은
상대 asset 경로를 사용하므로 /yapp-plus-frontend/ project site 하위 경로에서도 로드됩니다.
제품 앱의 Vercel Preview·Production 배포와 Storybook Pages 배포는 서로 독립적으로 실행됩니다.
이 저장소는 당근의 공식 Seed Design 스킬과 React 패키지를 사용합니다. 저장소 루트에서 다음 명령으로 같은 스킬을 설치할 수 있습니다.
pnpm dlx skills add https://github.com/daangn/seed-design --skill seed-designpackages/ui/seed-design.json은 React·TSX·RSC 스니펫 경로를 정의하며 telemetry를 끕니다. 현재
지원하고 검증한 범위는 React 19, @seed-design/react 2.5.0, @seed-design/css 2.8.3,
@seed-design/vite-plugin 2.1.0, CLI 1.7.0입니다. 공용 UI의 peer 범위는
@seed-design/react ^2.5.0, @seed-design/css ^2.8.3, React·ReactDOM 19 이상입니다.
공용 컴포넌트는 @yapp-plus/ui에서 가져옵니다. ActionButton은 Seed의 loading과 disabled를
독립된 상태로 유지하므로 클릭을 막아야 하는 로딩 사례에서는 두 prop을 함께 전달합니다. Seed
ActionButton은 기본 type을 지정하지 않으므로 폼을 제출하지 않는 버튼은 type="button"을
명시합니다.
import { ActionButton } from '@yapp-plus/ui';
<ActionButton type="button" variant="brandSolid" loading={isLoading} disabled={isLoading}>
계속
</ActionButton>;각 앱은 문서 진입점에서 Seed base CSS를 먼저 가져오고 공용 앱 스타일을 뒤에 연결합니다.
import '@seed-design/css/base.css';
import '@yapp-plus/ui/styles';기본 디자인 토큰과 전역 reset은 packages/ui가 관리합니다. 패키지의 일반 진입점을 가져오는
것만으로 전역 스타일이 적용되지는 않습니다. web의 global.css.ts는 스타일을 다시 선언하는
파일이 아니라 Turbopack이 외부 스타일 진입점을 추적하게 하는 연결 파일입니다.
web과 admin은 SSR 문서 head에서 @yapp-plus/ui/seed-theme의 공식 Manual 시스템 테마
스크립트를 사용합니다. Storybook은 Vite builder의 seedDesignPlugin()으로 preview iframe에
테마 스크립트와 color-scheme 메타를 주입합니다. Vite plugin은 Storybook만 개발 의존성으로
소유합니다.
스니펫을 추가하거나 갱신할 때는 packages/ui의 설정을 사용하고 생성 결과가 기존 React 19 코드와
공개 API를 덮어쓰지 않는지 diff를 검토합니다. 새 컴포넌트와 필수 종속 스니펫만 추가합니다.
pnpm dlx @seed-design/cli@1.7.0 add ui:action-button --cwd packages/ui
pnpm dlx @seed-design/cli@1.7.0 compat --cwd packages/ui
mise exec -- pnpm --filter @yapp-plus/ui test
mise exec -- node --run check
mise exec -- node --run build제품 앱은 mise exec -- node --run dev, Storybook은 별도 터미널에서
mise exec -- node --run storybook으로 실행합니다. 버튼 상태와 테마 검증 절차 및 실제 검증 결과는
Seed Design 검증 가이드와
검증 결과를 따릅니다.
웹 앱은 Next.js의 React Compiler와 Turbopack Rust 구현을 사용합니다. Rust 구현은 아직 실험 기능이므로 Next.js를 올릴 때 관련 변경 사항과 빌드 결과를 함께 확인합니다.
웹과 관리자 앱은 yapp-plus Vercel scope의 서로 다른 프로젝트로 배포합니다.
| 앱 | Vercel 프로젝트 | Root Directory | Framework |
|---|---|---|---|
web |
yapp-plus-web |
apps/web |
Next.js |
admin |
yapp-plus-admin |
apps/admin |
TanStack Start |
.github/workflows/ci.yml은 Pull Request와 main push의 품질 검증만 담당합니다.
.github/workflows/deploy-preview.yml은 내부 브랜치 Pull Request의 CI가 성공한 뒤 검증된
commit을 Preview 환경으로 배포합니다. 외부 fork와 Dependabot Pull Request에서는 배포용 Secret을
사용하지 않습니다. .github/workflows/cd.yml은 main의 CI가 성공하면 검증된 commit을
Production 환경으로 배포합니다. .github/workflows/deploy-manual.yml은 GitHub Actions
Run workflow에서 앱과 환경을 선택해 같은 Vercel 배포 절차를 수동 실행합니다. 각 앱은 독립된
matrix job으로 배포되므로 한 앱의 실패가 다른 앱의 실행을 취소하지 않습니다. 배포 workflow는
.github/actions/vercel-deploy 복합 액션을 사용해 같은 Vercel CLI 버전과 배포 절차를 공유합니다.
배포 전에 turbo query affected가 각 앱의 build task와 workspace 의존성 그래프를 기준으로
변경 영향을 계산합니다. 앱 또는 앱이 의존하는 내부 패키지가 영향을 받지 않았다면 해당 앱의
Vercel build와 배포를 생략합니다. 배포 절차 자체를 바꾸는 다음 파일은 앱 그래프와 무관하게
Web/Admin 배포를 모두 실행해 workflow 회귀를 검증합니다.
.github/workflows/deploy-preview.yml.github/workflows/cd.yml.github/workflows/deploy-manual.yml.github/actions/deployment-config-changed/action.yml.github/actions/turbo-affected/action.yml.github/actions/vercel-deploy/action.yml
자동 배포 workflow는 .github/actions/deployment-config-changed 복합 액션으로 배포 설정 변경을
먼저 확인하고, .github/actions/turbo-affected 복합 액션으로 앱별 Turbo build 영향 범위를
계산합니다. 배포 설정이 바뀌면 turbo-affected의 force-all 입력으로 Web/Admin 배포를 모두
검증하고, 그렇지 않으면 앱별 build 입력이 바뀐 경우에만 배포합니다. 실제 Preview 배포가 실행된
경우에만 Pull Request의 Web 및 Admin 댓글을 작성하거나 갱신하며, 배포를 생략한 경우에는 Job
Summary에만 생략 사유를 기록합니다. 생략 시 기존 sticky 댓글은 삭제하거나 수정하지 않습니다.
workflow를 사용하려면 저장소의 GitHub Actions 설정에 다음 값을 등록합니다.
| 종류 | 이름 | 값 |
|---|---|---|
| Secret | VERCEL_TOKEN |
yapp-plus scope에 접근 가능한 토큰 |
| Variable | VERCEL_ORG_ID |
yapp-plus scope ID |
| Variable | VERCEL_WEB_PROJECT_ID |
yapp-plus-web 프로젝트 ID |
| Variable | VERCEL_ADMIN_PROJECT_ID |
yapp-plus-admin 프로젝트 ID |
VERCEL_TOKEN은 Vercel Account Tokens에서 만들고
GitHub 저장소에만 Secret으로 등록합니다. 이 토큰은 Vercel 배포와 Turbo 원격 캐시 인증에 함께
사용하며, workflow는 TURBO_TOKEN에 VERCEL_TOKEN Secret을 전달하고 TURBO_TEAM에는
yapp-plus 팀 slug를 사용합니다. GitHub Actions에 Secret이 없는 fork Pull Request나 로컬
실행에서는 원격 캐시 없이 로컬 Turbo 캐시로 동작해야 합니다. 로컬 Vercel 인증 토큰이나
.vercel/, .env.local은 커밋하지 않습니다. 앱이 사용하는 환경 변수는 GitHub Actions가 아니라
각 Vercel 프로젝트의 Environment Variables에 Preview와 Production 환경별로 등록합니다.
Vercel GitHub App의 YAPP-admin/yapp-plus-frontend 접근 승인이 완료되면 장기 토큰 대신 OIDC 기반
Turbo 원격 캐시 인증으로 전환할 수 있습니다. OIDC 전환 전까지는 workflow 로그, 실행 요약과 캐시
artifact에 VERCEL_TOKEN 또는 TURBO_TOKEN 값을 출력하지 않습니다.
Production 수동 배포를 사용하려면 GitHub 저장소 Settings의 Environments에서 production
Environment를 만들고 deployment branches를 main으로 제한합니다. 1인 운영 중에는 Required
reviewer를 설정하지 않습니다. Required reviewer를 켜면 본인이 실행한 배포를 본인이 승인할 수 없어
workflow가 대기 상태로 멈출 수 있습니다.
같은 commit으로 새 배포가 필요하거나 영향 범위 판정을 우회해야 한다면 Deploy Manual workflow를
실행합니다. app은 web, admin, all 중 하나를 고르고 environment는 preview 또는
production을 선택합니다. Preview는 선택한 ref를 배포하고, Production은 항상 main을 checkout한
뒤 해당 commit에 성공한 CI push 실행이 있을 때만 배포합니다.
Preview 배포가 실패하면 GitHub Actions의 Deploy Preview, Production 배포가 실패하면 CD 또는
Deploy Manual 실행에서 실패한 job을 재실행합니다.
Admin은 TanStack Start와 Nitro를 사용하므로 Vercel Functions용 vercel preset으로 빌드되어야
합니다. 현재 배포는 GitHub Actions runner에서 vercel build를 실행한 뒤
vercel deploy --prebuilt로 결과를 업로드하므로, Vercel이 제공하는 시스템 환경 변수가 자동으로
주입되지 않습니다. 다음 로그가 함께 나타나면 Nitro가 node-server preset으로 빌드된 것입니다.
WARNING! Build not running on Vercel[nitro] ... (preset: \node-server`)`.output/public또는.output/server만 생성되고.vercel/output/functions/__server.func가 없음
이 상태에서는 배포가 성공으로 표시되어도 실행할 Vercel Function이 없어 Preview URL이
플랫폼의 404 NOT_FOUND를 반환합니다. .github/actions/vercel-deploy/action.yml에서
vercel build를 호출할 때만 Nitro 공식 CI/CD 설정인 NITRO_PRESET을 주입해 Vercel preset을
명시적으로 선택하도록 합니다.
NITRO_PRESET=vercel VERCEL_ENV="$DEPLOYMENT_ENVIRONMENT" vercel build --token="$VERCEL_TOKEN"정상 빌드에서는 [nitro:vercel] 로그와 .vercel/output/functions/__server.func가 생성되어야
합니다. Vercel 프로젝트 설정은 Framework Preset을 TanStack Start, Root Directory를
apps/admin으로 두고 Output Directory override는 비워 둡니다. .output을 Output Directory로
지정하면 정적 출력으로 처리되어 같은 문제가 재발할 수 있습니다. 자세한 preset 자동 감지
동작은 Vercel의 TanStack Start 배포 문서를
참고합니다.
Expo 앱은 WebView 셸입니다. 기기에서 불러올 웹 주소를 MOBILE_WEB_URL로 설정합니다.
로컬 개발 환경에서는 Metro 호스트의 3000 포트를 기본값으로 사용합니다. 네이티브 셸은
타입이 지정된 앱 브리지를 통해 현재 안전 영역 여백을 주입하며, 웹 앱은 이를
--safe-area-inset-* CSS 변수로 제공합니다.
네이티브 디렉터리는 의도적으로 커밋하지 않습니다. 이 워크스페이스는 iOS 셸만 담당하므로 iOS
네이티브 프로젝트가 필요할 때만 다음 명령으로 생성하고 apps/mobile/android는 생성하지
않습니다.
pnpm --filter @yapp-plus/mobile exec expo prebuild --platform iosKakao 로그인, 푸시 알림, 운영 앱 식별자, URL scheme, EAS 프로젝트 설정은 이후 작업으로
미뤄 두었습니다. 모바일 셸은 iOS만 EAS로 빌드하고 Android는 별도 Native 프로젝트에서
개발합니다. 앱 식별자 협의와 iOS 빌드 준비 절차는
apps/mobile/README.md를 참고합니다.
임시 OpenAPI 문서는 커밋하지 않습니다. 백엔드가 OpenAPI 문서를 제공하면 다음 명령으로 Ky 2 클라이언트, TypeScript SDK, Zod 4 스키마, TanStack Query 헬퍼를 생성합니다.
OPENAPI_INPUT=https://api.example.com/openapi.json \
pnpm --filter @yapp-plus/api generateSDK 요청 및 응답 검증은 기본적으로 비활성화되어 있습니다. Hey API의 Zod validator 연동을
활성화하려면 같은 명령에 HEY_API_SDK_VALIDATION=true를 설정합니다.
Spec Kit 1.0.4는 저장소 루트에 Codex 스킬과 함께 한 번 초기화되어 있습니다. Git 확장
기능은 설치하지 않았으므로 Spec Kit은 Conductor 워크스페이스 브랜치를 관리하지 않습니다.