hitomedia Tech Blog

株式会社ヒトメディアのテクノロジーに関するブログです

データ特性による圧縮されやすいJPEG/PNGファイルの作り方

はじめまして。
株式会社ヒトメディアでフロントエンドをしつつ、時々デザイン周りの監修をしている いとー です。

ヒトメディアでは毎月、有志が集まり勉強会を開いています。
今日はそこで発表した、画像形式とその内容における圧縮のしやすさについての資料を公開したいと思います。

この記事の内容

  • バナーやLP用の画像作成したものの、規定容量超えちゃった! どうしよう... 😅 ってよくあるよね
  • ファイルの形式によって、圧縮されやすい画像とされにくい画像があります
  • 圧縮されやすい構造を理解することで、質の高い軽い画像を作ろう✨

この記事を読んでほしい人

  • 容量とにらめっこすることの多いデザイナー
  • 少しでも軽いサイトを作りたいフロントエンドエンジニア

はじめに

一般的に「画像を軽くする」というと、書き出し時のクオリティ選択や、 書き出し後にメタデータを撤去するなど、ツールで処理する印象があると思います。
でも、画像制作時点での加工方法によっては書き出し時の容量が変えられます!
今回は、そのテクニックを紹介します。

ですがその前に、まずは基本的な画像形式についておさらいしましょう。

🏞 画像の形式、使い分けてますか?

基本的な画像形式

普段よく目にする画像形式をまとめると、以下のような種別で分けられます。

ファイル形式 色情報 圧縮方式(可逆・非可逆) 透過
JPEG ( .jpg / .jpeg ) 24bit (約1677万色) 非可逆 不可
GIF ( .gif ) 8bit (256色) 可逆
PNG-8 ( .png ) 8bit (256色) 可逆
PNG-24 ( .png ) 24bit (約1677万色) 可逆 可 (PNG-32)

※PNGは少し特殊な画像形式で、同じ拡張子でも内部的に扱える情報量が異なります。
ここではWeb上でよく使われる形式を、便宜的に PNG-8、PNG-24、PNG-32 として表記しています。

これらのファイル別の特性をざっくり言うと、

  • JPEGは写真や色調の多いグラデーションを扱う画像向き
  • GIF、PNGはイラストや文字・グラフなどのベタ塗り画像向き

と分けられます。

GIF or PNG

上記の内容から、写真であればJPEGを、そうでなければGIFやPNGを使うと良いことがわかりました。

さて、ここでみなさんからは次のような疑問が生まれてくると思います。

🧙「GIF or PNG、どっち使えば良いのさ」

答えは簡単です。

A. 迷ったらPNG

多くのケースではGIFよりPNG (PNG-8) の方が優秀です 🙌

PNG‑8 形式は GIF より優れた圧縮スキーマを使用しているので、PNG‑8 ファイルのサイズは同じ画像の GIF ファイルより 10 %から 30%小さくなります(画像のカラーパターンによって異なります)。

安心してPNGを使いましょう。

あれ? 俺のPNG、GIFよりデカイけど 🧐?

残念ながら、使ってる画像編集ソフトの書き出しロジックがしょぼい可能性が高いです 😜 (書き出し時にPNG-8が選べない等)

  • ImageOptim などの、画像圧縮ツールやサービスを使って圧縮しましょう。

各画像ファイル別の画像特性

お待たせしました!
ここからはそれぞれの画像ファイル種別における画像特性を知ることで、 どのような内容であればより圧縮しやすくなるかを解説したいと思います。

なお、今回はタイトルにもあるようにJPEGとPNGについて紹介します。

JPEG

JPEGの画像特性

JPEGは、人間にとって判別がつきづらい部分を重点的に加工することで、ファイルサイズを小さくする戦略を取ってます。 MP3ファイルが、人間にとって判別のつけづらい高周波をカットする戦略を取っているのと似てますね 🎧

特に風景や人物を写した写真などを扱う場合、 同程度の画質ではJPEGファイルのほうがPNGより軽く扱えることが多いです。

JPEGが得意な画像と苦手な画像

写真の中にも、JPEGにとって処理のしやすい、いわば得意な画像と、少々扱いづらい苦手な画像が存在します。 実際の加工方法を見ていく前に、JPEGにとって得意な画像と苦手な画像を感覚的に捉えておきましょう。

以下の2つの画像はどちらも会社の近くで撮影したものですが、撮影している内容によってファイルサイズが違うことがわかります。 左がJPEGにとって得意な画像、右が苦手な画像です。

得意 苦手
空などの一様な風景が多い写真 わちゃわちゃした風景の写真
685KB 944KB
・空などを含む、全体的に色味が均一でさっぱりとした風景 ・場所によって色や明るさが異なる、ごちゃっとした風景

このように、写している色や明るさで、ファイルサイズに違いが生まれます。
ちなみに上の画像はブログの都合上、画像幅が縮小加工されたものが表示されていますが、元のファイル同士では左の写真が2.6MB、右は3.5MBと、1MB近くも差がありました 😖

なんとなーく、どのような表示が得意な内容か分かりましたでしょうか ?
ここで紹介する方法は、この「JPEGにとって得意な内容に寄せる」加工をすることで容量削減を実現していきます。

今回は、ごちゃみ成分の強いこの画像で、各加工のテクニックを紹介していきます。 JPEGが苦手な紅葉風景 画像容量は1.4MBです。

テク1: 背景ぼかし

