DevLOVE「Atomic Design ~堅牢で使いやすいUIを効率良く設計する〜」の参加レポ

2018-06-29 DevLOVE「Atomic Design ~堅牢で使いやすいUIを効率良く設計する〜」の参加レポートです。

著者の五藤さんご本人から書籍「Atomic Design ~堅牢で使いやすいUIを効率良く設計する〜」の解説と本には載っていないお話を聞けるイベントでした。

devlove.doorkeeper.jp

直近で書いたAtomic Designに関する弊社のブログはこの日に合わせてまとめたところもあり、独自で考えた観点や手順が五藤さんが携わってこられた現場と比べてどれくらい一致しているか、答え合わせのような気持ちで参加しました。

アトミックデザインを使ったコンポーネント指向のUI開発:序 - UGAP Engineer's Blog
アトミックデザインを使ったコンポーネント指向のUI開発:破 - UGAP Engineer's Blog

印象に残った話

結論としてかなり同じ考え方ができてたと感じ、自信につながりました。ただ、中には観点として足りてなかったことや本で紹介されていることの理解が足りていないことがあり、お話が聞けてよかったと思いました。

その中でも印象に残った話をピックアップします。

デザインデータの負債

デザインデータの負債というワードはエンジニアは聞きなじみのない言葉かと思います。

エンジニアの立場でアトミックデザインに注目していましたが、デザイナー側にも構造化されていないことが原因による問題があることがわかりました。

その一つに「デザインデータの負債」があります。

具体的にはPhotoshopなどのデザインツールでUIをレイヤー分けする際、そのレイヤーの名前や分け方が適当であるとそれを引き継いだデザイナーはまずレイヤーを理解することからはじめなくてはならないということです。

理解した後、同じことの繰り返しにならないようにリファクタしようとしても、今度はそのページ外での影響範囲が分からず手が出せない問題が引き起こされます。

故にデザイナー側でもデザインの作り方、なにか構造化の手段が必要になってくるというわけです。

アトミックデザインの責務の種類を揃える

アトミックデザインの構造化とその責務は本にまとめられた表と解説を読み、「なるほどそういう考え方もあるか」と思っていただけでしたが、その考えにいたるまでの過程を聞くと、とても論理的でさらに理解が深まりました。

デザインの構造化をする上で、まず他の構造化されたものに着目しており、例えば次のものをみると

責務でわけられた各レイヤーの依存の方向が1方向であることがわかります。そのことから、アトミックデザインもまずは責務の種類を整理する必要があると考えられたようです。

さらに、デザインの構造化の手がかりを、「ユーザーの行動プロセス」から探し出すことにし、まずはユーザーの行動プロセスを順番に並べてることをされていました。

順番 ユーザーの行動プロセス
1 プロダクトを知る
2 画面全体から情報を探す
3 興味を引くコンテンツを見つける
4 コンテンツに促されて行動する
5 全体を通してサービスに良い印象を抱く

その次に、各レイヤーのデザイン対象を配置していくとこうなります。

順番 ユーザーの行動プロセス デザイン対象
1 プロダクトを知る マーケティング/プロダクトそのもの
2 画面全体から情報を探す 画面全体のレイアウト
3 興味を引くコンテンツを見つける コンテンツの見せ方
4 コンテンツに促されて行動する 行動を阻害しない操作性
5 全体を通してサービスに良い印象を抱く デザインの統一性

最後に、Atomic Designの各レイヤーを配置すると、各レイヤーの関心ごと(責務)が明確になりました。

詳しくは本に書かれていますが、依存方向が1方向であることを保ちつつレイヤーが分かれていることがわかります。

順番 行動プロセス デザイン対象 レイヤー
1 プロダクトを知る プロダクトそのもの Pages
2 画面全体から情報を探す 画面レイアウト Temlates
3 興味を引くコンテンツを見つける コンテンツの見せ方 Organisms
4 コンテンツに促されて行動する 行動を阻害しない操作性 Molecules
5 全体を通してサービスに良い印象を抱く デザインの統一性 Atoms

本を読んだときは各レイヤーの責務を(表を横に)見て、なるほどと思っていただけでしたが、行動のプロセスを(表を縦に)整理することから始めたと知り、自分の中でこの表の納得感がさらに増しました。

UIデザインは複数人が関わっている

こちらはかなり共感できることでした。 デザインはデザイナーだけではなく、エンジニアを含めて複数人で関わっているというお話です。

ディレクター:機能要件・画面仕様
デザイなー:視覚情報・操作体験
エンジニア:実現

そのため、「構造化」というプロセスも全関係者が関わるべきプロセスであるとおっしゃっていました。

デザインカンプという手段は、ユーザが情報を見つけられるか確認できる手段としては有用ですが、構造化の面がおいてけぼりになるため、これまでのようにデザインカンプをデザイナーがつくってエンジニアが実装するフローは見直す必要があります。

なるべきフローは以下のようになると語られました。

構造化の指針を決める(関係者全員)

情報をデザイン(デザイナー)

構造をレビュー(関係者全員)

ソフトウェアを実装する(エンジニア)

ここで重要なのは「常にレビューするようにする」ということだそうです。

レビューするにはGithubのようにバージョン管理と差分比較ができることが望ましいということで以下のツールを紹介していました。

