SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

アイデアをすぐ形に! K5 Playgroundで開発するクラウドネイティブな最新Webアプリ(AD)

Webアプリは会話するスピードで開発しよう~ReactやBFFで高速にプロトタイピングする技術

アイデアをすぐ形に! K5 Playgroundで開発するクラウドネイティブな最新Webアプリ 第5回

  • X ポスト
  • このエントリーをはてなブックマークに追加

 ここまで、K5 PlaygroundはアイデアをスピーディーにWebアプリの形にするツールであることを紹介してきました。アイデアをアプリに落としこむ際は、プロトタイピングの段階でエンジニアがアイデアを持ち帰って開発し、再度集まって動作を確認することが多いと思います。一見効率的なこの手法ですが、エンジニアの負担が大きくイメージとのかい離も発生し、モチベーションにも悪影響を与えかねません。これを改善する試みが「雑談開発」という、ユーザーやチームで雑談しながらリアルタイムに開発する手法です。アプリを目の前に置いて意見を出し合うことで現実的な意見の交換ができ、過剰な作り込みやイメージとのズレを減らせることが魅力です。今回はこういった開発に活用できるK5 Playgroundの使い方として、プロトタイピングを高速に行うテクニックをいくつか紹介していきます。

  • X ポスト
  • このエントリーをはてなブックマークに追加

デザインを高速に実現するReactとCSS in JS

React Componentが高速なプロトタイピングを実現する

 高速なプロトタイピングにはまず、スピーディーにUIを構築する手法が必要となります。これはReactの得意分野です。

 Reactの特徴の1つに、UIにおけるコンポーネント志向が挙げられます。再利用可能な小さいUI部品を組み合わせて全体を構築するアプローチです。Reactにおける基本単位はReact Componentであり、この小さなReact Componentを組み合わせてページを構成します。

 さらにReactではデザインもReact Componentの中に閉じ込めることが可能です。React ComponentにCSS相当の装飾やレイアウトを記述しておくことで、React Componentを呼び出すだけでデザイン適用済みのUI部品を表示できます。CSSをJavaScriptのUI部品の中に閉じ込めることからCSS in JSとも呼ばれています。

 例えば、AppBarというReact Componentを利用する例を見てみましょう。下記の通り記述し呼び出すと、AppBarが表示されます。

<AppBar title="Title" />  //AppBarというReact Componentを利用する例
AppBarの表示
AppBarの表示

 このようなアプローチで、CSSに詳しくない開発者でもある程度のUIのアプリを作ることができます。K5 PlaygroundではMaterial-UIというCSS in JSを採用したUI部品を用意していて、呼び出すだけでデザイン済みの部品が表示されるようになっています。

レイアウトの部品化

 CSS in JSの応用の1つが、レイアウトの部品化です。

 これまでのレイアウトは地道にCSSを記述する方法が主で、Bootstrapなどのライブラリを使うにしても、フレームワークの作法に従ってDOMの階層構造を作成して、指定されたクラス名を記述する…など、手数が多くなりがちでした。

 レイアウトの部品化はこれを一変させます。レイアウト用のReact Componentに対して、表示させたいReact Componentを「代入」するだけで適切にレイアウトを行い配置してくれます。HTMLやCSSの知識を一切持たなくてもレイアウトを実現できます。

 具体例を挙げて説明しましょう。Webにおける代表的なレイアウトにHoly Grailレイアウト(聖杯レイアウト)があります。聖杯レイアウトは、ヘッダー、フッター、左右のメニュー、中央コンテンツの5要素から構成されるレイアウトです。

 K5 Playgroundでは聖杯レイアウトのための <BaseLayout> というReact Componentを用意しています。これを使えば聖杯レイアウトは次のように実現できます。 <Header /> などはReact Componentとします。

<BaseLayout
  top={<Header />}     //ヘッダーのReact Component
  left={<Menu />}            //左メニューのReact Component
  center={<Content />}    //メインコンテンツのReact Component
  right={<Navi />}        //右メニューのReact Component
  bottom={<Footer />}     //フッターのReact Component