JPEGはコントラストの強い、エッジの立った画像を圧縮するのは苦手です。 そこで、背景をぼかして境界線が滑らかになるように加工してあげると、容量をぐっと落とすことが出来ます。

元画像 加工後
オリジナル画像 ぼかし加工後の画像
1.4MB 786KB (約44%の削減 ✨)

例えばバナーの背景など、一部をぼかしても構わないような写真であれば、このようにぼかしをかけて書き出すことで容量を抑えることができますね。

実際の加工方法としては、Photoshopであればメニューから フィルター > ぼかし > ぼかし (ガウス)... などでぼかしフィルターをかけて、効果をつけたい範囲にレイヤーマスクを適用すると良いでしょう。 ぼかし加工のやり方

テク2: モノトーン

写真を白黒やセピアカラーなどに加工した場合も、容量を落とすことが出来ます。 色情報が少なくなり、量子化した際に圧縮しやすくなったため軽くなるのだと思います。

元画像 加工後
オリジナル画像 白黒画像
1.4MB 658KB (53%の削減 🌟)

Photoshopで加工する場合、単純な白黒であればメニューから レイヤー > 新規調整レイヤー > 白黒... で調整を掛けると楽ちんです。
セピアカラーなど、色を付けたい場合は 着色 にチェックを入れて任意の色を付けると良いでしょう。
色を付ける時は、なるべく彩度の低い、薄い色を付けるようにすると容量の削減度合いがアップしてイイカンジです。 白黒加工のやり方

テク3: カラーフィルター

JPEGでは似たような色味が連続していると圧縮されやすい特性があります。 そこで、写真の上から全面に色を敷いた半透明のレイヤーを被せてカラーフィルターが掛かったような効果を出しつつ、色調を寄せることで書き出し時の圧縮率を高めます。

元画像 加工後
オリジナル画像 カラーフィルター画像
1.4MB 1.2MB (約12%の削減 🌝)

Photoshopで加工する場合、画像と全く同じサイズのシェイプを別レイヤーで被せて、不透明度を40%程度に抑えることで簡単に作成できます。 カラーフィルターのやり方

PNG

PNGの画像特性

PNGは可逆圧縮なので、ZIP圧縮が近いイメージです 📁
基本的に画像内容そのものは書き換えられず、データ配置の転換によって圧縮されます。 画像に使用する色数と、その連続度合いでPNGの容量が変わります。

PNGをより圧縮したい場合

そもそもPNGで書き出す画像はノイズの少ないイラストや文字を扱っている前提のため、画像加工時に取れる施策でJPEG程劇的に減らせるものはあまり多くありません 😣

ですが、たとえば

  • 写真を一部切り抜いて透過加工するために使いたい
  • イラストの背景に実写を使用したいが、メインのイラスト自体のクオリティは下げたくない

といった場合には次のような方法を試すことが出来ます。 ここからはPNG用に以下の画像を使って説明します。 f:id:cucumisin:20180608142320p:plain 写真の周りに透過を使用している画像(PNG-32)で、容量は1.3MBです。

テク4: PNG-8で書き出す

PNG-8を指定して書き出した際、画像データは256色に減色されて書き出されます。
可逆圧縮のメリットが本末転倒な気もしますが 🙉
これを利用すれば画像の容量をPNG-24やPNG-32の時よりも減らして書き出すことが可能です。

多くの場合、PNG-8 で減色して書き出した画像がその画像でできる最軽量の画像になります。 そのため PNG-8 で書き出したものよりもさらに軽くする、ということは難しいです (色数を256色よりも減らせば可能)。

元画像 PNG-8 書き出し
オリジナル画像 PNG-8で書き出した画像
1.3MB 380KB (約70%の削減 😻)

(ブログの都合上、アップロード時にPNG-32に書き換えられているため上記は手元のサイズを表記しています)

Photoshopではメニューの ファイル > 書き出し > 書き出し形式... から、
右上のファイル設定を PNGファイルサイズ小 (8-bit) を選ぶとPNG-8形式で書き出すことができます。

PNG-8の書き出し方

ただし、書き出された画像は作成時のものから色を間引いて書き出されています。 減色度合いが大きいため、内容によっては塗りがガタガタと表示されてしまうこともよくあります。

PNG-8とPNG-24(PNG-32)の影の比較

上の画像は元の画像とPNG-8の画像を拡大して比較したものです。
枠で囲った箇所をよく見ると、PNG-8の影のところがカクカクとした表示になっていることが分かります。
PNG-8で書き出した後は、画像の減色され具合を確認しつつ、内容に問題がなければ使うようにしましょう。

テク5: 一部の階調数を調整して書き出す

上のように、PNG-8で書き出すと減色度合いが大きすぎたり、メインで描いたイラスト部以外の加工でなんとか全体の容量は減らしたい、みたいな時は意外とあります。

そういう場合は、任意の箇所のみの色数を削りつつ、PNG-24(またはPNG-32)で書き出すことで、クオリティとのバランスを調整しながら容量削減を狙うことが出来ます。

下記の加工では、写真の箇所のみを対象にして階調数調整を行い、色を間引いて書き出しています。

元画像 加工後
オリジナル画像 "ポスタリゼーションで加工した書き出し"
1.3MB 1.1MB (約15%の削減 😉)

階調数の調整はPhotoshopのメニュー レイヤー > 新規調整レイヤー >ポスタリゼーション から行えます。 調整を行いたいレイヤーに対して マスククリッピングマスク 等を用いて範囲を絞り込み、任意の階調数を入力することで対象箇所で使用する色数を減らすことができます。

