LCL Engineers' Blog

夜行バス比較なび(高速バス比較)・格安移動・バスとりっぷを運営する LCLの開発者ブログ

iOSDC Japan 2017 day1 レポート

前夜祭を含んだ3日間で開催されるiOSの国内カンファレンス、iOSDCに参加してきました。

今回はその1日目(9/16)のレポートです。

今年のiOSDCは最大4つのメインセッションが並行で行われるということで、

前日から公開されていたタイムテーブルを見ながら、当日参加する予定を立てました。

タイムテーブル | iOSDC Japan 2017

レポート

オープニングは10時でしたが、急遽私用ができてしまい午後からの参加となりました。

ランチ前の時間に到着したのですが、ボランティアスタッフの方々が 終日受付対応をしてくださっていたので、スムーズに入場することができました。

ノベルティ

ノベルティは トートバッグ一杯に入っていました。 チケットのグレードが一段階上の「個人スポンサー」の方には更に扇子、ハンドタオル、 パーカー、チートシート下敷きがあったそうです。

f:id:lcl-engineer:20171031113640j:plain

日常的に使えるグッズばかりで助かります。

ランチ

iOSDCさんのツイート: “【ランチ手順】食べたいサンドイッチを選ぶよ!(3種類!)#iosdc https://t.co/W2abjyyjXJ”

サンドイッチ、サラダ、飲み物がそれぞれ複数種類用意されていました。 自分はタンドリーチキンのサンドとチリコンカンを選びましたが程良いピリ辛感でおいしかったです。

Build high performance and maintainable UI library

13:30~14:00 / Track A / Realm

Building High Performance and Testable UI component // Speaker Deck

要点

  • 高速なUIを作るには「計測」と「トレードオフ」が不可欠
    • 計測
      • 負荷の高い処理を減らす(原因を知る、速度を上げるテクニックを学ぶ)
    • トレードオフ
      • パフォーマンスを上げるに辺り、犠牲は付き物である。伴うトレードオフを知る。
      • 例:パフォーマンスを上げるために、コードが複雑になる
  • メンテナンスしやすい = テストしやすい
    • UIコンポーネントをテストするのは難しい
      • 内部状態が多く複雑 / 状態を変化させる要因が多い / 状態が相互作用を及ぼす / 正しい挙動が明確でない
    • Unit Testing vs UI Testing
      • UIテストはテスト対象に直接アクセスできない
      • 成功・失敗の基準が明確でない
    • テストの段階は大きく4つにわけられる
      • 内部状態、パラメータ、関数、出力結果
      • マッシブなUI(ViewController)は、各段階でイレギュラーで明確でない要素が複雑に絡み合っている
    • テストしやすい構造とは
      • データの流れを1方向にする
      • 状態をモデルに分離する
    • テストしやすい構造にすることで振る舞いをモックに置き換えれる
    • 薄いビュー(コントローラー)、分厚いモデル
  • Q/A
    • View側でイレギュラーなアクションが発生する場合は網羅的なテスト書くことを避けられない(使い分けが必要)

感想

弊社のiOSプロジェクトの「高速バス比較 – 国内の路線と最安値を検索するアプリ」でも 現在、安定性やパフォーマンスの向上を図るためにリファクタリングやUnit Test、UI Testの導入を行っています。

設計から見直したリファクタリングを経て、マッシブなVCから責務を細かく分割することができつつありますが、 テストとの兼ね合いを考えると構造が最適化できていない箇所もあるため、改めて見直したいと思いました。

モバイルアプリで困らないエラーハンドリングとロギングのベストプラクティス

14:20~14:35 / Track C / Mercari

モバイルアプリで困らないエラーハンドリングとロギングのベストプラクティス // Speaker Deck

エラーハンドリング、ロギングのやり方は多くの人が必要としている情報なのか、立ち見が出るほど人が集まっていた

