ZEALS TECH BLOG

チャットボットでネットにおもてなし革命を起こす、チャットコマース『Zeals』を開発する株式会社ZEALSのテックブログです。技術やエンジニア文化について情報を発信します。

Zshの起動を爆速にするプラグインマネージャーZplugin【コーナーで差をつけろ】

f:id:zeals-engineer:20190916134827p:plain


皆さんこんにちは!現在SREチームでエンジニアインターンをしており、4月から新卒入社する玉城です。
今回はZshプラグインマネージャーのZplugin導入のススメです!

  • Zpluginの紹介
  • Zpluginインストール方法
  • Zpluginを用いた.zshrcの設定例
    • はやくなった要因解説
    • zplugの起動時間も計測してみる
  • まとめ

Zpluginの紹介

Zpluginは柔軟で高速なZshellプラグインマネージャーで、zshの起動を50~73%高速化するTurboモードを備えた唯一のプラグインマネージャーです。

github.com

  • zplug使っているけど起動時間1秒以上かかるし辛い…
  • Zplugin使ってみたいけどドキュメントは英語だし日本語の情報も少ないし…
  • Zshを使ってみたいからこの記事を読んでみよう!

という方には、とてもおすすめです!

私も実際にZshとZpluginを使っていて、以前使っていたプラグインマネージャーzplugよりも約0.5秒ほど起動速度が早くなりました。これなしの生活は今や考えられません!(あやしいサプリをすすめているわけではないですよ)

また、ZEALSのエンジニアが使っているシェルはみんな多種多様です 。
良ければ御覧ください!

tech.zeals.co.jp

tech.zeals.co.jp

続きを読む

【週刊 Kubernetes 連載】ミニマリストのための CI/CD パイプライン!!

https://skaffold.dev/images/architecture.png

こんにちは!!

8月からZEALSにジョインしたぱんでぃーです!TypeScriptとAureliaを愛するバックエンドエンジニアとして、現在はGolangでMicroservices化を目指したAPIを開発しています!

このAPI開発のプロジェクトについても、近いうちにブログで公開していきたいのですが、今回はKubernetes環境でのCI/CDについてご紹介します。(おもにCD)

このトピックについては、以前、CTO島田のエントリーでも少し紹介していましたが、さらに進化させた内容となっております!

tech.zeals.co.jp

  • TL;DR
  • KubernetesエコシステムのCDツールの現状
  • できるだけミニマムスタックでスタートするためには?
    • Gitブランチ戦略
    • アプリケーションの実行環境
    • 目指すゴール
  • ZEALSの実際のCI/CDパイプラインを紹介
  • 今後の伸びしろ
  • さいごに

TL;DR

  • Kubernetes導入初期で最適なCDパイプラインを構築するハードルが高い・・・
  • できるだけミニマムスタックでラーニングコストの小さいCI/CDパイプラインとは!?
  • GitOps likeなCIOpsで安全かつ高速なデプロイサイクルを実現!!
続きを読む

行動ログをRailsからBigQueryに流す仕組みの導入

f:id:zeals-engineer:20190820120026p:plain

はじめに

こんにちは、分析基盤を担当している鍵本です。
本日は DB に保存されている行動ログを BigQuery に流すように修正 したお話をしようと思います。

  • はじめに
  • 背景
  • 非RDB化の方針
  • Rails 側の設定
    • logger の再定義
    • 出力データ用の Struct の定義
    • 出力処理の追加
  • Stackdriver Logging のシンク設定
  • BigQuery でのテーブル作成
  • Cloud Functions の設定
  • まとめ

背景

これまでチャットボットに流入させる「モーダルを開いた」とか「クリックした」といった行動ログは 直接DB に保存されていました。

レコード数が少ないサービス開始直後はそれでもよかったのですが、だんだんお客様の数が増えてくると、それに応じてモーダルにアクセスしてくださるユーザーさんの数も増え、DB への書き込み頻度が高まり、次第にアプリへの負荷が高くなってきました。
このままサービスを続けていくと、いずれDBへの書き込み処理が足枷になってしまうだろうということで、 このログを DB に書き込むのではなく、ログデータとして外部出力 することにしました。

非RDB化の方針

ZEALS では GKE でアプリケーションサーバを運用しているので、Rails アプリでは単に必要なデータを標準出力するだけとなります。
標準出力されたログは、同一クラスタ内で稼働している fluentd によって Stackdriver Logging に送られますので、それを Cloud Storage を経由させて BigQuery にロードするようにしました。

流れ図は以下の通りです。

f:id:zeals-engineer:20190819194127p:plain (ログ出力構成図)

