Wiz テックブログ

Wizは、最新のIoTやICTサービスをお客様に届ける「ITの総合商社」です。

useReducerの活用法について

こんにちは、フロントエンドエンジニアの松尾です。

Reactにはフック(Hooks)と呼ばれる機能があり、useStateuseEffectuseCallbackといった様々な関数が用意されています。

その中の一つにuseReducerがありますが、useContextと併用して使うものとして認識されたり、useStateの影に隠れたりといった理由であまり活用されていない傾向があります。

しかし、useReducerは様々な場面で活用でき、可読性・パフォーマンス向上に大いに役立ちます。

本記事では、useReducerの基本的な使い方と様々な活用法について説明したいと思います。

useReducerとは?

useReducerとは、useStateと同様に状態管理ができるフックです。

特に複雑なロジックが絡んだ状態を管理するのに適しています。

useReducerは以下のように宣言することで使用できます。

const [state, dispatch] = useReducer(reducer, initialState)

必要な引数は、reducerinitialStateです。

  • reducer:stateとactionを受け取り、新しいstateを返す関数

  • initialState:stateの初期値

戻り値としてstatedispatchを返します。

  • state : 現在のstate値

  • dispatch : reducerを実行するための関数

では実際に、useReducerの使用例を見てみましょう。以下はReact公式ドキュメントから引用したコードになります。

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: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

initialStateでカウント値の初期値を定義しており、reducerにてincrementdecrementのアクションを用意しています。

処理の流れは以下の通りです。

  1. ボタンを押すとdispatchが実行され、中身がreducerのactionに入る。

  2. switch文によって処理を分岐させ、新しいstate値を計算して返す。

  3. useReducerが更新されたstate値を受け取り、新しいstate値を描画させる。

reduxの書き方と非常に似ていますが、useReducerはdispatchにオブジェクトを返す必要はなく、値をそのまま入れることも可能です。

const initialState = {count: 0};

function reducer(state, action) {
  return state + action;
}

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

このようにシンプルに記述できることもuseReducerの魅力の一つです。

ここまでの説明で「useStateでもよくない?」と思われた方も多いのではないでしょうか?

ここからは、useReducerならではの強みについて説明したいと思います。

状態管理をuseReducerにまとめられる

useStateで状態管理していると、宣言が多すぎて複雑になってしまうことはありませんか?

例えば以下のような場合です。

const App = () => {
  const [isOpen, setIsOpen] = useState(false)
  const [type, setType] = useState('small')
  const [phone, setPhone] = useState('')
  const [email, setEmail] = useState('')
  const [error, setError] = useSatte(null)

  return (
    ...
  )
}

このように状態管理が多い場合、ロジックが複雑になるにつれ可読性が悪化してしまうケースが多々あります。

状態管理をuseReducerにまとめることで「何を状態管理しているか」「初期値はなんなのか」が明確になります。

const initialState = {
  isOpen: false,
  type: 'small',
  phone: '',
  email: '',
  error: null
}

const reducer = (state, action) => {
  switch (action.type) {
    ...
    default:
      return state
  }
}

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    ...
  )
}

ロジックとビューを切り離すことができる

useReducerのもう一つの強みは、ロジックとビューを切り離すことができる点です。

今回サンプルとして、以下を実装します。

3つのitemがあり、それぞれの値の更新・削除ができるといったシンプルな実装です。

上記をuseStateuseReducerそれぞれで実装し見比べてみましょう。

まずuseStateの場合です。

const App = () => {
  const [items, setItems] = useState([0, 0, 0]);

  const increment = (index) => {
    const newItems = items.slice()
    newItems[index]++
    setItems(newItems)
  }

  const decrement = (index) => {
    const newItems = items.slice()
    newItems[index]--
    setItems(newItems)
  }

  const deleteItem = (index) => {
    setItems(items.filter((_, i) => i !== index))
  }

  return (
    <>
      {
        items.map((v, i) => (
          <div key={i}>
            item{i}: {v}
            <button onClick={() => increment(i)}>+</button>
            <button onClick={() => decrement(i)}>-</button>
            <button onClick={() => deleteItem(i)}>削除</button>
          </div>
        ))
      }
    </>
  );
}

このように、useStateの場合は、ロジックとビューが混ざった状態となっています。

この程度の規模であれば問題ないかと思いますが、もしロジックが複雑になってきた場合、どの関数がどの状態を更新しているのかが分かりづらくなってしまいます。

