🐰

大規模チームの中でフロントエンドを立ち上げて2ヶ月経ったのでまとめる

2021/03/07に公開3

とある大規模開発プロジェクトの中で WebView 用のフロントエンドシステム開発を立ち上げて2ヶ月経ちました。Android, iOS専任のエンジニアがいないため、外部協力者の指導のもと、モバイルアプリの画面を WebView で作るためです。

ある程度その営みについて見えてきたものがあるので記事にまとめることにしました。

  • プロジェクト参加人数は30名以上
  • プロジェクト自体は4ヶ月前から動いてる
  • このプロジェクトへのフロントエンドチームの参加は1月から
  • 現在 WebView とモバイル・バックエンドなどの結合試験をはじめている

背景

去年12月いまの会社にテックリードとして入社し、前述とは別のプロジェクトでフロントエンドチーム立ち上げを行っていました。同タイミングで、いまの会社に誘ってくれた飲み仲間もテックリード・チームリーダーとして入社しています。フロントエンドチームはこの2名がプロパーとしてメインで引っ張ってる形で、もう2人はモダンフロントエンド(ここではReact, Vue, Angularである程度の規模のウェブアプリケーションという意味合い)をガシガシ開発してきたようなタイプではありません。この時点でフロントエンドチームは合計4名。

入社時点でモバイル関連の人材が社内にいないため、どのプロジェクトをやるにしてもモバイルに関わらずにはいられず、12月の間はそのプロジェクトをやりながら、モバイルに関して試行錯誤してた、かつ初期支給のマシンが Windows だったため要らない苦労をしていたところでした。

1月から現在のプロジェクトに参加しました。

iOSを見られる人がいないため、1月中旬にiOS開発マシンという名目でMacBook Pro(M1)が支給されました。やはりフロントエンド開発マシンとしてはMacが圧倒的にやりやすいです。あとM1 Macは Intel Mac や Windows とは異次元なレベルで動作がキビキビしています。開発には Apple Silicon Mac を導入すべきです。TypeScriptやjestへのストレスが無くなります。

現プロジェクトでは、フロントエンドチーム4名以外も、フロントエンドのコードをいじることがあります。ファンクショナルチームではなく、フィーチャーチームとして開発をしていきたいという背景があります。

技術選択

この新プロジェクトの webview システムは、まだリポジトリも無い状態から始まりました。新しい技術の導入にとても理解のある職場なため、俺たちが考える最強のフロントエンドを作れる、むしろ積極的に作っていこうという合意があります。とても良い職場です。

  • ECMAScript2020 / TypeScript
  • React / React Hooks
  • Next.js
  • Tailwind CSS
  • eslint, prettier...
  • Node.js, npm, ...
  • jest
  • Cypress
  • React Hook Form + Yup

ECMAScript2020 TypeScript

言語仕様は ECMAScriptの最新である ECMAScript2020 です。現在までやってきて、ECMAScript の最新版を導入することは全く問題ありません。ここに技術的難易度はなく、メンバーへのフォローもしやすいです。積極的に stage-x の技術を取り入れても問題は無さそうです。

TypeScript の採用は現代において必須です。使わないという選択肢はもう存在しなくなりました。

ただ、静的型付け言語未体験の人には少しつらいかもしれません。この点においては現代のフロントエンド開発においては、昔からフロントエンドをやっている人よりも、バックエンドをガシガシやってきた人、ネイティブアプリをやってきた人などの方が圧倒的に有利だと言わざるを得ません。

そこで、そういった TypeScript つらい人へのフォローが課題となります。この層の人たち向けの TypeScript 教材を渇望しています。そもそもにして、型に関する日々の営みにピンと来ないようなので、型があると何が嬉しいのか、型がない状態で大規模ウェブアプリを開発するとどんな苦しみが待っているのか?を何かしら簡単に説明できればとは日々思っています。

現状ではアドホックに型について説明していっています。

React / React Hooks

React 自体を説明する必要はもうないでしょう。散々過去の記事でも書いてきているのでここでは割愛します。