Stackdriver Logging から直接 BigQuery にインポートすることはできますが、データとスキーマとの間の不一致によるインポートエラーが発生した場合に再取り込みが面倒なこと、リアルタイム性がそれほど必要ではなかったことから Cloud Storage 経由にしています。

なお Stackdriver Logging から Cloud Storage へのエクスポートは一時間に一回行われます。
したがって、BigQuery では約一時間遅れでデータの閲覧が可能となります。

Rails 側の設定

Rails 側では次の3つの対応をしました。

  • logger の再定義
  • 出力データ用の Struct の定義
  • 上記 logger による出力処理の追加

logger の再定義

Simma さんの GKE上RailsのアプリケーションログをStackdriver Loggingで運用する方法 で紹介されている Custom log を参考にさせていただきました。
修正した点は progname が定義されてなかった場合に rails_app を代入するという点のみです。

当初は Rails.logger を上書きして利用することを想定していたため、production.log が progname = rails_app で送られるようにして、行動ログと分離できるようにしたかったからです。

出力データ用の Struct の定義

ログ出力部分に勝手に項目を追加されないように、 出力データを構造体で縛る ことにしました。
以下のようなものを lib 以下に配置しています。

FooStruct = Struct.new(:foo, :bar, :baz) do
  def to_emptify
    to_h.map { |k, v| [k.to_s, v.to_s] }.to_h
  end
end

null を空文字にするためのメソッドを内部で定義しています。
ZEALS ではまだ ruby-2.5 系なので上ような書き方をしていますが、2.6 以降では前後の to_h が不要となります。

出力処理の追加

実際にDBに書き出している部分にログ出力を追加します。

JsonStdOutLogger.instance.logger.info(:foo_log) do
    FooStruct.new(
        foo_val,
        bar_val,
        baz_val
    ).to_emptify
end

上記のように Rails.logger を上書きするのではなく、JsonStdOutLogger.instance.logger を直接呼ぶようにしています。
なので「当初は…」なんて言い方を先ほどいたしました。

Rails.logger = JsonStdOutLogger.instance.logger

を記述して上書きしたところ ActiveSupport::TaggedLogger が継承されてしまいました。
その結果 ActiveSupport::TaggedLogger::Formatter#call が先に呼ばれ、その内部で Hash データが文字列化され想定通りの出力にならなかったため JsonStdOutLogger.instance.logger を直接呼ぶ方法を採用しました。

Stackdriver Logging のシンク設定

高度なフィルタに変更し、以下を設定します。

resource.type="container"
resource.labels.cluster_name="クラスタ名"
resource.labels.namespace_id="ネームスペース名"
logName="projects/プロジェクト名/logs/サービス名"
jsonPayload.progname="foo_log"

これによって抽出されたログに対してシンクの設定をします。

冒頭でご説明しましたように、シンクサービスには Cloud Storage を選択します。
同時に必要ならばバケットも作成しますが、その際の注意点は以下の二点です。

  • アクセス制御モデルとして オブジェクトレベルを選択 する
  • ストレージの場所はアプリケーション、BigQuery とで 全て統一 する

BigQuery でのテーブル作成

Stackdriver Logging のログには timestamp が存在するので、これをログが生成された日時として BigQuery にも取り込むことにしました。

また、この値を用いてテーブルパーティションすることにしました。
これは常に全件検索をしていると、有名な BigQuery で破産しました みたいなことになってしまうからです。

qiita.com

テーブルは以下のようなクエリで生成しています。

CREATE TABLE app_logs.foo_logs (
  foo int64,
  bar string,
  baz string,
  timestamp timestamp
) PARTITION BY DATE(timestamp);

Cloud Functions の設定

以下のような Python スクリプトを作成して、google.storage.object.finalize をトリガーとして実行されるようにデプロイします。

import os
import sys
import json

import gcsfs
from google.cloud import bigquery

MAX_REC_LENGTH = 1000
bigquery_client = bigquery.Client()

def bq_insert(bq_client, table, data):
    errors = bq_client.insert_rows(table, data)
    if errors != []:
        print("BQインポートエラー({})".format(errors))
        sys.exit(1)
    return len(data)