次に、useReducerの場合です。

const myReducer = (state, action) => {
  const newState = state.slice()
  switch (action.type) {
    case 'increment':
      newState[action.index]++
      return newState
    case 'decrement':
      newState[action.index]--
      return newState
    case 'delete':
      return state.filter((_, i) => i !== action.index);
    default:
      throw new Error();
  }
};

const App = () => {
  const [items, dispatch] = useReducer(myReducer, [0, 0, 0]);

  return (
    <>
      {
        items.map((v, i) => (
          <div key={i}>
            item{i}: {v}
            <button onClick={() => dispatch({type: 'increment', index: i})}>
              +
            </button>
            <button onClick={() => dispatch({type: 'decrement', index: i})}>
              -
            </button>
            <button onClick={() => dispatch({type: 'delete', index: i})}>
              削除
            </button>
          </div>
        ))
      }
    </>
  );
}

このように、useReducer内にロジック処理を記述できるため、ビューと完全に切り離すことができます。

もし機能追加となった場合も、わざわざ関数を用意せずに、reducerのswitch文に新しく処理を追加するだけで済みます。

また、useReducerを用いるとdispatch関数はメモ化されるため、もし返ってくる値が変わらない場合はコンポーネントの再レンダリングを防ぐこともできます。

以上のことから、ロジックが複雑な処理に関しては、なるべくuseReducerを使用することをおすすめします。

まとめ

以上useReducerの基本的な使い方と活用法についての説明でした。

useReducerは、useStateと比べると使い方が馴染みづらく避けがちですが、活用することで様々な処理の可読性・パフォーマンス向上に役立ちます。

是非今回紹介した方法をお試し下さい!

最後になりますが、Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください↓

careers.012grp.co.jp

LaravelでStrategyパターン & Factory Methodパターン

こんにちは、バックエンドエンジニアの青山です。今回はデザインパターンをLaravelのプロジェクトに適用してみました。
実際にありそうな仕様を想定して、ナイーブな実装からデザインパターンを使った実装にリファクタリングする形で進めていこうと思います。
たとえば

  • ユーザーが単発の仕事に応募してその報酬をもらえるようなサイトがあるとする
  • 仕事情報は管理画面からCSVでアップロードする仕様になっており、アップされたCSVの内容はDBに保存される
  • 仕事にはいくつかの種類があり、扱うデータが異なる
  • ↑の理由からCSVのフォーマットは仕事の種類ごとに異なる
  • 管理画面にあるCSVアップロードフォームは1つで、フォーム内にはファイル用inputとファイルのタイプ(どんなジャンルの仕事が記載されているCSVなのか)を選択するselectが存在する

みたいな仕様があったとします。
フォームはこんな感じのものを想像してください。

仕事のジャンルはいくつもある想定ですが、ここでは一旦「オフィスワーク」「運送」「飲食」の3つが存在するとします。このどれかをselectで選択して、そのジャンルの仕事情報のCSVをアップロードするというわけです。
一旦そのまま仕様を満たしてみるとして、一番ナイーブな実装はこんな感じだと思います。

  リクエストの値を見てifで条件分岐させる単純な作りです。これをデザインパターンを適用してリファクタリングしてみます。

デザインパターンの適用

今回使用するのは

  • Strategyパターン
  • Factory Methodパターン

この2つです。

Strategyパターン

f:id:qingshanhuangye:20210401181113p:plain

状況に応じて処理を動的に切り替えることを可能にするパターンです。それぞれの処理をクラスとして定義し、共通の呼び出し部分から呼び出して処理を代替できるようにします。
今回の場合だと、"CSVを読み込んでDBにデータを保存する"という処理をそれぞれの種類で分けてクラス化することで実現します。

各Strategyの実装

まずそれぞれのクラスの共通のインターフェースを作成します。データをやりとりするためのDtoクラスもついでに作成しておきます。

CSVのデータを読み込んで配列を返すメソッドと、データをDBに保存するメソッドの2つを定義しました。実装内容は各実装クラスに委ねることになります。

これらのクラスが図中の各ConcreteStrategyに相当します。
これで具体的な処理を行うクラスを実装できました。次に必要なのはこれらのどのクラスを使用するか決めるクラス、上の図のContextに相当する部分です。今回の場合だと、フォームのselectで選択された値が何であるかによってどのクラスをインスタンス化するか判別する処理となります。
ifやswitchを使用して条件に引っかかれば指定のクラスをインスタンス化するという方法でもいいかもしれませんが、今回はここでもう一つデザインパターンを適用してみたいと思います。

