Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React Native + Cloudflare Worker で個人開発アプリを作っている話
Search
Kyosuke Hiraizumi
August 07, 2024
0
190
React Native + Cloudflare Worker で個人開発アプリを作っている話
React Native Meetup #17 の登壇資料です。
Kyosuke Hiraizumi
August 07, 2024
Tweet
Share
Featured
See All Featured
Facilitating Awesome Meetings
lara
50
6.1k
How to Ace a Technical Interview
jacobian
276
23k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
8
1.2k
Music & Morning Musume
bryan
46
6.2k
Become a Pro
speakerdeck
PRO
26
5k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
95
17k
Building an army of robots
kneath
302
44k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
6
450
Fashionably flexible responsive web design (full day workshop)
malarkey
405
66k
Bash Introduction
62gerente
609
210k
Transcript
React Native + Cloudflare Worker で 個人開発アプリを作っている話 2024/08/06 React Native
Meetup #17
自己紹介 平泉 京祐 / @hirai_kyo / github.com:HiraiKyo 所属: CA技研 技術:
仕事で ROS / Python / C++ 趣味・副業で Typescript / Node.js / C# 趣味: ポーカーやってます!
アプリ名: Hashigo コンセプト • テニスの対戦アプリ • ワンボタンで適切な対戦相手 が見つかる
LT趣旨 • 完成まで来たので、とりあえず自慢したかった!! • 技術的な深堀りポイントは特にない… ◦ 企画 ~ 設計 ~
実装 の俯瞰的LTができれば • ビジネスモデルから詳細設計まで質問対応するLTは珍しい(?) • (おまけ) バックエンドにCloudflare Workerを採用した話
アプリ企画
Hashigoで解決する課題 既存テニスアプリの調査 顕在ニーズ 実力が自己申告制 Lv1 ~ 10 を自分で設定 同レベルでも結構差がある ⇒
ELOレーティング導入 潜在ニーズ 相手を探すのが手間 場所, 日時, 参加者, レベル, etc… Tinderくらい手軽に探したい ⇒ マッチメイキング方式を採用 ELOベースのマッチメイキング方式テニス対戦アプリ
Hashigo システム概要 クライアント マッチメイキングへ参加 マッチ成立の通知受け取り マッチ成立後のコミュニケーション サーバ マッチメイキングの定期実行 マッチメイクアルゴリズム マッチ成立のプッシュ通知
アプリ設計
Hashigoの技術スタック・構成 フロント Expo (React Native) Typescript バック Cloudflare Worker Typescript
フロント技術選定 ビジネス要件 • マッチ成立通知 • 位置情報 • サービス開始時にユーザーがある程度必要 技術要件 •
Dartつらいかも、TS, React はたくさん触ってる
アプリ実装
Hashigo クライアント側の機能 機能要件 位置情報 expo-location 地図表示 react-native-maps ユーザプロフィールの編集 react-hook-form プロフィール画像のアップロード
expo-image-picker プッシュ通知 expo-notifications WebSocketでマッチメイキングサーバと通信 標準ライブラリ 非機能要件 認証 Firebase JS SDK UIライブラリ React Native Paper エラーログ収集 Sentry
Expo SDK が提供するライブラリを 使っただけ。
None
Thank you.
None
Q. 営業どうしてるの? A. 私がテニスしに行って宣伝してます。 暑すぎて熱中症2回なりました…。
Q. Cloudflare Worker 良い? A. とても良い。 ホットスタートなのに安い(コンテナベースではないので) DurableObjectsが直観的で使いやすい WebSocket Hibernation
APIでWebSocketのコストダウン狙える Cloudflare といえば WAF がある!
マッチメイキングサーバ OpenMatch • ゲーム向けのOSSマッチメイクフ レームワーク • マッチメイクロジックをどこまで弄れ るか分からなかった • ドキュメントが整っていない
自作 • コアであるマッチメイクロジックを 外部に依存したくなかった • Cloudflare DurableObjectsが 使いやすい! • クラウド上に存在するシングルトン クラス • WebSocket Hibernationでコス トダウンも可能
サーバサイド : Cloudflare DurableObjects の実装例 export class MatchmakingDurableObject extends DurableObject
{ private _queues: QueueType[] = []; constructor(readonly state: DurableObjectState, readonly env: Env) { super(state, env); this.state.blockConcurrencyWhile(async () => { let stored = await this.state.storage.get<string>('queue'); this._queues = stored ? JSON.parse(stored) : []; }); } async addQueue(participant: Participant): Promise<void> { const q: QueueType = { queueId: uuidv4(), participant, createdAt: new Date(), }; this._queues.push(q); await this.state.storage.put('queue', JSON.stringify(this._queues)); } } • “マルチユーザーのシングルトンク ラス”のように書ける • サーバーレスでありながら、一意 のクラスインスタンスのように振る 舞う • DBやKVStoreではなく In-memory stateにデータ保持 • 衝突はCloudflare側でキュー管 理してくれる
認証 • Firebase JS SDKを利用 ◦ Authentication, Firestore, Realtime Database,
Storageのみ 利用可能 ◦ react-native-firebase の利用も検討したが、prebuildで苦労した ◦ Firebase Crashlyticsを使わずにSentryを使うことにした • Cloudflare Access を導入してみたい
位置情報 • expo-location を利用
地図表示 • react-native-maps を利用 ◦ GoogleMapsAPIの設定が必要 ◦ Web対応していないので、パッチ処理を行う ▪ https://stackoverflow.com/questions/76629674/unable-to-resolve-utilities-platform-error-with-metro-bundler
• Expo SDK 51ではGoogle MapsがiOSでサポートされず、Apple Mapsに置換された ◦ provider={PROVIDER_GOOGLE} を削除する ◦ https://github.com/expo/expo/issues/28705
ユーザプロフィールの編集 • react-hook-form を利用 ◦ register を使って書けない(?) control を使う export
default function App() { const { control, handleSubmit, formState: { errors }, } = useForm({ defaultValue : { firstName: "", }, }) const onSubmit = (data) => console.log(data) return ( <View> <Controller control ={control} rules ={{ required: true, }} render ={({ field: { onChange, onBlur, value } }) => ( <TextInput placeholder ="First name" onBlur ={onBlur} onChangeText ={onChange} value ={value} /> )} name ="firstName" /> {errors.firstName && <Text>This is required. </Text>} </View> ) }
画像のアップロード • expo-image-picker を利用 ◦ blobだと送信できず、 右記のような形で送信する らしい ◦ 詳細は、
https://www.simpletraveler.jp/2021/09/30/%E3%80%90reactnative%E9%96%8B%E7% 99%BA%E3%80%91formdata%E3%81%A8%E3%81%9D%E3%82%8C%E3%81%AB%E9%9 6%A2%E3%82%8F%E3%82%8B%E6%8A%80%E8%A1%93fetchxhrmultipart-form-data- %E3%82%92%E7%90%86%E8%A7%A3/ const permissionResult = await ImagePicker .requestMediaLibraryPermissionsAsync (); const result = await ImagePicker .launchImageLibraryAsync ({ mediaTypes: ImagePicker .MediaTypeOptions .Images, allowsEditing: true, aspect: [1, 1], quality: 1, }); const uri = result.assets[0].uri; const mimeType = result.assets[0].mimeType; const fileExt = uri.split(".")[uri.split(".").length - 1]; const formData = new FormData(); formData.append("file", { uri, name: `image.${fileExt}`, type: mimeType, }); const res = await fetch(uploadUri, { method: "POST", body: formData, });
プッシュ通知 • expo-notifications を利用 • expo-server-sdk-node でバックエンド実装 ◦ Cloudflare Worker
の Node.js Compatible モードがうまく機能 せず、Node.js API でエラー ▪ assert は自分で書いた ▪ zlib は pako に置換
WebSocket通信 • 標準ライブラリのWebSocketを利用 ◦ バックグラウンドからの復帰で接続切れを起こす ▪ react-native の AppStateを監視して、フォアグラウンドに戻っ た時に再接続を実行
• Cloudflare DurableObjects に WebSocket Hibernation API があ り、WebSocketがお得に使える!
UIライブラリ選定 Expo SDK 51 で使えるもの • React Native Paper •
Tamagui 使えなかったもの • RNUILib • React Native Base ⇒ ビルドまで含めてテストすべき