要点

  • Crashlyticsで一番重要なこと
    • ログはクラッシュ後にアプリが再起動した時に送られる
    • application:didFinishLaunchingWithOptionsを無事に通過しないとダメ
  • application:didFinishLaunchingWithOptionsでしそうなこと
    • 各種フレームワークの初期化 / DBのマイグレーション / キャッシュのクリア
      • メインスレッドを長時間止めない
  • Crashlyticsには便利な付加情報がある
    • UserID、Email、名前などが追加可能
    • Key/Valueを追加可能(制限あり)
    • クラッシュしなくてもログを送ることが可能
  • iOS10から使えるようになった OSLog と組み合わせて自作ロガーを作る
    • コードはスライド参照
  • guard文のアンラップ時など、ありえない想定でもログレポートを吐かせるのが大事
    • 例えば、チームメンバーが変更した際など 将来的に発生する可能性もある

感想

標準のレポートに加えてログを渡す方法など参考になりました。

「ありえない想定でもログレポートを吐かせる」 、 これができていなかったので早い段階で各所に対応しようと思います。

短期間でやり遂げるための、大規模リニューアルの進め方

15:10~15:40 / Track A / Retty

短期間でやり遂げるための、 大規模リニューアルの進め方 // Speaker Deck

要点

  • 何故リニューアルをするか
    • 成長 / ピポット / 陳腐化 / リファクタリング / セキュリティ
  • どのようにリニューアルを進めるか
    • 徐々に / 一気に長期間 / 一気に短期間 / プロダクトを分けて
    • 各メリデメはスライドを参照
  • リニューアルあるある
    • 膨らむ仕様 / 足りない人手 / あとで増やされる人手 / 見積もりの不正確さ
      • 1日8時間コードを書けると思っている
      • 見えないタスクがあることを考慮しない
      • 仕様決定が遅れても工数調整しない
  • スコープを明確にする
    • 新規のはできるだけ増やさない
    • やらないを明確に
    • 決める人を明確に
  • Design Doc
    • プロダクト全体の仕様書のようなもの
    • 目次はスライドを参照
    • 今回のリニューアルに向けて、これを作るのに3ヶ月掛かった

感想

リニューアルあるあるについては、普段の業務の中でも気をつけたいことだと思いました。 特に 「1日8時間コードを書けると思っている」 は、普段から気をつけていても ふとした時に上のように単純計算をしてしまい、見積もりが合わないことが自分にもたまにあります。

Design Docについては、今後もし大きめのリニューアル(もしくは新規プロジェクト)に関わる機会があったら 実際に試してみようと思いました。

ディープリンクの設計と実装

16:00~16:30 / Track A / 一休.com(一休レストラン)

ディープリンクの設計と実装 - iOSDC2017 // Speaker Deck

要点

  • どういう用途で使うか
    • Webやメールからアプリ内の特定のページを開く
  • 他社アプリを開くには独自URLスキームをハードコーディングする必要がある
  • iOSでは似たような機能でUniversal Linksがある
    • Googleから開く
  • なぜディープリンクが求められるのか?
    • スライドを参照
  • 実装方法
  • アプリ内URLを設定すると便利なこと
    • PUSH通知からも開ける
    • アプリ内WebViewから開く画面を指定する
  • 悩むこと
    • タブバーを利用している場合どこから開くか
    • モーダルはユーザビリティがあまり良くない
    • ドリルダウンの再現(中間のページを考慮する)
    • 全画面に対応することはない
      • WebページのPVで判断
  • URLスキームの設計方法
    • path = host じゃないか(スライド参照)
  • Universal Linksお悩みポイント
    • Web側のPV計測が狂う
    • Webとアプリのビジュアルの統一性
      • 連続性を感じられるデザインにする
    • サブドメインで発動してしまう
  • Q/A
    • テストはルーターのみ行っている
      • それ以外は手動
  • その他
    • 自由なドリルダウンが発生するのは良くないのでdismissは遷移の事前にやっておいたほうが良さそう

感想

弊社のプロダクトもWebとiOS,Androidでディープリンクを採用可能なページがありますが、 まだアプリに一任するメリットが大きくないと考えているため、採用には至っていません。

しかし、どこかのタイミングで今後採用する可能性は大きくあるので その際はURLスキームの設計方法など活かしたいと思います。

