Liberent-Dev’s blog

株式会社リベル・エンタテインメントのテックブログです。

Matrixプロトコルを試してみよう

こんにちは。
システム開発部のこたつみかんと申します。
夏はガツンとみかんが美味しいので楽しみです。

はじめに

さて、オンラインゲームにおいて、プレイヤー同士、またはファンとのコミュニケーションが取れることは非常に重要な要素となりました。
この手のサービスはゲーム外で賄うことが多く、最近はDiscordのシェアが非常に高いですが、代替手段を検討することは技術的な観点からも有効です。

現在Discordの代替サービスとして、Matrixに注目しています。

本記事では、分散型リアルタイム通信プロトコルであるMatrixと、そのクライアント実装であるElement、サーバー実装であるSynapse(次回以降予定)について簡単に紹介します。

今回はクライアント実装までをご紹介しますので、どなたも簡単に試してみることができると思います。

ゲームにおけるコミュニケーションの例

Matrixについてお話しする前に、ゲームにおいて通信を使ったコミュニケーションといえば皆さんはどのようなものを思い浮かべるでしょうか。
以下に少し例を挙げてみます。

プレイヤー情報の表示

通信をあまり使わないコミュニケーションの例です。
遅延を気にしなくてよく、通信量も少ないため以前よりよく使われています。
プレイヤー情報をあらかじめ記録しておき、必要に応じて取り出して表示することでそれぞれの個性が出せます。

ゲーム内チャット・掲示板・通話

ジャンルを問わず普及しました。
もはや必須機能と言っても過言ではありません。
また通信の発達とともに即時性が求められるようになりました。
特に戦闘中の仲間とのやり取りなどは遅延がひどいと致命的です。

観戦、実況

動画サイトの普及とともに急激に伸びてきた分野です。
人気が出れば出るほど広帯域、高負荷への対策が必要になります。
また、視聴者とのコミュニケーションの手段があるとさらに楽しくなります。

Discordのようなサービスは主に後者の目的で利用されています。

Matrixとは

Matrixは分散型のリアルタイム通信プロトコルです。
公式サイトであるmatrix.orgではさまざまな情報を公開していますので、興味の出てきた方はご覧になることをおすすめします。

基本的な通信の仕組み

Matrixはアプリケーション層のプロトコルで、その通信はRESTful APIで行われます。
クライアントは接続するサーバーを指定して通信を行います。
このサーバーのことをホームサーバーと呼びます。
ホームサーバーに自分を登録することになるため、クライアントのアカウントは@account:homeserverとなります。

クライアントからは「events」と呼ばれるJSONオブジェクトが送出されます。
全ての通信はこのeventsのやり取りです。

クライアント-サーバー間の通信はClient-Server APIにより行われます。

分散型

分散型の通信というとMastodonを思い出す方もいるかと思いますが、Matrixも同様にどこかのサーバーに接続すれば他のサーバーと連携しており、どのサーバーの誰とでも会話が可能です。

仕組みとしてはクライアントとホームサーバー間の通信ログを他のホームサーバーと同期することで実現しており、結果としてルームの全ログを参加している全てのサーバーが保持することとなります。
この同期は「Federation」と呼ばれ、Server-Server API により行われます。

相互接続性

Matrix独自の大きな特徴がその相互接続性です。

「Bridge」という仕組みを使った複数の通信システムとの相互接続が可能であり、様々なサービスをまたいだコミュニケーションが取れます。
現在公式ではSlack、IRCXMPP、Gitterへのブリッジがメンテナンスされており、さらにコミュニティによってDiscord、Facebook、Hangoutsなどへのブリッジが提供されています。

通信の秘匿性

すべての通信はHTTPSで行われます。
End to Endでの暗号化により、ユーザー間の通信内容をサーバーが知ることはできません。

採用実績

プロトコルがオープン標準でオープンソースのリファレンス実装が提供されていることからオープンソースと親和性のある欧州での採用例が多く、フランス政府やドイツ政府の独自のインスタントメッセージングツールとして利用されています。
また、Mozillaのように開発コミュニティでのコミュニケーション手段をIRC(Internet Relay Chat)から切り替えた例もあります。

他サービスとの違い

Mastodon

Matrixの分散型のシステムはMastodonと近い部分です。
サービスとしては以下のように役割が違いますので、使い分けが可能です。