[参考]デザインデータのバージョン管理ができるAbstractを試してみた✌ | UXデザイン会社Standardのブログ

上のツールを使うと、デザインカンプ作成中に構造のレビューができるのと同時にデザインの工程もオープンになる利点があります。
(嫌がるデザイナーもいそうですが。。)

レビューコストは気にされていて、レビューを効率化するために、デザイナーがCSSやReactコンポーネントを修正してしまうことを是としたり、 オープン(モブ)・デザイニングといった、モブ・プラグラミングのように難しいところは一気にみんなで解決して情報伝達も効率化しまおうという取り組みもされたようです。

「構造化づくりはチーム一丸になってやる」を理想ではなく、ちゃんとみんなが行動できる形にされていてすごいなーと感じました。

会場から出た質問

完全にメモったわけではないので、ざっくり書いときます。

MoleculesとOrganismsの判別の仕方は?

チェアマンの市谷さんからの質問でした。
そのときどのサイトを例に取り上げていたか忘れてしまったのですが、例えば次のようなショッピングリストはどこがMolecules(分子)で、どこがOrganisms(有機体)なのかという質問です。

f:id:uggds:20180702001913p:plain

回答
Organisms(有機体)はスタンドアローンであるといえるのでパーツとして切り離しても意味がわかる単位がOrganisms(有機体)といえる。
例えば評価(星)だけが画面にあっても意味がわからないので、それはMolecules(分子)になる。
プロジェクトで共通認識であればどちらでもいいとも思う。

個人的な意見
私はOrganisms(有機体)を「広告バナーのようにどの別のページでもそのまま使えるもの」と定義しました。 下からフェードインしてきたり、フローティングしてたり、埋め込まれてたりと大小様々な広告がありますが、どの画面にでても意味が通じます。 また、広告には訴求する文章が必要です。そのため、Organisms(有機体)には画像でもいいんですが、訴求文が含まれているべきかなと思います。例外はありますが。

[参考] アトミックデザインを使ったコンポーネント指向のUI開発:破 - UGAP Engineer's Blog

ページ上部にあるような戻るボタンはどこに属すか?

回答
戻るだけだと意味がわからないので、どこかのコンテンツには属すはず

AmebaTVとは別のサービスを作る場合は作り直すか?

回答
なにも決まってないまま作りはじめることが多いが、Amebaの冠をつけるならAmebaブランドとは何かから考えるはず。使い回すかどうかはわからない。

Storybookと開発の差分が生まれがちなのだが、Storybookのメンテはどうしているか?

回答
レビューの中にStorybookが含まれているので、そういうことにはならない。差異がでてたら気づく。

デザインレビューはどういう観点でしているか

回答

再利用性はどうでもいい。 デザインカンプを作りきってしまうまえに、Reactでは難しいからこうしてほしいとかを伝えている。

最後に

今回お話を聞いて、スピーカーの五藤さんがエンジニアにも関わらずデザイナーのこともよくご存知で、すごいと思いました。

すると、実はもともとはデザイナー出身だったようです。

デザイナーとエンジニアはお互いのやっていることが見えず壁ができがちですが、お互いが知識に垣根を作らず、理解し合うことでUI開発現場の次のステップに進めるのではという考えを体現されている方だなと思いました。 とてもよい刺激になりました。

Atomic Design ~堅牢で使いやすいUIを効率良く設計する

Atomic Design ~堅牢で使いやすいUIを効率良く設計する

Atomic Designを使ったコンポーネント指向のUI開発:破

Atomic Design(アトミックデザイン)とは、UI(画面)をコンポーネント単位で設計し、それらを組み合わせて使い回し可能なUIを作成するための手法です。
Atomic Designの考えのものとUIコンポーネントを切り出していくことはいくつかの利点があります。

しかし、Atomic Design はUIを構築するための方法論でしかありません。
つまり、デザインや実装方法、運用方法は独自に考える必要があります。
実際に導入してみるといくつか乗り越えなくてならないハードルが存在することがわかりました。

本稿(破)ではそのハードルを乗り越えるために、どのような工夫したのかを紹介します。
同様に困っている現場の参考になれば幸いです。

※本稿は(序)(破)(Q)のシリーズの2番目の記事になります。
Atomic Design概要は(序)で説明していますので、そちらをご覧ください 。

本稿の対象読者

  • Atomic Design導入してでた課題を他の現場がどう解決しているか気になる方

導入に際してやった次の3つについて説明します。

  1. コンポーネントの具体的な定義
  2. コンポーネント一覧の作成
  3. デザイナーとフロントエンドエンジニアの認識合わせ

導入に際してやったこと1:コンポーネントの具体的な定義

各階層のコンポーネントの仕分け方で悩むことが多かったため、具体的なコンポーネントの定義をして共有しました。
※以下でPowerPointという言葉が出てきますが、一旦まずは定義を紹介します。

Atoms(原子)

  • リンクやボタンやフォームのパーツ単位など UI の最小要素
  • 見た目に関することはこのモジュールで表現する
  • PowerPointの一つの図形で表現できるもの
    ラジオボタン・セレクトボックスなどのフォームは例外)