LT

  • 第3の課金形態「寄付モデル」ってどうなの?
    • 寄付モデルにトライ&エラーした話
    • 当日の最優秀LT賞になっていた
  • 多次元宇宙と画面遷移
    • 宇宙の話からいつの間にか画面遷移について説明
  • iOSで利用できるデバイスファームのメリット・デメリットの紹介
    • デバイスファームの各サービス比較
  • ローカライズの苦しみに立ち向かう
    • デザインの段階で単位の書き方を控える(単数、複数、○位)などなど
  • クラス名に個人の名前を含めるとこうなる
    • 純粋に面白かった(盛り上がっていた)
    • Swiftを書いている内はクラスにプレフィックスを付ける文化には会わなさそう

終わりに

現在直面していることやこれから対応を考えていることについて、 詳しく話を聴くことができたのでとても参考になりました。

会場の環境もWi-Fi環境が用意されていたり、各所で案内板があったりと 快適且つ親切な環境でとても参加しやすかったです。

当日聴けなかったセッションについては、登壇者の方々がSpeaker Deckなどに スライドを公開してくださっているのでBlogやTwitterをチェックしていと思います。

パフォーマンス計測ツール:SpeedCurveの使い方

フロントエンドエンジニアの岡田です。今回は、パフォーマンス計測ツール:SpeedCurveをLCLでどのように使っているかお伝えします。 f:id:lcl-engineer:20170628054829p:plain

設定について

計測タイミング(Times)

計測タイミングは多いに越したことはないとは思いますが、コストとのバランスで、今のところ1日1〜2回計測をしています。 1日分のリリースが終わるタイミングとアクセスが増えるタイミングに計測をしています。

計測回数(Checks)

3回以上で任意の回数が設定できます。今のところ最低ラインの3チェックにしています。 3回チェックすることで、標準よりも早かったり遅かったりする結果が排除され、結果の中央値が選択されるそうです。 What is a check and how many do I need? | SpeedCurve Support

ブラウザ

LCLのサービスは、スマホユーザーが多いため、スマートフォン向けページのみ計測しています。(1URLをスマホ・PC等複数デバイスで計測することも可能です)
ブラウザは、Chromeのエミュレート環境でiPhone6のサイズで設定しています。 回線については、Newrelic Browser(RUM)と比べて値が近かった、4G設定で計測をしています。 Newrelicの結果では、実際には4Gよりもやや早いような印象でした。 f:id:lcl-engineer:20170619064716p:plain

料金プラン

Synthetic Pay-As-You-Go plan. にしています。

SpeedCurve | Pricing

1チェック $0.01で、月額$20〜 です。 1日1回計測 × 1計測3回チェック × 1ブラウザの場合、21ページまでなら$20です。(31日間の月の場合)

結果の見方について

Siteタブ

設定したタイミングで計測したデータが表示されます。 Start Render, SpeedIndex, Visually Complete の他、Backendの時間も確認できます。 f:id:lcl-engineer:20170628054829p:plain

グラフにはnoteを追加することができるので、パフォーマンスに関連しそうなリリースの履歴を入力しています。 noteはAPI経由でも追加できるので、設定すると便利かもしれません。

https://api.speedcurve.com/#add-a-note

BROWSER WATERFALL

ファイルの読み込まれる順番が確認できます。 f:id:lcl-engineer:20170619095505p:plain Chromeのデベロッパーツールなどでも確認はできますが、以下の点が見やすくて良いと思います。

  • レンダリングブロックしている要素がわかりやすい(斜線マークがブロッキング要素)
  • 要素をクリックすると詳細情報が表示される

参考 SpeedCurve | SpeedCurve Waterfall

他のツールとの連携

データベースへ保存

テスト結果は社内ツール用のデータベースにも保存しています。 以下のAPIを使ってデータを取得しています。
SpeedCurve v1 API

Re:dashで表示

データベースへ保存したデータは、Re:dashで表示しています。
以下のグラフは、複数ページの結果を表示したものです。 SpeedCurveでは表示できない形式ですが、このように重ねると、どのページが遅いのかがひと目でわかります。 f:id:lcl-engineer:20170628112059p:plain

chatworkへ通知

first byte, start render, speedindex, visually complete, Google PageSpeed Insightsスコア をchatworkへ通知しています。 通知を出すようにしてから、日々の変化に気づきやすくなり、また、担当者以外にも関心を持ってもらえるようになったことが良かったです。 f:id:lcl-engineer:20170628052734p:plain