Discord

こちらはMastodonと違い、サービスの形としては両方ともプライベートなインスタントメッセージングサービスです。

Discordは内部の仕組みが一部紹介されてはいますが、1社提供のプロプライエタリなサービスです。
インフラを意識する必要なく使え、DiscordAPIを使った開発も可能で自由度は比較的高いですが、自分でサーバーを立てることはできません。
Matrixはオープンソースであるため技術情報は全て公開されており、開発の自由度は非常に高いです。
加えてリファレンス実装で自分のサーバーも立てられるので、誰でもサービス提供者になることが可能ですが、サービスの運用も自ら行う必要があります。

環境やケースに応じて適したものを利用するのが良いかと思います。

クライアントのリファレンス実装 Element

Matrixのクライアント側リファレンス実装として、Elementがあります。
WindowsMacOSXLinuxiOSAndroidのアプリとWebブラウザに対応しており、幅広いプラットフォームからの利用が可能です。

今回はWindowsで試用してみます。

インストール

Element公式Webサイトの「Get Started」から入手できます。
「Desktop」の「Windows (64bit)」をダウンロードしたら、インストーラーに従って、インストールします。

インストール後の起動画面がこちらです。
ElementはDiscordと同様にElectronを使用しているアプリで、この画面自体はWebブラウザ版と全く同じです。

アカウント作成

起動できましたら次はアカウントの作成です。
「アカウントを作成」を選ぶと以下の画面になります。
デフォルトのホームサーバーは「matrix.org」です。
特に接続するホームサーバーがなければこのまま進めます。

また、OpenIDでの接続に対応していますので、認証情報を入力して新規登録を行わなくてもアカウントの作成は可能です。
今回は説明の都合上、認証情報を入力します。

ホームサーバーの運営方針を確認し、問題なければ「同意」を選びます。

確認のためのメールが送られます。

メール内のVerifyを選ぶとアカウントの作成が完了します。

使ってみる

では早速使ってみましょう。
サインインします。

サインインすると暗号鍵のバックアップ方法について確認が入ります。
今回はセキュリティーキーを生成します。
生成されたものは適宜保存してください。

お待たせしました。
こちらがホーム画面です。
まず基本となる公開ルームを使ってみましょう。

「公開ルームを探索」を選びます。

こちらはmatrix.org内のルームです。
「プレビュー」が表示されているところは参加前に内部を閲覧可能なルームです。
matrixについての公式ルームである「Matrix HQ」をプレビューしてみましょう。

こちらがプレビュー画面です。
DiscordやSlackを使っていた方々には見慣れた画面かと思います。
問題なさそうであれば参加ボタンを押します。

参加しました。
これで発言できます。

自分でルームを作成することも可能です。
では非公開のルームを作成してみましょう。
ホームの右側にあるプラスを選び、「新しいルーム」を選びます。

名称を決めて暗号化を有効にするかを選び、ルームを作成します。
高度な設定を表示すると他のホームサーバーと連携しないルームの設定もできます。

空のルームができたので他のユーザーと共有します。
ユーザー名を指定して招待します。

招待された方はこのように表示されます。
同意するとルームに入れます。

投稿が出来ました。
それぞれの投稿に対してはリアクションの絵文字を返すことができます。
また、ルーム内ではビデオ会議を開催できます。
こちらはDiscordと同様にWebRTCを利用しています。

次はダイレクトメッセージを使ってみます。
「ホーム」の右側にあるプラスを押し、「チャットを開始」を選びます。

送りたいユーザー名を記入して「続行」を選びます。

ダイレクトメッセージのやり取りができました。

まとめ

Elementの使い勝手はいかがだったでしょうか。
Matrixは、リアルタイム通信が身近になった今こそ知っておくべきプロトコルです。
ぜひ一度試してみてください。
次の機会には、サーバー側のリファレンス実装Synapseについて説明する予定です。


リベル・エンタテインメントでは、このような最新技術などの取り組みに興味のある方を募集しています。 もしご興味を持たれましたら下記サイトにアクセスしてみてください。 https://liberent.co.jp/recruit/

iOS、Androidのサブスクリプションのサーバ通知の実装について

こんにちは!
システム開発部のK.Mです。