ポスタリゼーション加工のやり方

階調数で指定する数値が小さければ小さいほど容量は削減できますが、その分使われる色数がどんどん減っていくので、画像の原型がとどまらなくなっていきます。また逆に、あまり大きな数字を入れたり、あるいは指定した範囲で使われている色数がそもそも少なかったりすると、元画像よりも容量が大きくなってしまう場合もあったので要注意です。

今回は階調数を30として指定していますが、だいたい20〜40ぐらいの範囲で指定すると見た目と容量のバランスが良いのではないかと思います。

以上が今回紹介する画像編集時のテクニックでした。
みなさんの制作時の参考になれば幸いです 🙏

おまけ

Webページの実装 「これってスタイルでやるべき? 画像側で書き出すべき?」

これまで書いてきたようなことが分かっていると
Webページの画像実装時、CSSなどのスタイル側で加工するべきか、画像側で加工するべきかの判断がつきやすいです。

例えば全体的に赤っぽい加工をしたい場合

以下のように加工をかけた表示を行うページがあったとします。 カラーフィルター画像

このとき考えられる選択肢は、

  • A) オリジナル画像 + CSSなどで色を被せるスタイルを指定して使用
  • B) 赤っぽい加工を施した画像を、そのまま書き出して使用

の2択が多いかなと思います。

画像特性から、最適な実装方法を考える

キャンペーン用LPのメイン画像の場合

テクニックのところで紹介したように、JPEGの特性 → 似たような色調が続くと圧縮しやすいです。そのため、加工済みの画像を書き出した方が、ブラウザ閲覧時にロードする容量が軽くなると考えられます。

キャンペーンページのようなファーストビューの表示が重要な場合、とにかく初回のロードが大事だと思います。そのような場合は、 予め加工済みの画像を載せたほうが軽くなりそうだな と分かります 👑

使う画像は同じでも、ページによって色を変えたい場合

例えば「カテゴリーのヘッダー画像として1枚の画像を使いたけど、カテゴリによって被せる色を出し分けたい」みたいな場合はこれに当てはまります。

この場合、1つの画像を使いまわしてスタイル側で色付けしたほうが、キャッシュが効いてDLが1回で済むのでページ遷移時のスピードは早まりそうだな と分かります 🎩

どちらを優先するかはその時の状況次第だとは思いますが、画像について知ることで、優先順位を決めやすくなったのではないかな、と思います。

まとめ

以上が勉強会で紹介した内容です。
いかがでしたでしょうか?

どのような加工内容が容量に影響を与えるかを知っていると、その時に取れる選択肢や判断の幅が広がって、より最適な手段を選ぶことができます。 この記事がその手段を広げる手助けになれば幸いです。

さて、そんなヒトメディアでは現在、一緒に働いてくれるデザイナーをゆるく募集中です。 興味のある方は、是非声をかけてみてくださいね!

www.wantedly.com

Google Spreadsheets APIを使用して、Railsの翻訳ファイルを自動生成できるように

サーバーサイドエンジニアのはやしです。🌲🌲

Railsアプリケーションの国際化対応、したことありますか?
翻訳ファイルの管理、めんどくさくないですか?

最近携わったプロジェクトで、Google Spreadsheets APIを利用して翻訳ファイルを自動生成できるようにしたので、その流れとソースコードを以下にまとめました🙏

Railsアプリケーションの国際化について

本記事では翻訳ファイル自動生成の流れについて書いていますので、
概要・手順等は Rails国際化 (I18n) API | Rails ガイド を参考にしてください🙏

YAMLの定義と出力

以下のような翻訳ファイルがあった場合に👇

config/locales/(ja|en).yml

ja:
  common:
    view:
      login: "ログイン"
      regist: "新規登録"
en:
  common:
    view:
      login: "Login"
      regist: "New Registration"

こうするとview内でymlで定義した文言を出力できるようになります👇

I18n.t("common.view.login")
=> "ログイン" # localeがjaの場合
=> "Login" # localeがenの場合

省略記法

ディレクトリ構造とymlのキーを合わせると省略記法が使用できます。
以下のようなymlが定義されていると👇

config/locales/views/users/new/ja.yml

ja:
  users:
    new:
      pagetitle: "新規登録"
      pagetext: "新規登録のテキストです"

view内では以下のような省略記法が使用できます。👇

app/views/users/new.html.erb

<%# 省略記法を使わない場合 %>
<%= I18n.t("users.new.pagetitle") %>
=> "新規登録"


<%# 省略記法を使った場合 %>
<%= I18n.t(".pagetitle") %>
=> "新規登録"

機能のゴール

このgemを使って

gimite/google-drive-ruby: A Ruby library to read/write files/spreadsheets in Google Drive/Docs.

こんなスプレッドシートから

f:id:seyayase:20180530145722p:plain

こんなコマンドを叩いて

$ bundle exec rails locale_generator:run

こんなYAMLファイルを自動生成して

ja:
  users:
    new:
      pagetitle: "新規登録"
      pagetext: "新規登録のtextです"
en:
  users:
    new:
      pagetitle: "Sign Up"
      pagetext: "Sign up text"

こんな風にViewで呼び出したい

app/views/users/new.html.erb

<%= I18n.t(".pagetitle") %>
=> "新規登録"  # localeがjaの場合
=> "Sign Up"  # localeがenの場合

ソースコード