自走できる人には React の公式ドキュメントを読んでいただくと良いのですが、ここで一つ問題があります。公式ドキュメントは、古い情報も並列で書かれていて、むしろバランス的には古い情報の方が多いかもしれません。最近気づいたのですが、公式自体が戸惑いポイントになってる気がしなくもないです。あと、どうしても Flow type を採用したい React 公式の立場では TypeScript に関する記述が出来ないため、ここも問題点となります。(というか、そもそも型の説明自体がほぼ無い)

つまり、現実的に使われている React + TypeScript という組み合わせが、公式ドキュメントにほぼ無いのです。

React Hooks は、技術としては絶対に採用すべきですが、公式ドキュメントが手厚くなく、Hooks に慣れてない人には、ここもピンと来にくいポイントのようです。特に useMemo, useCallback がなぜ必要なのか?第2引数が分かりづらいようです。ここを説明するためには、React の再描画という概念及び、クロージャの概念を教える必要があります。React だけの説明ならまだしも、クロージャの概念とかを教えないといけないためそこがちょっとしたハードルになります。

Next.js

Next.js のシェアは現在進行系で増大しています。Gatsby との棲み分けがあるので、大体現代の React 採用プロジェクトでは、Next.js か Gatsby を採用すれば、ほぼ問題ありません。

問題があるとすれば Next.js 固有のハマりどころがそれなりにあることでしょう。

それと Next.js も公式ドキュメントが TypeScript first ではないため、TypeScript の営みをしていく上では少し大変になることなどでしょう。

デキる人相手でも、Next.js に慣れてない人に対してはフォローが必須となります。

Tailwind CSS

現代の React プロジェクトでは、だいたいは Emotion か Styled Components が採用されていると思いますが、これらは共に Webpack の設定がクッソめんどくさい、div が増殖しまくる、パフォーマンス上の致命的な問題を抱えやすい、ファイルが多くなりがちなどの問題点が多いライブラリで、たいていアンガーを集めています。

Tailwind はそこらへんの問題をすべて吹き飛ばすことができるうえ、bootstrap, bulma に慣れている人には扱いやすいため、今回は Tailwind を採用しました。

ここで重要になるのが VSCode の拡張導入です。適切に Tailwind CSS intelliSense 拡張を駆使できれば書きやすいです。そもそも何を入力し始めればいいかわからん!困ったみたいな場合は「〇〇 tailwind」みたいにな検索ワードでググって都度覚えていく必要があります。

Tailwind CSS で気をつけた方がいいのは、設定を追加したつもりでオーバーライドしちゃった事故でしょうか。

WindiCSS:次世代 Tailwind CSS コンパイラ はめちゃくちゃ気になってますが、現時点ではちょっと導入実験してる暇が全くないため、次の機会で検証してみたいと思っています。

ちなみに CSS ガイドラインは最初に決めておく方が絶対にいいです。センタリングをするときはどうやるか?margin は top, left で取るのか?position や Flexbox の使い方などもです。

eslint, prettier...

eslintやprettierはしなくてもいいような指摘を防ぐために必須のものです。

特にこれらはVSCodeの拡張により、自動で、フォーマットや指摘されるようにしておくことが絶対に望ましいです。

Git の pre-commit hook に関しては、今回のようなチームであれば必須です。

Lint設定をどこまで強くするかはチームの性質によりますが、TypeScript で躓くタイプのひとが多い・バックグラウンドが違いすぎる人が多い、今回のようなチームであれば強めの設定にして、かつ粘り強いフォローが必要になります。

eslintやTypeScriptのエラーが取れない、ということで作業時間がかかりすぎるみたいなケースもあるようなので、粘り強くフォローをしていくことが大切となります。

Node.js, npm...

現代のフロントエンド開発ではNode.jsは必須です。問題は導入手順が割と面倒くさいことがあります。特に Windows が大体鬼門です。

当プロジェクトでは Node.js の LTS 最新で作っています。

