$30 off During Our Annual Pro Sale. View Details »

React Native×Firebaseで オンライン指導用の チャットアプリを開発している話

meijin
March 04, 2021

React Native×Firebaseで オンライン指導用の チャットアプリを開発している話

meijin

March 04, 2021
Tweet

More Decks by meijin

Other Decks in Programming

Transcript

  1. React Native×Firebase

    オンライン指導⽤の
    チャットアプリを開発している話

    View Slide

  2. ⽬次
    ⾃⼰紹介
    開発しているサービス
    React Native
    の利⽤ライブラリ
    React Native
    の品質管理
    React Native
    の落とし⽳
    Firebase
    の利⽤ライブラリ
    Firebase
    の品質管理
    Firebase
    の落とし⽳
    この技術選定で良かったと思うこと
    まとめ

    View Slide

  3. ⾃⼰紹介
    ハンドルネーム”
    名⼈”
    株式会社NoSchool CTO
    オンライン家庭教師を広める教育サービス”
    マナ
    リンク”
    を開発しています(https://manalink.jp)
    好きな⾔語はTypeScript
    好きなIDE
    はPHPStorm
    趣味
    将棋(
    指すのも観戦するのも好き)
    ゲーム(
    最近はゼルダ無双)

    View Slide

  4. 開発しているサービス
    マナリンク
    中⾼⽣向けのオンライン家庭教師サービス
    Web
    上で先⽣を探して指導依頼
    指導開始後はアプリを使ってご家庭↔
    先⽣でやり
    取り

    View Slide

  5. 図にするとこんな感じ

    View Slide

  6. 図にするとこんな感じ

    View Slide

  7. アプリの開発体制と利⽤技術
    開発体制
    業務委託エンジニア1
    名(+
    ときどき
    CTO)
    めっちゃベンチャーです
    利⽤技術
    React Native
    Expo(Managed Workflow)
    Firebase
    TypeScript

    View Slide

  8. React Native
    の利⽤ライブラリ

    View Slide

  9. react-native-gifted-chat
    <
    GiftedChat
    messages
    messages=
    ={
    {chatMessages
    chatMessages}
    }
    onSend
    onSend=
    ={
    {onSend
    onSend}
    }
    placeholder
    placeholder=
    ="
    メッセージを⼊⼒"
    "
    メッセージを⼊⼒"
    renderBubble
    renderBubble=
    ={
    {renderBubble
    renderBubble}
    }
    renderInputToolbar
    renderInputToolbar=
    ={
    {renderInputToo
    renderInputToo
    renderActions
    renderActions=
    ={
    {renderActions
    renderActions}
    }
    renderComposer
    renderComposer=
    ={
    {renderComposer
    renderComposer}
    }
    renderSend
    renderSend=
    ={
    {renderSend
    renderSend}
    }
    renderMessageImage
    renderMessageImage=
    ={
    {renderMessageI
    renderMessageI
    infiniteScroll
    infiniteScroll
    //
    以下略
    //
    以下略
    /
    />
    >
    チャット画⾯を丸っと囲う
    GiftedChat
    コンポーネントを置くだ

    各メッセージや送信ボタンなどは外
    からFunctional
    なComponent
    を渡す
    ことでカスタマイズできる
    カスタマイズしすぎるとライブラリ
    の範囲を逸脱してくるので、いつか
    はリプレイスする

    View Slide

  10. チャットをゼロから作るのは割と⼤変
    以下の内容は
    react-native-gifted-chat

    やっている
    チャットの吹き出しの形
    よく⾒ると、連投したときの⾓丸の形が微妙に
    違う
    連投すると最後の投稿だけユーザーアイコンが
    つく
    ⽇時表⽰
    ⽇付が変わるたびに⽇付表⽰をインサートして
    いる
    表⽰位置
    新着メッセージが来るたびに⾃動でスクロール
    Web
    ではVue
    で全部⾃作したけど結構⼤変だった(

    View Slide

  11. Firebase×React Hooks
    関連のライブラリ
    react-firebase-hooks
    https://github.com/CSFrequency/react-firebase-hooks
    Firestore
    のCollection
    データ、読み込み中フラグ、エラー内容をセットで返すHook
    const
    const [
    [values
    values,
    , loading
    loading,
    , error
    error]
    ] =
    = useCollectionData
    useCollectionData<
    T>
    >(
    (query
    query,
    , options
    options)
    );
    ;
    react-firebase-pagination-hooks
    チャットのメッセージをページングして、無限スクロールを実装
    loadMore
    メソッドを実⾏すれば次のデータが読み込める
    const
    const [
    [messages
    messages,
    , {
    { loaded
    loaded,
    , loadingMore
    loadingMore,
    , loadMore
    loadMore }
    },
    , error
    error]
    ] =
    = usePaginationData
    usePaginationData<
    T>
    >

    View Slide

  12. React Native
    の品質管理
    React Native
    に関してはテストコードは皆無(
    汗)
    機能が少なく、ライブラリ依存が⼤きいため
    毎回ウォークスルーテストを⼿動でしている
    エラー検知はSentry
    画⾯遷移やメッセージ送信時などでイベントを発⽕して記録
    エラーログが出ると、イベントを辿ることでユーザーの動きを擬似再現できる
    ユーザーID
    も紐付けができる
    ソースマップをSentry
    にデプロイすることで、エラーが起きたソースコードの該
    当箇所を特定しやすい
    Sentry
    Sentry.
    .addBreadcrumb
    addBreadcrumb(
    ({
    {
    category
    category:
    : 'ACTION'
    'ACTION',
    ,
    message
    message:
    : 'SEND_CHAT_MESSAGE'
    'SEND_CHAT_MESSAGE',
    ,
    data
    data:
    : {
    {
    roomId
    roomId:
    : room
    room.
    .id
    id,
    ,

    View Slide

  13. React Native
    の落とし⽳
    ハマると解決が厳しい
    某ライブラリと某ライブラリを併⽤し、かつ特定の動作をユーザーが⾏うとアプリ
    がクラッシュする事案があった
    画⾯が突然ブラックアウトして強制的に再起動になる
    クラッシュが突然ブラックアウトするためエラーログが取れない
    ライブラリのソースを追っても、ある程度追うとネイティブのコードになり謎が
    深まる
    結局何が原因か分からないままに、当該ライブラリの組み合わせを変えることで解消...

    View Slide

  14. いつでもiOS/Android
    で同じと思うなよ現象
    チャットで画像を送信するとき、画素数を落とさずにファイルサイズを⼩さくする
    ために圧縮した
    expo-image-manipulator
    のImageManipulator.manipulateAsync
    で圧縮しようと
    すると、同じ圧縮率を指定してもAndroid
    から実⾏すると画像が圧縮されすぎて
    しまった
    OS
    ごとに分岐して圧縮率を調整することで解消
    ソースレビューの段階では何ら問題なく⾒えるので恐ろしい
    他にも通知周りなど、細かいところで結局OS
    ごとに分岐する箇所があるので、両
    OS
    での実機確認は必須

    View Slide

  15. Firebase
    の利⽤ライブラリ
    Cloud Functions
    側で使っている技術を軽く紹介していきます

    View Slide

  16. firestore-simple
    Firestore
    に対する形安全なDAO
    が⼿に⼊れられるライブラリ
    const
    const roomDao
    roomDao =
    = firestoreSimple
    firestoreSimple.
    .collection
    collection<
    Room>
    >(
    ({
    { path
    path:
    : 'rooms'
    'rooms' }
    })
    )
    const
    const allRooms
    allRooms =
    = await
    await roomDao
    roomDao.
    .fetchAll
    fetchAll(
    ()
    ) // allRooms: Room[]
    // allRooms: Room[]

    View Slide

  17. Firebase
    の設計
    Firestore
    は書き込み処理に重点を置く
    SQL
    と違って読み取りクエリが貧弱
    ⾮正規化を許容して、読み取りの仕様に合わせて最適化したコレクションを作る
    例:チャット部屋⼀覧で最新のメッセージを⾒せるために、messages
    コレクション
    のonCreate
    でrooms
    コレクションに最新のメッセージを遅れて同期させる
    例:全ての未読数の合計を出すために、messages
    へのonWrite
    ハンドラでユーザー
    ごとに未読数を集計して別途保存する

    View Slide

  18. Firebase
    の品質管理
    Functions
    でのエラーログをSlack
    通知
    https://zenn.dev/meijin/scraps/94d4a70eb77507
    Functions
    でキャッチされなかった例外も含めSlack
    等でキャッチアップしたかった
    Cloud Functions
    ログ→GCP→
    ログルーター→PubSub
    トピック→Cloud
    Function→Slack
    通知

    View Slide

  19. Firebase
    の落とし⽳
    Firebase
    というかExpo
    の落とし⽳だが、Expo
    へのPush
    通知は割と⾼頻度(
    週に数
    回)
    で502
    エラーになる
    400
    系なら分かるが、502
    ならどうしようもない
    Expo
    に問い合わせたが、リトライ処理を実装してくださいとのことだったので、
    ⾃前で実装しました

    View Slide

  20. React Native
    で良かったと思うこと

    View Slide

  21. 1.
    簡単な修正ならWeb
    エンジニア(
    僕)
    でも理解・実装できる
    そもそもReact
    が個⼈的に好き(
    突然の主観)
    最近練習を兼ねてRN
    で個⼈開発しているが、TSX
    を書くのが楽しいしスタイルも
    CSS
    感覚なので敷居が本当に低い
    Expo
    のおかげで環境構築時やコードを書くときに各OS
    の存在をほとんど意識しな
    くて良い
    Firebase JS SDK
    のコードをNuxt.js
    とRN
    でほぼコピペ可能
    先⽇Firebase
    のStorage
    にファイルを上げる時に、content-disposition
    ヘッダを指
    定することでダウンロード時にファイル名を維持する施策を実装したが、ほぼ同
    じコードでWeb/
    アプリを実装できたので便利さを実感した

    View Slide

  22. 2.
    受験シーズンに間に合うように開発できた!
    昨年6
    ⽉から開発スタートし10
    ⽉にリリース
    React Native
    で開発することで受験シーズンに間に合い、積極的に使ってもらう
    ことができた
    ほぼチャット機能のみのシンプルなアプリでも好評だった
    親・先⽣・⽣徒がやり取りでき、ファイルや画像も送れるのは当然に⾒えて、案
    外事業者側でそこまで内製する企業が少ないらしい
    宿題や過去問などファイルをやり取りすることは想像以上に多く、特に先⽣から
    好評をいただくことができた
    教育事業が最も盛り上がる受験シーズンまでに、最低限どんな機能があればニーズ
    が満たせるか理解できた
    スタートアップの検証サイクル的にはとても有り難い技術
    (
    初期リリースの内容を予定から絞って、チャットだけを作り込む判断も功を奏し
    た)

    View Slide

  23. まとめ
    React Native
    のチャット機能開発
    UI
    はreact-native-gifted-chat
    が便利
    データ読み込みはHooks
    関連のライブラリが便利
    エラー検知はSentry
    が便利
    Firebase
    のチャット機能開発
    読み取りの都合に合わせて⾮正規化や冗⻑性を許容する
    Cloud Functions
    のonWrite
    等を使って同期
    開発体制を柔軟に決められる技術スタックなので⼤変助かっている

    View Slide

  24. 告知

    View Slide

  25. ご清聴ありがとうございました
    Twitter: @Meijin_garden

    View Slide