全てのソースコードをここに書くと長くなってしまうので、gistを参考にしてください🙏
気になる点等、コメントお待ちしています🙏🙏

hayashiseiya/google_sheet.rb - Github Gist

所感

いまいちなこと

  • APIの有効化や認証情報を取得しないとコマンドを実行できないので、環境を整備するのに初期コストがかかってしまいました。。😢 (とはいえwikiに手順書いておけば問題なさそうでした)
  • スプレッドシートではなく翻訳ファイルの方を直接更新すると、直接更新した分は次回コマンド実行時に削除されてしまうので、運用ルールを守れないとかえってコストがかかりそうかなと・・・

よかったこと

  • 翻訳者の方がスプレッドシートを更新、エンジニアがコマンド一発で一括更新、という仕組みができて、管理する手間が減ったような気がしてます👌
  • 日々の運用業務を技術で解決するのは、エンジニア感あって楽しかったです👌

サービスから学んだデザイナーの心構え

デザイナーのばっこ(@is178)です。

ヒトメディアでは、毎月有志で勉強会を開いているのですが、そこで「サービスから学んだデザイナーの心構え」という発表をしまして、その時の資料を公開したいと思います。

はじめに

f:id:is178:20180528112146p:plain

これまでいくつかの現場でいくつかの「サービス」に携わり、自分なりに、できる範囲で、プロダクトや開発フロー、チームの意識を改善しようと努力してきました。

その過程で、上手くいったこともあれば、いかなかったこともたくさんあり、良い面も悪い面も見てきたことで、デザイナーとしても社会人としても成長させてもらったように思います。

今日はそのサービスから学んだことを共有したいと思います。

f:id:is178:20180528112203p:plain

サービスから学んだことを書き出して整理してみると、「何かに偏ったり拘ったりせずに、中庸でいること」が軸にあるなと感じたので、そういった切り口で以下の4つまとめてみました。

では、1から順番に見ていきたいと思います。

1.「制作者視点」に囚われない

f:id:is178:20180528112230p:plain

組織で分業をして仕事に取り組んでいると、良くも悪くも、お金の話や利用者から少し距離を置かれた環境で、デザイナーは美的な観点(見映え、論理的整合性など)の追求を求めたり・求められたりして、制作者視点に偏りがちです。

しかし、本来は「制作者・事業者・利用者」3つの視点をバランスよく持つ必要があります。

なぜなら、制作者視点で、どんなに精巧・立派に見えるものができたとしても、それが利用者に喜んでもらえるとは限らないし(利用者視点)、事業として成立するとも限らないからです(事業者視点)。

ものをつくることが仕事だと思っているのと、ものをつくることが仕事の一部だと思っているのとでは、仕事の質が全く異なります。

2.「実行」に偏らない

f:id:is178:20180528112240p:plain

サービスに携わっていると、「これが問題なんじゃない?」とか「これやったら良さそう!」というアイデアはたくさん思いつきます。 そのまますぐに作業に飛びついてしまえば、何かやっている感が出ますし、前に進んでいるような気もしてきて、安心感が得られます。

しかし、本来は、「つくること」に囚われずに、「つくる前」と「作った後」にも注力し、その3つをバランスよく見る必要があります。

つくることに夢中になってしまうと、「そもそも今これは何のためにやっているんだっけ」とか、「こないだやったアレの結果はどうだったんだっけ」というような、一歩引いた視点を持つことができず、いつも何かを一生懸命やってはいるけども、何も成果は出ていない、なんてことに陥りがちです。

絶対に正しいと思えるような取り組みも、思わぬ結果を招くことがありますし、見込み通りうまく行ったときも、そこから学びを得られれば、再現性のある知見となります。

また、見込みが外れたときにも、そこから学びを得て次に活かせるようにする必要があるので、振り返りをしなくて良いという状況はありません。

3.「自分の評価」にこだわらない

f:id:is178:20180528112251p:plain

自分を賢く見せようとしたり、自分が手柄を立てて成果を出すことにこだわったり、自分の正しさを証明しようとしたりしていると、何事も上手くいきません。 自分の評価にこだわらず、話を前に進めることにこそ、こだわります。

例えば、わからないことはきちんとわからないと言えるだけで、自分が腹の底から理解した情報を元にした判断や行動ができるようになります。その結果失敗したとしても、何らかの見立てがあった訳ですから、何となくでやるよりも大きな学びがあります。

また、自分が手柄を立てることよりも組織が成果を出すことを優先できると、自分の案よりいい案があれば採用しますし、自分より上手くできる人がいれば任せることができます。自分は何もせずとも、人と人を繋ぐだけで解決することすらあります。

さらに、失敗を過剰に恐れなくなるので、批評という形だけではなく、自らも責任を背負って、行動という形で仲間に協力することもできます。

4.「環境」に依存しない

f:id:is178:20180528112304p:plain

「環境」に依存してしまっていると、何が起こっても周りのせいにする癖がついてしまいます。

「あの人がああだから気分が上がらない」とか、「自分はこの肩書きだからあれは自分の仕事じゃない」とか、「やっぱりこれは最初から無理だったんだよ」とか。

しかし、問題が起こるということは、今のままでは上手く行かないということであり、成長の機会でもあります。

もちろん、なんでも自分ごと化すればすぐに解決できるわけではありません。とても難しいこと、数年単位掛かってしまうこともあります。

それでも、「他責」にしたまま振り回され続けるよりかは、「自分ごと化」してコントロールを試みる方が、精神衛生上も、成果を目指す上でも良いのではないでしょうか。

