1日1%成長するブログ

毎日成長するために仕事/プライベートで得た学びをアウトプットするブログです

リモートワークでチーム開発する時に便利なツール・ドキュメントまとめ

Webサービスをチームで開発する時に便利だなと思ったツールや、ドキュメントをまとめてみました。 これからプロジェクトを始める時の参考にブックマークして使ってもらえると、嬉しいです。

プロジェクトの説明資料

  • Googleドキュメントが多いです
  • 作る目的やターゲットユーザー、機能を明記しておきましょう

ER図

  • システムの全体像を把握するために必須
  • 新しくエンジニアが加入した時のために必ず用意しておきましょう

MySQL :: MySQL Workbench

MySQL Workbenchを使うと既存のデータベースをそのままER図に出力できるので、とても便利です。

ワイヤーフレーム & 画面遷移図

  • これは新しく画面を作る時にデザイナーさんと認識を合わせるのに、あると便利です
  • これはGoogleドキュメントやワイヤーフレームツールで作ることが多いですかね

デザイン共有ツール

  • ワイヤーフレームからデザインが固まったらデザインカンプを共有するわけですが、いちいち画像をダウンロードするのは面倒です
  • そこでデザインデータをオンラインで共有できるZeplinというツールがあるのでこれを使うようにしています。

zeplin.io

モックアップ

  • これはデザインの画像同士を繋げて画面遷移を試せるサービスです
  • 実際にサービスを触りながら雰囲気が掴めるので、オススメです

www.invisionapp.com

環境構築手順書

  • これはGithubのREADME.mdやWikiに書いておくことが多いです
  • 新しい人が入ってくる時には必須なので用意しておきましょう

シードデータ

  • Webサービスを最低限動かすために最初から必ず必要となるデータです
  • これが無いと、自分で一つずつデータを入れる必要があるので必須です

自動テスト環境 (CI)

  • 書いたコードが実行できるかを担保する上でテストは必須です
  • ロジックが多めになるサーバーサイドは必ず書きましょう。
  • 自動テストツールとしてはCircleCIが手軽でオススメです。

circleci.com

コード解析ツール

  • コードの品質を保つためにコード解析ツールも必須です
  • 国内だとSideciがオススメです

コードレビュー自動化のためのCIサービス | SideCI

動作確認用サーバー

  • 実際に今作っているサービスを動かして試す環境です
  • HTML/CSSのみの場合と、データ以外は本番環境と全て同じのステージング環境を用意する場合があります

コード共有

  • Githubの有料版ならプライベートリポジトリが作れます。
  • あとはBitbucketやGitlaboが候補になると思います。

github.com

チャットツール

  • Slackで連絡を取りつつ1日1回はappear.inで顔を見ながらビデオ会議が良いかと思います。 slack.com appear.in

タスク管理

開発者同士で使うなら、Trello, Waffle, Pivotal Tracker等色々とありますが、 Slackとビデオ会議で情報共有がしっかりと出来てれば、ぶっちゃけ何でもいい気がしてます笑

trello.com waffle.io www.pivotaltracker.com

ActiveRecordのEnum型のデータをJavaScriptから保存する

enum(列挙型)の使い方

ActiveRecordenumを使うと、プログラムからは文字列(名前)でアクセスでき、 DBには数値で保存される属性を作ることができます。

Modelに属性にセットできる文字列を列挙する

class Post < ActiveRecord::Base
  
  enum status: {
    draft: 0,
    published: 1
    deleted: 2
  }

end

DBにカラムを追加する

class AddStatusToPostss < ActiveRecord::Migration
  def change
    add_column :postss, :status, :integer, default: 0
    add_index :posts, :status 
  end
end

追加されるメソッド

  • モデル名.属性名で現在の値を文字列で取得
  • モデル名.文字列?で状態チェック
  • モデル名.文字列!で状態変更
  • モデル名.属性の複数形で列挙した値を全てハッシュで取得
  • モデル名.文字列でその状態のレコードを検索
post = Post.new(status: :draft)
post.status # => draft
post.draft? # => true 
post.published! # statusをpublishedに変更