Factory Methodパターン

f:id:qingshanhuangye:20210402143442p:plain

共通のインターフェースを実装したいくつかのクラスを作成し、どのクラスをインスタンス化するかをサブクラスに任せるパターンです。インスタンス生成処理と使用部分を分割することでメンテナンスしやすい状態にできるというメリットがあります。

インスタンス生成用クラスの実装

まず、Strategyパターン図中のConcreteStrategyに相当するクラスをインスタンス化するクラスを作成します。

インスタンス化される条件はリクエストのfile_typeの値が指定の値であった場合です。この条件判定を実現するために、FileImporterInterfaceに新しくtypeというメソッドを追加します。

おのずと各実装クラスもtypeメソッドを持たなければならなくなるので、修正します。

このような固有の数値を返すだけのメソッドを、残りの各クラスにも実装します。このtypeメソッドを使ってImporterFactoryクラスで条件判定とインスタンス生成を行います。

条件に応じて処理を切り替えるクラスの実装

Strategyパターン図中のContextにあたる部分です。インスタンスの作成はImporterFactoryが担っているので、かなりスッキリした作りになりました。

JoblistFileImportContextの呼び出し

こちらの処理をどこに置くかは好みが分かれると思います。これぐらいならControllerに書いてもよさそうな気がしますが、ビジネスロジック用にJoblistFileImportServiceクラスを作成してそちらに配置することにします。

この処理をコントローラーから呼び出します。

FormRequest

コントローラーを実装する前にFormRequestを作成します。このクラス内でバリデーションとリクエスト値をFileImportDtoに変換する処理を行います。

Controller

最後にコントローラーからJoblistFileImportServiceの処理を呼び出して完成です。とてもミニマルなコントローラーになりました。

メリット

最初に提示したifでの分岐による実装では、種別が増えるたびにどんどん分岐が追加されていくので、見通しが悪くなってしまうというデメリットがありました。
リファクタリング後の実装では種別が増えても新しくクラスを追加してImporterFactoryのimportersプロパティにクラスを登録するだけで済むので、見通しが良くメンテナンスしやすい状態になりました。
ちなみに元ネタとなる記事をちょっと前にQiitaに投稿していますので、よければそちらもご覧ください。

qiita.com

さいごに

Wizではエンジニアを募集しております。 興味のある方、ぜひご覧下さい。

careers.012grp.co.jp

イージングでスムーズなアニメーションを作ろう

こんにちは、フロントエンドの高野です。
皆さんはアニメーションは好きですか?
私は大好きです。

今日はド派手なアニメーションからUIのモーションまで、幅広く応用できるイージングについての記事を執筆しようと思います。

イージングとは?

アニメーションの速度に緩急をつけることで、物体の動きを滑らかに見せることをイージングと言います。 実際に動きをみてみましょう。

See the Pen easing-demo by iricocco3 (@iricocco3) on CodePen.

青い要素は常に動きの緩急が変わりませんが、灰色の要素は緩急があるので滑らかに見えます。

現実の物理法則に近い形でアニメーションのイージングを設定することで、よりリアルで直感的な動きとなります。
通常、上記青色の要素のような常に一定の速度を保つ動き、というのは現実世界ではレアなケースです。

こちらのイージングに関するUIアニメーション考察の記事が非常に面白かったので、
興味のある方は是非合わせてご確認ください。 note.com

CSSで実装できるイージング