iOSAndroidの各プラットフォームに用意されている自動更新のサブスクリプション(以下、サブスク)に関して、調査のため実装を行い、サーバ部分に関して得た知見を記載していきます。

前置き

近年、アプリゲームにて自動更新はされないが、購入すれば30日間ログイン時にガチャ用の石が一定数貰える+αという定期購入的な課金アイテムが増えてきております。
ただ、このような課金アイテムは各プラットフォームが用意している自動更新のサブスクを使わずに通常の課金アイテム扱いとして、ゲームサーバ側で30日間のチェックを行い、30日間経過したら無効状態にし、またユーザーに購入してもらうという形となっていたりするものが多いです。

この部分を各プラットフォームが用意している自動更新のサブスク機能を使って実装すれば、ユーザーは毎回購入する必要は無く、一度購入すれば自発的に解約しない限り毎月自動で更新する形が実装できます。
(提供者側からすると売上の予想が立てやすくなるメリットがあります。)

ただ、自動更新のサブスクに関しては通常の課金アイテムとは異なる実装が必要になってきます。
ここでは実際に自動更新のサブスクをどのように実装したかを、主にサーバ観点での情報を記載しております。

概要

Unityを使った実装を想定しており、クライアント(アプリ)側はUnityIAPで処理を行い、サーバ側の下記部分に関しての内容に言及しています。

  • サブスクのレシート検証
  • サブスクの自動更新方法

また、AppStoreConnectとGooglePlayConsoleにてサブスクのアイテムは登録済を前提としております。

iOSAndroid共にサブスクで決まった期間、例えば1ヶ月分のサブスクを購入し1ヶ月後の更新タイミングにて更新処理をする必要があるのですが、クライアントのみで対応するか・サーバにて対応するかの2パターンがあります。
今回記載している内容はサーバにて更新するケースとなります。

クライアントのみで更新する場合に考えられるのは

  • 毎回の起動時に各プラットフォームのAPIへアクセスしてサブスク有効かを確認
    • 無効ならサブスク解除
    • 有効ならサブスク更新

または

  • サブスク購入後、1ヶ月以上経過した後の初回起動時に各プラットフォームのAPIへアクセスしてサブスク有効かを確認
    • 無効ならサブスク解除
    • 有効ならサブスク更新

が考えられますが、前者に関しては

  • 起動するたびに各プラットフォームにアクセスするため無駄な通信が発生する
  • プラットフォームによってはAPIのアクセス回数が制限されている

という難点があり、後者に関しては

  • 時間の判定を端末内の時計で判断していると、端末の時間を変更して更新処理を回避される
    • サーバから日時を取得して判断すれば対応自体は可能だがメモリや通信を弄られると対策が難しい

という難点があるため、クライアントのみでサブスクの更新を行うのは少々リスクが伴います。
そのため、サブスクの更新処理はサーバで行うのが安全かと考えられます。

(現時点で自動更新のサブスクを使わずに通常の課金アイテムでサブスクっぽい動きをするものが多いのは、実装・テストが大変なのとセキュリティ的な問題でゲームアプリでは使用しているものが少ないのでは?というのが個人的な所感です。)

続きを読む

公式のgo-swaggerに機能追加した

https://static1.smartbear.co/swagger/media/assets/images/swagger_logo.svg

こんにちは。
システム開発部ネットワーク課のsupercontinueです。

swaggerとは?

  • swaggerは定義したスキーマからAPIコードを生成してくれるジェネレータです。
  • オープンソースです。
  • go-swaggerは定義したスキーマからgo言語のサーバのコードを生成します。
  • 弊社でも実際にリリースしたゲームで使いました。

  • もうずいぶん前になりますが、公式のgo-swaggerに機能追加したプルリクエストを送って、取り込まれた話をします。

何を追加したのか?

  • 例として、swaggerでは下記のようにAPIスキーマを定義します。
  sessionData:
    type: object
    properties:
      sessionId:
        type: string
      deviceId:
        type: string
      uMain:
        $ref: '#/definitions/u_main'
  • すると、下記のようなコードが生成されます。
