kqito's 技術ブログ

技術やプログラミングについて思ったことを呟くブログ

5日間のハッカソンに参加してWebフロントした話

はじめに

2020/5/2 ~ 2020/5/6の間、お家ハッカソンに参加してきました。

このブログをハッカソン直後に書いたのですが、公開するのを忘れて約1ヶ月後に気づくという...

概要

お家ハッカソンといっても特別なことはあまりなくて、「ゴールデンウィークの間フルリモートでハッカソンしよう!」的なイベントです。

お題は「お家で楽しめる何か」というものでした。

私のチームは4人の構成で、「zoomの背景を作成・共有するサービス」を作成する事にしました。 

自分のブログは、「サービスのコンセプト」を説明するものではなく、「技術的な感想」をまとめたいと思います。

技術選定

開発初日では

  • 何を作成するか
  • どういう機能を実装するか
  • 技術選定

などを行いました。

自分はWebフロントを担当したのでそこの技術選定を行いました。

チームメンバーはreact + typescriptという構成が好みらしく、私も偶然同じだったので技術選定はすぐ終わりました。

このハッカソンにおいてパフォーマンスはあまり重要視されない評価形式でしたが、webpackなどをよし何にしてくれるnext.jsを採用しました。

nextjs.org

あとは、背景画像をPCから保存させるためのライブラリが必要でした。背景画像はDOM上で描画されているので、

DOMをcanvasに描画 -> 保存

のようなフローを行うためにhtml2canvasというライブラリを採用しました

html2canvas.hertzen.com

というかnext.jsv9.4強いですね!(このときは9.4はリリースされてなかった...)

技術的な振り返り

ハッカソンあるあるですが、限られた時間の中で実装しているので「これってどうなん?」みたいな部分が多少あると思います(自分でソースを振り返ってもそう思う部分が多々...)。そこはご了承ください...

仕組みという表現が不適切な気もしますが、こんなことをメインで取り組みました。

Storeの仕組み作成

まず、コンセプトとしてはreduxのようなstateライブラリは採用する必要がなさそうという事を踏まえて、hooksだけでstoreの仕組みを作成したいという思いがありました。

実際に利用したのは

  • useContext
  • useDispatch

だけでした!useStateは使いどころが限られるので利用しませんでした...

ここでひと工夫加えた部分があります。

useDisptchは以下のように利用することができます。

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

このstate, dispatchですが、storeが肥大化するたびに多くなることが予想されます。

これらをcontextのプロパティにする際、以下のようにオブジェクトを加工するように工夫しました。

const useReducerToObject = <R, I>(
  reducer: Reducer<R, I>,
  initialState: ReducerState<Reducer<R, I>>
) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return {
    state,
    dispatch,
  };
};

const store1 = useReducerToObject(
  storeContext.reducer,
  storeContext.initialState
);

// store1 = {
//   state,
//   dispatch
// }

配列で操作するより、オブジェクトの方が必要となったプロパティのみを取得し易い(リーダブルになりそう)という理由からオブジェクトにしました。

これらを複数組み合わせたのがcontextです。

const context = useContext(Context)

// context = {
//   store1,
//   store2,
//   store3
// }

今振り返ると反省点はまぁまぁあります。contextにdispatchを含める必要があったのかなどですね...(多分ない)

あとは各reducerにおけるaction typeが悲惨なことになっている点です

enum ActionType {
  UPDATE = 'UPDATE',
}

// Templateはstoreの型
export type TemplateAction = {
  type: keyof typeof ActionType;
  payload?: { [P in keyof Template]?: Template[P] };
};

UPDATEしかないのは酷いですね。あきらかにreducerの利用方法間違っていますね。

ここは急いで実装していたという言い訳を免罪符にしたいと思います...

next.jsにおけるdotenvの仕組み作成

next.jsはnext.config.jsにwebpackなりenvの設定なりいろいろ切り替えることができるので

const env = require('./env.js');

module.exports = {
  env: env || {},
};

こんな感じでenv.jsをプロジェクトルートに配置させる感じで実装。env.jsファイルが無ければ自然とrequireでエラーが掃き出されるのでそれでいいかなと思いエラーハンドリング等々は注力してませんでしたね...(手抜き)

eslint, prettier, lint-staged仕組み導入(airbnb採用)

ここは特別なことはしてないとは思います。

ただ、import構文のパスを絶対パス にした場合、プロジェクトルートから解決できるようにしているのでそこら辺をwebpack.resolve.alias + eslint + tsconfigで設定を加えています。

結果論ですが、絶対パスでimportできるのは非常に../地獄になることを避けることが出来るので、ハッカソンとは言え、導入してよかったなと思います。

componentの構成を考慮・作成

今回の実装では1つのコンポーネントファイルに実装を全て記述するのではなく、関心に分けてファイルを分割するようにしました。

Hogeというステートを持ったコンポーネントを作成する場合は以下のようにしました

  • index.ts
  • Hoge.tsx
  • HogeHooks.ts
  • Hoge.module.css

これらのように記述を分割することでリーダブルにすることができたのではと思っています。

しかし、ハッカソンという短時間において記述量・ファイル量が増えてしまったという点に関しては反省すべきだと感じています。

まとめ

技術的な面で振り返るとこんな感じでした。

今回のハッカソンは自分の同期達と参加したのですが、みんな技術レベルが高く、コミュニケーションを柔軟にとることができたなと思います。

特に、技術的な相談や認知のすり合わせを重点的に行えた点がよかったと思います。

また次回、ハッカソンに参加する際にはコミュニケーション取りつつ、開発して行きたいです!