Post.statuses # => { draft: 0, published: 1 }
# Post.statuses[:draft]のようにして数値を得られる

Post.draft

JSから保存する

JSからAjaxRails側に保存する時は文字列で送れば良い。

data: {
  status: 'draft'
}

文字列にしておけばEnum型が自動で数値に変換してDBに保存してくれる。 JS側で数値を管理する必要がないので便利。

Reactでエンターキーのイベントを取得する方法

onEnterは存在しない

Reactがサポートしているイベントは以下の公式サイトに記載されています。

facebook.github.io

onClick のノリで onEnterは無いのかな?と探してみてもありません。 ただし、代わりになるキーイベントがあります。

onKeyDownを使う

それが onKeyDown です。Propertiesを見ると、keyCodeが取得できるので この値を判定すればエンターキーを判定できます。

https://facebook.github.io/react/docs/events.html#keyboard-events

キーイベントをJSXから受け取る

<textarea onKeyDown={  (e) => props.onKeyDown(e) } />

後はこんな感じでpropsで受け取ったイベントハンドラにイベントオブジェクトを引数で渡して実行します。

keyCode === 13で判定する

if (e.keycode === 13) {
  // エンターキー押した時の処理
}

あとはkeycodeの中身を判定するだけですね。簡単です。

Webアプリの新規作成の実装パターン

一覧 -> 作成画面に遷移して新規作成

フォームでPOSTして、エラーならエラーメッセージを表示、成功したら一覧画面にリダイレクトさせるというオーソドックスなパターン。

Flashメッセージの実装は必要。作成後にセッションに保存して一覧画面で取得したらすぐに消すような仕組み。大体フレームワークに用意されてることが多い。

バリデーションをどうするかも重要。 サーバーサイドでバリデーション結果をテンプレートに埋め込んで表示するタイプか、JSでバリデーションをリアルタイムに実装するか。

Vueでリアルタイムバリデーションして、システム側のエラーは例外。ユーザー起因のエラーはFlashメッセージがいいかな。

一覧からモーダルを開いて新規作成

非同期処理が完了したらJSで動的に一部の画面を切り替える

このパターンはユーザーに画面遷移をさせないで済み、変更がすぐに反映されるので一番理想。 実装方法としては以下の2つになると思う。

ただこの方法で厄介なのは、JS側でテンプレートを描写することになるため サーバーサイドのテンプレートエンジンが使えないこと。

最たる例がi18n対応。サーバーサイドで翻訳した結果を出力する形になっている場合、 JS側でもi18n用の機能を実装する必要が出てくる。

また非同期処理が完了するまで画面に何も表示されないというのも難点。 これを解決するにはローディングを出したり、取得中はDOM自体をださないとか、 gon等のライブラリを使ってDOMにJSの値を吐き出しておく等の方法がある。

またこの方法の場合、前述のFlashメッセージではなくトースターを表示する実装が必要になる。

ということからもわかるように、この方法はJSフレームワークのコード資産があまり無い段階では 実装コストが高めなのでスピードを優先する時はオススメできない。

非同期処理が完了したら画面をリロードさせる

このパターンは非同期処理が完了したら、location.href で画面をリロードさせることで変更を反映させる。 データ数が多くなければ、そこまで違和感が無い。テンプレートエンジンも流用できる。

ただ作成した時のトースターを表示したい時は一工夫が必要。 非同期処理が成功したという情報をどこかに持っておかないといけない。

自分はグローバル変数は汚したくないので、ローカルストレージを使って実装することが多い。 ローカルストレージの実装は必要になるが、JSで動的に切り替えるよりは実装コストは低いのでオススメ。

JavaScriptのオブジェクトとJSONは別物

JavaScriptのオブジェクトとJSONは見た目がほとんど変わらないので、 違いがよくわかっていなかったのですが、明確に違いがあるようなのでメモしておきます。

JavaScriptのオブジェクト

キーがクォーテーション無し & 文字列をダブルクォーテーションで囲む

{ 
  id: 0,
  title: "title",
  description:  "desc"
}