以上です。 まだ使えていない機能もあるのですが、しばらくはこちらで計測を続けていこうと思います。

Baltoを利用して素早くサービスを改善する

LCLでは、今年の初めから開発フィードバックのツールとしてGoodpatch様のBaltoをメインで活用しています。 先日、Goodpatch様へ事例として取り上げていただきましたが、より具体的な利用方法について紹介します。

goodpatch.com

Baltoとは

Webサイトやモバイルアプリに対して、簡単にスクリーンショット・動画付きのフィードバックを送れるツールです。フィードバックした内容は、スレッド別に管理され、それぞれにコメントやステータス管理ができます。

f:id:lcl-engineer:20170614100938g:plain

以前はIssueトラッカーを利用していましたが、スマートフォンからフィードバックはかなり面倒でした。(スクショを取って、チャット等を経由でPCに渡して、PCでIssueトラッカーへ添付 & コメント )

Baltoの場合は、チャットのように気軽に送れて、Issueトラッカーのようにステータス管理もできるので、それぞれのいいとこ取りのツールだと感じています。

利用フェーズ

開発中の中盤から最後にかけて利用するが多いですが、サイトの動きが大きく変わるような改善の場合は、初期段階から利用するケースもあります。また、プロダクションのサイト・アプリをBaltoに登録しておき、ふとした時に気になったことをフィードバックするという活用もしています。

Baltoにはいくつかプランがありますが、弊社が利用しているスモールプランの場合はプロジェクトが4つまでしか登録できません。そのため、完了したプロジェクトは、必要フィードバックがあればIssueトラッカーに転記し、プロジェクトは削除するという運用にしています。

導入方法

Webサイトの場合は、管理画面からURLを登録するだけで使えます。

iOS/Androidアプリの場合は、SDKを導入してアプリを配信します。

SDKの導入ページがあるため、こちらの通りにやれば特に詰まることなく、実装できるかと思います。

なお、Balto SDKはiOS9以上が対応ですが、弊社のアプリはiOS8から対応しているため、BaltoのSDKが導入できません。そのため、Balto専用のブランチを用意し、iOS9からがターゲットとなるように一時的に書き換えて導入しています。nアプリの配信はAPIが用意されているため、faslaneに組み込んでビルド・配信は自動化しており、一度導入してしまえば手間なく配信できます。

lane :balto do
  ・・ビルド部分省略・・
   system("curl -F project_token=[project_token] -F user_token=[user_toke] -F package=@./../[アプリ名].ipa -F ready_for_review=1 https://balto-api.herokuapp.com/api/v2/builds/upload")
end

テスト配信に関しては、Fabric/Crashlyticsも併用しています。使い分けとして、基本的にはBaltoを利用してフィードバックサイクルを回し、最終チェック段階でFabric/CrashlyticsでBalto SDKを組み込んでいないアプリ配信しチェックしています。

まとめ

Baltoを利用すること、ちょっとした隙間時間でフィードバックできるようになり、フィードバックの数が増えたと感じています。

サービスを良くするには、いかにフィードバックを集めて地道に改善していくかが、重要な要素の一つであると捉えています。気軽にフィードバックができ、漏れなく管理が行えるBaltoは、フィードバックサイクルを回すプラットフォームとして、適していると思ってます。

1ヶ月間の無料トライアルができるようなので、みなさんも是非使ってみて下さい。

www.balto.io

AWS EC2パラメータストアを利用したRails 秘匿情報の管理

DBの接続情報・APIキーなどの秘匿情報は、git管理下に置くべきではないですが、皆さんはどのようなに管理しているでしょうか?

先日のAWS Summit Tokyo 2017のDMM様の事例で、EC2 System Managerパラメータストアの紹介がありましたので、使ってみました。

※ タイトルにRailsとついてますが、特にRailsに依存する内容ではありません。

EC2 SystemManager パラメータストアとは

簡単に言うと、key/value形式のパラメータをAWSで集中管理できる仕組みだと理解しています。