def gcs_placement_handler(data, context):
    gcs_fs = gcsfs.GCSFileSystem(project = os.environ['PROJECT_ID'])
    table_ref = bigquery_client.dataset(os.environ['DATASET_ID']) \
                               .table(os.environ['TABLE_ID'])
    table = bigquery_client.get_table(table_ref)

    total_length = 0
    rows_to_insert = []
    with gcs_fs.open('/'.join([data['bucket'], data['name']]), 'r') as fd:
        for line in fd:
            line_dict = json.loads(line.rstrip('\n'))
            payload = line_dict['jsonPayload']
            # foo が空の場合に NULL に変換する
            if payload['foo'] == '':
                payload['foo'] = None
            payload.update({'timestamp': line_dict['timestamp']})
            rows_to_insert.append(payload)

            # rows_to_insert のサイズが MAX_REC_LENGTH に達したら BigQuery にインサートする
            if len(rows_to_insert) >= MAX_REC_LENGTH:
                total_length += bq_insert(bigquery_client, table, rows_to_insert)
                rows_to_insert = []

    # バッファリングした残りを BigQuery にインサートする
    if len(rows_to_insert) > 0:
        total_length += bq_insert(bigquery_client, table, rows_to_insert)

    return 'Successfully insert {} records.'.format(total_length)

Rails から出力されたログが jsonPayload に入っており、BigQuery に取り込みたいものがjsonPayload の中身と timestamp の値なので、それらをマージしてインポートするのが上記スクリプトの役割です。

また整数値 foo が null となる可能性があるのですが、Rails 側から空文字で送るようにしているので、BigQuery にインポートできるよう null に設定する処理も入れています。
(ここは Rails から直接 null で出力しても良かったのかもしれません)

また一時間に一回とはいえ、レコード数が多かった場合にメモリが足りなくなる可能性もあるので、1000件ずつインポートするようにしています。

続きを読む

五反田界隈のエンジニアコミュニティが盛り上がってる件

f:id:zeals-engineer:20190812003619j:plain

皆さんこんにちは! 
VPoE兼サーバサイドエンジニアをやっている福本です!

五反田には開発言語の地域コミュニティをはじめ、たくさんのエンジニアコミュニティが存在しているのですが、7月に多くのイベントが開催されました。

開催されたイベントの多くでZEALSのエンジニアが登壇しましたので、こちらの記事でまとめてご紹介できればと思います!

最後までお付き合いいただけますと幸いです。

  • 開催された五反田コミュニティ
    • Gotanda.js
    • Gotanda.rb
    • Gotanda.EM
    • その他のコミュニティ
  • Gotanda.js
  • Gotanda.rb
  • Gotanda.EM
  • さいごに

開催された五反田コミュニティ

Gotanda.js

五反田のJavaScriptコミュニティ『Gotanda.js』です!5月に復活したばかりですが、申し込みの段階で40人と、前回同様非常に多くのエンジニアに集まっていただき開催されました。今回はZEALSオフィスでの開催となりました!

gotandajs.connpass.com

 

Gotanda.rb

過去にテックブログの記事でも何度か取り上げております、五反田のRubyコミュニティ『Gotanda.rb』です。ZEALSのオフィスでの開催が続いておりましたが、今回はトレタ社オフィスでの実施となりました。

gotanda-rb.connpass.com

Gotanda.EM

『Gotanda.EM』は”エンジニアリング・マネージャー”をテーマとした、珍しいコミュニティです。今回は私福本が登壇させていただきました(こちらもトレタさんでの開催です)。

gotanda-em.connpass.com

その他のコミュニティ

開催はされていませんが、五反田には以下のようなコミュニティもあります。ZEALSで導入されていない技術のコミュニティもあり、なかなか参加することは難しいかもしれませんが、機会があれば積極的に参加したいですね!

  • Perl:Gotanda.pm

gotanda-pm.connpass.com

  • TypeScript:Gotanda.ts

gotanda-ts.connpass.com

Gotanda.js

f:id:zeals-engineer:20190812001820j:plain

 

以前5月に復活したばかりのGotanda.jsですが、復活2回目となる今回はZEALSオフィスで開催されました!

ZEALSからはかなっちが登壇し、ヘッドレスブラウザのライブラリ『puppeteer』の活用についてLTをしてくれました。

github.com

ほぼpuppeteerの再生動画だったため登壇スライドはありませんが、勤怠管理システムに向けて実験してみるなど、イタズラ的な使い方も披露してくれ、会場を大いに盛り上げてくれました(笑)

また、登壇時に会場のお客さんに聞いてみたところ、puppeteerを使っているユーザーが意外と多く(90%くらい)、ヘッドレスブラウザが普及している感が強かったですね!

tech.zeals.co.jp

Gotanda.rb

f:id:zeals-engineer:20190812001746j:plain

お次はZEALSのテックブログでもおなじみGotanda.rbです!
登壇は、こちらもGotanda.rbでの登壇が板に付きつつある高久田さんです。

今回のGotanda.rbイベントのテーマが「バージョンアップ」だったので、最近高久田さんが業務で関わっているgemのアップデートについて語ってくれました!
 

speakerdeck.com