キーがクォーテーション無し & 文字列をシングルクォーテーションで囲む

{ 
  id: 0,
  title: 'title',
  description:  'desc'
}

キーをクォーテーションで囲むことも出来るけど、囲まない方が推奨。

JSON

キーをダブルクオーテーションで囲み、文字列をダブルクォーテーションで囲むのがJSONJavaScriptのオブジェクトでもある。

{
  "id": 0,
  "title": "title",
  "description":  "desc"
}

JSONのフォーマットとして正しいかどうかは以下のツールを使うと、簡単に判定できるので一度使ってみてください。

lab.syncer.jp

JSON.stringify()とJSON.parse()

JSON.stringifyJavaScriptのオブジェクトをJSON文字列に変換する関数。 JSON.parseJSON文字列をJavaScriptオブジェクトに変換する関数。

出力を見ると、JSON文字列はキーがどちらもダブルクォーテーションで囲まれていることがわかると思います。

var jsVar = {
  id: 1,
  title: "title"
};

var jsonStr = JSON.stringify(jsVar);

var jsParsed = JSON.parse(jsonStr);

console.log(jsVar); // {id: 1, title: "title"}
console.log(jsonStr); // {"id":1,"title":"title"}
console.log(jsParsed); // {id: 1, title: "title"}

WebpackでReactを本番反映する時に行う設定

webpackの各種プラグインを導入

DefinePluginでprocess.env.NODE_ENVを置換する

特定の文字を任意の文字に置き換えることのできるプラグインです。

たとえば、

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production')
        }
      }),
],

という定義があった時に、Reactのコード内の

if process.env.NODE_ENV !== ‘production’ { 
  //開発環境用のコード
}

if ‘production’ !== ‘production’ {
  //開発環境用のコード
}

に置き換えられる。

この設定単体だとダメで、後述のUglifyJSPluginと組み合わせることで、 未到達のコードは削除されてファイル容量を削減することができます。

UglifyJsPluginでJSをminifyする

minifyとは、コメントや改行,スペースを削除したり、変数名を短くして無駄な部分を取り除くことです。

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production')
        }
      }),
      new webpack.optimize.UglifyJsPlugin()

とすると、

  WARNING in xx.js from UglifyJs
    Condition always false [./~/react/lib/React.js:29,0]
    Dropping unreachable code [./~/react/lib/React.js:29,0]
    Declarations in unreachable code! [./~/react/lib/React.js:30,0]
    Condition always false [./~/react/lib/React.js:44,0]

のような警告文が大量にでることがあるので、

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production')
        }
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false
        },
        comments: require("uglify-save-license")
      })

にして、警告文を非表示にします。 またuglify-save-license を使うことでライセンスのコメントは残す設定も入れておきましょう。

その他

あとは環境に応じて、APIのリクエスト先を変えるような場合 以下のようにして置き換えられるようにしておくと良いと思います。

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production'),
     'API_URL': 'https://api.xxx.com'
        }
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false
        },
        comments: require("uglify-save-license")
      })

ReduxのProviderコンポーネントとContainerコンポーネントを理解する

Providerコンポーネント

const App = () => {
  return (
    <Provider store={createStore(reducers)}>
      <View>
         <Header>
         <PostList>
         <Footer> 
      </View>
    </Provider>
  )
}

Containerコンポーネント

  • connect関数でReactコンポーネントをラッピングしたもの
  • Connected Componentとも呼ぶ
  • Reactでは「Stateを親コンポーネントに持たせる」と表現するが、それをReduxでは名前をつけただけ
  • ただし、ReduxではStateはStoreに持つので、connectしてStateをPropsとして受け取る形になる
class PostList extends React.Component {
  render() {
    //ここにstateの中身がくる
    console.log(this.props);
  }
}

const mapStateToProps = state => {
  return { posts: state.posts };
}

//PostListをconnectでラッピングしたここがContainerコンポーネント
export default connect(mapStateToProps)(PostList);

このようにすることで、stateの中のpostsのみをpropsで受け取ることができる