/>

 また、次のように右メニューやフッターを省略すれば、聖杯レイアウトから右メニューとフッターを引いたレイアウトが実現できます。

<BaseLayout
  top={<Header />}     //ヘッダー
  left={<Menu />}            //左メニュー
  center={<Content />}    //メインコンテンツ
/>

 縦並びや横並びのレイアウトを実現するレイアウト部品VerticalLayoutやHorizontalLayoutも用意しています。例えば、TextLabelというReact Componentを縦に並べたい場合は、以下のようになります。

<VerticalLayout>
  <TextLabel />
  <TextLabel />
        :
</VerticalLayout>

 K5 Playgroundで独自の画面に変更したい場合は、これらのレイアウトコンポーネントを使うのが簡単です。聖杯レイアウトと、縦並び・横並びレイアウトを組み合わせれば大抵のレイアウトは実現できます。もちろん通常のCSSを使うことも可能ですが、まずはレイアウト部品にUI部品を流し込んで複雑なUIをスピーディーに作ってみましょう。

 具体的には、以下の流れでUIを構築できます。

  1. まず、frontend 配下で npm start を実行します。
  2. すると webpack-dev-server という開発用のサーバーが起動して、ブラウザにアプリが表示されます。
  3. 次に、 webpack-dev-server が起動した状態で frontend/app/components 配下に格納されているReact Componentを修正します。
  4. その後、修正が webpack-dev-server に検知されてブラウザが自動的にリロードされて、新たなUIが表示されます。

 文法ミスなど、React Componentに対する修正内容が誤っている場合は、エラーとなって即座に分かります。このように npm start で開発用サーバーを起動して、表示を確認しながら少しずつ修正すると効率的です。

 デザインに関する作業には際限がなく、後から改善も可能なため、細部にとらわれすぎず大部がある程度できたら次に進むスタイルでスピーディーにUIを作っていきましょう。

BFFでモックAPIを作ろう

 UIに表示させるデータはどうすればよいでしょうか。バックエンドを全て取りそろえてからフロントエンドを開発する方法だと、プロトタイピングで求められるスピードに到達できないでしょう。

 これを解決する手法の1つがモックAPIです。モック(mock)は「模造的な」という意味で、モックAPIはRDBMS等のバックエンドのサービスは呼び出さずに、HTTPリクエストに対して固定的なダミーのJSONを送受信します。重要な部分以外はモックAPIを使って、スピーディーにフロントエンドを形にして価値を評価しましょう。

K5 PlaygroundでモックAPIを作成する

 K5 PlaygroundでモックAPIを作成するのは簡単です。既存のAPIロジックと空のAPIロジック(Empty Logic)を交換して、モックデータを書くだけです。具体的な手順を見てみましょう。

  1. まず中央にあるAPIロジックを、×をクリックして削除します。
  2. 次に右メニューのCustomにある「Empty Logic」を中央にドラッグ&ドロップします。
  3. 最後にEmpty Logicの next() メソッドの引数にモックオブジェクトを渡します。

 ここでは静的なオブジェクトを next() に渡しただけですが、条件に応じて動的にオブジェクトを生成すると、より高度なモックAPIが作れます。HTTPリクエストの内容に応じてレスポンスを分岐させたい場合は、HTTPリクエストのオブジェクトreq を参照します。例えばURLのクエリ文字列ならば、 req.query.KEY_NAME で参照できます。HTTPリクエストオブジェクト req の詳細は、K5 PlaygroundのBFFでも利用しているNode.jsのWebアプリケーションフレームワークexpressのドキュメントを参照してください。

APIの設計を向上させるためのモックAPI

 モックAPIのもう1つの利点は、動作する設計であることです。

 API単体ではよさそうな設計であっても、フロントエンドの設計を考えるとの設計を考えると「1ページあたりのトランザクション数が多い」「レスポンスのJSONの構造や型が扱いづらい」といった不満が出てくることは珍しくありません。早期にモックAPIを作成することで、フロントエンドの動作を考慮して改善点を洗い出し、APIの設計を修正できます。モックAPIの段階でさまざまなAPI利用者の声を聞いて質を高めることが大切です。

 またBFFがないアーキテクチャの場合、技術的にはモックAPIそのものを容易に修正できても、本物のバックエンドは組織やコストの面から修正が困難なケースが多々あります。K5 PlaygroundにはBFFがあるため、バックエンドと距離を置いてフロントエンドと親和性の高いAPIをBFFで実現できます。