Molecules(分子)

  • AtomsとMoleculesから構成されるグループで、比較的小さいコンポーネント
  • 中身の文言や配置を変えることで使い回しが可能なもの
  • 別のOrganismsを跨いで出現するもの、または、これから出現することがわかっているもの
  • PowerPointの図形をグループ化して表現するもの

Organisms(生命体)

  • AtomsとMoleculesから構成されるコンポーネント
  • 広告のバナーのように、どのページに配置しても意味がわかる独立したコンポーネント
  • デザイン上初めてでてくるもので、比較的小さいコンポーネントだが使い回すかどうか不明なもの。 (未知の生命体
  • 幅を固定しないとデザイン通りにならない、使い方が限定的なもの。
  • PowerPointの図形をグループ化して表現するもの。

これらの定義は次の2つの課題を解決しようとした結果こうなりました。

  1. Atomsの粒度を細かくしすぎないようにする
  2. Molecules(分子)とOrganisms(生命体)を判別できるようにする

2つの課題について順番に説明していきます。

課題1:Atomsの粒度を細かくしすぎないようにする
概念ではAtomsの粒度は、ラベル、入力フォーム、ボタンなどHTMLタグレベルになります。
しかし、実装していると、ラベル単位では粒度が細かすぎるように感じました。
粒度が細かすぎるとそれをコンポーネント一覧に登録することが手間だし、増えすぎてコードの見通しも悪くなります。

そこで、細かすぎずチーム全員が「これは最小単位のモジュールだ」と機械的に判別できる何かを探して辿りついたのがパワポの図形です。

download.png

スクリーンショット 2018-04-13 18.04.53.png

パワポの図形を使って以下のようにatoms(原子)を定義しました。

Components Definition
Atoms パワポの図形1つでつくれる単位
Molecules, Organisms パワポの図形をグループ化してつくる単位

この考えでいくと、例えば次のように仕分けすることができます。

Atoms(パワポの図形1つでつくれる) f:id:uggds:20180624213053p:plain

Molecules(パワポの図形をグループ化してつくる) f:id:uggds:20180624213107p:plain

上の例ではAtomsにもMoleculesにもbuttonがあります、これをつくることでボタンの中にアイコンをいれたボタンをつくりたい場合に、最小単位のアイコンを、最小単位であるはずのボタンが制御しなければはならないというジレンマを解消します。

またこれにより、Atomsのbuttonは内側にあるアイコンの位置などは指定せず、色やフォント、シャドウのみを表現することに専念し、最小構成に保つということを守りつつアイコンや他のUIパーツを組み合わせたボタンを作成することができます。

※注意点
図形をグループ化して作ったコンポーネントの中にあるプレーンな「タイトル」や「テキスト」はグループ化して作ったコンポーネントの要素にします。

たとえば、上の図のcardは2つの図形でできているためMoleculesにしますが、 グループ化した後の「タイトル」と「テキスト」はAtomsとして切り出すのではなく、cardの「タイトル」と「テキスト」になります。
そうしないと、結局Atomsの粒度が細かくなりすぎてしまうためです。

パワポの発想はチームの体制が関係しています。
UI/UXディレクションとビジュアルデザインをやる人が別だったため、下のようなフローで作業していました。
①UI/UXディレクターがパワポの図形を使ってワイヤーフレームを描く
②それを基にデザイナーがデザインカンプをつくる
③フロントエンドエンジニアがマークアップ
画面をつくるときの起点はUI/UXディレクターで、UI/UXディレクターは図形をグループ化したり、コピペしてワイヤーフレームを作成していました。
そこで、コンポーネントパワポの図形単位で考えるといいのではないかと思いつきました。

課題2:Molecules(分子)とOrganisms(生命体)を判別できるようにする
Atomic Designの悩みどころにMoleculesとOrganismsの判別があります。
概念では「Moleculesは比較的単純で、Organismsは複雑で大きいコンポーネンント」であるとしていますが、判断できないコンポーネントが出現し、その都度悩みます。

そこで、まずOragnisms(生命体)について調べてた結果、独立したコンポーネントと考えるのがよいと感じました。 具体的には「広告バナーのようにどの別のページでもそのまま使えるもの」です。

下からフェードインしてきたり、フローティングしてたり、埋め込まれてたりと大小様々な広告がありますが、どの画面にでても意味が通じます。

さらに、広告には必ず訴求文が存在します。画像でもいいのですが、訴求文があるか、ヘッダーやフッターなどなくても意味がわかるコンポーネント、それを独立したコンポーネントとしました。

次に、Molecules(分子)は適当につくると無駄に膨れ上がってしまうので「Organismsを跨いで使われている、使われる可能性があるもの」としました。
親子コンポーネントの子コンポーネントに相当しますが、親から離れて別の親にも付いて行っちゃう子です。

最後に、Organismsの子コンポーネントとして切り出したが、今後別のOrganisms内で使われるかわからない場合(Moleculesにするか悩む場合)があります。
そんなときは、未知の生命体ということで、一旦Organismsにすることにしました。

安易にMoleculesにせず、一旦Organismsにして、Organismsを跨いで使うようなコンポーネントだとわかればMoleculesにリファクタします。
悩む時間がもったいないし、Moleculesにしても使わない可能性が高いからです。

ここまで説明した仕分け方法をフローチャートで表現すると以下のようになります。

f:id:uggds:20180702015538p:plain

導入に際しやったこと2:コンポーネント一覧の作成

コンポーネントを作りすぎると、探すのが大変になりました。
増えていくと、探すのをあきらめて新しいファイルを作成してしまうかもしれません。 デザイナーも忘れて新しくデザインしてしまうかもしれません。

この問題に対しては単純な話ですが、コンポーネント一覧を作成しました。

Atomic DesignのUIコンポーネントを一覧化に使えそうな3つのツールを紹介します。 どのツールを使うかはデザイナーの有無と使用しているフレームワークによって変わってきます。

本シリーズ(Q)では、Storybookを用いた具体的な開発例をご紹介したいと思います。(近日公開)

一覧化手順について、Atomic Designの考案・著者のBrad Frost 氏が Interface Inventory というものを提唱しています。 コンポーネントを分解してKeynote等でリスト化する手順を説明しているものです。
コンポーネントがチーム共通の認識で正しく切り分けられているかを振り返りには有用かと思います。
ただ、開発前はKeynote等でもいいかもしれませんが、Web上で確認できるようしておいた方が使い勝手がよいかと思います。
上で紹介したツールはどれもWeb上で確認できるツールです。

導入に際しやったこと3:デザイナーとフロントエンドエンジニアで認識合わせ

デザイナーとフロントエンドエンジニアで進め方の認識合わせを行いました。
やったことは主に次の3つです。

①Atomic Designを導入する目的とコンポーネントの階層定義を共有
先述した定義をデザイナーとフロントエンドエンジニアで共有します。

UIがAtomic Desingの考え方で階層分けされるとフロントエンド実装ではとても助かるので短絡的に導入しがちです。
しかし、デザイナーからするとパーツからつくっていくのはかなり難しいため、デザイナーの理解が必要です。

いきなりコンポーネント指向でデザインするのは大変なので、開発初期はデザイナーは今まで通りページ単位でデザインし、フロントエンドエンジニアがUIコンポーネントに分けるフローにしました。
のちのちUIコンポーネントが溜まってきたらUIコンポーネントを使いながらデザインしたり、慣れてきたらデザイナーがコンポーネント分割をやることを目標にします。

Atomic Designを導入する目的は、UI(画面)をコンポーネント単位で設計し、それらを組み合わせて使い回し可能なUIを作成するためです。
そうすれば、(私の現場に限った話ですが)ゆくゆくはUI/UXディレクターもOfficeの図形でお絵かきではなく、コンポーネント一覧からコンポーネントを選び出しデザイナーなしでデザインカンプをつくれるようになるだろうし、さらに将来的にUI/UXディレクターが何かしらのツールで組み合わせたらマークアップも終わっている状態もつくりだせるとかもと思っています。

②色、タイポグラフィ、余白は無限につくらず段階的に拡張できるようにしておく
例えば、CSS設計手法のBEMを使用しているならば、

.someClass--s {
   ...
   margin: 10px;
   ....
}
.someClass--m {
   ...
   margin: 15px;
   ....
}
.someClass--l {
   ...
   margin: 20px;
   ....
}

といった3段階まではmodifierを用意して変えられるようにしておきます。
拡張できるのはその段階までとデザイナーと認識あわせしておき、デザイナーはそれ以上の幅を持たせないように気をつけます。

③エンジニアからフィードバックする
コンポーネント切り出しをエンジニアが担っているならば、既にコンポーネント化しているかどうかはエンジニアが発見しやすいです。
なので、段階的な拡張の範囲外だったり、意味合いとして一緒なのに微妙に違うデザインを発見した場合は、エンジニアからデザイナーにフィードバックするよう心がけます。

(破)のまとめ

実際に導入してみて出てきた問題点に対して工夫したことを紹介しました。
Atomic Designの理論と実装にはいくつか折り合いをつけなくてはならない点がありそうです。

最近ではReactやVue.jsのstyled-componentsという CSS in JSのライブラリがAtomic Designと相性がよいため、Atomic Designが再注目されています。
qiita.com

次の(Q)では、このstyled-componentsを使ったAtomic Design実装の紹介と、フレームワークを使わない従来のHTMLとCSSだけでやった場合の両方をご紹介します。(近日公開)

Atomic Designを使ったコンポーネント指向のUI開発:序

はじめに

UI(画面)コンポーネントを切り出したいときにどういう粒度で切り出したらいいか迷う時があります。

適切な粒度で切り出しておかないと、位置を組み替えたりデザインを修正したい場合に、使い勝手が悪くて作り直しになってしまうからです。

メンテナブルなUIコンポーネントに切り出すにはどうしたらよいでしょうか。

いろいろ調べた末、Atomic Design(アトミックデザイン)というアイデアに行き着きました。
このAtomic Designを使ったコンポーネント指向のUI開発を(序)(破)(Q)のシリーズを使って紹介したいと思います。

  • (序)/ VIEW ARE (NOT) ALONE.では、Atomic Designがどのようなものかをご紹介します。
  • (破)/ VIEW CAN (NOT) ADVANCE.では、Atomic Designを導入した際にうまくいかなかった点と工夫した点をご紹介します。
  • (Q)/ VIEW CAN (NOT) REDO.では、具体的な開発例をご紹介します。
    (近日公開)

このシリーズが同様に困っている現場の参考になればかと思います。

本稿(序)の対象読者

  • ReactやVue.jsのようなフレームワークを使用しているがコンポーネントの切り出し方で悩んでいる
  • BEMなどのCSS設計手法を使用しているがブロックの切り出し方で悩んでいる

Atomic Designとは

UI/UXデザイナーのBrad Frost氏によって考案・書籍化されたもので、以下のサイトで読むことができます。
http://bradfrost.com/blog/post/atomic-web-design/

Atomic DesignはUIコンポーネントの切り出し方と組み上げ方ついて、UIの粒度を明確にし、組織的な指標となるよう考え出された概念です。

Atomic Designは自然界で起こっているのと同じプロセスを適用してUIを設計していきます。
自然界で起こっていることとは、すべての物質は原子で構成され、原子は互いに結合して分子を形成し、より複雑な生物に結合し、最終的には宇宙のすべての物質を創造している世界のことです。

小さなUIコンポーネントが集まり、一つのコンポーネントとなり、さらにそれらを組み合わさるとページになる画面UIの比喩としています。

Atomic Designは階層的なUI設計システムを作成するために、コンポーネントを以下の5つの単位に分別します。

スクリーンショット 2018-04-19 14.47.45.png

左から順に次のことを意味しています。
原子 -> 分子 -> 生体 -> テンプレート -> ページ

TemplatesとPagesはあえて自然界の比喩から脱却しているようです。
これらは最終的な成果物になるため、クライアント、上司、同僚に理解してもらえる言葉にする必要があるためだそうです。

具体的にこれらをHTML要素に当てはめると次のようになります。

f:id:uggds:20180620163024p:plain

[画像引用元]
http://atomicdesign.bradfrost.com/chapter-2/#atomic-design-is-for-user-interfaces

  1. Atoms(原子)
    フォームラベル、入力、ボタンなどの基本的なHTML要素。これ以上分けられないもっとも小さなUIパーツ。
  2. Molecules(分子)
    原子を組み合わせてつくる比較的単純なグループ。たとえば、いいねボタン、コメントボタン、詳細ボタンを組み合わせてできるユーティリティ。
  3. Organisms(生命体)
    原子と分子を組み合わせてできる大きなグループ。ヘッダーやフッターなど。
  4. Templates(テンプレート)
    原子〜生体までのコンポーネントを配置した、ページレベルのフレーム。中の画像や文章などは「サンプル」になる。
  5. Pages(ページ)
    テンプレートに具体的にコンテンツを埋め込んだもの

小さなUIコンポーネントを組み合わせてページができあがっていることがわかります。

さらに、Atomic Design ~堅牢で使いやすいUIを効率良く設計する ではUIコンポーネントが階層化されることで次のような利点があると述べられています。

全体を考慮する必要がなく1つの階の責務に関わる課題だけに集中できる
各階層の責務は以下のようになっていて、課題は各階層をみなおすことで解決する可能性があります。

課題 見直すべき箇所 コンポーネント
欲しい情報をみつけられない 画面全体のレイアウト Templates
コンテンツが魅力的でない ユーザーの行動を促すコンテンツ Organisms
コンバージョンに至っていない 行動を阻害しない操作性 Molecules

Atoms(原子)はラベルのフォントやアイコンやボタンの色味に関わる部分の責務をもっています。 もし、企業イメージにあってないと感じたらAtoms(原子)を並べてみて俯瞰的にみてみると解決するかもしれません。

同一階層に属するコンポーネントであれば差し替えることができる
あるUIコンポーネントで解決できない場合は同一層の別コンポーネントに差し替えることが容易です。差し替えが容易であればA/Bテストも容易になります。

上位層のUIコンポーネントの変更が下位層に影響することがない
階層化されたUIでは下位層は上位層の変更に影響を受けないように設計されるため、変更を加えた階層によって影響範囲の見極めが可能です。 例えば、Templeteを見直し、メインとサイドバーを左右逆転させても、その下の階層Atoms、Molecules、Organismsには影響はありません。 逆に、Atomsでフォントなどの見た目を変えた場合、それを使用しているMoleculesではバランス調整のため余白や折り返しを修正し、さらにその影響で上位層を修正しなくてはならないかもしれないとインパクトの違いがわかります。

(序)のまとめ

ざっくりと説明してきましたが、Atomic Designの考えのものとUIコンポーネントを切り出していくことはいくつかの利点があることがわかりました。
しかし、実はAtomic Design はUIを構築するための方法論でしかありません。

つまり、デザインや実装、運用方法は独自に考える必要があります。

実際、導入してみるといくつかの超えなくてはならないハードルがありました。

次の(破)では、実際にAtomic Designを導入した際のうまくいかなかった点と工夫した点をご紹介します。

参考

bradfrost.com

Atomic Design ~堅牢で使いやすいUIを効率良く設計する

Atomic Design ~堅牢で使いやすいUIを効率良く設計する

サイトの描画スピードをGoogle PageSpeed Insights APIを使って計測し、Kibanaで可視化する

概要

サイトのリファクタリング前後でページスピードがどれくらい改善されたかを調べるために、 Google PageSpeed Insights APIを使って計測し、さらにKibanaで可視化してみました。

f:id:uggds:20170728175213p:plain

使用技術

  • embulk
  • Google PageSpeed Insights API
  • elasticsearch 5
  • kibana 5

embulk

embulkは、さまざまなデータベース、ストレージ、ファイル形式、およびクラウドサービス間のデータ転送をサポートするオープンソースのバルクデータローダーです。

github.com

pluginが豊富で、今回は以下のpluginを使って実現しました。

一覧サイト
http://whttp://www.embulk.org/docs/ww.embulk.org/plugins/

Page Speed Insights

Page Speed InsightsはURLを指定するだけでサイトのパフォーマンスを診断してくれるGoogleのサービスです。
APIも提供しているので、各種アプリケーションからデータを取得することができます。
APIのガイドはこちら
https://developers.google.com/speed/docs/insights/v2/getting_started

Responseは次のものが返ってきます。

{
 "kind": "pagespeedonline#result",
 "id": "https://developers.google.com/speed/pagespeed/insights/",
 "responseCode": 200,
 "title": "PageSpeed Insights",
 "ruleGroups": {
  "SPEED": {
   "score": 94
  },
  "USABILITY": {
   "score": 100
  }
 },
 "pageStats": {
  "numberResources": 17,
  "numberHosts": 8,
  "totalRequestBytes": "2570",
  "numberStaticResources": 13,
  "htmlResponseBytes": "70113",
  "imageResponseBytes": "14985",
  "javascriptResponseBytes": "709324",
  "numberJsResources": 10
 },
 ...

具体的な秒数ではなく、scoreという形で評価しているところが特徴です。
上記ガイドによると

PageSpeed のスコアの範囲は 0~100 ポイントです。スコアが大きいほど良好で、85 以上のスコアはそのページのパフォーマンスが高いことを示します。

だそうです。

環境準備

KibanaとElastisearch

macVirtualBoxで作ったLinux環境のDockerコンテナ上にElasticsearchとKibanaを構築します。
セットアップ方法下でまとめました。 ugap.hatenablog.com

Page Speed Insights

Googleのサービスになるので、Google Cloud PlatformにログインしてAPIキーを発行してください。

embulk

git cloneで取得します。

$ git clone https://github.com/embulk/embulk.git

embulkのセットアップは公式のreadmeを確認してください。
セットアップが完了したら、pluginをインストールします。

$ embulk gem install embulk-input-http embulk-parser-json embulk-output-elasticseach embulk-filter-add_time embulk-filter-column

config.ymlに記述する例を記載します。

in:
  type: http
  url: https://www.googleapis.com/pagespeedonline/v2/runPagespeed
  params:
    - {name: url, value: '[計測したいサイトのURL]'}
    - {name: key, value: '[取得したAPIキー]'}
  parser:
    type: jsonpath
    root: $
    schema:
      - {name: id, type: string}
      - {name: pageStats, type: json} #---------(1)
      - {name: ruleGroups, type: json} #---------(2)
  method: get
filters:
  - type: add_time #---------(3)
    to_column: {name: created_at, type: timestamp}
    from_value: {mode: upload_time}
  - type: expand_json
    json_column_name: pageStats #---------(4)
    root: "$." #---------(5)
    expanded_columns:
      - {name: numberResources, type: double}
      - {name: totalRequestBytes, type: double}
      - {name: numberStaticResources, type: double}
      - {name: htmlResponseBytes, type: double}
      - {name: cssResponseBytes, type: double}
      - {name: imageResponseBytes, type: double}
      - {name: javascriptResponseBytes, type: double}
      - {name: otherResponseBytes, type: double}
      - {name: numberJsResources, type: double}
      - {name: numberCssResources, type: double}
  - type: expand_json
    json_column_name: ruleGroups
    root: "$.SPEED." #---------(6)
    expanded_columns:
      - {name: score, type: double}
out:
  type: elasticsearch
  nodes:
  - {host: 192.168.33.10, port: 9200}
  index: googleinsights
  index_type: googleinsights
項番 説明
(1) APIの戻り値のうち、jsonオブジェクトであるruleGroups.SPEED.scoreが欲しいため記載する。
(2) APIの戻り値のうち、jsonオブジェクトであるpageStats以下の項目が欲しいため記載する。
(3) 戻り値にtimestampが含まれていないため、embulk-filter-add_timeプラグインを使ってelasticsearchに送る際にtimestampを示すカラムを追加する。
(4) そのままでは扱えないので、embulk-parser-jsonプラグインを使ってパースする。
(5) どこを起点にjsonオブジェクトの値を取得するかを定義する。
pageStats直下のnumberResources等を取得したいため、この設定となる。
(6) ruleGroups.SPEED.scoreを取得するため、この設定となる。

実行

それでは実行します。

1. elasticsearch、kibanaを立ち上げます。
2. embulkを実行します。

$ embulk run config.yml

3. kibanaにアクセスしてみて、Discoverにデータが投入できていれば成功です。 f:id:uggds:20170714194807p:plain 4. Visualizeで可視化してみます。
数時間おきにelasticsearchにデータを投入しておいて、折れ線グラフを作成してみました。

横軸にcreated_atを指定し、縦軸にjavascriptResponseBytesを指定してみます。 f:id:uggds:20170714200924p:plain

うまく可視化することができました。

さらに突っ込んでみる

他にも、cssやimageのResponseBytesが取得できるのですが、それらを足し合わせたResponseBytesはないのが残念です。
そこで、Kibanaの画面上でこれらの項目を足し合わせるscriptを書き、新しい項目としてカラムを追加することをやってみます。

以下のサイトを参考にさせていただきました。
Kibanaでの項目集計について - 日本語による質問・議論はこちら - Discuss the Elastic Stack

サイトにあったやりかたと同様に
Kibana > Management > scripted fields > Add Scripted Field
からscriptを作成します。

f:id:uggds:20170714202313p:plain

作成したスクリプトは以下になります。

def t = [];
if (doc['cssResponseBytes'].value != null) {
  t.add(doc['cssResponseBytes'].value);
}
if (doc['htmlResponseBytes'].value != null) {
  t.add(doc['htmlResponseBytes'].value);
}
if (doc['imageResponseBytes'].value != null) {
  t.add(doc['imageResponseBytes'].value);
}
if (doc['otherResponseBytes'].value != null) {
  t.add(doc['otherResponseBytes'].value);
}
if (doc['javascriptResponseBytes'].value != null) {
  t.add(doc['javascriptResponseBytes'].value);
}

return t;

それぞれの値を配列に格納し、それをtotalResponseBytesというフィールドに集め、可視化するときに縦軸のAggregationでsumを指定することになります。

f:id:uggds:20170714203849p:plain

javaScriptのResponseBytesが全体のほとんどを占めていて、絵的には変わっていないように見えますが、 横軸をみるとうまく足し合わせたものになっているようです。

まとめ

今回はサイトの速度を可視化するため、Google PageSpeed Insights APIを使用しましたが、 embulkとelasticsearchの連携は他にも応用がききそうなので、他のデータも可視化して楽しめそうです!

Dockerコンテナ上にElasticsearchとKibanaの環境をつくる

概要

Dockerのお勉強として、Dockerを利用して、ElasticsearchとKibanaの環境をつくって見たいと思います。 また、複数のコンテナを扱うため、docker-composeを使用してみます。

Dockerの環境構築は下で記事でまとめました。 ugap.hatenablog.com

環境

  • Docker 1.11.1
  • docker-compose 1.7.1

構成

$ tree
.
├── docker-compose.yml
└── es_config
    ├── elasticsearch.yml
    ├── log4j2.properties
    └── scripts

docker-compose.yml

es: #--(1)
  image: elasticsearch:5 #--(2)
  ports:
    - "9200:9200" #--(3)
    - "9300:9300" #--(3)
  volumes:
    - "$PWD/es_config:/usr/share/elasticsearch/config" #--(4)
  environment:
    ES_JAVA_OPTS: "-Xms512m -Xmx512m" #--(5)

ki:
  image: kibana:5
  ports:
    - "5601:5601"
  links:
    - es
  environment:
    - ELASTICSEARCH_URL=http://192.168.33.10:9200
項番 説明
(1) elasticsearchの設定をesというサービス名で定義。
(2) elasticsearch公式のdockerイメージのversion5を指定。
(3) Elasticsearch で使用する HTTP用のデフォルトポートは9200、ノード間のコミュニケーション用に 9300を使用するため、ポートを公開する。
(4) es_configというディレクトリとelasticsearchを動かすdockerコンテナ上の/usr/share/elasticsearch/configのパスを紐づけ。
/usr/share/elasticsearch/configにはelasticsearchの設定ファイルelasticsearch.ymlが置いてある。(中身は後述)
(5) デフォルトではヒープサイズが2gのようで、下げてあげないとエラーがでてしまうので、ここで設定する。

Kibanaの設定も同様の考え方なので省略します。

elasticsearch.ymlの設定

http.host: 0.0.0.0 #--(1)

http.cors.enabled: true #--(2)
http.cors.allow-origin: "*" #--(3)
項番 説明
(1) HTTPサービスをバインドするホストアドレス。
(2) クロスドメイン通信を有効にする。
(3) 許容するドメインを指定。(今回は*としていますが、セキュリティ上のリスクがあります。)

実行

docker-composeを実行してみます。

$ docker-compose up -d

-dオプションでバックグラウンド実行になります。

ブラウザでhttp://192.168.33.10:5601/にアクセスしてみます。

f:id:uggds:20170716232919p:plain

うまく表示されました。

まとめ

docker-composeで複数のdockerコンテナを管理することができました。
elasticsearch、kibanaは様々なデータの解析・可視化をするのに注目されているOSSですが、設定できることがたくさんあるので、 一度つくった環境をどこでも作れるようにするansibleやdockerとのコラボが適しているなと思いました。

次はこの環境を利用して、何かKibanaで可視化してみたいと思います。

Vagrant + AnsibleでDocker環境をつくる

概要

Vagrant + Ansibleのお勉強です。
Macをコントロールマシンとして、Vagrantを使ってVirtualBox上にDockerインストール済みのCentOS7をセットアップします。
また、CentOS上のDocker以外の部分のプロビジョニングをAnsibleを使って構築します。

f:id:uggds:20170527130304p:plain

環境

  • Vagrant 1.8.1
    • box: williamyeh/centos7-docker
  • ansible 2.1.0.0

Vagrantのセットアップ

適当なディレクトリを作成し、Vagrantの初期化を行います。
次のコマンドを打つと、Vagrantfileという設定ファイルが作られます。
box名にはwilliamyeh/centos7-dockerを指定しています。
このboxは次のサイトでdockerと検索した結果の中から選びました。

Discover Vagrant Boxes - Vagrant Cloud

vagrant init williamyeh/centos7-docker

Vagrantfileができていると思います。 Vagrantfileを開いて、次の修正をおこないます。

  ・・・
  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
   config.vm.network "private_network", ip: "192.168.33.10" #(1)
  ・・・
  config.vm.provider "virtualbox" do |vb| #(2)
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
    vb.memory = "1024" #(2)
  end #(2)

(1)内部ネットワーク(192.168.33.10)を利用してホストからゲストに接続できるようにします。
(2)メモリ数を増やしました。デフォルトだと480MBのようです。

プロビジョニングの設定

作成したCentOSには最低限のものしかインストールされていません。
作業をしていく上で必要な(筆者が個人的に)GitやVimzshなどをインストールする必要があります。
ただ、これらをゲストマシンを作るたびに手でインストールしていくのは面倒です。

そこで、Vagrantの自動プロビジョニングの機能を使います。
この機能を使用すると、Vagrantは起動後に自動的にソフトウェアをインストールするため、ゲストマシンがすぐ使用できるようになります。

詳しくは以下を参照してください。
Provisioning - Getting Started - Vagrant by HashiCorp

プロビジョニングにはAnsibleを使用します。
Vagrantfileに次の処理を記述してください。

  ・・・
  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   sudo apt-get update
  #   sudo apt-get install -y apache2
  # SHELL
  config.vm.provision "ansible" do |ansible| # (1)
     ansible.playbook = "provisioning/vagrant.yml" # (2)
     ansible.inventory_path = "provisioning/hosts" # (3)
     ansible.limit = 'all' # (4)
  end
end

(1)プロビジョニングにansibleをつかいます。
(2)ansibleのplaybookの場所を指定しています。
(3)ansibleのiventoryの場所を指定しています。
(4)ansible-playbook を実行するホストやグループを指定しています。

ansible-playbookの準備

先ほど記述した場所に設定ファイルを用意しておきます。

$ tree
.
├── Vagrantfile
└── provisioning
          ├── config
          │       └── .vimrc
          ├── hosts
          └── vagrant.yml

provisioningディレクトリを作成し、その階層にhostsファイルとvagrant.ymlファイルをつくりました。

hosts

 [vagrants]
 192.168.33.10 #(1)

(1)Vagrantfileで設定した内部ネットワークIPを記述します。

vagrant.yml

---
- hosts: vagrants
  become: yes
  user: vagrant
  tasks:
  - name: install packages #(1)
    yum: name={{ item }} state=installed #(2)
    with_items:  #(3)
      - zsh
      - vim
      - git
  - name: copy
    copy: src=./config/.vimrc dest=/home/vagrant/ owner=vagrant #(4)

(1)タスク名を指定します。
(2)yumでインストールするタスクです。インストールはwith_itemsに記載されたものを順に行います。
(3)ここに記載されたツールがインストールされます。
(4)srcのものをdestにコピーするタスクです。vimの設定ファイルをゲストマシンのhomeディレクトリに配置する設定をしています。

vagrantの実行

vagrantを実行します。
williamyeh/centos7-dockerが初めて使うボックスである場合、ダウンロードに時間がかかります。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'williamyeh/centos7-docker' is up to date...
==> default: A newer version of the box 'williamyeh/centos7-docker' is available! You currently
==> default: have version '1.11.1.20160506'. The latest is version '1.12.1.20160830'. Run
==> default: `vagrant box update` to update.
==> default: Clearing any previously set forwarded ports...
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 (guest) => 2200 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
・・・省略・・・
==> default: Running provisioner: ansible... 
    default: Running ansible-playbook...

PLAY [vagrants] ****************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.10]

TASK [install packages zsh] ****************************************************
changed: [192.168.33.10] => (item=[u'zsh', u'vim', u'git'])

TASK [copy .vimrc] *************************************************************
changed: [192.168.33.10]

PLAY RECAP *********************************************************************
192.168.33.10              : ok=9    changed=8    unreachable=0    failed=0

仮想マシンが立ち上がった後にAnsibleによるプロビジョニングが始まったことが確認できます。

立ち上がった仮想マシンに接続して、zshvimやgitが使えることを確認します。

$ vagrant ssh
vagrant@localhost ~ $ zsh --version
zsh 5.0.2 (x86_64-redhat-linux-gnu)
vagrant@localhost ~ $ git --version
git version 1.8.3.1

最後に、目的であったDockerがインストールされていることも確認します。

vagrant@localhost ~ $ docker --version
Docker version 1.11.1, build 5604cbe

まとめ

はじめからDockerがインストールされたBoxを使うことで簡単に環境構築をすることができました。
また、今回はライトにしか使っていませんがansibleを使うことで冪等性の持った環境にすることもできました。

次回はこの環境を使って、Dockerコンテナ上にElasticsearch、Kibanaをたててみたいと思います。