主に、以下の特徴があります。

  • AWS API、CLIを利用してアクセス可能
  • KMSを利用してパラメータ値の暗号化が可能
  • IAMを利用して、各パラメータへのアクセス権を細かく制御可能

管理コンソールからの登録

EC2 Managemtn Consoleの左下に、Parameter Storeのメニューが有ります。Parameter Storeへアクセスするには、事前に、IAM Policyの「AmazonSSMFullAccess」を付与しておきます。 ( AmazonSSMFullAccessは、他の権限も含まれているため、厳密に権限管理する場合は、独自にポリシーを作成したほうが良いかと思います)

f:id:lcl-engineer:20170606214647p:plain

登録画面は、至ってシンプルです。この画面で登録するだけで、AWS CLI等で参照できるようになります。

f:id:lcl-engineer:20170606215310p:plain

AWS CLIからの参照

EC2インスタンスから、AWS CLIを使ってアクセスする方法を紹介します。

「AmazonSSMReadOnlyAccess」PolicyをAttachしたIAM Roleを作成し、EC2に割り当てておきます。

以下のコマンドでパラメータの取得可能です。

aws ssm get-parameters --region ap-northeast-1 --name production.db.user
->
{
    "InvalidParameters": [],
    "Parameters": [
        {
            "Type": "String",
            "Name": "production.db.user",
            "Value": "db_user"
        }
    ]
}

valueだけ取得したい場合は、queryを利用して以下のようにとれます。

aws ssm get-parameters --region ap-northeast-1 --name production.db.user  --query "Parameters[0].Value" --output text
-> db_user

パラメータストアのアクセス制御

ポリシーを独自で作成することで、細かいアクセス制御が可能です。 以下のポリシーの場合は、productionで始まるパラメータのみにアクセス可能です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1482841904000",
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters"
            ],
            "Resource": [
                "arn:aws:ssm:[region]:[AWSアカウント]:parameter/production.*"
            ]
        }
    ]
}

パラメータの命名ルールとして、環境名やサービス名を入れておくと、環境やサービス単位で適切な制御ができると思います。

KMSを利用した暗号化

暗号化については、クラスメソッド様の記事が非常に参考になりますので、紹介だけに留めておきます。

AWS KMS で EC2 Systems Managerパラメータストアを暗号化 | Developers.IO

Railsで利用する

例えば、Railsのデータベース接続情報で利用する場合は、database.ymlで環境変数から読み込むようにします。

production:
  <<: *default
  database: db
  username: <%= ENV['DATABASE_USER'] %>
  password: <%= ENV['DATABASE_PASSWORD'] %>
  host: <%= ENV['DATABASE_HOST'] %>
  port: <%= ENV['DATABASE_PORT'] %>

Railsの起動ユーザの .bash_profileで、パラメータストアから値を取得して環境変数にセットしておけば、Railsアプリから参照できるようになります。

export DATABASE_USER=$(aws ssm get-parameters --region ap-northeast-1 --name production.db.user --query "Parameters[0].Value" --output text)
export DATABASE_PASSWORD=$(aws ssm get-parameters --region ap-northeast-1 --name production.db.password --with-decryption --query "Parameters[0].Value" --output text)
export DATABASE_HOST=$(aws ssm get-parameters --region ap-northeast-1 --name production.db.host --query "Parameters[0].Value" --output text)
export DATABASE_PORT=$(aws ssm get-parameters --region ap-northeast-1 --name production.db.port --query "Parameters[0].Value" --output text)

bash_profileに書いておくと、該当ユーザでログインするたびに毎回、パラメータストアへのアクセスが発生してしまいます。 そのため、初回アクセス時に別ファイルに書き出し、2回目以降はパラメータストアではなくファイルから読み出すようにしています。

if [ -e ~/.credentials ]; then
  source ~/.credentials
else
  echo "export DATABASE_USER='"$(aws ssm get-parameters --region ap-northeast-1 --name production.db.user --query "Parameters[0].Value" --output text)"'" >> ~/.credentials
  echo "export DATABASE_PASSWORD='"$(aws ssm get-parameters --region ap-northeast-1 --name.production.db.password --with-decryption --query "Parameters[0].Value" --output text)"'" >> ~/.credentials
  echo "export DATABASE_HOST='"$(aws ssm get-parameters --region ap-northeast-1 --name production.db.host --query "Parameters[0].Value" --output text)"'" >> ~/.credentials
  echo "export DATABASE_PORT='"$(aws ssm get-parameters --region ap-northeast-1 --name production.db.port --query "Parameters[0].Value" --output text)"'" >> ~/.credentials
  source ~/.credentials
