feat: bootstrap lunch picker miniapp with backend, docs, and branding assets

This commit is contained in:
mingking2
2026-04-15 14:03:08 +09:00
commit 7faf251fd3
85 changed files with 31332 additions and 0 deletions

312
APPS_IN_TOSS_STARTER.md Normal file
View File

@@ -0,0 +1,312 @@
# Apps in Toss Starter
새 프로젝트를 시작할 때 기준으로 쓰는 최소 설정이다.
## 목표
- RN `0.84.0` 타깃 번들이 포함된 `.ait` 생성
- 불필요한 Granite 플러그인 없이 Apps in Toss 기준으로 안정적으로 시작
## 기본 원칙
- `build`는 반드시 `ait build`
- `granite.config.ts`에는 `target: '0.84.0'` 명시
- `appsInToss({...})` 중심의 최소 구성 유지
- `router()`, `hermes()` 같은 추가 Granite 플러그인은 꼭 필요한 경우에만 사용
## 시작 순서
### 1. 프로젝트 생성 후 의존성 설치
프로젝트 루트에서:
```bash
npm install
```
### 2. `package.json` 설정
아래 형태로 맞춘다.
```json
{
"name": "my-app",
"private": true,
"scripts": {
"dev": "granite dev",
"build": "ait build",
"deploy": "ait deploy",
"check:build": "./check-apps-in-toss-build.sh"
},
"dependencies": {
"@apps-in-toss/framework": "^2.4.1",
"@granite-js/native": "1.0.10",
"@granite-js/react-native": "1.0.10",
"brick-module": "0.5.1",
"react": "19.2.3",
"react-native": "0.84.0"
},
"devDependencies": {
"@types/react": "19.2.0",
"babel-preset-granite": "1.0.10",
"typescript": "^5.8.3"
}
}
```
### 3. `babel.config.js` 추가
루트에 아래 파일을 반드시 둔다.
```js
module.exports = {
presets: ['babel-preset-granite'],
};
```
이 파일이 없으면 `react-native` 패키지 내부 구문을 Metro가 잘못 파싱해서
`Missing semicolon` 에러가 날 수 있다.
### 4. `index.js` 엔트리 설정
루트 `index.js`는 아래처럼 시작한다.
```js
const { register } = require('@granite-js/react-native');
register(require('./src/_app').default);
```
그냥 `require('./src/_app')`만 하면 Granite 개발 엔트리포인트인 `shared`가 등록되지 않아
`"shared" has not been registered` 에러가 날 수 있다.
### 5. `granite.config.ts` 설정
아래처럼 시작한다.
```ts
import { appsInToss } from '@apps-in-toss/framework/plugins';
import { defineConfig } from '@granite-js/react-native/config';
export default defineConfig({
appName: 'my-app',
scheme: 'intoss',
plugins: [
appsInToss({
target: '0.84.0',
brand: {
displayName: '내 앱 이름',
primaryColor: '#0064FF',
icon: 'https://static.toss.im/appsintoss/your-icon.png',
},
navigationBar: {
withBackButton: true,
withHomeButton: true,
},
permissions: [],
}),
],
});
```
### 6. `src/pages/_404.tsx` 추가
파일 기반 라우터가 fallback 페이지를 요구하므로 아래 파일을 기본으로 넣는다.
```tsx
import { StyleSheet, Text, View } from 'react-native';
import { SafeAreaView } from '@granite-js/native/react-native-safe-area-context';
export default function NotFoundPage() {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<Text style={styles.title}>Page not found</Text>
<Text style={styles.description}>
.
</Text>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#F4F7FB',
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 24,
},
title: {
color: '#111827',
fontSize: 24,
fontWeight: '700',
marginBottom: 8,
},
description: {
color: '#4B5563',
fontSize: 15,
textAlign: 'center',
},
});
```
## 권장하지 않는 시작 방식
아래는 이유 없으면 넣지 않는다.
```ts
router()
hermes()
```
이 조합은 프로젝트에 따라 Apps in Toss 패키징 결과를 꼬이게 만들 수 있다.
## 개발 명령어
개발 서버:
```bash
npm run dev
```
빌드:
```bash
npm run build
```
배포:
```bash
npm run deploy
```
검증:
```bash
npm run check:build
```
## 빌드 후 반드시 확인할 것
### 1. 빌드 로그 확인
아래 같은 로그가 보여야 한다.
```text
[1/2] Built for RN 0.84.0
[2/2] Built for RN 0.72.6
```
### 2. `.ait` 안 번들 확인
아래 명령으로 확인:
```bash
python3 - <<'PY'
import zipfile
path='my-app.ait'
with zipfile.ZipFile(path) as z:
for name in z.namelist():
if name.startswith('bundle.') and name.endswith('.js'):
print(name)
PY
```
정상 예시:
```text
bundle.ios.0_84_0.js
bundle.android.0_84_0.js
bundle.ios.0_72_6.js
bundle.android.0_72_6.js
```
중요한 건 최소한 아래 두 파일이 포함되는 것이다.
- `bundle.ios.0_84_0.js`
- `bundle.android.0_84_0.js`
## 문제 생겼을 때 체크리스트
### `지원하지 않는 번들이에요. 최신 SDK를 사용해주세요`가 뜨면
순서대로 확인:
1. `granite.config.ts``target: '0.84.0'`가 있는지
2. `package.json``build``ait build`인지
3. `.ait` 내부에 `bundle.*.0_84_0.js`가 실제로 들어 있는지
4. 예전 `.ait` 파일을 업로드한 건 아닌지
5. 불필요한 Granite 플러그인을 추가하지 않았는지
## 새 프로젝트 시작용 빠른 체크
프로젝트 준비 후 아래만 실행하면 된다.
```bash
npm install
npm run build
```
개발 실행 전에는 아래 파일들이 있는지 먼저 확인한다.
- `babel.config.js`
- `index.js`
- `src/pages/_404.tsx`
그 다음 `.ait` 확인:
```bash
python3 - <<'PY'
import zipfile
import pathlib
ait_files = sorted(pathlib.Path('.').glob('*.ait'))
if not ait_files:
raise SystemExit('No .ait file found')
path = str(ait_files[0])
print(path)
with zipfile.ZipFile(path) as z:
for name in z.namelist():
if name.startswith('bundle.') and name.endswith('.js'):
print(name)
PY
```
## 실전 기준 요약
- 새 프로젝트는 Apps in Toss 최소 설정으로 시작
- `build``ait build`
- `target: '0.84.0'` 필수
- `.ait``0_84_0` 번들이 있는지 꼭 확인
## 템플릿 폴더
`starter/template` 폴더에는 새 프로젝트 시작용 기본 파일 세트를 넣어뒀다.
- `package.json`
- `granite.config.ts`
- `babel.config.js`
- `index.js`
- `tsconfig.json`
- `require.context.ts`
- `.watchmanconfig`
- `src/_app.tsx`
- `src/pages/index.tsx`
- `src/pages/_404.tsx`
- `check-apps-in-toss-build.sh`
새 프로젝트를 만들 때는 이 폴더 내용을 복사한 다음 아래 값만 먼저 바꾸면 된다.
- `package.json``name`
- `granite.config.ts``appName`
- `granite.config.ts``brand.displayName`
- `granite.config.ts``brand.icon`
- `src/pages/index.tsx`의 화면 문구