ZEALSでは最近dependabotを導入したこともあり、gemのアップデートの体制やフローが大きく変わったり、実はRailsのバーションが思っていたのと違っていた(←「え?」って思った人は資料をぜひ)という話もあって、その知見を踏まえたLTをしてくれました!

dependabot.com

他にも、福本と一緒にOrganizerをしてくれているエンジニアの方がRailsを4系から5系に上げた話をしてくれるなど、なかなかに濃くて独特な雰囲気の中で進んでいきました!

Gotanda.EM

さいごは、私福本が登壇しましたGotanda.EMです!

ひよっこマネージャーとして何を話すか非常に迷ったのですが、今回は『エンジニア採用』について話をさせていただきました!

ZEALSでは、これまでエンジニア採用をしっかりと行っていなかった背景があり、社外のエンジニアの方に自分たちの魅力を伝えきれていないと感じることが多々ありました。

そのような状況を改善するために取り組んだことを、当日のLTで共有しました!

当日は他のマネージャーの方も採用の話をされるなど、エンジニア採用はとても重要な取り組みであるということを改めて感じました。

speakerdeck.com\

僕も他のマネージャーの方に負けずに、いろいろな工夫をしてエンジニア組織の価値を最大化させていきたいと思いました。貴重な機会を頂いたGotanda.EMの運営の方々、ありがとうございました!

続きを読む

React Hooksを使用してみました!

f:id:zeals-engineer:20190809134906p:plain (※画像はReact Hooks Tutorialから引用)

はじめに

こんにちは!
普段RailsとReactを書いている中村です!

最近本格的にReactを書きはじめたのですが、今年リリースされたhooksを早速触ってみました!
今回はhooksを触って得た知見を共有していこうと思います!

  • はじめに
  • hooksとは
  • useState
    • useStateの基本的な構文
    • stateの遅延初期化
  • useEffect
  • useContext
  • useReducer
  • おわりに

hooksとは

一言で説明すると
state管理やライフサイクルメソッド等がFunctional Componentで使用できる
という感じです!

百聞は一見に如かずなので、早速コードと共に説明していきます!
※HooksはReact 16.8より古いversionだと動きません。

useState

Functional Componentにstateを持たせられる APIです。

useStateの基本的な構文

const [state, setState] = useState(initialState);

stateの遅延初期化

initialStateは初回render時に使われるstateの値です。後続のrender時にはその値を使いません。
もし初期のstateに複雑な処理が必要な値である場合は、代わりに関数を渡すことができます。この関数は初回のrender時にのみ実行されます。

const [state, setState] = useState(() => {
  const initialState = somethingFunction(props);
  return initialState;
});

useStateはステートフルな値と、それを更新するための関数を返します。
それらを分割代入で取り出し、変数として定義しています!

基本的な構文を説明したところで早速実践に移っていきます!
ボタンを押すと数が増えたり減ったりする簡易的なAppを例にしてみていきましょう。
hooksが導入される以前は状態管理をするために、Class Componentを使う以外に方法はありませんでした

こんな感じ
↓↓↓

import React, { Component } from "react";

class Counter extends Component {
  state = { count: 0 };
  
  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button
          onClick={() =>
            this.setState(prevState => {
              return { count: prevState.count + 1 };
            })
          }
        >
          +1
        </button>
        <button
          onClick={() =>
            this.setState(prevState => {
              return { count: prevState.count - 1 };
            })
          }
        >
          -1
        </button>
      </div>
    );
  }
}

hooksを導入することにより、Functional Componentでも状態管理ができるようになりました!
こんな感じです
↓↓↓

import React, { useState } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(0)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+1</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>-1</button>
    </div>
  );
};

どうでしょうか?Hooksで書いた方がシンプルでわかりやすいと思います。
Class Componentではstateがオブジェクトである事、this.setStateがオブジェクトの一部を更新する関数のため冗長に感じます(個人的に)。
Hooksは、countというstateに対してsetCountという専用の関数を使用しているので、より直感的なコードになっていると思います!

useEffect

レンダリング後に処理を行うAPIです。 useEffectはClass Componentのライフサイクルメソッドでいえば、
componentDidMountcomponentDidUpdateに相当するものです(厳密には少し違う) 早速みていきましょう!

const Counter = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("like componentDidMount or ComponentDidUpdate");
  }); //①第二引数省略

  useEffect(() => {
    console.log("like componentDidMount");
  }, []); //②第二引数に空配列

  useEffect(() => {
    console.log("only change count");
  }, [count]); //③第二引数にcountを渡す


  const increment = () => setCount(prevCount => prevCount + 1);
  const decrement = () => setCount(prevCount => prevCount - 1);

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </div>
  );
};

第1引数にはコールバック関数、第2引数には値の配列を定義し、配列のいずれかの値が変わったときのみ、コールバック関数を実行します。

  1. は上記でも記載した通り、componentDidMountcomponentDidUpdateのタイミングでコールバック関数が実行されます。
  2. はcomponentDidMountのタイミングのみコールバック関数を実行します。(配列に値が存在しないため)
  3. はcountの値に変更があった時のみコールバック関数を実行します。

今回の場合はstateがcountだけなので、第2引数を省略しても変わりませんが、他にもstateがある場合に不必要な処理が発生しないようにする効果があります。
以前のclass ComponentではcomponentDidMountやcomponentDidUpdateに同じ処理を書くことがあったので、useEffectで一括に管理ができ、コードがより綺麗になります。

useContext

React.createContext からの戻り値を受け取り、そのContextの現在値を返します。
contextとはReact V16.3で導入されたAPIです。

Contextはprop drilling問題を解決します。
最初にuseContextを使わずにContextのみを使った実装だと こんな感じです 。
↓↓↓

import React, { createContext } from "react";
const RootContext =createContext();

const GrandChild = () => (
  <RootContext.Consumer>
    {value => <p>{value.name}</p>}
  </RootContext.Consumer>
);

const Child = () => <GrandChild />;

const Father = () => {
  return (
    <RootContext.Provider value={{ name: "taro" }}>
      <Child />
    </RootContext.Provider>
  );
};

本来なら、Child Componentからpropsとして{name: 'taro'}をGrandChild Componentに渡してあげる必要があります!
Contextのみでもprops drilling問題は解決できますが useContextを使った方がより簡潔でわかりやすいです!

useContextを使うとこんな感じです。
↓↓↓

import React, { createContext, useContext } from "react";
const Context = createContext();

const GrandChild = () => {
  //分割代入
  const { name } = useContext(Context);
  return <p>{name}</p>;
};

const Child = () => <GrandChild />;

const Father = () => {
  return (
    <Context.Provider value={{ name: "taro" }}>
      <Child />
    </Context.Provider>
  );
};

useContextの引数に作成したContextを渡すとvalueの値が返ってきます(今回でいうと{name: 'taro'})

いかがでしょうか?
Contextのみを使用するより、useContextとContextを合わせて使用した方が、簡潔で読みやすいですよね?
僕個人prop drilling問題にかなり悩まされていたので、もっと早く知りたかったです。。。笑

useReducer

useReducerはuseStateの代替品です。
基礎構文

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducerは、現在のstateをdispatchメソッドとペアにして返します。
useStateとの使い分けとして、複数の値にまたがる複雑なstateロジックがある場合はuseReducerを使う方が好ましいと思われます。

また、useReducerを使えばコールバックの代わりにdispatchを下位コンポーネントに渡せるようになるため、複数階層にまたがってstateの更新を 発生させるようなコンポーネントでは効果的であると思います。
コードは以下のようになります!

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

初期stateの指定 useReducerの初期化の方法には 2 種類あります。最も単純な方法は第 2 引数として初期stateを渡すものです。

 const initialState = {count: 0}
 const [state, dispatch] = useReducer(reducer, initialState);

注意

React では、初期値はpropsに依存している可能性があるため、フックの呼び出し部分で指定します。
そのため、**reducerの引数でstate = initialState のように、Reduxで普及した慣習を推奨しておりません。**  

普段Reduxを使っている方は若干違和感を覚えるかもしれません!
Reactの公式ドキュメントに記載されているので、気になる方はこちらからどうぞ! reactjs.org

遅延初期化
初期 state の作成を遅延させることもできます。
そのためにはinit関数を第 3 引数として渡してください。初期stateがinit(initialArg) に設定されます。

これにより初期 state の計算をreducerの外部に抽出することができます。
アクションに応じて state をリセットしたい場合にも便利です。