FluxでモックAction CreatorやモックStoreを作成しよう(1)

 開発のスピードアップをさらに追求したい場合、データの生成点をモックAPIではなく、フロントエンドの内部に移動させることもできます。APIを呼び出した後にダミーデータを生成するのではなく、APIを呼び出さずにフロントエンド内部でダミーのデータを生成するアプローチです。

 一般的にSingle Page Application(SPA)では、バックエンドから取得したデータなど、フロントエンドの「状態(state)」を何らかの方法で管理して、UIに表示させます。ReactとFluxの場合は、「Store」というFluxのコンポーネントがこのstateを管理する役割を担います。stateをさまざまな方法でモック化し、フロントエンド内でダミーデータを生成することでアプリを高速に開発できます。

 まずは、state管理にとって重要なFluxアーキテクチャを見ていきましょう。

Fluxアーキテクチャ概説

 FluxはFacebookが提唱したフロントエンドのためのアーキテクチャです。フロントエンドの「状態」の管理に重きを置いています。Reactと親和性が高いだけでなく、JavaScrip以外の言語やフレームワークのフロントエンドに適用できるアーキテクチャです。

 FluxアーキテクチャはView、Action/Action Creator、Dispatcher、Storeから構成されます。

Fluxの
構成要素 
概要 K5 Playgroundでの
ファイル格納場所
View React ComponentやContainer frontend/app/components
ActionCreator イベントを処理して結果をActionという形式にしてDispatcherに渡すメソッド  frontend/app/actions
Action  イベントの処理結果(APIレスポンスなど) frontend/app/actions
Dispatcher ActionをStoreに送信するハブ機能 frontend/app/dispatcher
Store 状態を管理する非永続的なデータストア frontend/app/store

 FluxアーキテクチャをFacebookのFluxライブラリを用いたサンプルコードを通して説明します。

 まずはViewであるReact Componentを起点とします。ユーザーがReact Componentに対してマウスクリックなどのイベントを発生させるとイベントハンドラが呼ばれます。ButtonというReact Componentを利用する例です。クリックされると同じReact Component内に定義された handleClick メソッドが呼ばれます。

 <Button onClick={this.handleClick} title="Update"/>

 イベントハンドラは、Action Creatorを呼び出します。

 handleClick = () => ActionCreator.updateOrder();

 Action Creatorの基本的な書式です。イベントに対する処理を行い、結果をActionというオブジェクトにして、Dispatcherの dispatch()メソッドに渡します。イベントに対する処理の例は、API呼び出しやDB呼び出し、業務ロジックの実行などバックエンドが関わるものからフロントエンド内部に閉じたものまで多岐に渡ります。 dispatch() に渡されたActionはその後、Storeに送信されます。

//複数のAction Creatorの集合
const ActionCreators = {
 //Action Creator
 updateOrder: () => {
   //1. APIを呼び出す
   api.get(url).then(body => {
     //2. APIのレスポンスからActionを生成する
     const action = {
       type: 'order/update' //Acitonの種別を表す識別子
       data: body  //イベントの結果
     }
     //3. Dispatcher経由でStoreにActionを送信する
     Dispatcher.dispatch(action);
   })
 },
}

 上記は説明的に記述しましたが、より簡潔にも記述できます。

 updateOrder: () => api.get(url).then(body => Dispatcher.dispatch({type: 'order/update', data: body}))

 StoreではActionCreatorがDispatcherに渡したActionを受け取り、新たな状態(state)を作り出します。StoreはgetInitialState() と reduce() の2種類のメソッドを持ちます。 getInitialState() の戻り値にはStoreの初期値を設定します。 reduce()では、現在の state と受け取った action から新たな state を作成します。

import { ReduceStore } from 'flux/utils'; //ReduceStoreを使うのが基本です。
import AppDispatcher from '../dispatcher/AppDispatcher';