type SessionData struct {

    DeviceID string

    SessionID string

    UMain *UMain
}
  • プロパティの順番に注目してください。

    • スキーマでは、sessionId、deviceId、uMain の順番になっています。
    • ところが、go-swaggerでは、アルファベット順にプロパティを並べ替えてコードを生成します
    • 生成されたコードでは、DeviceID、SessionID、UMain の順番になっています。
  • 順番が違うと何が困るのか?

    • MsgpackやProtobufではプロパティ名でなく、プロパティの順番や番号でシリアライズを行います。
    • 型と順番さえ同じなら、プロパティが追加されても、シリアライズ・デシリアライズすることが可能です。もちろん定義が無いプロパティは扱えませんが、存在するプロパティは扱えます。
    • スキーマ変更の前後で、プロパティの順番が違ってしまうと、コンパチビリティが失われます
    • また、スキーマにプロパティを追加したり、順番を変更すると、生成されるコードに意図ぜず大きな差分が発生し、差分が見づらくなります
  • そこで、プロパティの順番を明示的に指定する、x-orderというカスタム属性を追加することにしました。

    • swaggerでは、x-なんとかという属性は、プラットフォームごとの独自拡張の属性として扱われています。
    • たとえば、下記のようなカスタム属性があります。
      • x-go-name: "string": give explicit type name to the generated model
      • x-nullable: true|false (or equivalently x-is-nullable:true|false): accepts null values (i.e. rendered as a pointer)
  • x-orderを定義したスキーマ

  sessionData:
    type: object
    properties:
      sessionId:
        x-order: 0
        type: string
      deviceId:
        x-order: 1
        type: string
      uMain:
        x-order: 2
        $ref: '#/definitions/u_main'
  • すると、下記のようなコードが生成されます。
    • 構造体のプロパティの順番がx-orderで指定した順番になります
type SessionData struct {

    SessionID string

    DeviceID string

    UMain *UMain
}

実際に機能追加提案したコード

  • まずはレポジトリをForkします。

  • 主要な変更は下記です。

// 変更前

func (g GenSchemaList) Less(i, j int) bool { return g[i].Name < g[j].Name }
// 変更後

func (g GenSchemaList) Less(i, j int) bool {
    a, ok := g[i].Extensions[xOrder].(float64)
    if ok {
         b, ok := g[j].Extensions[xOrder].(float64)
       if ok {
            return a < b
       }
    }
    return g[i].Name < g[j].Name
}
  • 変更点はわずかですが、「どこを変更すればいいのか?」を正確に把握するには、swaggerのコードを読み込む必要があります。

  • 今回は、たまたまLessというメソッドが実装されていたので、変更が最小限ですみました。

  • テスト用のコードの方が多いです。

  • 実際のプルリクエストは下記です。

  • 結構さくっとマージしてもらえました。

結果

  • 公式のマニュアルにもx-orderカスタム属性が記述されています。

  • 当時はさくっと採用されて嬉しかったです!

    • オープンソースへの貢献は嬉しいものですが、負荷が高くなると辛くなるので。
  • 独自レポジトリで改変して利用していると、公式が更新された場合、追っかけなくてはなりません。公式にマージしてもらうほうが楽です。

  • さっき見たら、下記のような2020年のissueがありました。(まだOpenしたまま)


リベル・エンタテインメントでは、このような最新技術などの取り組みに興味のある方を募集しています。もしご興味を持たれましたら下記サイトにアクセスしてみてください。 https://liberent.co.jp/recruit/

試してみたlocustバージョンアップ対応

こんにちは!
システム開発部のK.Mです。

以前こちらで紹介した負荷試験ツールのlocustですが、当時使っていたバージョンがv1.6.0でしたが、既にv2.8.2(記事執筆時、現在の最新はv2.8.6)までバージョンアップされている状況でしたので、最新のものにバージョンアップを行い以前に実施した各種手順やテストシナリオに影響が出ていないかを確認していきます。

メジャーバージョンアップ時に破壊的な仕様変更が入っていたりするので要注意ですが、v2.0.0のリリース内容を見る限りそこまで影響があるような変更は無さそうです。

  • 1.6.0から2.8.2へのバージョンアップ
  • GKE環境の更新
  • テストシナリオの修正
  • 最後に
続きを読む

BigQueryとデータポータルでデータ加工・可視化をする方法

f:id:Liberent-Dev:20220325121021p:plain

こんにちわ。 システム開発部ネットワーク課のsupercontinueです。