イージングを語る上で欠かせない要素はベジェ曲線です。
...ですが、数学的な部分を正確に説明できる自信がないので、 ベジェ曲線という曲線でアニメーションの緩急を調整します、と軽い説明に留めて起き、参照のみを紹介します! 逃(・_・;

ja.javascript.info

CSSのアニメーションでイージングを設定するには、
animation-timing-function プロパティで指定します。
developer.mozilla.org

ここで指定できるいくつかの値は3次ベジェ曲線を指定するプロパティの糖衣構文になっています。 (cubic-bezier)

  • ease // cubic-bezier(0.25, 0.1, 0.25, 1.0)
  • ease-in // cubic-bezier(0.42, 0, 1.0, 1.0)
  • ease-out // cubic-bezier(0, 0, 0.58, 1.0)
  • ease-in-out // cubic-bezier(0.42, 0, 0.58, 1.0)
  • linear // cubic-bezier(0.0, 0.0, 1.0, 1.0)

それぞれの動きのサンプルと、グラフを用意しました。

See the Pen css-timing-demo by iricocco3 (@iricocco3) on CodePen.

f:id:iricocco:20210330164516p:plain
ease // cubic-bezier(.25,.1,.22,1)

easeは始まりは早く、終わりは遅いですね。

f:id:iricocco:20210330165310p:plain
ease-in // cubic-bezier(.42,0,1,1)

ease-inは始まりは遅く、終わりはゆっくり追い上げます。

f:id:iricocco:20210331095515p:plain
ease-out // cubic-bezier(0,0,.58,1)

ease-outゆっくり加速し、ゆっくり減速します。

f:id:iricocco:20210331095720p:plain
ease-in-out // cubic-bezier(.42,.01,.58,1)

ease-in-outease-inease-outの組み合わせです。

f:id:iricocco:20210330165537p:plain
linear // cubic-bezier(0,0,1,1)

linearは名前の通り直線的です。増加量が常に同じです。

上記のグラフは下記のベジェ曲線ジェネレーターのサイトで作成しました。 実際に手で動かしてシミュレートもできる優れものです。

cubic-bezier.com

anime.jsで実装できるイージング

CSS標準のもの以外にも、アニメーション系ライブラリを使用すればものが跳ねる動きやがたつく動きなど、色々なイージングを簡単に実現することができます。
次は、anime.jsで実装できるイージングを一部紹介していきます。

Robert Penner's Easing Functions

よく使うEasingのサンプルをまとめた素敵なサイトがあります。 便利なチートシートもあるので紹介します。

easings.net

f:id:iricocco:20210331110915p:plain
easing cheet sheet

このように色々なイージングを一括でサンプル付きで確認することができます。 ここで出てくるグラフの細かい計算式を確認したい場合は、GitHubにコードが載っているのですぐに確認できます。

github.com

なお、anime.jsでは上記のイージングを元にイージングの設定を調節することが可能です。

animejs.com

spring

重さ/剛性/減衰/速度などを指定して、物質の跳ねる動きをシミュレートできます。

See the Pen VwPmEJX by iricocco3 (@iricocco3) on CodePen.

最後に

物質の動きを完璧にシミュレートしようとすると物理学・数学が絡んで難しい!という印象があるかもしれませんが、特にそれらに詳しくなくても イージングを簡単に実装できる糖衣構文やライブラリの機能、チートシート・ジェネレーターが用意されていましたね。
是非みなさんもお気軽にイージングを楽しんでみましょう!

Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp

コードレビュー会はじめました。

こんにちは。フロントエンドの仲本です。
3月からフロントエンドの一部チームでコードレビュー会をはじめました。
こちらの記事では、コードレビュー会に至った経緯、その内容、感想をこちらでご紹介したいと思います!

コードレビュー会の目的

最初入社したての頃は、基本的に全てのコードに対してレビューしていただいていたのですが、1人で業務を任せられるようになるとレビューをしていただく機会が徐々に減りました。

それにより以下のような不安要素を感じました。

  • コードを書き正しく動いたらそれが正になる。なのでそれ以外のベストプラクティスが思いつきづらい
  • 知見が広がりにくい

今回コードレビューを行うことで以下のようなメリットがあると思い提案いたしました。

  • より知見が広がる
  • レビューする側にとっても学習になる
  • 他人がみてくれるのでより正しい記述になりやすい

上記の不安要素とメリットを用いて提案を行なったら「来週からやろう!」となりました。良いと思ったことをすぐ行えるのはWizの良いところだと思います。

コードレビュー会の内容

開催内容

開催頻度:週1(基本的に週末。難しい場合はslackにて調整)

題材:自由(フロントエンドの領域であればなんでも可)

時間:30分

開催内容についての補足

題材について

今回題材を自由としたのは以下の理由があります。

  • 特に気にならないコードであっても他人が見ると発見がある

コードは読む側の視点ではなく書く側の視点になって書くことが多いと思います。

(引用元:O’Reilly Japan「リーダブルコード――より良いコードを書くためのシンプルで実践的なテクニック」https://www.oreilly.co.jp/books/9784873115658/”)

なので、自分の中で読みやすいと思っていても、他人からすれば読みづらいこともあると思うので、今回は自由にしました。

時間について

今回時間を設けた理由としては、複数人でコードをみた際に時間を特に設けず行うと何時間でもしゃべることができる可能性があったため、時間を設けました。

開催の様子

以下が開催の様子になります。

https://i.gyazo.com/12e78a836e3ed26d2fd85857b69cff13.png

上記画像のように一つの会に対して3~4人で行なっています。

少人数で行なっているので、発言がしやすいのでコードに対して意見をだしやすいです。

また普段のコードレビューだと、しっかりコードをみて「プラスになるように書かなきゃ」などどうしても敷居が高くなってしまいがちです。

ですが私たちが行なっているコードレビュー会では、雑談を含め、なんとなく気になったことをどんどん発言していくので、画像からは伝わりづらいですがかなりわいわいしています。なので敷居は低い状態で行えていると実感しています。

  • コメントを少し紹介

「全然関係ないですけど、みなさんコミットメッセージどうしてます?」

「なんか最近ttlとかの命名の省略話題になってますが、なんかルールとか決まってたりしますか?」

「data-target-tabcontentselectって単語つなげるときどうしている?」

などなど、コードをみてふと思ったことやコード関係なしのことでも話したりします。普段なかなか聞けるタイミングがなく聞けなかったことなどを話したりできるので、とても有意義な時間だったと思います。

コードレビュー会を行なっての感想

メリット

コードレビュー会を行うことで以下のようなメリットを感じました。

  • 他の人からの意見が聞けるので、よりリーダブルなコードを追求できる

  • レビューする/される側双方向に知見が増える

  • レビュー会が毎週あることで、他人に見られるという意識が高まりよりリーダブルなコードを意識して実装をすることができる

  • 毎回議事録をとっているのでこの知見がたまっていくと、自社のコーディングルール策定ができる可能性ができた

課題

  • 時間が短い

  • 時間が確保しづらい

課題についての補足

時間が短い

コードによっては、議論がもちろん長引きます。ただ30分で強制的に終わるようにしているので、明確な答えが出せずに終わってしまいます。

そもそもコードによっては、明確な答えというものはないというのもあると思いますが、議論に対する納得のいく区切りをつけるのが難しいという解釈をしていただけますと幸いです。

時間が確保しづらい

どうしても案件の都合により時間が確保しづらい時があります。そういった時に無理くり時間を確保してやるのがベストなのか、リスケするべきなのかというベストな判断が難しかったので、ここは意見をもらいつつベストな手段を見つけていきたいです。

おわり

以上がコードレビュー会の紹介でした!

現在は一部チームでしか開催していませんが、周りのチームにもこの開催をもとになにかしらの影響を与えられると良いなと思っています。

外部に公開しても良さそうなコードが題材であれば、外部公開して社員以外の方も参加できる形にするのも楽しそうですね!

Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp

データベースの正規化(第1〜第3正規形)

こんにちは!バックエンドエンジニアの小室です。

先日、4月から入社予定の方に向け「データベース設計」について研修を行いました。

その中でもメイントピックであった「正規化」について改めてまとめてみました。

さっそくですが、データベースにおける正規化とは、
データベースで保持するデータの冗長性を排除し、
一貫性と効率性を確保するためのデータ形式へ変換することを指します。

一般的に第3正規形までで十分とされているため第3正規形までを取り上げます。

第1正規形

テーブルの行と列が交わる1つマスを「セル」と呼ぶことにします。

第1正規形の定義は「1つのセルには1つの値しか含まれない」です。

f:id:wiz012:20210331171106j:plain
社員テーブル

このように1つのセルに1つの値が含まれているとき、この値を「スカラ値」と言います。

f:id:wiz012:20210331171522j:plain
社員テーブル(非正規形)

上のようなテーブルがあった場合、1人の社員は複数の子を養っているので、このように表現したくなりますが、

リレーショナルデータベースでは規則違反になります。

以下のように第1正規形に変換する必要があります。

f:id:wiz012:20210331171900j:plain
社員テーブル(第1正規形)

子の数だけ行を増やしました。これで全てのセルがスカラ値となり、第1正規形となりました。

しかし、以下の問題が発生。

1. 主キーが決められない。

  • この場合1レコードを特定するには「社員ID、社員名、子」の3列を
    指定する必要があるが、主キーの定義上、その一部がNULLを保持してはならない。

1. テーブルの意味やレコードの単位がすぐに分からない。

  • このテーブルは「社員、扶養者」という2つのエンティティを含んでしまっている。

下記のように分割することで、この2つの問題を解決できます。

f:id:wiz012:20210331174252j:plain
社員・子テーブル(第1正規形)

「扶養者」テーブルに子を持っている社員レコードのみに限定でき、
かつ主キーがNULLになることを防げています。

なぜセルにはスカラ値でないとダメなのか?

セルに複数の値を入れてしまえば、主キーが各列の値を一意に特定できないからです。
ここで1つ大事な概念があります。「関数従属性」です。

関数「 y = f(x)」のように、入力(x)に対し出力(y)が一意に決まります。
この関係を「yはxに従属する」と表現し、「{x} → {y}」と表記したりします。

上の「社員テーブル(非正規形)」を見てみると、
「{社員ID} → {子}」という関数従属は不成立になります。

一方「社員テーブル(第1正規形)」は、

{社員ID} → {社員名}

{社員ID} → {子1}

{社員ID} → {子2}

という関数従属が成立しています。

第2正規形

f:id:wiz012:20210331174929j:plain
社員・会社テーブル(第1正規形)

このテーブルは全てスカラ値からなるため、第1正規形であると言えます。

しかし第2正規形ではありません。
このテーブルの主キーは「会社コード、社員ID」です。

したがって、主キー以外の他の列はこの主キーに従属するのに対し、
「会社名」だけは「会社コード」のみに従属しています。

このように主キーの一部に対して従属する列がある場合、
この関係を「部分関数従属」と言います。

これに対し主キーを構成する全ての列に従属性がある場合を「完全関数従属性」と言います。

第2正規形の定義は、「部分関数従属を排除し、完全関数従属にする」です。

f:id:wiz012:20210331175605j:plain
社員と会社が分割されたテーブル(第2正規形)

部分関数従属の関係にあったキー列と、従属列を独立させました。
これにより社員テーブルにおける全ての列が主キーに対して、完全関数従属関係となりました。

第2正規形でないと何がダメか?

「社員・会社テーブル(第1正規形)」に対し、社員情報が不明の会社を登録したくなった場合、
主キーの一部に社員IDが含まれているため、社員IDがNULLになり登録ができません。

しかし「社員と会社が分割されたテーブル(第2正規形)」では、
会社テーブルのみに登録する事で、社員情報が不明な会社も登録できます。

このように見ると第2正規形はエンティティ(実体)をテーブルごとに分割する作業
という見方もできますね。

第3正規形

f:id:wiz012:20210331175605j:plain
社員と会社が分割されたテーブル(第2正規形)

「部署コード」と「部署名」に注目してみます。
会社コードが「C0001」のA商事に関してテーブルからわかることは、
「営業、開発、人事」の3つの部署があるということです。

しかし実際には「広報」という部署も存在するかもしれません。

しかし「社員テーブル」には社員情報がないと登録できないため、
広報に社員が0人ならば広報部署は登録することができません。
理由は主キーがNULLになってしまうためです。

このような不都合が発生するのは、まだ隠れた従属が残っているからです。
注意深く「社員テーブル」を見てみると、

{部署コード} → {部署}

という従属関係が見えます。

{会社コード、社員ID} → {部署コード}

という従属関係も明らかです。つまりは、

{会社コード、社員ID} → {部署コード} → {部署}

ということが言えます。このように段階的な従属関係を、
「推移的関数従属」と言います。

下記のように社員テーブルと会社テーブルと部署テーブルに分割しました。

f:id:wiz012:20210331181223j:plain
社員と会社と部署を分割したテーブル(第3正規形)

これにより、非キー列は主キー列に対してのみ従属するようになり推移的関数従属もなくなりました。
これにより先ほど問題であった、社員情報なしに部署を登録することが可能となりました。

第3正規形の定義とは、
「第2正規形のテーブルから、推移的関数従属している列が切り出されたもの」です。

正規化の功罪

正規化のメリットとしては、
データを一元管理し、データの整合性を保ちやすくなります。

その反面、正規化によりテーブルの数が増える事で、結合操作がより必要になり
検索SQLのパフォーマンスが劣化すると言ったデメリットがあります。

検索SQLと正規化のパフォーマンス関係はトレードオフであり、
第1正規化、第2、第3 ... と正規化が進むにつれて、
検索パフォーマンスは低く、データの整合性は高くなります。

結論としては、正規化された方が良く、非正規化は最後の手段として考え、
他の手法によってパフォーマンスが向上されないか検討し切羽詰まった時にとる最終手段と言われています。

最後に〜

 Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp

第3回LT会を行いました。

第3回LT会レポ

今回のLT会の内容は

発表者: 3名

制限時間: 自由

テーマ: 自由

コメントツール:CommentScreen

で行いました。

それでは1つずつ発表を紹介していきます。

「経験学習」について考える

「経験学習」について考える
「経験学習」について考える
1人目の方には「経験学習」について考える。という内容を発表していただきました!
人の成長は「経験」によるものが大きいことを7:2:1の法則から理解し、経験するだけで成長するのではなく

  1. 経験
  2. 内省:経験から学んだことの振り返り
  3. 概念化:内省から学んだことを抽象化・概念化
  4. 実践

上記項目をサイクルとして回すことで成長できることを紹介していただきました。1人では振り返りの限界があるので、積極的に上司からフィードバックをいただき成長速度を上げていきたいですね! 

PlantUMLを使おう 〜UMLは友達〜

PlantUMLを使おう 〜UMLは友達〜
PlantUMLを使おう 〜UMLは友達〜
2人目の方にはPlantUMLについて発表していただきました!
PlantUMLとはオープンソースUMLダイアグラム作成用の、テキストベースの言語でございます。

  • 特徴と良い点

  • 導入・使用方法

  • 活用例

といった内容を発表していただきました。PlantUMLはNotion Web Clipperと組み合わせてギャラリーとして残すこともできるので、使ってみたいですね。

情報セキュリティに関して

情報セキュリティに関して
情報セキュリティに関して
3人目の方には情報セキュリティについて発表していただきました!
情報セキュリティの基礎や、脆弱性種類の紹介からそれぞれの対策などを紹介していただきました。実務を行なっていると、セキュリティについて時間を使って学習する時間があまり確保できないので定期的にこういった内容の勉強会など行なっていきたいですね。

おわり

第3回はこのような内容でした。

この記事ではざっくりとした内容しか紹介できませんでしたが、

LT会の内容をもっと社外へ公開できるよう目指していきたいです。

Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp  

Next.jsのCSR(SPA),SSR,SSG,ISRのまとめ&メリットデメリットについて

はじめまして。最近無性に海外旅行に行きたいと思っているフロントエンドエンジニアの内田です。

Next.jsってページごとに色々なレンダリング方法を柔軟に切り替える事が出来て便利ですよね。

ですが、利用する際にはきちんとそれらレンダリング方法のメリットとデメリットを理解する必要があるのでここにまとめておきたいと思います。

ではいってみましょー!!

f:id:romeoromen:20210325084701p:plain

CSR(Client Side Rendering)

そのままなんですが、クライアントサイドでレンダリングするフロントエンド技術手法のアーキテクチャのことを指します。

ブラウザからHTTPリクエストされると、サーバー側はビルドされたJSとCSS、中身ほぼ空っぽなHTMLファイルをHTTPレスポンスとして返却します。

初回アクセス時はHTMLファイルの中身はほぼ空なので何も表示されず、その後初期データを取得してブラウザがHTMLをレンダリングします。

f:id:romeoromen:20210325085054p:plain

メリット

  • ページ遷移によるユーザーストレス軽減。一度読み込んだページはその後必要な部分のコンテンツのみを描画させるため画面全体が再描画されるストレスがなくなる。 よってユーザには優れたUI/UXを提供出来る。

デメリット

  • 初回アクセス時にWebサイトのデータをまとめて読み込むので、初回の表示まで時間がかかる。
  • クライアントサイドのJavaScriptの処理が増えるので、CPUやメモリーが少ないスマホなどのデバイスでは、操作性が損なわれる可能性がある。
  • SEOで不利かもしれない。(初回アクセス時にサーバーから返されるHTMLがほぼ空のファイルなのでクローラーがコンテンツを認識できない場合がある)
  • 動的なOGPの設定ができない。OGPの設定はサーバーから返されるHTMLのhead内に情報がないと読み込めない為、動的にページごとにOGPを分ける対応が難しい。
  • 直帰率が高いサービスにCSRを採用してもあまりメリットはない。

適したサービス

ユーザーが頻繁にページ遷移やコンテンツの操作するような滞在時間の長いサービスに適している。 SEOをそこまで意識しなくてもいいなにかしらのサービスの管理画面などに利用するのがいいかもしれない。

SSR(Server Side Rendering)

こちらもそのままなのですが、ブラウザではなくサーバサイドでレンダリングして、クライアントサイドで描画(ペインティング)する手法です。

初回アクセス時はクライアント側からHTTPリクエストが送られて、APIからデータを取得します。

その後、サーバ内で動的にHTMLファイルを生成(Node.jsが実行される)しHTTPレスポンスとしてレンダリング済のHTMLを返します。

f:id:romeoromen:20210325085129p:plain

nextjs.org

メリット

  • 初回アクセス時はクライアント側でAPIを叩かないので描画が早い。
  • SEOに強い。
  • ユーザーの通信環境に左右されにくい。(サーバサイドでレンダリングするので、CPUやメモリーが少ないスマホなどのデバイスでも操作性に影響が少ない。)

デメリット

  • SSRするためのNode.jsを実行出来るWebサーバーが必要になる。
  • サーバ側の負荷が高い。(サーバのCPU負荷が増える。)
  • 上2つの結果、ホスティングサーバの課金がつらくなる。
  • 秘匿情報が含まれたページがCDNにキャッシュされた場合、個人情報の漏洩などに繋がる。(Aさんの情報がBさんにも見えちゃう場合が!!)その為、色々な事を考慮するとキャッシュ設定が面倒。

Web版メルカリの個人情報流出

適したサービス

コンテンツ更新頻度の高いサービスなど。動画投稿サービスやSNSサービスが例としてあげられる。 比較的に大規模向け。 サーバサイドの知見がある人がチーム内にいればフロントエンドとしては安心出来るかもしれない。

■SSG(Static Site Generator)

静的サイトジェネレーターの事です。 アプリケーションのビルド時に、APIなどからデータを取得し、HTMLを最初に生成(プリレンダリング)し、 サーバーへのリクエストがあった場合には、この生成されたHTMLファイルを返却します。 また、生成された各HTMLはそのページに必要最小限なJavaScriptコードと関連づけられ、ブラウザによってページが読み込まれると そのページに必要なJavaScriptコードが実行されます。

f:id:romeoromen:20210325085253p:plain

nextjs.org

メリット

  • SSRよりもレスポンスが高速。
  • SEOに強い。
  • 動的なOGP対応が可能。
  • キャッシュを気にしなくていい。
  • なんか全体的にいい感じにやってくれる。
  • VercelがSSGを推してる。

デメリット

  • ビルド以降、データが更新されてもページに反映されない。
  • ページの数やコンテンツの数が多くなるとビルド時間が長くなる。
  • 頻繁にデータ更新があるサイトには向かない。

適したサービス

更新頻度の少ないブログやコーポレートサイト等。 比較的に中規模向け。

■ISR(Incremental Static Regeneration)

ISRはインクリメンタル静的再生成という手法を指します。

基本的にはSSGの挙動と同じなのですが、クライアント側のリクエストに対しビルド時に生成された静的ページを返し、 尚且、バックグラウンドで一定期間ごとに静的ページの再生成をサーバー側で行うといったものです。

nextjs.org

具体例

export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    revalidate: 20, // In seconds
  }
}