パッケージマネージャは yarn ではなく npm を採用しました。個人的には yarn の方が好きですが、最近の npm は昔よりは遥かにまともなツールになっているため、最近は npm を採用しても問題がありません。

一つだけ注意点は、npm v7 から package-lock.json のバージョンが変わっているため、誰か一人でもプロジェクト参加者が npm を最新にした途端、package-lock.json の diff 祭りが発生します。そのため全員必ず npm i -g npm で npm を最新にしてもらう必要があります。

さすがにこのようなケースはあまり発生しませんが、定期的に npm を最新にする運用で良いような気はします。

jest

現代においてテストランナーはjest一択です。

最近、Reactのユニットテスト2021 という記事を書きました。

Cypress

Cypressはjestとは異なる観点で導入するものです。jestが重くならないようにするという意図もあります。

最近、Cypress で aria-label を使う という記事を書きました。

React Hook Form + Yup

正直ものすごく悩みました。ただ、以前よりも React Hook Form が使いやすくなってること、Yup 慣れしてしまったので、これらを採用。

バリデーションスキーマに Zod のような TypeScript フレンドリーなのがあるものの、あまりフォームという仕組みとマッチするとは思い難いので、今回はドキュメントが豊富な Yup にしています。Zod はマイナーっぽさが否めないです。

似たような理由で React Final Form も、ドキュメントが手厚くないので採用を見送りました。

今日、Reactでウェブフォームを作る2021 という記事にまとめています。

API連携

バックエンドの事情により swagger/OpenAPI v3 が採用されています。

そのため、OpenAPI genarator を使うことにしました。また実際のコード生成では、axios に無駄に依存したくなかった(サイズを増やしたくなかった)ので、typescript-fetch を採用しましたが、これが曲者です。

  • レスポンスデータのプロパティ全部に ? がついている(オプショナルな)ため、とても扱いづらい。API的には絶対あるはずの項目なのに、いちいちチェックを入れなければならない
  • API種別ごとにクラスがあって、細かいエンドポイントごとに、メソッドが生えるため、目的のクラスとメソッドを探し出すのが面倒くさい
  • 挙動が typescript-axios と全く別もの

最近、OpenAPI Generator typescript-fetch を使う という記事を書きました。

? を外すの、一番最悪の場合は生成されたコードをさらに加工するという頭の悪い手段はありますが、それは本当の最終手段としたい。

あまりにも OpenAPI generator のクォリティに難がありすぎるため、時間があれば pure js で書かれた、別のコード生成を試したかったところです。もしくはもういっそ自作した方が確実なのでは?とすら思っています。

ちなみに、swagger/OpenAPI 定義ファイルを更新したときや、インストール時に OpenAPI generator を起動させるために、ある特定のファイルが更新されたらコマンドを実行する という仕掛けを使っています。

CORS

サーバー側の設定次第という話でしかないですが、だいたいCORSが悪さするので気をつけないといけないです。

chrome devtools でなぜかエラーレスポンスが帰ってこないように見える

APIエラーのときに、Chrome Developer tools の network / response タブが空に見えるという現象がありました。

プロジェクト内では再現は取れるんですが、それ以外の最小限での再現が取れてないので記事にはしていません。

ちなみにオチとしてはエラーレスポンスに対して、fetch の response.json()response.text() を実行すれば、タブにも現れるというものでした。

教訓としては、エラーハンドリングは、最初のうちにやっておいた方がいいというものです。

WebView

WebView はとにかく色々と曲者です。

ちょっと前に、Android で WebView メモ という記事を書きました。

この当時気づかなかった問題として、alert confirm などは WebView では利用不可能なこと。ユーザーダイアログを出す手段がほしかったですが、どうやら自前でモーダルを実装するしかない(もしくはライブラリを使う)らしいです。まぁ、ネイティブ側でモーダルを出すメソッドを作成した上で、JS 側から叩けるようにするという手もあるでしょう(今回はそんなネイティブ開発リソースがないので、その手は使えませんでした。)

他にも WebView と、ブラウザでの挙動は細かいところが色々と異なるので、実機テストがどうしても必須となります。