fi

パラメータストアの追加・更新があった場合は、一度ファイルを削除する運用にしていますが、もう少し良い方法がないか模索中です。

まとめ

Railsでの秘匿情報を管理する方法としては、「yaml_vault + AWS KMS 」が良いと思ってましたが、EC2 System Managerのパラメータストアも手軽にセキュアな環境が構築でき非常におすすめです。EC2 System Managerは地味ですが、他にも良い機能があるので、今後は是非活用していきたいと思います。

参考

EC2 Systems Manager のパラメータストアを利用したアプリケーション環境設定の管理 | Developers.IO

AWS Key Management Serviceを使ってconfigファイルを暗号化すると便利 - Qiita

パフォーマンス計測、はじめました(フロントエンド編)

フロントエンドエンジニアの岡田です。 LCLでは、フロントエンドのパフォーマンスを計測するために、SpeedCurveを導入しました。 speedcurve.com

検討した計測サービス

導入にあたっては、以下のサービスも検討しました。
最初は、RUM (Real User Monitoring)ができるサービスを中心に探していたこともあり、以下の2社を検討しました。

site24x7

良い点
  • RUMの割には費用も低め(2,000万PVで$506〜 / 月額:調査当時)
  • 日本語対応もされている(ちなみに、サポートメールは日本語で送っても英語で返ってきました)
導入を見送った理由
  • RUMの結果は思ったほど詳細が見えなかった(見方がわからなかっただけかもしれませんが。。)
  • RUMなしのプランも検討したが、ユーザーエージェントの設定が効かないことがあり、スマホページの計測ができない場合があった(PCが計測されていた) www.site24x7.com

New Relic

New Relは、サーバーのパフォーマンス監視のためにすでに入っていましたが、このサービスでRUMを見ることができます。 RUMの見方は、以下のページがとてもわかりやすかったです。
New Relic Browser : ユーザーのパフォーマンス(RUM)も計測できるよ - Qiita

良い点

すでに導入済みのサービスで使える

導入を見送った理由

費用が高い(50万PVで$149〜 / 月額:調査当時。50万PVを超えるとどうなるのかは不明)

newrelic.com

SpeedCurve

良い点
  • RUMは高い($800〜 / 月額)がSynthetic Monitoringはお手頃($20〜 / 月額)
  • トライアル中の結果が安定している
  • 英語だけでもUIがわかりやすい speedcurve.com

RUM(Real User Monitoring)の導入を止めた理由

最初はRUMで計測できるサービスを探していましたが、以下の理由から今回はSynthetic Monitoringを選びました。

  • RUMはすでに導入済みのNew Relic(Lite)で見ることもできる(ただし見えるデータに限りがある)
  • RUMの場合、同じユーザーが毎日決まった時間にアクセスしてくれるわけではないため、定点観測しにくい(サイトの改修で問題が起きても気づきにくい)
  • RUMは高い

ということで、SpeedCurveを導入しました。

次回は、SpeedCurveを使って、どのようにパフォーマンス改善をしているかをお伝えしたいと思います。

2017/6/7 追記

New Relic には New Relic Synthetics という定点観測サービスがあり、無料〜で使えるそうです。 Twitterで教えていただきました。ありがとうございます。

Railsのデプロイ時にwebpackのビルドを実行する

弊社が運営しているサービス:夜行バス比較なびのサーバーサイドはRuby on Railsですが、フロントエンドの一部はReact & ES2015で書かれています。今回はデプロイにおけるRailsとwebpackの連携について紹介します。

www.bushikaku.net

React & ES2015導入の詳細もブログで公開しています。ぜひ読んでみてください。

techblog.lclco.com

これまでの問題

