Slide 1

Slide 1 text

ZOZOマッチを支える GraphQL実装の裏側 株式会社ZOZO
 ブランドソリューション開発本部 新規事業部 フロントエンドブロック
 ブロック長
 池田 一成 Copyright © ZOZO, Inc. 1

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO ブランドソリューション開発本部 新規事業部 フロントエンドブロック ブロック長 池田 一成 2022年 株式会社ZOZOに中途入社 ZOZOTOWN Androidアプリの開発を担当後 FlutterでのZOZOマッチアプリの開発を担当 GitHub:@ike04 X:@ike_pikmin 2

Slide 3

Slide 3 text

© ZOZO, Inc. https://zozomatch.jp/ 3 ● 2025年6月30日提供開始 ● 全身見える直感型マッチングアプリ ● ファッションジャンル診断などの情報をもとに、ZOZO独自のAIが 「好みの雰囲気」の相手を紹介(最大20人/日) ● プロフィールに登録された全身写真からユーザー自身の雰囲気も 分析 ● 全身写真と自分らしさを表すキャッチコピーを登録するプロ フィールで、個性や魅力が一目で伝わりやすい設計 ● 公的証明書を利用した本人確認や24時間365日の監視体制

Slide 4

Slide 4 text

© ZOZO, Inc. 4 アジェンダ 1. GraphQLとは
 2. ZOZOマッチでの採用理由
 3. GraphQLの実装で苦労したところ
 4. メッセージ機能の実装で工夫したところ


Slide 5

Slide 5 text

© ZOZO, Inc. 5 GraphQLとは Meta社が公開したAPIのクエリ言語
 REST APIの課題を解決するために開発された 操作タイプ 
 用途
 REST APIでの相当 
 Query
 データの取得
 GET
 Mutation
 データの作成・更新・削除
 POST/PUT/DELETE
 Subscription
 リアルタイムデータの購読
 WebSocket/SSE


Slide 6

Slide 6 text

© ZOZO, Inc. 6 ZOZOマッチでの採用理由 ZOZOマッチの機能についておさらい ● お互いにいいね!を送信しマッチングした ユーザー間でメッセージのやりとりができる ● いいね!を送ったお相手からいいね!が返っ てくるとメッセージを受信できる

Slide 7

Slide 7 text

© ZOZO, Inc. 7 ZOZOマッチでの採用理由 ZOZOマッチのメッセージ機能の要件 ● メッセージの送受信がリアルタイムで反映 される
 ● オフラインの時に受信したメッセージもオ ンライン復帰時に受信できる
 ● 既読の管理ができる
 


Slide 8

Slide 8 text

© ZOZO, Inc. 8 ZOZOマッチでの採用理由 REST APIの課題 1. リアルタイム性の実装が複雑 ポーリングでは遅延 WebSocketの実装は複雑 2. 複数回のAPIコール メッセージ一覧、ユーザー情報、既読 状態などを別々に取得する必要がある 3. オーバーフェッチング 不要なデータも含めて取得してしま い、通信量が増加 GraphQLによる対応 WebSocketベースのリアルタイム通信を 標準機能として提供し、リアルタイムでの メッセージの反映を実現 1回のリクエストで必要なデータをまとめて取得可能 必要なフィールドのみを指定して取得でき、通信 量を最適化 Subscription 単一エンドポイント 柔軟なクエリ

Slide 9

Slide 9 text

© ZOZO, Inc. 9 アジェンダ 1. GraphQLとは
 2. ZOZOマッチでの採用理由
 3. GraphQLの実装で苦労したところ
 4. メッセージ機能の実装で工夫したところ


Slide 10

Slide 10 text

© ZOZO, Inc. 10 GraphQLの実装で苦労したところ graphql_codegenで生成されたコードの理解 ● 生成されるコードの行数が多く、何を呼び出すと何ができるのかを把握するのが大変だっ た


Slide 11

Slide 11 text

© ZOZO, Inc. 11 メッセージ機能の実装で苦労したところ GraphQL Subscriptionの疎通
 ● WebSocketの認証失敗時にログが出力されないため原因特定が難しい
 ● App Sync側にもSubscriptionにイベントを通知したかのログが残らないため
 アプリの実装かApp Syncの設定の問題なのか切り分けるのが難しい
 → アプリ側で詳細なログを出力できるようにして、コミュニケーションを取ること
   で解決


Slide 12

Slide 12 text

© ZOZO, Inc. 12 アジェンダ 1. GraphQLとは
 2. ZOZOマッチでの採用理由
 3. GraphQLの実装で苦労したところ
 4. メッセージ機能の実装で工夫したところ


Slide 13

Slide 13 text

© ZOZO, Inc. 13 メッセージ機能の実装で工夫したところ ● 楽観的更新によるUXの工夫
 ● AWS AppSyncとの統合における課題と解決策
 ● アプリバックグラウンド時のSubscription管理
 


Slide 14

Slide 14 text

© ZOZO, Inc. 14 楽観的更新によるUXの工夫 Optimistic Update(楽観的更新) 
 ● ユーザーが行った操作をサーバーからのレスポンスを待たずにUIに反映する手法
 ● メリット
 ○ ネットワーク遅延をユーザーに感じさせないため、アプリが軽快に感じられる
 ○ すぐに反応があるため、操作に対するフィードバックが直感的で体験が良い
 ● 利用例
 ○ いいねボタンタップ時のUI反映