終わりに

「僕はこのような理由でこのように考えた」というだけであって、常にすべての項目を実践できている訳ではありませんが、それを目指して精進しています💪

ちなみに、ヒトメディアでは現在デザイナーをゆるく募集中です。 よろしくお願いします。

www.wantedly.com

非エンジニアにプログラミングを教えた話

ヒトメディアでサーバサイドエンジニアしております、@nakaearth です。

今回は少し前の話になりますが、出向先で非エンジニアにプログラミング講習をした話です。

どんな講習をしたのか

そもそもどうして非エンジニアに対しプログラミング勉強会をすることになったのか。きっかけは企画の方との会話からです。

f:id:nakaearth:20180501111117p:plain

そして開催することに

f:id:nakaearth:20180501111246p:plain

参加者はそれほど多くないと思っていたのですが、結構多かったです。

f:id:nakaearth:20180501111338p:plain

予想以上の参加人数だったので、マシンの都合もあり二人一組でやっていただくことにしました。講師側は私ともう一人の二人体制にしました。 講習内容ですが、rubyを使った文字列操作やループ処理などの簡単なプログラミングを体験するものにしました。

f:id:nakaearth:20180501113318p:plain

所感

環境設定でつまずく

まず最初に躓いたのでは環境設定のところでした。あらかじめ「このマシン使ってやってください」と用意すればよかったかもしれませんが、準備ができず当日参加された方々のマシンにRuby環境やエディタをそれぞれ作るのに時間がかかってしまいました。そこの手順も明確にしておくか、可能なら事前に済ませておけば良かったと思いました。

説明は細部まで丁寧に

ruby -vをしてくださいと言った時に受け取り方が様々でした。ruby-vだったりruby - vだったり。普段自分が何気なくやって当たり前と思うことでも注意して話をしないと伝わらないと改めて思いました。

動いたものを見るとテンションが上がる

自分たちで書いたプログラミングが動くのを見ると盛り上がります。動いたら自分たちで少しずつ変えて実行結果がどう変わっていくのか見てもらいました。

後日

後日アンケートをしたら前向きな意見が結構多かったのでやってよかったと思います。非エンジニアの方も結構プログラミングに興味あるようで、第2回目があれば参加したいいう声が結構ありました。 そこで・・・・近日中に第2回目をやるにしました。その様子もまたブログに書こうと思います。

ノンデザイナー向けに「デザインとはなんぞや勉強会」をした話

デザイナーのばっこ(@is178)です。

ヒトメディアでは、毎月有志が集まって勉強会を開いているのですが、先月、ノンデザイナー向けに「デザインとはなんぞや」という発表をしまして、その時の資料を公開したいと思います。

デザインとはなんぞや

f:id:is178:20180410164813p:plain 「デザイン」と聞くと、最初に思い浮かぶのはきっと、華やかな感じの、センスがどうとかクリエイティブがどうとか、何か見た目が良いものを作るようなイメージが強いと思います。

しかし、それはあくまでデザインの一面であり、僕の場合は、UXデザインとか、サービスデザインとか、もう少し広い意味の、地味な方のデザインに興味があります。

今回は、その辺りの関係性とか、それぞれどういうことをするものなのかとか、僕の認識をまとめました。まずは概念のお話をして、土台となる共通認識を作れればと思います。

デザインはめっちゃ範囲が広い

f:id:is178:20180410164817p:plain デザインと一言に言っても、対象範囲がめっちゃ広いです。

パッと思いつくだけでも、都市デザイン、建築、インテリアデザイン、ファッションデザイン、プロダクトデザイン、コミュニティデザイン、サービスデザイン、Webデザイン、グラフィックデザイン、UIデザイン...

同じデザインと名のつくものでも、別の分野になれば、素材も加工方法もまるで違う未知の世界です。 今回は僕がわかる範囲のデザインについてお話ししますが、それ以外にもいろいろな「デザイン」があることは頭においておいてもらえると助かります。

グラフィックデザイン、UIデザイン、UXデザイン

f:id:is178:20180410164830p:plain 僕がわかる範囲のデザインは、僕が今まで経験してきたデザインで、この3つになります。

それぞれ定義があってないようなものなのですが、僕の解釈ではこの図のような関係性で、 グラフィックデザインとUIデザインはかぶる領域がありつつ、それらは全てUXデザインの中の要素、というイメージです。



ひとつ断わっておきたいのが、ここでは純粋に「○○デザイン」のお話をするのであって、「○○デザイナー」のお話をするわけではありません。*1

それぞれの特徴

f:id:is178:20180410180519p:plain グラフィックデザインは、商品を売るための手段です。いわゆる広告です。 これ単体で考えると、商品が売れさえすれば良いという、商業に魂を売りがちなデザインでもあります。

UIデザインは、使い勝手を高めるための手段です。 対象をインターフェイスに限定して注力するので、自然と細部まで意識することになり、せっかくのこだわりが自己満足になりがちなデザインでもあります。*2

UXデザインは、他二つのように「売ったり、使いやすくしたり」することをゴールにするのではなく、その先で良い体験を生むための手段です。 ただ、それを実現するためには、前述の2つのデザインや他の要素も必要になるので、これ単体ではただ理想論を振りかざすだけになりがちなデザインです。

では、一つずつ詳しく見ていきます。

グラフィックデザインは「視覚(ビジュアル)のロジック」を考える

f:id:is178:20180410164837p:plain グラフィックデザインの目的は、広告・販売促進のためのスタイリング(雰囲気、美しさ、心地よさを演出)。スタイリングによって情報整理、価値の付与をします。

芸術色が強く、斬新なものが好まれがちです。 また、分業によって専門性が高められた結果、スタイリングによって成果を出すという本来の目的から外れ、正確な効果検証などがされず、単に見た目の美しさを評価するアートのような扱いを受けている面もあります。

よく「デザインってセンスだよね」と言われるのがこの分野だと思うのですが、ほとんどの場合、天性の才能だとか、神の啓示によってデザインしているわけではなくて、作る時間と同じくらいかそれ以上にリサーチをした上で制作するのが一般的です。

また、「こういう時はこうしましょう」というお作法もたくさんあるので、そういったことを元に「視覚(ビジュアル)のロジック」を考えるデザインです。

デザイン対象:紙、Webサイト(PC)、商品パッケージ、デジタルサイネージ、etc…
デザインスキル:情報設計、レイアウト、配色、タイポグラフィ、コピーライティング、ロゴデザイン、アイコンデザイン、写真加工、イラスト、etc…


UIデザインは「対話(インタラクティブ)のロジック」を考える

f:id:is178:20180410164842p:plain UIデザインの目的は、使いやすさ・分かりやすさを高めることです。

「複雑な機能を持つプロダクトが増えた」「機能や見栄えだけでは売れなくなってきた」という流れから、 使い勝手が注目され始めて、人間工学や認知心理学の考え方がデザインに持ち込まれるようになったという経緯があります。

よくグラフィックデザインの一部だと済ませられがちですが、単にそれっぽいものを作って終わりではなく、「作ったものが実際使われたらどうなるか、そのあとの改修はどうするか」といったところまで踏み込めてこその「UIデザイン」だと僕は考えています。

斬新なものが好まれるグラフィックデザインとは逆で、とにかくガイドライン準拠、「標準」から逸脱しないことが好まれがちです。 余計なことをせずに、変に記憶に残らない、ユーザーに意識されないUIが良いUIなので、地味な仕事ではあります。

モノだけではなく利用者にも目を向けて、使い勝手を高めるために「対話(インタラクティブ)のロジック」を考えるデザインです。

デザイン対象:Webサイト(モバイル)、アプリ、(デジタル)プロダクト、etc…
デザインスキル:利用文脈の理解、対象媒体のデザインガイドラインの理解、人間工学、認知心理学、ユーザーテスト、etc

UXデザインは「工程(プロセス)のロジック」を考える

f:id:is178:20180410164846p:plain UXデザインは、単なる見た目の良さや使いやすさを超えて、それらが良い体験を生み出すことを目指します。 
プロダクト起点ではなくユーザー起点で、最終的に提供したい体験から作るものを逆算するため、プロダクトである必要もなくなる場合もあります。

アウトプットとしては、ユーザー調査の結果や学びを共有するための資料、仮説検証のためのプロトタイプ、課題や解決策などの認識合わせをするための資料など、それ自体がユーザーに直接影響を与えることのない、中間制作物が多くなります。


プロダクトが良い体験を生むために、チームが正しいものを正しく作る「工程(プロセス)のロジック」を考えます。

デザイン対象:ユーザー、チーム、ワークフロー、etc...
デザインスキル:ユーザーインタビュー、ジャーニーマップ 、プロトタイピング、etc…

デザインがデザイナー以外にも解放された

f:id:is178:20180410164851p:plain UXデザインの大きな特徴として、「デザインがデザイナー以外にも解放された」というところがあるように思います。

というのも「グラフィックデザイン」や「UIデザイン」は、基本的には前提となるプロダクトを疑わずに「どう見せるか」を考える部分であり、デザイナーは製作フローに途中参加する形でした。

ところがUXデザインにおいては、制作フェーズより前の段階や後の段階にも関わることになるので、デザインの範疇が一気に広がりました。 それと同時に、デザインがデザイナーだけの手に負えるものではなくなってしまったので、全てのフェーズでチームの協力が必要になります。

UXデザインは、単なるバズワードではなく、デザインの在り方を大きく変える考え方を持ち込みました。

デザイン≠センス、デザイン≠ロジック

f:id:is178:20180410164900p:plain 最後に、これだけ覚えておいてもらいたいのが、 紹介したこれらのデザインはすべて、センスではなくロジックだということです。

もしかしたら、世界トップ数%のクリエイターは天性の「センス」で仕事をしているかもしれないのですが、 基本的には、グラフィックもUIもUXも、なぜそうデザインするのかに理由があり、ひどいデザインには大抵ロジックがないです。

この主張をする理由は2つあって、「デザインってセンスだよね〜」で済まされてしまうと、

  • アウトプットに至る過程の努力(日頃の勉強、製作時の調査やラフ制作など)がまるで無かったことになってしまう
  • デザインの範囲が広がり、チームでデザインする必要が出てきた今、「デザインはセンス」という考え方がハードルになってしまう

という二点から、「デザイン=センス」という意見は、積極的に否定していきたいと考えています。

終わりに

ヒトメディアでは現在デザイナーもエンジニアも募集中です! よろしくお願いします!