設計

atomic design

Atomic Design を採用して、ある程度ありだとは思ったものの、しっくり来ていません。

see. Atomic Designから派生した、“オルタネイティヴ”な5つのデザインシステム - ログミーTech

ただ、指針として悪くない部分もあるので、噛み砕いて、設計論としてうまく考えていくべきだとは思います。ただまぁ、これらのどれが適切かなんて誰もわからないので、現時点では、残念ながら、自分たちにあったやり方をカスタムするしかないでしょう。

Context によるステート管理

今回ステート管理のライブラリは導入していません。Recoil は導入してもいいかもと思います。

とりあえずまだできてないですが、カスタムフックを駆使して、完全に具象から切り離した抽象化されたデータ管理レイヤーを設けるべきだったという反省点はあります。

ネーミングルール

今回、React 界隈の標準である、ファイル名やディレクトリ名にパスカルケース・キャメルケースを導入しましたが、これはとても苦痛と後悔を伴っています。

コンポーネントは何も考えずにパスカルケースを採用しておけばいいという基準にはなります。

では、そうではないモジュールはどうするべきか?大半の関数は小文字始まりになるが、その場合小文字で始まるディレクトリと大文字で始まるディレクトリが混在していまいます。

またNext.jsのpages以下はどうするのか?URLは普通キャメルケースは採用しません。

次は、ファル名・ディレクトリ名はケバブケースを採用したいと思っています。

これは React 界隈の大罪の一つなのでは。そもそも Mac も Windows もファイルシステムの都合で大文字・小文字が混在するのは望ましくないので、全部小文字にすべきです。

WAI-ARIA

今回完全には導入できてないですが、なるべく早めに導入した方が良さそうです。ユニットテスト・E2Eテストしやすくなるという利点もあります。

残っている課題

モダンウェブ開発は、登場する技術が多いため、メンバーへのフォローが結構たいへんです。毎日一定の時間をとって、少しずつフォローアップしていく必要があるかもしれません。

また、コロナのワクチンがまだ行き渡ってない現状ではリモートワークが必須であり、リモートワークは、やる人を選ぶところがあります。意図的に雑談をしないと会話が激減してメンタルケアが必要になります。技術一辺倒な僕としてはなかなか難しい課題の一つです(そもそも僕自身がメンヘラなんやで……)。

さて、そう言いながら、他にも技術的に導入したいものはあります。

Storybook

現状ではまだ不要だと思うのでいったんスキップしています。

画像回帰テスト

導入したいなーと思い始めているのですが、導入検証が面倒なのでいったんスキップしています。

VSCode番長

VSCode番長がいると良いなと思い始めています。VSCodeインストールしてね、だけだと驚くほどみんな躓きやすいのです。

  • 入れておくべき拡張
  • 設定すべき項目
  • 使うと良い使い方・操作方法

などを随時アップデートし、フロントエンドを触る人に指導できる人が最低は一人はほしいです(自分でもいいけどそれはさておく)。

JetBrains IDE よりはまだ、導入も使い方も簡単ですが、それでも拡張と設定で、開発効率が随分違います。その点をサポートしなければいけません。

使い方や操作方法の伝達は、ペアプロやモブプロである程度カバーできるはずですが、入れておくべき拡張や、設定すべき項目に関してはルール化した方が良さそうです。

今後は

JS関連技術の考察を続けてみる で、まとめて続けようと思っています。

Discussion

erukitierukiti

あー、なるほど。ありがとうございます。確かに。
.vscode は基本的には ignore するものという思い込みがあったかもしれません。

実際どのような挙動になるか、注意すべき点はないかなど、検証は必要そうですが、ありかもですね。検討してみます。ありがとうございます。

yuuxzyyuuxzy

? を外すの、一番最悪の場合は生成されたコードをさらに加工するという頭の悪い手段はありますが、
それは> 本当の最終手段としたい。

定義に required: trueがついていないから発生していると思います。
それとは別に required を付けれない問題があるのでしょうか