Slide 15

Slide 15 text

© ZOZO, Inc. 15 楽観的更新によるUXの工夫 ZOZOマッチではメッセージ送信の際に楽観的更新の処理を追加 1. 即座のUI更新
 ○ ユーザーがメッセージ送信ボタンをタップすると、一時的なメッセージオブジェクトをローカルに 追加し、即座にUIに反映
 2. バックグラウンドでの送信 
 ○ 並行してGraphQL Mutationでサーバーへメッセージを送信
 3. データの同期 
 ○ Subscription経由でサーバーから正式なメッセージデータを受信後
 一時的なメッセージを置き換え
 


Slide 16

Slide 16 text

© ZOZO, Inc. 16 楽観的更新によるUXの工夫 楽観的更新に未対応 楽観的更新に対応済み すぐに送信したメッセージが画面に反映されるので快適!

Slide 17

Slide 17 text

© ZOZO, Inc. 17 AWS AppSyncとの統合における課題と解決策 課題 ● バックエンドでAWS AppSyncを利用している場合、標準のWebSocketLinkでは AWS AppSync独自の認証形式に対応できない 解決策 ● 特別な形式の認証ヘッダーを要求するため
 カスタムのWebSocketLinkを実装する https://github.com/zino-hofmann/graphql-flutter/issues/682

Slide 18

Slide 18 text

© ZOZO, Inc. 18 AWS AppSyncとの統合における課題と解決策 /// AWS AppSync固有の認証形式に対応するためのリクエストシリアライザー /// 参考: https://github.com/zino-hofmann/graphql-flutter/issues/682#issuecomm ent-759078492 class _AppSyncRequest extends RequestSerializer { const _AppSyncRequest({required this.authHeader}); final Map authHeader; @override Map serializeRequest(Request request) => { 'data': jsonEncode({ 'query': printNode(request.operation.document), 'variables': request.variables, }), 'extensions': {'authorization': authHeader}, }; } 固有の認証形式に対応した リクエストシリアライザーを実装 https://docs.aws.amazon.com/appsync/latest/devguide/real-ti me-websocket-client.html#subscription-registration-message

Slide 19

Slide 19 text

© ZOZO, Inc. 19 AWS AppSyncとの統合における課題と解決策 /// [WebSocketLink]をベースにした、カスタムのWebSocketLink class CustomWebSocketLink extends Link { Future connectOrReconnect() async { await _socketClient?.dispose(); _socketClient = SocketClient( url, config: SocketClientConfig( serializer: _AppSyncRequest(authHeader: authHeader), ), onMessage: (message) { logger.info('GraphQL Subscription message: $message'); }, ); } ① WebSocketLinkを自作で実装 serializerに先ほどの自作した AppSyncRequestを設定 独自の形式でリクエストボディを送信 ② onMessageの中でログを出力すること で ● Subscriptionの接続が継続してい ること ● 購読したイベントの中身 の確認が容易になる

Slide 20

Slide 20 text

© ZOZO, Inc. 20 アプリバックグラウンド時のSubscription管理 課題 ● アプリをバックグラウンドに移行した際にSubscriptionで使用している WebSocket接続が自動的に切断されてしまうことがある → OS側でバッテリー消費を抑えるため、バックグラウンドアプリの   ネットワーク接続を制限する 発生する事象 1. アプリをバックグラウンドにすると、WebSocket接続が切断される
 2. この間に送信されたメッセージは、Subscriptionでは受信できない
 3. ユーザーがプッシュ通知でメッセージを確認してアプリに戻っても、受信したはずのメッ セージが表示されない

Slide 21

Slide 21 text

© ZOZO, Inc. 21 アプリバックグラウンド時のSubscription管理 解決策 1. 自動再接続の実装 autoReconnect: trueの設定により、アプリがフォアグラウンドへ復帰した際WebSocket接続 を自動的に再開し、Subscriptionの購読を復帰
 2. データの再取得 
 アプリのライフサイクルイベントを監視し、フォアグラウンド復帰時に
 最新のメッセージリストをQueryで再取得


Slide 22

Slide 22 text

© ZOZO, Inc. 22 まとめ ● GraphQLの用途と登場人物を紹介 → リアルタイム通信かつ効率的に必要なデータ取得する際に用いるAPI言語 ○ Query:データ取得(GET) ○ Mutation:データ更新・登録・削除(POST) ○ Subscription:リアルタイムデータの購読 ● GraphQLの実装で苦労した点を紹介 ○ graphql_codegenで生成されるコードが複雑 ○ Subscriptionの疎通確認が難しい ● メッセージ機能の実装での工夫点を紹介 ○ 楽観的更新によるUXの改善 ○ アプリバックグラウンド時のSubscription管理 ○ AWS AppSyncとの統合における課題と解決策

Slide 23

Slide 23 text

© ZOZO, Inc. 23 参考資料 1. https://graphql.org/ 2. https://pub.dev/packages/graphql_flutter 3. https://pub.dev/packages/graphql_codegen 4. https://developers.cyberagent.co.jp/blog/archives/48956/ 5. https://qiita.com/ryotanny/items/451d770bc191942fa30f

Slide 24

Slide 24 text

No content