はじめに

ゲームサービスをより良いものにしていくためには、ユーザーの行動分析が必要です。

分析するためには、データを収集・加工・可視化(≒集計)する必要があります。

http://takano-plantuml-server.herokuapp.com/svg/SoWkIImgAStDuU8gpixCAqWiIinLoCtFoq_EAChFJLKeAIfDvUBIUDhVzt9scwQ2hfs2njEk1K-tBzNaWZ4WWlthNooRzhHdcYQNGsfU2j100000

  1. 収集

  2. 加工

    • 上記のログを、「1日間」や「新規登録者」や「購入額」などのような分析に適した加工をします。
  3. 可視化

    • 加工されたデータを表やグラフを使い、分析に適した表現で可視化します。
    • 今回はGoogleのデータポータルを使います。

http://takano-plantuml-server.herokuapp.com/svg/SoWkIImgAStDuU8gpixCAqWiIinLoCtFoq_EAChFJLKeAIfDvUBIUDoqwN7pdiVD2nutRGKp9Zzksgcfe6kdeE6KcHvOcbfKgSc5O88b7ZUtWvGsxN_SF1V6MAt9vP2QbmAq3m00

そもそも、何のために・何を・どう分析するのかが重要ですが、それについてはここでは触れません。

以下では、加工・可視化の工程例を説明します。

加工・可視化の工程

基本的な工程は下記となります。

  1. BigQueryでデータセットを作成・選択、加工したデータの結果を格納するテーブルを作成します。

    • BigQueryのUIで作成できます。
  2. データを加工・格納するクエリーを作成します。

  3. デイリーでの集計などのため、必要に応じてクエリーをスケジュールします。

    • BigQueryでクエリーのスケジュールを作成できます。
  4. データポータルでデータを可視化します。

BigQueryでデータを加工するクエリーの例

  • 下記の構成で説明をします。

    • my_project プロジェクト名
    • my_dataset データセット
    • app_log ログのテーブル名
    • jsonPayload.kpitype ログの種類を示すカラム
  • クエリーはBigQueryのSQLワークスペースのものです。

    • BigQueryのSQLワークスペースではクエリパラメータが使えません。
    • クエリパラメータを使いたい場合は他のプラットフォームを利用する必要があります。

DAU

  • DAU(Daily Active Users)は、その日にプレイした人の一覧で、人数の合計値は一般的なKPIの一つです。
  • DAU一覧は各種の分析を行うにあたり、ソースの1つとすることが多いテーブルです。

DAU一覧

  • 集計テーブルは下記です。
フィールド名 種類 説明
date DATE 年月日
uid INTEGER ユーザーID
ostype INTEGER AndroidiOSなどOS種類を示す数値
  • 前日のDAUを集計するクエリーは下記のようになります。
  • 以下の例では、jsonPayload.kpitype = 23 が、プレイしたユーザーのその日最初のログです。
  • タイムゾーンに注意する必要があります。ここではAsia/Tokyoで集計を行なっています。
  • 再試行しやすくするために、集計する直前に結果を格納するレコードを削除しておきます。 
DELETE FROM`my_project.my_dataset.dau` WHERE date = DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY);

INSERT INTO `my_project.my_dataset.dau`
 SELECT 
  DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY) AS date,
  labels.uid AS uid,
  CAST(jsonPayload.ostype AS INT64) AS ostype

 FROM `my_project.my_dataset.app_log`
  WHERE jsonPayload.kpitype = 23
   AND  DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY) =  DATE(timestamp, "Asia/Tokyo");

DAU合計

  • DAU合計値は参照する機会が多いので、あらかじめ算出しておきます。
  • OSの種類ごとに合計値を出します。
  • 集計テーブルは下記です。
フィールド名 種類 説明
date DATE 年月日
total INTEGER DAUの合計ユーザー数
ostype INTEGER AndroidiOSなどOS種類を示す数値
  • 集計済みの dau テーブルから算出します。
  • クエリーは下記のようになります。
DELETE FROM`my_project.my_dataset.dau_sum` WHERE date = DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY);

INSERT INTO `my-project.my_dataset.dau_sum`
 SELECT 
  DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY) AS date,
  COUNT(DISTINCT uid) AS total,
  ostype AS ostype
 FROM `my_project.my_dataset.dau`
  WHERE date = DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY)
 GROUP BY ostype
 ORDER BY ostype;

データポータルでデータを可視化する方法

基本的な流れ

  1. データポータルで「レポート」を作成します。 f:id:Liberent-Dev:20220325113531p:plain

  2. データソースを追加します。

    • 「データの追加」で、BigQueryを選び、データソースを追加します。
      • プロジェクト、データセット、表を選びます。
    • トップメニューにある「データソースの追加」からも行えます。 f:id:Liberent-Dev:20220325113722p:plain f:id:Liberent-Dev:20220325113754p:plain
  3. 表やグラフを追加します。

    • 「挿入」からグラフの種類を選びます。 f:id:Liberent-Dev:20220325114719p:plain

    • 下記のようなグラフが選べます。
      f:id:Liberent-Dev:20220325114827p:plain

    • 「データソース」を指定します。

    • 「ディメンジョン」、「指標」などを指定します。 f:id:Liberent-Dev:20220325115131p:plain:h512

データソースの扱い方

データポータルではデータソースを使い、データの集計・結合・フィルタなどが可能です。

表現したい内容に応じて、下記のいずれかの方法でデータソースを扱います。

  • 1つのデータソースの内容をそのまま使う。

    • 単純な一覧表、そのテーブルだけで集計できる値、集計済みの値のみを表示する場合です。
    • 例えば、下記のような日付ごとに集計済みのKPIをグラフで表示したいケースです。
      f:id:Liberent-Dev:20220325115529p:plain
  • 複数のデータソースを結合して使う。

    • 複数のデータソースを結合して、新たなデータソースを定義することができます。
    • 1つのグラフに複数のデータを表示させたい場合。
      • 例えば、同じ日付で、別々のデータ(DAUと継続日数分布など)を表示させたいケースです。
    • 表示用の文字列があるマスターデータを使い、値を文字列へ変換して表示したい場合。

      • 例えば、データソースにはキャラクター番号が入ってるが、表示するときはキャラクター名を使いたい場合などです。 f:id:Liberent-Dev:20220325115632p:plain
    • クエリーを実行せずに、複数のデータソースを使ってアドホックな分析をしたい場合。

      • 例えば、DAU一覧とプレイ日数一覧と行動数一覧があり、結合して分析をしたいケースです。
    • ただし、なるべくBigQueryで結合や加工した集計済みのテーブルを用意する方が、データポータルだけで頑張るよりも効率的と感じます。

グラフの扱い方

  • グラフは非常に多岐に渡り、設定する情報も多いため、それぞれ必要な時にやりかたを調べるのがいいです。

  • グラフのスタイルの調整が、最も時間を食います。 最初は見栄えに拘らない方がいいと思います。

  • エクセルなどに比べ、安全にインタラクティブなレポートを作れるのが、データポータルの強みです。

    • フィルタ機能やソート機能をうまく使えるようになると、レポートを見る側の利便性が上がります。
  • 「期間」のフィルタは、データポータルで扱いやすくなっており、DATEの列があれば、勝手に設定してくれます。

    • 指標ごとに「期間」の範囲で値を、「合計」するのか「平均」するのかなどを設定する必要があります。たとえば、「新規ユーザー数」は「合計」が適切ですし、「DAU」は「平均」が適切な場合と「最大」のほうが適切な場合があるかもしれません。
  • 下記にデータポータルのサンプルのレポート画面をあげます。 見栄えには少々手間をかける必要はありますが、このようなレポートが作れます。

f:id:Liberent-Dev:20220325120010p:plain

f:id:Liberent-Dev:20220325120043p:plain

おわりに

  • BigQueryとデータポータルを使用して、開発コストを抑えて、ゲームのログデータの可視化を行えます。

  • エクセルなどと異なり、期間や項目などをインタラクティブに変更できるレポートが比較的簡単にできます。

  • KPIのようなルーティーンのレポート閲覧であれば、これで充分・コスパが良さそうです。

  • データポータルと同様のサービスであるLookerについても、確認した方が良いでしょう。


リベル・エンタテインメントでは、このような最新技術などの取り組みに興味のある方を募集しています。もしご興味を持たれましたら下記サイトにアクセスしてみてください。 https://liberent.co.jp/recruit/