const init = (initialCount) => {
  return {count: initialCount};
}

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}
const Counter = ({initialCount})  => {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>

        Reset
      </button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

今回のケースはサンプルなのでuseReducerを使うまでもないですが、 普段のアプリケーションのコードはより複雑なので、useReducerは活躍しそうですね!

おわりに

最後まで読んでいただきありがとうございます!

続きを読む

RailsエンジニアのReact入門

f:id:zeals-engineer:20190729181230p:plain

こんにちは。
最近暑くなってきて通勤が大変な阿部です。

さて、普段Railsを書いている私ですが、先月初めてReactのPRを出しました!
新しい言語を勉強すること、それを実際に使って何かを作り上げることは楽しいなぁと改めて感じ、エンジニアの良さを再認識した出来事でした。

そんなわけで、今回のブログはタイトルの通りRailsエンジニアのReact入門ということで、業務の合間にどうやって勉強したかを記事にしてみました。
もしこれからReact始めてみたい!という方の参考になれば幸いです。

  • React入門した時のわたし
  • React入門、その前に…
  • いざ入門
  • 実戦投入!
  • おわりに

React入門した時のわたし

Reactの勉強を始めた時のわたしのスペックは下記です。
見ての通り、JSはjQueryくらいしか触ったことがありませんでした。

  • 普段はRails(Ruby)でバックエンドの実装に携わる
  • Rubyの他にちゃんと触ったことある言語はHTML・CSS・JS(jQuery)くらい
  • 業務でフロント連携することが多いのだが、簡単な修正くらい自分でできるようになりたかった

React入門、その前に…

そもそも、Reactは公式サイトにあるようにJSのライブラリです。
(開発をおこなっているのはFacebook社)

React はユーザーインターフェースを作成する為の JavaScript のライブラリです。

Getting Started – React

というわけで、Reactに入門する前にまずはJS(ES6)の基礎から学ばねばと思い、Udemyで買ったES6の基礎講座を通勤中や始業前にちょこちょこ見ながら勉強しました。

https://www.udemy.com/

アプリにDLして見れるので、スマホから空き時間にささっとできるのがいいと思います!

いざ入門

で、Reactに入門するために何をしたかといえば…

Reactチュートリアルです!笑

ja.reactjs.org

  • React公式のチュートリアル
  • 現在日本語版もあり、入門しやすい
  • 環境構築なしでブラウザからサクッと入門もできる(ちなみにわたしはローカルに環境構築からやりました)

Reactのポイントを押さえながら三目並べ(マルバツゲーム)を作っていきます。

これを、以前このブログでも紹介させて頂いた開発合宿in三浦海岸でやってきました!
普段も土日に勉強したりしていますが「環境の違う場所でひたすら開発!」という経験がなかったので、とても集中して取り組めたと思います!

また、チームのみんなといつもと違うことをすることによって、「この人こんな技術に興味があるんだ」とか「それ面白そう!」などの発見もありとても楽しかったです。

そんな楽しかった合宿で一通り基礎をさらいながらチュートリアルの五目並べは完成!

f:id:zeals-engineer:20190728223553p:plain

チュートリアルを最後まで進めると動く三目並べゲームができるわけですが、このあとさらに6つの拡張課題が待っています。

  1. 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。
  2. 着手履歴のリスト中で現在選択されているアイテムをボールドにする。
  3. Board でマス目を並べる部分を、ハードコーディングではなく 2 つのループを使用するように書き換える。
  4. 着手履歴のリストを昇順・降順いずれでも並べかえられるよう、トグルボタンを追加する。
  5. どちらかが勝利した際に、勝利につながった 3 つのマス目をハイライトする。
  6. どちらも勝利しなかった場合、結果が引き分けになったというメッセージを表示する。

これらは課題だけで、サンプルコードなどは載っていません。
合宿中に全て終わらなかったので、通常の業務に戻った後も空いている時間を見つけて取り組んでいましたが、

  • どうやったら実現できそうか
  • 実現したいことを解決できる関数などはあるのか?

など考えながら進めるのは実際の業務に近く感じ、結果最後までやってよかった!と思いました。

また、JS(ES6)・Reactの基礎の振り返りもしつつ理解を深めることができました。
これからやる場合、拡張課題までやることをオススメしたいです!笑

(コードの良し悪しはともかく)やりきった記録をQiitaにあげてありますので興味のある方はそちらも見てみてください。

qiita.com

実戦投入!

ここまでコツコツ勉強が終わった段階で(約1ヶ月)なんと実戦投入されました。
当時担当していたリニューアルプロジェクトのフロント一部分の実装を任され、悪戦苦闘しつつもなんとか完成し、今実際に動いています。

続きを読む

RDBの歴史をディグってみた

f:id:zeals-engineer:20190721141625p:plain

こんにちは!
4月に新卒で入社したエンジニアの荒木です。

業務ではデータの不整合を解消したり、データベースの構造を作り替えたりしています。

今回の記事のテーマは、【リレーショナルデータベース(RDB)の歴史】です。
RDBが生まれる以前のDBの歴史から、どのようにRDBが登場し、世に広まったのか、という流れを追います。

その背景としては僕が、歴史の勉強が大好きなことがあります。

単純に新たな知識・考え方が身につく、という理由もありますが、
それ以上に、物事のルーツを辿ることで、現在身の回りに当たり前のようにあるものが実は全く別の目的で生まれていたり、偶発性の産物だったりすることに好奇心が駆り立てられるからです。

僕たちが普段扱っている技術も全て、いつかの時代の何処かの国に住んでいた誰かの天才的な発見、発明の積み重ねによって確立されたものです。

遥か昔からの種々の学問的知見によって、今の技術が目の前にあると思うとワクワクしますよね!!

そこで今日は、RDBの歴史について調べてみたので、
それを極力分かりやすくシェアしたいと思います!

  • RDB以前のデータベースの歴史
  • RDBの登場
  • SQLの誕生
  • RDBの時代へ
  • まとめ
  • 参考文献

RDB以前のデータベースの歴史

データベースと言われると、エンジニアの方々はソフトウェアとしてのデータベースを想像するかもしれませんが、 広義に解釈されるデータベースは、もっと昔から存在します。

例えば、「鳴くよ(794年)ウグイス平安京」 と言いますが、
何年に起こったことなのかが判明しているということは、少なくともその時代から国の歴史のデータを蓄積するなんらかの機能があったということです。

我々は当たり前のように何年のどこで何が起きたのかを習ってきましたが、
それはソフトウェアとしてのデータベースがなかった時代から、脈々と受け継がれてきたものです。
すごいことですよね。

さて、初めてデータベースという単語が使われたのは、第二次世界大戦後、1950年代の米軍基地において
各基地に点在していたデータを、1箇所に集約した時であると言われています。

長らくデータを蓄積・保持してきた人類ですが、1960年代に入り、遂に ソフトウェアとしてのデータベース を発明します。

この時代では、IBMにより開発されたIMSと呼ばれる階層型モデルのデータベースと、
CODASYLの発表した関連ネットワークモデルのデータベースが人気のデータモデルでした。

f:id:zeals-engineer:20190720192253p:plain

階層型のデータベースはツリー型の構造で表され、現在で言う組織図のような構造になっていました。

1つのデータが1つ、もしくは複数のデータを子として持つことが許されていました。
そのため、「1対1」や「1対多」の関係を表現することができました。

しかし、このデータモデルはデータの冗長が発生しやすく、
例えば「多対1」を表現する時は、その子データは属する親データの数だけ存在する必要がありました。
また、エンジニアが階層型モデルでデータにアクセスするためには、その階層構造を理解している必要がありました。

その他に、階層型モデルを用いたプログラム開発は、プログラムが大きくデータ構造に依存してしまい、
保持・運用にかかるエンジニアの工数や必要とされる知識量が大きくなるという問題点を抱えていました。

一方、関連ネットワーク型のデータベースではデータは網の目の形で結ばれ、1つのデータが複数の親を持つことができるようになりました。
そのため、上記の冗長性の問題は解消されましたが、
階層型モデルの時の問題点であった、プログラムが大きくデータ構造に依存している問題は引き継がれ、
依然として保持・運用は難しい状況が続きました。

RDBの登場

そんな中、1970年にIBMのEdgar Frank "Ted" Codd(以下、コッド)が

"A Relational Model of Data for Large Shared Data Banks" https://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf
という論文を発表しました。

これが現在にリレーショナルデータベースとして広く知られるデータモデルの基礎的な考えでした。

リレーショナルデータモデル(以下、関係データモデル)では、全てのデータをテーブルという二次元表で表現しました。
さらにそのテーブルでは、行や列という位置情報にデータが縛られないという革新性を持っていました。

関係データモデルでは、階層型モデル等のようプログラマに複雑な実装や高い専門性を求めず、
データベースを欲する全ての人々にその利用を解放しました。

しかし、これらの革新性にも関わらず、当時のIBMはリレーショナルデータベースを実装することを拒みます。

当時IBMが開発・販売していた、階層モデルのIMSによる収益を守るためです。
いわゆる、イノベーションのジレンマでしょうか。

ja.wikipedia.org

SQLの誕生

その間に他の会社や研究チームはコッドの発表した関係データモデルに大きな可能性を感じ、開発に着手します。

最初に開発に着手したのは1973年、UC BerkeleyのMichael Stonebrakerによるもので、このDBは"Ingres"と名付けられました。
Ingresは後に、"Post"(後の)+ "Ingres"から、"Postgres"へ、そして"PostgreSQL"へと名前を変えます。

f:id:zeals-engineer:20190721222943p:plain

1974年にはIBMも関係データモデル型のDBの開発を始めますが、
コッドをプロジェクトマネージャーに指名することはしませんでした。

代わりに指名されたのは、コッドの関係データモデルの理論を快く思っていないチームで、
この開発チームがコッドと関わりを持たないようにすることが目的だったとされています。

コッドは関係データモデルの論文を発表した翌年の1971年に、
RDBを扱うための言語として"ALPHA"という言語を発表しましたが、
このアイディアが実装されることはなく、代わりにアサインされた開発チームにより、"SEQUEL"という言語が作られました。

とは言え、これは元のシステムよりも優れていました。
1975年、IBMは試験的なRDBである"System R"を発表します。

この"SEQUEL"が後に"SQL"と呼ばれるようになります。

RDBの時代へ

1977年、コッドの論文を読み、RDBに可能性を感じたLarry Ellisonが商用RDBを開発するべく、会社を設立します。
これが後のOracle社です。

1979年にこの会社は、世界初の商用RDBである"Oracle Database"をリリースします。
最初はミニコンピュータ専用のDBであったOracle Databaseも、1983年にはIBMのPC上でも利用可能になりました。

Oracleはどんどん成長し、同年1983年にIBMもついに商用RDBを開発・販売しますが、
既にOracleのDBはIBMの顧客にも導入されており、市場はOracleに支配されていました。

f:id:zeals-engineer:20190721223343p:plain

(↑Oracleの本社)

その後短期間でRDBは次々に開発され、1989年にはPostgreSQLが、1995年にはMySQLが最初のリリースを迎えます。

MySQLはMySQL ABというスウェーデンの企業により維持されていましたが、
2008年にMySQL ABはサン・マイクロシステムズに買収され、2010年にはサン・マイクロシステムズがOracleに買収されたことで 現在はOracleが商標権を有しています。

また、コッドは1981年にチューリング賞を受賞するなど、多くの賞を受賞しました。
その後、独立してコンサルティングの会社を設立しますが、2003年に心不全で亡くなりました。

まとめ

いかがだったでしょうか。

  • RDBも考案された当初はすぐに実装されることはなく、その可能性を信じた外部の人間によって今のような普及に至ったこと
  • RDBもSQLもIBMから生まれたものの、それらは異なる開発者から生まれたこと

この辺りを知れたのは、面白かったなあ、と思います。

僕自身もRDB以前のDBのモデルを調べることで、勉強になりました!

続きを読む

第1回 ZEALS Developer Meetupと称し、社内LT大会を実施してみた!

f:id:zeals-engineer:20190712114843p:plain

こんにちは!味わい深い担当のサーバサイドエンジニアKodyです!

先日 ZEALS DeveloperMeetup (社内LT大会) を開催したので、その模様をお届けしたいと思います!!!
同じく社内LT大会を企画しようとしている人に参考になれば幸いです!

  • Developer MeetUpを実施した背景
    • 目的
    • 内容
    • 発表人数
    • 発表時間
  • LTの内容を簡単にご紹介
    • 後藤 『GitHub Actions Hello World』
    • 阿部 『エンジニアでもできる Good UI & UX』
    • 中村 『React Componentの作り方』
  • さいごに

Developer MeetUpを実施した背景

目的

  • LTの練習場所を提供
    • LTしたいと思ってるけど、いきなりは怖い...というメンバーへ
    • プレゼンテーション能力の向上
  • 技術力の向上
    • アウトプットする機会を作ることで技術に対する理解度を深める
  • メンバー間の相互理解
    • メンバーが今どんなことに興味を持っていれるのか知ることができる

内容

  • 基本的になんでもOK(技術,組織, 作ってみたetc...)

発表人数

  • 3人前後
  • 飛び込みあり

発表時間

  • 5分 発表
  • 2 ~ 3分 質疑応答

LTの内容を簡単にご紹介

後藤 『GitHub Actions Hello World』

f:id:zeals-engineer:20190712114236p:plain

先日リリースされた Github Actions について、ごっちこと後藤が、自分の調査結果をダイジェストでLTしてくれました!
発表後に「これをこう使ったら便利かも?」と、Github Actionsの活用方法 がさっそく議論されていたのが印象的でした!

github.com

阿部 『エンジニアでもできる Good UI & UX』

f:id:zeals-engineer:20190712114538p:plain

あべじゃこと阿部ですが、最近プロジェクトで UI設計を担当した経験を活かしたLTをしてくれました!
彼女はサーバサイドエンジニアなのですが、UIにもこだわり、 少しの色の違いで改善されるユーザー体験 について話してくれました!

f:id:zeals-engineer:20190712115218p:plain

実際にスライドでUIを見比べて「おー、確かにこれは見やすい」「実装するときに意識します!」といった声が聞こえていました。

中村 『React Componentの作り方』

f:id:zeals-engineer:20190712114637p:plain

ごーやさんこと中村にはReactを用いたフロントエンド開発を設計から任せているのですが、その中で学んだコンポーネント設計についてのLTをしてくれました!

f:id:zeals-engineer:20190712115241p:plain

続きを読む