webpackの環境は、各自の開発PC(Mac)につくります。 もともとRailsのsprocketsを使っていたため、React化(ES2015化)完了までは今まで通りsprockets を使うことにしました。 つまり、webpackで書き出したファイルを、sprocketsで管理しているディレクトリ以下へコミットしてしまいます。 (良い方法ではないと思いますが、サーバーにnode.jsの環境を作るまでのつなぎです。)

このように上記の記事でも言及していますが、今までwebpackのビルドは開発者がローカルで実行して、生成されたjsファイルもGitで管理していました。ソースコードの二重管理は手間ですし、手順が多いということはミスの誘発につながるため避けたいところです。

解決策

タイトルのとおり、デプロイ時にサーバーでwebpackを実行するようにしました。具体的にはAsset PipelineのPrecompileにフックしてビルドを実行しています。ビルド後はsprocketsに任せてダイジェストの付与を行っています。

この方法は下記の記事を参考にさせていただきました。

WebPackを使ってRailsからJavaScriptを楽に良い感じに分離する - Qiita

設定の詳細は以下のとおりです。

各種バージョン

Ruby 2.1.2
Rails 4.1.5
node.js 6.9.1
babel 6.11.4
webpack 1.13.1

ディレクトリ構成

webpackの入出力のディレクトリを簡単に説明します。実際より簡略化しています。詳細はこちらをご覧ください。

React & ES2015のソースコードはfrontend/src/scripts/に配置しています。

frontend
├── src
    └── scripts
        ├── xxxx
        │   └── xxxx
        │       ├── xxxx.jsx
        │       └── xxxx.jsx
        ├── xxxx     
        │   ├── xxxx
        │   │   └── xxxx.js
        │   │   └── ...
...

ビルドしたファイルはapp/assets/javascripts/es/にbundleされて出力されます。

app
├── assets
    ├── javascripts
        ├── es
        │   └─ xxxx.js
        │
        ├── xxxx // 通常のjsファイルがあるディレクトリ
        │   └─ xxxx.js
...

gitignoreの設定

ビルドで生成されるjsファイルのバージョン管理は不要になるのでgitignoreに追加します。この場合はapp/assets/javascripts/es/配下を無視します。

Precompileへwebpackビルドのフックを追加

lib/tasksにRakeタスクを作成します。これでデプロイ時にwebpackのビルドが実行され、その後sprocketsで処理され配信されます。

task :build_frontend do
  cd 'frontend' do
    sh 'npm install --production'
    sh 'npm run build'
  end
end

Rake::Task['assets:precompile'].enhance(%i(build_frontend))

おわりに

この方法で煩わしい手動ビルドからも解放され、デプロイもスムーズに行うことができています。

ここから更にフロントエンドの環境を整えてより良いサービスを提供していきたいです。

ES5からWebpack管理下の関数を呼び出す方法

フロントエンドエンジニアの岡田です。 先日ご報告したとおり、LCLが運営する「夜行バス比較なび」では、jQueryで作られたサイトをReactへ置き換えています。 techblog.lclco.com

記事を書いた後も細々と置き換えを進めていますが、ページ単位での置き換えは、それなりにまとまった時間が取れないと進められません。 しかしSPAのように複数のjsがあるページでは、その時間が取れず、なかなか置き換えが進められませんでした。

そこで、LCLではもっと小さい単位で置き換える方法を試してみました。
手順は以下のとおりです。

  1. 既存の関数(ES5)を、ES2015へ変換してWebpack管理下のディレクトリへ移動
  2. 移動した関数をグローバル関数へ変更
  3. 既存のjsからグローバル関数を呼び出し

この方法を使うと、関数単位で置き換えが可能です。

ES2015への変換ツール

変換にはlebabを使いました。 lebab.io

具体的な方法

既存の関数(ES5)をlebabでES2015へ変換

// 変換後の関数
const hogehoge = () => {
};

移動した関数をグローバル関数へ変更

window.grobalHogehoge = hogehoge;

既存のjsからグローバル関数を呼び出し

// グローバルに露出している関数を呼び出し
window.grobalHogehoge();

グローバルに露出させるのであまりよくないかもしれませんが、あくまでも移行中の一つの手段としては使えます。この方法で関数をES2015化して、最後にきれいにReactへ置き換えたいと思います。