class OrderStore extends ReduceStore {
 //Storeの初期のstateを返す必須のメソッドです。
 getInitialState() {
   return {}; //初期のstateを定義します。
 }
 //現在のstateとactionを受け取って新たなstateを返す必須のメソッドです。
 reduce(state, action) {
   switch (action.type) { // actionのtypeによってStoreのstateの作り方を分岐させます。
     case 'order/update': { //注文更新の例
       return action.data; // 受け取ったactionをそのまま新たなstateとする例です。
     }
     case 'order/delete': { //注文削除の例
       return {}; // 空のオブジェクトを新たなstateをとする例です。
     }
     default: {
       return state;
     }
   }
 }
}
export default new OrderStore(AppDispatcher);

 Storeの内容が更新されると、それを自動的にContainerが受け取ります。Containerは次のとおり、Storeからデータを受け取るだけの特殊なReact Componentです。ContainerがStoreの内容を受け取ったら、配下のReact Componentに渡します。

import React, { Component } from 'react';
import { render } from 'react-dom';
import { Container } from 'flux/utils';
//サンプルの(Reduce)Storeです。
import OrderStore from './stores/OrderStore';
import RecommendationStore from './stores/RecommendationStore';

class OrderContainer extends Component {
 static getStores() {
   return [OrderStore, RecommendStore]; //stateを取得したいReduceStoreを定義します。
 }

 static calculateState() {
   return { //取得したstateはここで定義した形式で参照できます。
     order: OrderStore.getState(),
     recommendation: RecommendationStore.getState(),
   };
 }

 render() {
   //Storeから取得したstateを配下のReact Componentに渡します。
   <OrderPage
     order={this.state.order}
     recommendation={this.state.recommendation}
   />
 }
}

export default Container.create(OrderContainer);

 Viewからスタートして、ActionCreator、Dispatcher、Storeを経由して再びViewに戻ってきました。本ページ冒頭の図のように、データを一方通行で流して単純化するのが特徴です。

 次のページから、Fluxアーキテクチャでモックを実現する方法を2つ紹介します。

FluxでモックAction CreatorやモックStoreを作成しよう(2)

方法1. モックAction Creator

 Action Creatorで、ダミーのActionを生成して dispatch() に渡します。実際はAPIやDBをコールしませんが、コールしたと仮定して、結果をActionという形式で dispatch() に渡します。

const ActionCreators = {
  getArticle: () => Dispatcher.dispatch({type:'type1', data: {id: 1, text: 'How to use...'}}),
        :
}

 モックAPIであっても物理的なAPIが存在する場合、APIの設計に多少の難があったとしてもフロントエンド側で無理をしてしまいがちです。モックAPIより前の段階でモックAction Creatorを作って試行錯誤することで、フロントエンド内部の設計を洗練させることができます。

モックAction Creatorによるネットワーク透過性

 試作段階でデモを行う際に、ネットワークの影響を排してアプリの振る舞いを固定したい場合はないでしょうか。次はモックAction Creatorを使ってあらかじめ取得したツイートを利用する例です。

import mockTweet from `../mock/tweet.json`; //あらかじめ取得したTweet
const ActionCreators = {
  fetchTweet:() => Dispatcher.dispatch({type:'foo', data: mockTweet}),
    :
}

 ネットワークの状態に応じて、APIから取得するかモックデータを使うか、切り分けることもできます。以下はAPIがエラーの時だけモックデータを使用する例です。

fetchTweet:() => api.get(url)
  .then(res => Dispatcher.dispatch({type:'foo', data: body}))  //APIで取得したTweet
  .catch(Dispatcher.dispatch({type:'foo', data: mockTweet})) //事前に取得したTweet

 あらかじめネットワーク状態を設定して処理を切り替えることもできます。

fetchTweet:() => isReachable
  ? api.get(url).then(body => Dispatcher.dispatch({type:'foo', data: body})) // APIで取得したTweet
  : dispatch({type:'foo', data: mockTweet}) //事前に取得したTweet

