現在JavaScriptの設定が無効になっています。このページを利用するにはJavaScriptの設定を有効にしてください。2018の25日目の記事です。一覧はこちらメリークリスマス。アドベントカレンダーも最終日です。トリを担当する事になりました、Node.js言語サポートチームの伊藤(@koh110)です。2日目のMYMの記事は読んでいただけたでしょうか。自分がどうしても聴きたくて栗山さんにねだりにねだって書いてもらいました。安らかに眠れ2日目の記事では主に振り返りや数字、インシデント、チャットシステムのバックエンドなど全体の仕組みについて詳細に語ってもらいました。そこで本記事では、途中から開発に加わった視点からチャットシステムの面白かったところを、フロントエンドやユーザー体験寄りの視点からまとめたいと思います。MYMは2011年に社内ハッカソンのHackDayで生み出されました。フロントエンドを当時のJavaScript黒帯、バックエンドは現Node.js黒帯の2人がメインとなって開発されました。他に関わったエンジニア・デザイナーも優秀な人ばかりです。新卒のひよっこにとっては楽しくも大変な日々でしたが、ここから学んだものは数え切れません。蛇足ではありますが、エンドロール後のおまけと思って少しお付き合いいただければ幸いです。検索(WebWorkerとの戦い)サジェスト(複雑な文字列処理とDOMとの戦い)アップローダー(Canvasとの戦い)サムネイラー(ヘッドレスブラウザとの戦い)戦のおわりに僕とフロントエンド戦争の始まりMYMは2011年にリリースされました。僕が初めてMYMに触れた2013年当時は、社内でもbackbone.jsやAngularJSなどフロントエンドフレームワークを使うことが当たり前になってきたところでした。MYMのフロントエンドのコードは今も当時の構成を受け継いでいるのですが、BabelやTypeScriptといったトランスコンパイル処理がなく、ほぼピュアなJavaScriptで書かれています。ライブラリもレンダリングに関わる部分に薄くjQueryの依存があるだけで、大半の機能がフルスクラッチで実装されています。完全なSPA実装になっていてサーバーサイドのコードは一切含まれません。いった構成が成り立つのは社内のPCはスペックがほぼ固定化できることに加えて、外部サービスで必要とされるSEOなどの要件がないというのも大きいです。MYMにあるもう一つの面白い方針は、なるべく機能をフロントエンドによせるというものです。することで要望がきた際の対応速度をあげる狙いがあります。バックエンドのリリースはサーバーの再起動など、かなりセンシティブになる要素が多いですが、SPAアプリはただ静的ファイルを配置するのみなので、ある程度大胆なリリースが可能です。MYMでは要望やバグへの応答速度はかなり気にされていて、速いものであれば報告から数分で修正・リリースされます。ユーザーから送られてきたプルリクエストもPJメンバー個人の裁量でマージ・即時リリースがされます。この方針は前の記事でも語られましたが、「なんでも聞きやすくコミュニケーションが取れる」という空気を作ることが重要視されているためです。HackDayで開発された時も、当時ヤフーメッセンジャーで少数の仲間うちでされていた気軽なコミュニケーションをもっと全社的にできるようにしたかった、という理由があったようです。いった起源から「気軽に投げ込んだら爆速で実装される」という雰囲気が今でも続いています。求められた機能をいかに最小のコストかつ最速でリリースするかを常に考えることが、MYMとの戦いのキモになっていきます。jQueryと和解せよ世の中はすでにフレームワークの時代なのに、ピュアなJavaScriptと薄いjQueryというコストのかかる開発スタイルをとっているのか疑問でした。昨今、jQueryというとスパゲティコードやテスタブルでないなど負のイメージに捉えられることが多いです。自分もそういったイメージを持っていたのも事実ですし、実際そういう風になりやすい面もあるでしょう。MYMの開発に加わった時、そんな激闘を求められるのではないかと不安に思いました。実際に開発を進めてみるとViewとロジックが分離され、多くのコードが疎結合になっていたため予想してたほどつらいものではありませんでした。jQuery製のrenderメソッドに依存するプロパティーを注入する形なのですが、ES5の時代にReactのような実装をするとこうなるんだろうなという印象です。発言のテンプレートは下記のような形で利用します。属性を通してアクセスすることが徹底されています。追加されているということはコードから利用される可能性がある、と判断することが可能です。このおかげかMYMでは「DOMを変更したらコードがうごかなくなった」という問題に遭遇したことはほぼありません。</article></li>jQueryを使うことが悪なのではなく、フレームワークはロジックを疎結合にするために利用するものであるということを学び、嫌いになりかけていたjQueryと和解することができました。NEXTを作るとしたらフレームワークを利用します。MYMはピュアなJavaScriptでDOMと通信するという仕様であったからこそDOMをハックし、好きにカスタマイズできる楽しさからエンジニアに受けたという側面もあったと思います。過去に一度React化するという動きもあったのですが、当時はどうしてもパフォーマンスがでない部分があったため適応を諦めました。今は当時と状況が変わってきたのでまた挑戦してみたいですね。増え続ける部屋との戦いMYMでは会話するチャンネルを部屋と呼んでいます。x部屋への入室は招待かURL欄に部屋名を直接入力で可能です。入力した部屋が存在しなかった場合、新規に部屋が生成されます。非公開設定はいらないのではないか、とよく言われるのですが「入室を拒んでいるわけではないが検索を汚したいわけではない」という日本人的なニーズを満たしていて僕は気に入っています。先の記事で数字が載っていた通りMYM上には部屋が全部で15万ほど存在しますが、この公開設定のおかげかこれだけの部屋数になっていても検索性をあまり損なっていません。ヘビーに利用するユーザーであれば数百を超える部屋に入室するのは当たり前で、2000をオーバーすることも珍しくはありません。僕は1000部屋以上入室しています。li').length1109ぱっとこの数字だけ聞くと、まともに処理できる量を超えているでしょう。普段そこまでの不便を感じません。部屋は「全て・未読・部屋・1on1」という4つのフィルターをかけることが可能です。僕は「未読」を一番よく利用します。このフィルターをかけると、未読のついた部屋のみが一覧で表示されます。1on1はSlackで言うDMにあたり、1対1の会話のみがリストされます。MYMでは新規登録されたユーザーはIdenticonという仕組みで一意なデフォルトアイコンが設定されます。この仕組のおかげでアイコンが個別に設定されていなくても、なんとなく一覧から絵だけで誰かを判別できるようになっていきます。部屋ごとにもアイコンが設定できる点が視認性をあげています。人間の画像認識力というのは大したもので、部屋にアイコンがついていると名前を見なくても一覧の中から自分の中で重要度のランキングがつけられるようになります。部屋の中でも最優先で確認する部屋はお気に入りとして数件登録されていますが、それ以外の部屋もチェックの優先度が高い部屋(例えば利用している社内プラットフォームのアナウンス部屋など)がいくつか存在します。部屋名で検索した場合、検索するまで未読がついているかどうかはわかりません。スクロールするだけで視認できるのは思っている以上にとても快適です。自分宛ての通知がある場合は未読の表示が赤から黄色に変わり、自分がチェックするべき未読かどうかの判断がつきやすくなっています。部屋アイコンは一見システム負荷を高めてしまう機能ですが、画像にはかなり強力なキャッシュ戦略がなされているため、思っている以上にシステムに対する負荷は少なく、1000件以上の部屋に入室しても即座に表示されます。ServiceWorkerによるキャッシュも一時期導入しましたが、社内の環境ではそこまで高速化に寄与しなかったため削除されました。MYMでは多くの機能で、画像や色によって視認性をあげるという考えが現れています。フロントエンド激戦地区ここからは特徴的な技術の話についてまとめていきたいと思います。検索(WebWorkerとの戦い)MYMのフロントエンドコードには「富豪的プログラミングを行うこと」という少し変わった開発方針がありました。最初は富豪的に最速で作成し、どうしてもパフォーマンスが出ない時に初めて最適化するというものです。これは先に述べたように、PCのスペックやユーザーをしぼることができる社内システムだからこそとれる手法です。その一例としてユーザーの検索があります。これは全ユーザーデータの配列からフロントエンドのコードでフィルターを行う仕組みです。重み付けや形態素解析などの機能はありませんが、限定的な用途だとかなり実用的です。社内システムでは人の名前やアカウント、メールアドレスなどクエリのパターンが限られてきます。いった用途に限った場合、ネットワークを経由することなくオンメモリで高速に動作するというメリットが最大限に発揮されます。このユーザーデータはおよそ1万人のデータがあり5MBほどのデータ量です。普通のサービスであれば5MBもあるデータをダウンロードさせるなんて言語道断でしょう。社内なので割り切って実装しています。パフォーマンスが完全に無視されているわけではなく、WebWorkerを利用して別スレッドで実行されます。下記のようなコードで検索を利用します。WebWorkerでは直接DOM要素を直接いじれないため、onmessageで受け取った結果を描画します。汎用的な検索ができるライブラリとして作られています。邉邊部)もヒットします。簡単にコアとなる仕組みを解説すると下記のような流れになります。初期化時に配列を文字列化する検索時にクエリを正規表現化する文字列に正規表現化されたクエリをあて、ヒットした結果をブラウザーにpostMessageする下記はかなり省略した疑似コードですが、これでもかなり複雑で読み解くのにすごく苦労します。実際のコードはこれに加えページング処理や高速化のためのロジックがあります。この正規表現がローマ字検索や全角/半角、漢字の表記揺れなどを吸収してくれます。いう正規表現に変換されます。いう文字列がローマ字よみのひらがなに変換されているのが見て取れると思います。関数内部に用意されたローマ字変換用のマッピングテーブルで変換されています。ひらがな、カタカナの変換や漢字の表記揺れも同様にマッピングテーブルを用意して正規表現化されています。名前を検索しようと思って検索欄に入力したら英字入力だった、みたいなシーンはよくあると思うのですが、そんな時にもこの検索はうまく引っ掛けてきてくれます。力業にみえますが、スマートな解決策よりスピーディーに課題を解決できることが優先されているモデルケースだと思います。...};サジェスト(複雑な文字列処理とDOMの戦い)チャットシステムでの入力補完機能はかなり利便性を左右します。ユーザーにメンションを送りたいシーンや、スタンプの名前、特殊な記法(部屋への招待やアンケート)などを完全に覚えることは難しいでしょう。サジェストはどこからがサジェストの候補文字列の開始地点なのかを判断するのがかなり難しいです。スタンプをサジェストする場合を考えます。始まる文字列を画像に置き換えるという仕組みです。置き換えれば済む話です。問題はスタンプの前に一言そえた場合です。後ろにありますが、テキストエリア全てを置き換えてしまうと「ありがとうございます!」という入力までスタンプの記法で上書きされてしまいます。カットして置き換えるような展開処理が必要です。MYMではカーソルの位置から手前に戻していき、アルファベットや記号以外が出てきたらそこがサジェストの開始地点であり開始地点から対象の文字列をカットして置き換える、という仕様にしてこれを実現しています。下記のようなイメージです。start--;}また、文章を書いている途中で前に戻って書く場合もあり得るので、終了地点も開始地点と同様に最後までカットしてしまうとおかしなことになってしまいます。サジェストはDOMや文字列処理と密接に関係していて改修も難易度が高いのですが、チャットシステムならではの「ユーザが何をどう入力するかを予想する」という部分が使いやすさに直結して面白くもあります。お手軽なファイルアップローダーです。テキストエリアにファイルをドラッグアンドドロップすると一意のURLが発行される仕組みです。画像やテキストファイル、Excelファイルなど多種多様なバイナリデータをアップロードできます。画像ファイルは、ドラッグアンドドロップの他クリップボードからペーストが可能な他、アップロード直前に加工が可能です。仕事中ちょっとした画像加工をして画像共有しなければならないシーンは意外とありますが、都度画像加工ソフトを立ち上げずにチャットに投げて加工できるのはコンテキストスイッチが少なくて好きです。pasteとdropイベントに引っ掛けて、FileReaderから読みだしたcontentTypeが画像だった場合のみCanvasにわたして画像加工ができます。}}Pasterの画像処理は長方形の範囲選択が可能で、選択した範囲にトリミング・強調・モザイク・集中線の4つの加工ができます。モザイク処理はコードもそこそこ短くて、はじめてCanvasを扱う題材として楽しいと思います。dst.canvas.height);}集中線は有志が作った機能です。最初は要望としてあがったものでしたが要望したのがエンジニアだったので、このへんを修正すれば機能追加できるのでぜひプルリクください、と返したところ空いた時間を使って実装してくれました。いった部署を飛び越えてみんなで開発していける空気がMYMのいいところでもあります。サムネイラー(ヘッドレスブラウザとの戦い)チャットしかりSNSしかり、URLを貼ったら展開されるのは当たり前になってきました。MYMでもURLを投げると画像として返却するサムネイラーと呼ばれる機能があります。この機能はPhantomJSを利用して作成されていましたが開発の終了を受けて、2017年にpuppeteerで全面的にリプレースしました。サムネイラーの実装はとてもシンプルです。リクエストがきたらpuppeteerを立ち上げて、加工してからスクリーンショットを撮るだけです。puppeteerを実サービスに組み込む場合に必ず気をつけなければならない点が、下記コード中のscreenshotエラー時に受けてpuppeteerをcloseする部分です。puppeteerはタイムアウトやその他理由により、よくエラーを返します。その際closeを忘れると閉じそこなったChromeのプロセスが増殖していき、最終的にプロセスを食い尽くされます。事故というところでしたが、実運用して初めて気づいた部分でした。}};サムネイラーではリアルタイム検索などヤフーのいくつかのサービスが展開可能ですが、チャット上で見る場合にはPC版よりスマートフォン版の方が見やすいのでChromeのユーザーエージェントをiPhoneにしています。evaluateの中で必要な要素以外をremoveChildで消去し、screenshotでPNG画像化します。});}Linux上でpuppeteerを動かすためにはlibX~~など追加で必要なパッケージがいろいろあります。サーバーでpuppeteerを動かした時にfontが足りず豆腐になる問題がよく起きるので、fontのインストールも忘れないようにしましょう。戦のおわりに入社して初めてMYMを触った時、ヤフーの社内システムってこんなに作り込まれているのかと驚きました。当時こんなに作り込まれているのになぜ外に売らないのか、という疑問をぶつけたことがあります。その時に返ってきた答えは「売ってしまうと、どうしても大きな課題や多数のユーザーが持つ問題解決にしか目を向けられなくなるから、あえて社内に最適化するんだ」というものでした。ヤフーに新卒で入るからにはサービスをやりたい、そう思って入社したのに社内システム担当になりもやもやしていた自分にとっては目からウロコの考えでした。MYMには背景が雪模様に変わるクリスマスモードというものが仕込まれています。それを見た時とてもわくわくしたのを覚えています。社内のシステムであり毎日使うことを強制されるからこそ、少しでもストレスなく使ってほしいという人の顔が見えたような気がしたからです。「エンジニアだからといって技術だけが解決策ではない」という考え方もMYMを通して学びました。このシステムを通じて受け取った多くのものをまた次の人たちにプレゼントしていけたらいいなぁと思います。話は変わりますが、MYMの初代開発者は現在六本木でハッカー兼バーテンダーをしています。先日MYMのクローズを報告にいったところ、MYMのイメージカクテルを作ってくれました。「ショットグラスを落とすタイプのカクテル(○○・ボム)とソニック(音速)でヤフーの"爆速"を表現し、レッドベア(エナジー系リキュール)を落とすことで"みんなのいいね(エナジー)が溜まったら赤くなる"というMYMの代表的な機能を表現しました。」ハッカーとしてもバーテンダーとしても完成度が高くて驚いてしまいました。このカクテルを味わいながら、MYMと一緒にラストクリスマスを祝いたいと思います。JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。JAPANのサービス開発を支える技術やデザイン、文化を発信しています。Reserved.このページの先頭へ。