export default Blog

SSGで利用する際の非同期関数であるgetStaticPropsからreturnするオブジェクトの中で、revalidate指定を一行追加するだけです。 ここでは例として20秒を指定しました。

初回アクセス(CDNにキャッシュが存在していない状態)から20秒経過後、クライアント側からリクエストがあったら クライアントには既に生成されたページを見せつつ(CDNキャッシュ)、バックグラウンドでデータの再取得及び再レンダリングするページを再生成し、 次のリクエストに対しては再生成されたページを返します。

メリット

  • SSGのBuild時間を短縮できる。
  • SSRと比較したらDB負荷は軽め。

デメリット

  • 現段階ではVercel依存。脱Vercelとなった場合に困る。

適したサービス

常に最新の情報である必要性はないが適度な更新頻度のあるサービス。


■まとめ

Next.jsではページごとにご紹介したレンダリング方法をよしなに選択する事が出来るのでそのページに合った最適なWebパフォーマンスをユーザへ届ける事が出来る事でしょう!! 是非この機会にNext.jsに触れてみてはいかがでしょうか。

■感想

Next.jsは導入するサービスのビジネスモデルを理解してレンダリング選定を間違えなければ高いパフォーマンスを発揮するし便利だよってことー。

参考記事:

Next.js の zero-config の恩恵を受けて SPA を作る

Next.jsのプリレンダリング方式についてまとめてみた - Qiita

ブラウザレンダリングを理解するため簡単にまとめてみた - Qiita

最後に

Wizではエンジニアを募集中です!

興味のある方、ぜひご覧下さい。

careers.012grp.co.jp