313 lines
6.5 KiB
Markdown
313 lines
6.5 KiB
Markdown
# 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`의 화면 문구
|