方法2. モックStore

 Viewに表示させるデータを初めからStoreに格納しておく方法です。具体的にはStoreの getInitialState() の戻り値にモックデータを設定します。

class Store extends ReduceStore {
  getInitialState() {
    return [{id: 1, text:''}, {id:2, text: ''}, ...];
  }
  :
}

 モックStoreは、柔軟性はないもののこれまで紹介した中では最も高速にViewを構築できる手法と言えるでしょう。

 ReactとFluxを使うとアプリのViewから状態(Store)や操作(Action Creator)をきれいに分離できます。フロントエンドのあらゆる要素をコンポーネント化し、柔軟にモックと本物を切り替えられるため、プロトタイプから本番まで連続的に開発を進めることができます。

どのモック手法を使えばよいか

 これまでBFFやSPAで、さまざまな要素をモック化してフロントエンドをスピーディーに形作っていく手法を紹介してきました。実際の開発ではモック化する手法をどう使い分ければよいでしょうか。次の順序で重要度の高い箇所から移行させるのがおすすめです。

  1. モックStoreによってViewを構築する。Viewと状態を分離して、Storeの粒度を改善する。
  2. 本番のStoreとモックAction Creatorに切り替える。操作を洗い出し、粒度を改善する。
  3. 本番のAction CreatorとモックAPIに切り替える。APIの設計を改善する。
  4. モックAPIを本番のAPIに切り替える。

 ユーザーとの接点から先に仕上げていって感触をつかみ、うまくいきそうだったらバックエンドに移っていく「フロントエンド・ファースト」な開発スタイルです。

 とにかく大切なのは手を止めずに大ざっぱでもいいから先に進むことです。ReactとFluxという構造化されたフロントエンドを手にしたわれわれは、あらゆる要素をモック化でき、粒度を適正化しながら推移的に改善できます。モックをうまく活用して、楽器を演奏するようにテンポよく開発していきましょう。

 モックデータの作成にはMarak/faker.jsのようなダミーデータを作成するツールを用いて、プログラマブルに作れるようにしておくことも大切です。

次のアイデアを早期に試す

 アプリ開発においては、「よいと思ったアイデアを形にしてみたら、イメージと違っていた」といったこともしばしばあるでしょう。こういった場合はひとまずそのアイデアは横に置いて、次のアイデアに取り組むのが手です。新たな取り組みには失敗がつきものなので、早期に失敗を検知する「Fail Fast」の考え方も大切です。

 K5 Playgroundならば「Fail Fast」が容易に実現できます。通常のアプリ開発では慣れている人でもそれなりに手間や時間がかかるので、微妙だと気づいても心理的に失敗と認められないものです。一方、K5 Playgroundで手間をかけずに短時間で作ったアプリであれば「Fail Fast」する心理的なハードルは低いはずです。一筋縄ではいかないアクションゲームを何度もリトライするように、繰り返し新たなアイデアに挑戦してみましょう。

 ただし「Fail Fast」は「Move Fast」と対でなければなりません。

 Y Combinatorの創業者ポール・グレアムは、著書『ハッカーと画家 コンピュータ時代の創造者たち』に収録されている『メイド・イン・USA』という随筆の中で、

 "コードは、ピラミッドみたいに、慎重な計画をしてから苦労して組み立てていくものじゃない。一気に集中して素早く手を動かしながら、常に気を変えていく、木炭スケッチみたいなものだ。"

 あるいは、

 "ゆっくりと念入りに仕事をしていると、出来上がるものは当初のアイディアを精密に実現したものになるだろう。ただしそのアイディアは間違っているだろうけどね。遅く念入りな仕事は早すぎる最適化だ。むしろプロトタイプを素早く作り上げて、それによって新しいアイデアを得てゆくほうがよい。"

 と、ソフトウェア開発におけるスピードの重要性を説いています。失敗を恐れるのは人間の常ですが、今回紹介したようなテクニックを身につけて、木炭スケッチを描くように開発し、「Fail Fast」と「Move Fast」を繰り返して力をつけながら、よいアイデアが降って来るチャンスを待ちましょう。

K5 Playground 関連リンク

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加

【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10741 2018/03/30 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング