astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

CoreNLPを使ってみる(3) Spark編

山縣です。

前回に引き続き CoreNLP を触っていきたいと思います。 前回までに API の使い方を見てきたので、今回は Spark からの使い方を見ていきたいと思います。

spark-corenlp

セットアップ

spark からCoreNLPを容易に使用する方法として spark-corenlp パッケージがあります。 spark-corenlp は Spark Packages で公開されています。また CoreNLP も Maven Repository で公開されており、下記のように spark shell の引数 --packages で指定するだけで利用することができます。 (弊社では CDH の spark 2.2.0を scala 2.11で利用しています)

$ spark2-shell --packages databricks:spark-corenlp:0.2.0-s_2.11,edu.stanford.nlp:stanford-corenlp:3.6.0

ためしに下記のサンプルを実行してみます。

import org.apache.spark.sql.functions._
import com.databricks.spark.corenlp.functions._

import spark.implicits._

val input = Seq(
  (1, "Stanford University is located in California. It is a great university.")
).toDF("id", "text")

val output = input
  .select(explode(ssplit('text)).as('sen))
  .select('sen, tokenize('sen).as('words), ner('sen).as('nerTags), sentiment('sen).as('sentiment))

output.show(truncate = false)

pasteモードでコピペして Ctrl-D します。

scala> :pa
// Entering paste mode (ctrl-D to finish)

import org.apache.spark.sql.functions._
import com.databricks.spark.corenlp.functions._
...

// Exiting paste mode, now interpreting.

18/04/20 14:42:51 WARN scheduler.TaskSetManager: Lost task 0.0 in stage 0.0 (TID 0, cdh-dn03-dt.c.stg-astamuse-astamus
e.internal, executor 1): org.apache.spark.SparkException: Failed to execute user defined function(anonfun$ner$1: (stri
ng) => array<string>)
        at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.processNext(Unknown Source)
...
Caused by: java.io.IOException: Unable to open "edu/stanford/nlp/models/pos-tagger/english-left3words/english-left3wor
ds-distsim.tagger" as class path, filename or URL
        at edu.stanford.nlp.io.IOUtils.getInputStreamFromURLOrClasspathOrFileSystem(IOUtils.java:485)

エラーになってしまいました。モデルデータが無いのが原因です。Maven にはモデルデータも上がっているのですが、 使用するためには classifier を指定する必要があります。しかし --packages でのclassifier の記述の仕方が不明でうまく指定することが出来ませんでした。仕方がないのでモデルファイルをダウンロードして直接 jar ファイルを指定することで解決しました。

$ spark2-shell --packages databricks:spark-corenlp:0.2.0-s_2.11,edu.stanford.nlp:stanford-corenlp:3.6.0 \
  --jars ./libs/stanford-corenlp-3.6.0-models-english.jar

上記のようにダウンロードした英語のモデルデータ stanford-corenlp-3.6.0-models-english.jar へのパスを --jars で指定します。

再度実行してみます。

scala> :pa
// Entering paste mode (ctrl-D to finish)

import org.apache.spark.sql.functions._
...
// Exiting paste mode, now interpreting.

+----------------------------------------------+----------------------------------------------------+--------------------------------------------------+---------+
|sen                                           |words                                                 |nerTags                                   |sentiment|
+----------------------------------------------+----------------------------------------------------+--------------------------------------------------+---------+
|Stanford University is located in California .|[Stanford, University, is, located, in, California, .]|[ORGANIZATION,ORGANIZATION, O, O, O, LOCATION, O]|1        ||It is a great university .                    |[It, is, a, great, university, .]                     |[O, O, O, O, O, O]                                |4        |
+----------------------------------------------+----------------------------------------------------+--------------------------------------------------+---------+

今度は実行されました。これで Spark から CoreNLPが使用できるようになりました。

spark-corenlp の概要

spark-corenlp パッケージは小さいパッケージで、コードは object com.databricks.spark.corenlp.functions があるだけです。このobject に定義されている UDF として CoreNLP の各機能を提供する形になっています。 UDF として以下のものが定義されています。

  • cleanxml
  • tokenize
  • ssplit
  • pos
  • lemma
  • ner
  • depparse
  • coref
  • natlog
  • openie
  • sentiment

ソースコードを見ると以下のように SimpleAPI を利用して実装されています。

functions.scala

...
  def tokenize = udf { sentence: String =>
    new Sentence(sentence).words().asScala
  }
...
  def pos = udf { sentence: String =>
    new Sentence(sentence).posTags().asScala
  }
...
  def lemma = udf { sentence: String =>
    new Sentence(sentence).lemmas().asScala
  }

なお sentiment だけ、対応する SimpleAPIがないようで StanfordCoreNLP インスタンスを利用していました。

自前でUDFを定義する

spark-corenlp パッケージを使わずに直接 CoreNLP を使用する場合、私は以下のように UDF を定義して使っています。

package test2 {

  import java.util.Properties

  import org.apache.spark.sql.functions.udf
  import edu.stanford.nlp.ling.CoreAnnotations.{SentencesAnnotation, TokensAnnotation}
  import edu.stanford.nlp.pipeline.{Annotation, StanfordCoreNLP}
  import org.apache.spark.sql.{DataFrame, SparkSession}

  import scala.collection.JavaConverters._

  case class ann(word:String, lemma:String, pos:String)
  object NLProc {
    val props = new Properties()
    props.setProperty("annotators", "tokenize,ssplit,pos,lemma")
    @transient val pipeline = new StanfordCoreNLP(props)

    val nlproc_f = (text: String) => {
      val document = new Annotation(text)
      pipeline.annotate(document)
      document.get(classOf[SentencesAnnotation]).asScala flatMap { s =>
        s.get(classOf[TokensAnnotation]).asScala map { t =>
          ann(t.word(), t.lemma(), t.tag())
        }
      }
    }:Seq[ann]

    val nlproc = udf(nlproc_f)
  }
}

paste -raw モードでコピペします。

scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

package test2 {
...

// Exiting paste mode, now interpreting.

性能劣化を避けるために pipline を UDFの呼び出しの度に作らないようにしています。 以下のように使用します。

import test2._
val df = Seq(
  (1, "Stanford University is located in California. It is a great university.")
).toDF("id", "Text")
val df2 = df.select(NLProc.nlproc($"Text").as("nlp"))
df2.show(truncate=false)
scala> :pa
// Entering paste mode (ctrl-D to finish)

import test2._
...
// Exiting paste mode, now interpreting.

+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|nlp                                                                                                                 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|[[Stanford,Stanford,NNP], [University,University,NNP], [is,be,VBZ], [located,located,JJ], [in,in,IN], [California,California,NNP], [.,.,.], [It,it,PRP], [is,be,VBZ], [a,a,DT], [great,great,JJ], [university,university,NN], [.,.,.]]|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

spark-corenlp の問題点

spark-corenlp は手軽なので、私も当初使っていたのですが最近は上記のように自分でUDFを定義して使用しています。

理由のひとつは対応する CoreNLP のバージョンが古いままな事があげられます。現在の spark-corenlp が対応する CoreNLP のバージョンは 3.6.0 ですが、CoreNLP の最新バージョンは 3.9.1 まで上がっており、かなりバージョンの差が開いてしまっています。

またもう一つの理由は性能面で問題があることに気がついたからです。

性能面での問題を明らかにするため、spark-corenlp を使った場合と、上記のような UDF を定義した場合で処理時間を比較してみました。

spark-corenlp と 自前のUDFの処理時間の比較

データとして Kaggle の Amazon Fine Food Reviews を使ってみます。zip ファイルを展開して CSV ファイルを hdfs 上に上げて下記のように読み込みます。

import com.databricks.spark.corenlp.functions._

val df =  spark.read.format("com.databricks.spark.csv").option("inferSchema", "true").option("header", "true").
load("hdfs://nameservice1/user/y.yamagata/AmazonFineFoodReviews.csv").repartition(4096).persist
scala> df.count
res2: Long = 568454

scala> df.printSchema
root
 |-- Id: integer (nullable = true)
 |-- ProductId: string (nullable = true)
 |-- UserId: string (nullable = true)
 |-- ProfileName: string (nullable = true)
 |-- HelpfulnessNumerator: string (nullable = true)
 |-- HelpfulnessDenominator: string (nullable = true)
 |-- Score: string (nullable = true)
 |-- Time: string (nullable = true)
 |-- Summary: string (nullable = true)
 |-- Text: string (nullable = true)

件数は57万件弱、今回は Text カラムのみを利用します。

scala> df.select("Text").show(truncate=false)
...
|"This was a serious surprise to us... our cats have always refused wet cat food except for people quality tuna in the
 can. Soon as we opened these for them ( was a varity pack ) they scarfed one down and everyday wanted more .. it real
ly looks, smells like people food! The package says ""people food for cats"" and after reading the ingrediants I belie
ve it is!" |
|Not too over powering. Lots of powder and not too salty. Comes with pieces of dried veggies and very small beef piece
s but adding more beef is not a problem. lol
...

上記のようなテキストデータが入っています。 spark-corenlp で Text カラムのデータに以下のような処理を実行します。

    def run(df: DataFrame): DataFrame = {
      ...
      val df2 = df.select(tokenize($"Text").as("word"), lemma($"Text").as("lemma"), pos($"Text").as("pos")).persist
      df2.count
      df2.unpersist()

tokenize, lemma, pos の処理を呼び出します。 自前のUDFでは以下のような処理を実行します。

    def run(df:DataFrame):DataFrame = {
      val df2 = df.select(NLProc.nlproc($"Text")).as("nlp").persist()
      df2.count()
      df2.unpersist()

NLProc.nlproc は前に定義したものになり、こちらもtokenize, lemma, posを呼び出しています。

この2つの処理を3回ずつ実行してJob の実行時間をSparkUI から取得しました。 Spark の実行パラメータは、Executor数:4, Core数:4 Executorメモリ12GB となります。 結果は以下のとおりです。

spark-corenlp(分) 自前UDF(分)
1回目  9.0 1.9
2回目  8.9 1.8
3回目  9.0 1.9
平均  9.0 1.9

自前のUDFで実行したほうが4倍以上速くなりました。

このような差がついてしまう原因としては、spark-corenlp が tokenize, lemma, pos と各 UDF を呼ぶ毎に別々に CoreNLP の API を呼ぶためではないかと考えています。 たとえば lemma UDF は内部で lemma アノテータ を呼んでおり、lemma アノテータはアノテータの依存関係から tokenize, ssplit, pos を呼びます。そして pos UDF は内部で pos アノテータを呼び、posアノテータは tokenize, ssplit を呼びます。このようにそれぞれのUDF毎に別々に処理を実行すると同じ処理を何度も呼び出すことになり計算量が多くなっているのではと予想しています。実際、呼び出す UDFを lemma だけにすると処理時間は2.4分程度になり、自前UDFに近い処理時間となりました。

終わりに

以上、Spark から CoreNLP を使う方法を見てきました。spark-corenlp パッケージは便利ですが処理速度やCoreNLPのバージョンへの追随に問題があると思われます。 現時点では自前でUDFを使ったほうが良いのではないかと考えています。 Spark で使用する場合は大量のドキュメントを処理したいことが多いかと思います。NLP周りの処理は重く時間がかかることが多いので、性能が落ちないように処理するように注意したほうが良いと思います。

NO MORE リリース職人 〜リリースツールをごっそり置き換えた話〜

はじめまして、JVMの世界に4年目、勇気と根性で乗り切る脳筋系エンジニア、chotaroと申します。
astamuse.comのバグフィックスや改善活動を担当しています。

今回はそのastamuse.comにおける改善活動の一部をご紹介します。
同じ悩みを抱えている組織や、アスタミューゼの開発チームに興味を持たれた方の参考になれば、幸いです。

ブラックボックス化する運用ツール

長年メンテナンスを続けてきたサービスでは、その運用方法や使っている技術要素が負債になってしまう、というのはよくあることだと思います。
astamuse.comも例に漏れず、そんな状況下にあります。

実装やプログラムの品質面では過去にえいやさんが記事を上げていますが、運用においてはまた異なる起因からメンテナンスを行う必要が生じます。

弊社開発チームでは、直接的にはメンバーの離脱や参入が見直しのきっかけとなりました。
過去に作られたしくみが「どうやってかは分からないがとりあえず動いているからOK」という状態になってしまっており、修正も難しくなっていることを検知したためです。

具体的には、

  • webサイトの運用ツール
  • リリース用のシェルスクリプト
  • jenkinsのJOB構成(およびその環境)

などのツールが対象です。

それぞれ残されたドキュメントや動いているツールのコードをもとに改善・再構築を進めていますが、中でも特に問題であったのはリリース用のシェルスクリプトでした。

既存の仕組みの問題点

これまでのリリースのしくみは、以下のような構成になっていました。

構成

f:id:astamuse:20180418115311j:plain
構成図_before

  • 担当者はgitlabからリリース対象のリポジトリをcloneする
  • リリース用のシェルスクリプトを、リリースする環境ごとの設定ファイルを食わせて実行する
  • ローカルの開発環境でビルドした資材をアプリケーションサーバーに配備し、リリースが完了する

問題点

  • リリース作業の成否、および対象がローカル環境に依存する
  • シェルスクリプトの可読性が低い
  • リリース手順の保守性が低い

特にシェルスクリプトでは、

  • mvn、gradleなどのうちからプロジェクトの構成にマッチするものでテスト・パッケージ化実行
  • 設定ファイルの権限変更および再配置
  • cacheの削除

など多くのタスクを条件に応じて行っており、上から下に読み下すことが困難な状態となっていました。
そのため、リリースに必要なタスクの全容を把握することすらも難しくする要因となっていました。

新しい仕組みの設計

暗黙知化していたリリースプロセス

もともとの構成は、全体としてオペレーションする開発者に依存する形となっているため、以下のような前提に基づいていたと考えられます。

  • 開発者は優秀なので、何かリリースで技術的な問題が起きてもシェルスクリプトを読めばすぐに問題点を解決できる。
  • 開発者は開発環境についても博識で注意深いので、ローカルとステージング・プロダクション環境での差分にも注意を払うことができる。
  • 開発者は構成管理についての知識と経験値を持っているので、デグレした資材をリリースするなんてありえない。

ある意味で、性善説的な哲学に則っているように思われます。

このあり方は人に大きく依存してしまうため、ノウハウが暗黙知化されてしまい、結果的にその人がいなくなったときのオーバーヘッドがかなり大きくなってしまいます。
そのため、新しく開発チームに入ってきた人でも、少し努力すれば形式知に落とし込めるくらいにプロセスをわかりやすくする必要がありました。

形式知化させるためのリリース戦略

2つの方針を立てました。

リリースはリモートの環境からそれぞれ行う。

個人の環境に依存させないようにします。

  • 各開発者のローカル環境からリリースはできないようにする(=シェルスクリプトは廃棄。)。
  • リリース作業用を行うマシンをクラウド上に配置し、リリースはそこから行う。

リリースタスクを構造化してオートメーションツールに任せる。

複雑怪奇なシェルスクリプトをオートメーションツールでリファクタリングします。

  • リリースプロセスに伴うタスクを抽象化することで、実施するタスクの全容をわかりやすくする。
  • 構造化することによって、パラメータをタスクから分離させる(パラメータによって振る舞いを定義する)。
  • 条件分岐を可能な限りなくし、実行するタスクを明確にする。

新しい仕組みに組み替える

検討結果を踏まえ新しい構成は、以下のようになりました。

構成

f:id:astamuse:20180418115230j:plain
構成図_after

  • 担当者はリリースする環境ごとのjenkinsにアクセスし、リリース用のjobを実行する
  • jenkinsはgitlabからリリース対象のリポジトリをcloneする
  • jenkinsはcloneしたリポジトリをもとにビルドを行い、warファイルなどリリース資材を作成する
  • jenkinsはansible-playbookを実行し、ビルドした資材をアプリケーションサーバーに配備し、リリースが完了する

改善効果

タスクの実行管理ツールとしてjenkinsを、オートメーションツールとしてansibleをそれぞれ活用することにより、以下のような効果を生み出すことができました。

jenkins採用のメリット

  • クラウド環境のjenkinsから、開発チームであれば誰でもリリースが可能になった。
  • 常にgitから最新のリビジョンを取得するため、デグレリスクがほぼゼロになった。

ansible-playbook採用のメリット

  • リリースタスクが抽象化されたことで、参入したての人でもリリースにかかるタスクの流れを把握できるようになった。
  • シェルスクリプトのときと比較してコード量が削減され、可読性、および保守性が向上された。

astamuse.comのこれから。

さて、そんな改善活動を推し進めているastamuse.comですが、事業モデルや取り巻く環境の変化に合わせ、プロダクトの方向性を再検討する必要が生じてきています。

開発に携わるメンバーが変遷し、また各要素技術のバージョンやトレンドが移り行く環境下でそのような対応を進めるには、「節約」することが必要です。
すなわち、負債になる部分および負債となってしまっている部分を、できるだけ抑制できる体制を仕組みの面で作り上げなくてはなりません。

今後開発チームとしては、引き続き本記事のような改善活動、開発プロセスの整備と共にプロダクトの再構築や改修の検討、実装を推進して行きます。
そのためには当然マンパワーも必要になります。

というわけで奥付で恐縮ではありますが、弊社のサービスや、これらの活動に興味を持っていただける方がおられましたら、是非お気軽に、下のバナーやwantedlyよりご応募いただければと思います。

以上、Have a nice development day!

PWAの将来性についての考察

f:id:astamuse:20180411105633p:plain デザイン部でフロントエンドエンジニアをしているkitoです。
先日、iOS 11.3のリリースでiPhoneでもPWAの一部の機能が利用できるようになりました。(プッシュ通知等は利用できない)今回は、Web界隈で急速に注目を上げ始めているPWA(Progressive Web Apps)の将来性を考察したいと思います。

いわゆるフロントエンド界隈では、新しいツールや技術が短いスパーンで登場しては消えていくということを繰り返しています。新陳代謝が激しい故に、革新的な技術がいち早く取り入れられる若々しいエコシステムは利点ではありますが、反面、苦労して学習した技術やツールが、立ちどころに古びて投資が無駄になってしまうケースも間々あります。これは現代のフロントエンドエンジニアには避けられない運命でしょう。とはいえ、それをむしろ楽しみ新しい技術の到来やそれをリードする若きエンジニアたちの登場を喜ぶべきかもしれません。

今回取り上げるPWAも、まだこれから普及するかどうかわからない技術です。しかし、もし普及すれば大きなインパクトがある技術で特別ホットな技術だと思います。このエントリーでは、PWAの個別の技術ではなく、PWAが開拓するかもしれないアプリの未来について個人的な展望を書きたいと思います。

PWAの概要

PWA(Progressive Web Apps)とは、端的に言うとHTMLとCSSとJavaScriptなどのWebアプリを作成する技術で、スマホのネイティブアプリにしかない機能を実現できる技術です。
主に下記のようなものです。

  • キャッシュを利用してオフライン時でも操作できる。
  • 全画面表示。
  • ネイティブアプリのようなプッシュ通知。
  • App StoreやGoogle Playを通さず、Web経由でのアプリ配布が可能。

ローカルキャッシュによるオフライン操作やスマホでのプッシュ通知は、Webのスマホサイトでは実現できない機能で、ネイティブアプリ固有の大きなアドバンテージでした。ストアを通さないアプリの配布は、開発者にとっては利点はありますが、ストアからアプリをインストールする習慣が身についているユーザーにとっては、必ずしも利点とは言えません。
あるいは、PWAはネイティブアプリのような大きなアプリを最初にインストールする必要がないので、通信環境が良くない地域の人にとっては良い選択肢なるという見解もあります。それはつまり、ネイティブアプリよりPWAが「poor」であることを逆手にとって、その「poor」さこそPWAの強味であるというわけです。

ただ、私はその売り出し方では、あまりインパクトがないように思えます。少なくとも日本にいる多くのユーザーにって、PWAは自分には関係ないと思うでしょう。そうではなく、後で詳述するユニバーサルアプリの文脈で、より低コストで必要十分なユーザー体験が実現できるアプリの技術として位置付けるべきです。それを詳しく見る前に、現状のPWAが直面している問題点をいくつか上げたい思います。

PWAの課題

まずひとつ目は、先にも触れましたがpoorである点です。
PWAは、ネイティブアプリよりパフォーマンスが明らかに劣っています。そもそもネイテイブアプリは、使われているプログラミング言語を含め、デバイスに最適化されていることが多いので、Web技術を借用しているPWAの方がパフォーマンス上は不利でしょう。ただし、PWAのpoorさは看過できないほどpoorであるとは考えていません。

実際、Google mapと日経新聞のPWAを使ってみると、ユーザー体験はそれほど悪くはないなという印象です。確かに、ネイティブアプリのGoogleマップの方が地図描画などがスムーズですが、実用に耐えないほどではなく、経路を検索したりする通常の用途では必要充分で、まあこれでも良いのではないかと思えました。もちろん、これはあくまで個人的な感想です。あなたもhttps://google.com/maps?force=pwaにスマホでアクセスし、ホーム画面にアプリを追加して使ってみてはいかがでしょうか。(もしネイティブアプリの方が起動してしまう場合は、シークレットモードやプライベートモードで開い下さい) 日本経済新聞のPWAもあります。https://r.nikkei.com/こちらもコンテンツを閲覧するのに必要充分だと思いました。(現在、Androidではプッシュ通知は来ますがiPhoneでは来ません)

ではネイティブアプリより、PWAを選ぶ積極的に選ぶ理由とは何でしょうか?
それはユーザーが選ぶというより、開発者がPWAを選ぶ理由ということですが、簡単に言うならコスト面で有利だからだと思います。
デバイスごとに最適化されるリッチなネイティブアプリより、Web技術を活用したユニバーサル志向のプアなアプリを作るほうが低コストですむからです。 そして、これは重要な観点ですが、今後10年を見据えた場合、VR/ARデバイスが普及した時代になると新たに3次元のアプリを作製してメンテンスしていかなければならなくなるでしょう。中長期的に考えるなら、Web、スマホなどの2次元用アプリは、ユニバーサルアプリに収斂させて、3次元用アプリは独自のアプリとして最適化する道を辿るのではないかと推測します。
つまり、ユニバーサルアプリは2次元用アプリのことで、その宇宙にはVR/ARデバイス用のアプリなどは想定されていません。ユニバーサルアプリに3次元用アプリも含めれば良いと思うかもしれません。しかし、少なくとも3次元用アプリが普及して離陸していく段階では、3次元用アプリはそこに含まれないと思います。なぜなら、2次元用アプリと3次元用アプリでは、デザインのロジックからして全然別だからです。

(参考:リープモーション社の3次元用アプリUIのサンプル動画です。)

例えば、Googleのマテリアルデザインは、2次元用のユニバーサルアプリのデザインとしては機能するでしょう。しかし、これを3次元用のアプリのデザインとして見た場合、オブジェクトが掴めたり、回転させたりという3次元特有のインタラクションを感じさせるUIデザインは見あたりません。もし、ユニバーサルアプリに3次元用のアプリを含めようとするなら、もっと新しいデザインコンセプトが必要になるでしょう。しかし、そのようなデザインはゲームの中では一部存在していますが、今のところ統一されたアプリのUIデザインとして成立していないです。ですから、ユニバーサルアプリには3次元用のアプリは容易には取り込まれないだろうと考えられます。私見ですが、PWAのようなユニバーサルアプリとは、Web技術で構成された2次元用アプリのプラットフォームのことだと考えています。

もう一つPWAが直面している大きな問題点は、AppleがどれほどPWAを積極的にサポートしていくのだろうかという点です。
率直に言って、AppleがPWAを押し進めなければPWAは普及せず、それを構成しているService Workerのような一部の技術が普及するに留まるかもしれません。実際その可能性はあると思います。Appleは、現状ではApp Storeを重視するビジネスモデルで、そこから多くの利益を得ています。ストアを通さずに配布できるPWAのプラットフォームに対して、積極的に投資するとは簡単には思えません。しかし、先日のiOSのアップデートでPWAの一部の機能がSafariに実装されました。これはどう考えるべきなのでしょうか。

実は、既に報じられているようにAppleもユニバーサルアプリのプラットフォームを実現しようと動き出しています。ただ、これはWeb技術を活用したものだと言明されているわけではなく、まだまったく未知数です。
それでも、先行しているMicrosoftのUWPを考慮に入れるなら、おそらくそれはWeb技術を活用したプラットフォームであり、さらに全てではないにしてもPWAの技術を取り入れているのではないかというのが、私の推測です。仮にPWA準拠したものであったとしたら、やはりストアの問題がネックになるでしょう。

ストアの問題に対して、Appleが取りうる選択肢は大きく2つあると思います。
ひとつは、ユニバーサルアプリをストアからしかインストールさせないようにする方法です。Webからのダウンロード&インストールを禁止して、全て管理されたストアからインストールしなくてはならないようにするのです。開発者は、これまで通りアプリの審査に四苦八苦することにはなりますが、ユーザーにとっては管理されたストアから安心してアプリをインストールできるので、それほどマイナスとは感じないでしょう、Appleにとっては今まで通りのビジネスモデルで手数料を徴収できます。

もうひとつは、ストアがゴーストタウンになる可能性を恐れず、ストアからの手数料を犠牲にしてWebからアプリをインストールできるようにし、ストアからの手数料をあてにする代わりに、Apple Payの手数料を収益の柱にするビジネスモデルです。
先日のiOSのアップデートで、一部PWAに対応したと書きましたが、そのアップデートでは、Payment Request APIが含まれています。いわばWeb全体をストアだとみなして、Apple Payの決済で手数料を徴収するわけですが、これはAppleのビジネスモデルとしては大きな転換になるので、リスクも大きくなるでしょう。

今はAppleのユニバーサルアプリがどのような形になるか、予測の域をでません。先にあげた二つの予想のどちらかになるかもしれませんし、ふたつをミックスしたものかもしれません。それでも、MicrosoftがWindowsでPWAをサポートすることを決めたり、iOSのアップデートを見ているとPWA軸にしてユニバーサルアプリのプラットフォームが構築される可能性は充分あると思います。

PWAのWebへの影響

推測に推測を重ねることになりますが、PWAがユニバーサルアプリとして普及したとき、おそらくWebへの影響が少なくないだろうと思います。
もともとWebの技術なのだからPWAが普及したとしてもWebへは何の影響もないと考えるむきもあるかもしれませんが、しかし、あるサービスを新たにWebとスマホで開発して展開しようとしたとき、PWAとWebは出来るだけ同じコードを使い回したいと思うはずで、そうでなければコストを削減できません。
PWAはSPA(Single Page Application)として実装されるでしょうから、結局WebサイトもSPAとして作製されることになるでしょう。つまり、PWAが普及すれば普及するほどWebのSPA化が進むことになります。
必然的にSPAを支える技術としてReactやVueなどのJavaScriptフレームワークの重要性が更に増し、サーバーサイドは、DBからデータをとってきてjsonにして返すことと、SSR(サーバーサイドレンダリング)を行う役割が重要になってくるでしょう。
さらに、そうなるとJSフレームワークの役割が重くなってフロントエンドでの分業化が進み、分業化に適したフレームワークが必要とされると思います。今のVueやReactは、分業化にはそれほど向いているとは思えないので、VueやReactがJavaScriptフレームワークの回答ではなく、将来的には分業化に適したJavaScriptフレームワークが登場するのではないかというのが、私の見立てです。
そこまで行くとフロントエンド界隈の幼年期は終わり、入れ替わり立ち替わり現れていた様々なツールやJSフレームワークは収斂して固定化されていくのではないかと思われます。  

終わりに

PWAは、ユニバーサルアプリを支える技術として注目を集める時代がもうすぐやってくるかもしれません。あるいは、AppleはPWAに投資せず、もっと違った方法でユニバーサルアプリを実現するかもしれません。実際にどう転ぶのかわかりませんが、わからないだけに独断と偏見で考えるのは面白いことだと思いました。ユニバーサルアプリがどんなものかは、これからそう遠くない未来にAppleが何らかの発表を行うのではないかと期待しています。そのときPWAの未来が決せられるでしょう。

アスタミューゼでは、エンジニア・デザイナーを募集中です。ご興味のある方は遠慮なく採用サイトからご応募ください。お待ちしています。

Copyright © astamuse company, ltd. all rights reserved.