Twitterもやってます!良かったらフォローしてください!(@is178

www.wantedly.com

www.wantedly.com

*1:何が言いたいのかと言うと、グラフィックデザイナーはUIデザインもUXデザインもすることがあるので、「グラフィックデザインの特徴=グラフィックデザイナーの特徴」とはなりません。

*2:というのも、ほとんどの場合、どう作るかより何を作るかの時点で勝負が決まっていたりするので...

RSpecで悲観的ロックのテストを書く

こんにちは。花粉症で鼻水が止まらない日々が続いている hilotter です。

同一レコードに対し複数ユーザ(もしくはバッチ処理等)から同時に更新される可能性がある場合、排他制御を行う必要があります。

今回はシンプルなポイント付与機能を例に、悲観的ロックを用いた排他制御のテストを書いてみたいと思います。

シンプルなポイント付与機能仕様

  • ユーザは他のユーザにポイントを送ることができる

今回のサンプルは以下の環境で確認しました。

  • Rails 5.1.4
  • MySQL 5.7.20
  • MySQLのトランザクション分離レベル REPEATABLE-READ(デフォルト設定)

add_pointメソッドの実装

  • Userモデルにポイントを付与できるadd_pointメソッドを追加します

transaction内でlock!を用いて悲観的ロックをかけています。

# app/models/user.rb

class User < ApplicationRecord
  def add_point(point)
    ActiveRecord::Base.transaction do
      lock!
      self.point += point
      save!
    end
  end
end

RSpec

続いてテストを書いていきます。

RSpecのデフォルト設定では、テスト用のデータ登録を行った際に素早くデータのクリーンアップができるようにトランザクション内でテストが実行され、テストが終わった際にトランザクションがロールバックされるようになっています。

今回はトランザクションのBEGIN ~ COMMITが正しく行われることを確認したいため、この設定を無効化します。 1

# spec/rails_helper.rb

config.use_transactional_fixtures = false

続いて本題のUserのspecを書きます。

Threadを用いてポイント付与のメソッド(add_point)を同時に呼び出すようにします。

# spec/models/user_spec.rb

require 'rails_helper'

RSpec.describe User, type: :model do
  describe "#add_point" do
    # 100ポイントを持っているAさん
    let!(:user) { create(:user, point: 100) }

    context "2人のユーザから同時更新された場合" do
      before do
        threads = []
        threads << Thread.new do
          ActiveRecord::Base.connection_pool.with_connection do
            # Bさんから10ポイントを付与
            u = User.find(user.id)
            u.add_point(10)
          end
        end
        threads << Thread.new do
          ActiveRecord::Base.connection_pool.with_connection do
            # 同じタイミングでCさんから50ポイントを付与
            u = User.find(user.id)
            u.add_point(50)
          end
        end
        threads.each(&:join)
      end

      it { expect(user.reload.point).to eq(100 + 10 + 50) }
    end
  end

specの実行とtest logの確認

bundle exec rspec を実行するとテストが通ります 🎉

test実行時のクエリログも確認してみましょう。

# log/test.log

(0.6ms)  BEGIN
(1.6ms)  BEGIN
User Load (2.0ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 178 LIMIT 1 FOR UPDATE
User Exists (0.9ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = 'user1@example.com' AND (`users`.`id` != 178) LIMIT 1
SQL (0.9ms)  UPDATE `users` SET `point` = 110 WHERE `users`.`id` = 178
User Load (13.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 178 LIMIT 1 FOR UPDATE
(4.5ms)  COMMIT
User Exists (4.1ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = 'user1@example.com' AND (`users`.`id` != 178) LIMIT 1
SQL (0.9ms)  UPDATE `users` SET `point` = 160 WHERE `users`.`id` = 178
(6.2ms)  COMMIT

悲観的ロックにより、1つ目のupdateの完了を待ってから、2つ目のupdate文が実行されています。

悲観的ロックをかけなかった場合

悲観的ロックをかけなかった場合の実行結果も確認しておきましょう。

# app/models/user.rb

class User < ApplicationRecord

  def add_point(point)
    ActiveRecord::Base.transaction do
      # lock!
      self.point += point
      save!
    end
  end
end

この状態でrspecを実行すると、150が結果として返ってきてしまいました。

(実行の度に結果が変わるため150もしくは110が返ってきます)

F

Failures:

  1) User#add_point 2人のユーザから同時更新された場合 should eq 160
     Failure/Error: it { expect(user.reload.point).to eq(100 + 10 + 50) }

       expected: 160
            got: 150

クエリログを確認してみると、ロックをかけていないため(100 + 10)のupdate文と(100 + 50)のupdate文が実行されてしまい、2つ目のupdate文によって1つ目のupdate文の結果が失われてしまっています。(ロストアップデート)

# log/test.log

(0.5ms)  BEGIN
User Load (0.8ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 179 LIMIT 1
(0.8ms)  BEGIN
User Exists (1.1ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = 'user1@example.com' AND (`users`.`id` != 179) LIMIT 1
User Exists (1.3ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = 'user1@example.com' AND (`users`.`id` != 179) LIMIT 1
SQL (4.9ms)  UPDATE `users` SET `point` = 150 WHERE `users`.`id` = 179
SQL (9.5ms)  UPDATE `users` SET `point` = 110 WHERE `users`.`id` = 179
(5.7ms)  COMMIT
(2.0ms)  COMMIT

まとめ

同時実行処理の手動確認は難しいですが、Threadを用いることで同時実行処理をテストできるようになりました。

もっとシンプルに書く方法や、こうするとより良い等ありましたらアドバイスいただけますと嬉しいです。

参考


  1. データのクリーンアップに関してはdatabase_rewinderを利用しています。

prprでGithubのPullRequestレビュー依頼をSlack通知する

最近サーバサイドをやりつつiOSアプリ開発をやったり何でも屋になっている hilotter です。

GithubのPullRequestレビュー機能、便利ですね。

チーム開発においては相互レビューが非常に大事です。

今回は、レビュー依頼を行った際にSlackに自動通知するようにしたらより便利になったというお話を共有させていただきます。

SlackのGitHub連携の課題

Slack公式のGithub連携がありますが、こちらは以下の2点が課題となっていました。

1. メンション付きのコメントをしても、message-attachmentsの記法になっているため、うまく通知されずコメントを見落としてしまう

f:id:hilotter:20170824122029p:plain

このため、コメント後にSlackでも「コメントしました!」と2重で連絡をする必要がありました。

2. PullRequestのレビュー依頼を行ってもレビュアーへの通知が飛ばないため、レビューすべきPullRequestに気づけない

f:id:hilotter:20170824122242p:plain

こちらも同様にレビュー依頼の後、Slackでも「レビューお願いします!」と2重で連絡をする必要がありました。

prpr

これらの課題を解決するために、 githubのプルリクエストに反応するbotフレームワークであるprprを使わせていただくことにしました。

ぺろぺろ - Github pull request bot framework - - みずぴー日記

prpr及び、prpr-mention_commentプラグインを用いることで、課題1に挙げた「メンション付きのコメントをSlackに通知できない」を解決することができました。

また、よくある問題として「Githubのユーザ名とSlackのユーザ名が異なる」というのがありますが、この場合もMEMBERS.md というユーザ名の対応表を作って該当リポジトリにコミットすればユーザ名を変換してくれるので安心です。

MEMBERS.mdの記述方法

記述がない場合は、そのままのユーザ名で通知されるので、MEMBERS.mdには変換が必要なユーザのみを書いておけばOKです。

 * @Githubのアカウント名1: @Slackのアカウント名1
 * @Githubのアカウント名2: @Slackのアカウント名2

prpr-mention_reviewers

課題2の「PullRequestのレビュー依頼を行ってもレビュアーへの通知が飛ばない」に関しては、既存プラグインが見つからなかったのでプラグインを作ってみました。

prpr-mention_reviewers

できることとしては以下になっています。

  • PullRequestのレビュー依頼を行った際に、指定したchannelに通知
  • レビュー依頼時のテンプレート文言を設定
  • オプションで指定channelの代わりにDMで通知

DM通知オプションは一応作ってみたものの、PullRequestは可能な限り多くの人の目を通した方がいいと思うので社内では使っていません。

また、prpr-mention_reviewersはprpr-mention_commentと同時に利用することを想定しているため、通知先チャンネルを指定する環境変数(MENTION_COMMENT_ROOM)とユーザ名の対応表ファイルを指定する環境変数(MENTION_COMMENT_MEMEBRS)はprpr-mention_commentと同じ環境変数名を利用することにしました。

プラグイン開発Tips

Configuring your server | GitHub Developer Guide にあるようにngrokを用いて、ローカル環境にWebHookを通知するようにして開発を進めると捗りました。

使い方

prpr-templateにならってHerokuにデプロイするのが一番手軽です。

ということで、prpr-mention_reviewers版 prpr-template を作ってみました。

とりあえず試してみる場合

  • SlackのIncoming WebHooksを作成

  • GithubのAccessTokenを作成

    • ユーザ名変換の際に該当リポジトリのファイルを読み込む必要があるためrepoのフル権限が必要になります
  • prpr-mention_reviewers版 prpr-template にアクセスし、「Deploy to Heroku」ボタンをクリック

  • 環境変数入力画面が表示されるのでSLACK_WEBHOOK_URLとGITHUB_ACCESS_TOKENを入力しデプロイを実行

  • 通知を行いたいGithubリポジトリのWebhook設定画面を開き、HerokuアプリのURLを設定 f:id:hilotter:20170824123949p:plain

これで導入完了です!

Slackの通知先channelを変更したい場合は別途、環境変数MENTION_COMMENT_ROOMを設定してください。

また、MENTION_REVIEWERS_BODYにレビュー依頼時のテンプレート文言を設定できます。

社内で設定している環境変数は以下のようになっています。

MENTION_COMMENT_ROOM - #github-mention
MENTION_REVIEWERS_BODY - %{title} のレビューをお願いします :pray:

この状態で、PullRequestのレビュー依頼を行うと、以下のようにSlackに通知されます!

f:id:hilotter:20170824124457p:plain

運用Tips

slackのユーザ名をGithubのユーザ名と同じにしたけど通知が飛ばない場合

slackアプリ上のプロフィール編集でユーザ名を変更するとdisplay_nameという項目が変更されますが、name自体は以前のままになっています。

この場合、Webサイト上からusernameの編集を行う必要があります。

↓のURLにアクセスし、usernameを変更すると通知が飛ぶようになります。(team名は各自変えて下さい)

https://{your_team}.slack.com/account/settings?updated_username=1#username

f:id:hilotter:20180507161026p:plain

まとめ

prprを使うことで、SlackのGithub連携機能の課題を解決することができました。

また、prprはプラグインが作りやすい設計になっているので素早くプラグイン開発ができて助かりました。

今後、Githubに新機能が増えた場合も手軽にプラグインを作っていけそうです!

「GithubのPullRequestレビュー依頼をSlack通知したい」とお考えの場合はぜひ試してみてください。