とろろこんぶろぐ

かけだしR&Dフロントエンジニアの小言

Zod のスキーマが使えるAPIクライアントZodios を紹介したい

概要

TypeScriptでフロントエンド開発をしているとバックエンドのAPIを呼び出す際に、APIのパラメータ、レスポンスの型付けをしたくなります。

僕は最近この型付けにZodを使い、APIクライアントにはZodiosというライブラリを使っています。

github.com

この記事では、Zodios でZodのスキーマ定義から型安全なAPIクライアントを作る方法を紹介します。

他のやり方

Types定義

純粋にパラメータとレスポンスの type を Type Alias で定義し、fetch やaxios などの素のAPIクライアントに型付けするやり方です。 APIのパスと、パラメータ、レスポンスの紐付けを人間が管理することになるため、ミスを防ぐことができません。

aspida

この課題のためにaspidaがよく使われている印象があります。

github.com

Open APIからaspidaのAPIクライアントのコードが書かれているファイルを自動生成できるので、バックエンド側でOpen APIがすでに用意されているプロジェクトではとても有用だと思います。

自動生成されたファイルから適切なクライアントをimport することで、パス、クエリ、レスポンスといった型補完を効かせながらクライアントを記述できます。

またOpen APIがなくても、あらかじめ決められた型の形式でAPIの型定義を書くことで、その型定義を元にAPIクライアントが記載されたファイルを自動生成してくれます。

課題に対して効果的な解決策になります。

ただ、(好みの問題ではありますが)書き味や使い方に少し癖があります。 APIクライアントのメソッド名は自動的に作られたものを利用することになります。 ファイルを自動生成させるため、npm scriptにaspidaのビルドスクリプトを追加して、watch しておく必要があります。

gRPC, tRPC

バックエンド側の開発に制約がなければgRPCや最近流行りのtRPCを利用することで、バックエンド側で記載されるAPI定義をそのままフロントエンドを型として利用する方法もあります。

ちなみにZodiosでもzodios-expressを利用すれば、tRPC的に開発することもできます。

GraphQL

同様にバックエンド側で開発可能であればGraphQLを使う方法もあります。Schemaを定義することで、バックエンドとフロントエンドで共通認識を持つことができるだけでなく、フロントエンドではAPIクライアントまで自動生成可能です。 Relayであれば公式にrelay-compilerが用意されているため、公式が型補完までサポートしてくれています。

ZodとZodios

Zod

zod.dev

Zodは主にバリデーションのために使われるスキーマ定義ライブラリです。

バリデーション文脈で言えば、yupもよく使われています。 またNext.jsは最近v12.3ajvを公式にサポートしています。

Zodは、近年バリデーションだけで使われることを飛び越えて、スキーマ定義を活用したライブラリが数多く開発されてきており、エコシステムか発展しつつあります。

Zodios

github.com

ZodiosはZodのスキーマ定義を活用したAPIクライアントです。 あまりメジャーではありませんが、zodのスキーマ定義をベースにAPIクライアントを動的に生成するものとしては十分なライブラリです。

ただ名前からもわかる通りaxios のインターフェースをベースにしているのですか、最近 axios を使いたいモチベーションが低いので、名前とaxiosが依存に入るのが微妙だなと思っています。

実際のAPI呼び出しのライブラリにはaxiosを利用せず、プラグイン的にfetchを使うことができます。 ただしfetchを利用する場合でもaxiosのI/Fをベースに記述されているためaxiosの依存は必要です。

Zodiosを使う

まずはZodでスキーマ定義をします。 API のレスポンスが以下のような形式だったとします。

type User = {
  id: number;
  name: string;
  email: string;
};

Zodのスキーマ定義は以下のようにします。

const userResponse = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string(),
});

以下のように TypeScript の型を手に入れることができます。

export type User = z.infer<typeof userResponse>;

Zodのよいところはスキーマ定義を行うことで、単純なTypeScriptの型では表現できない制約を正しく表現できることが利点です。 そのため、以下のように書くことで得られる Type は同じですが、ドキュメンテーション的な役割を担うことができます。 

const userResponse = z.object({
  id: z.number().int(),
  name: z.string().min(1).max(1024),
  email: z.string().email();
});

スキーマ定義を元にZodiosのAPI定義を作成します。

export const userApi = zodios
  .apiBuilder({
    method: "get",
    path: "/user/:id",
    alias: "getUser",
    description: "Get a user",
    response: userResponse,
  })
  .addEndpoint({
    method: "post",
    path: "/user/:id",
    alias: "postUser",
    parameters: [
      {
        name: "body",
        description: "User Info",
        type: "Body",
        schema: z.object({
          name: z.string().min(1).max(1024),
          email: z.string().email(),
        }),
      },
    ],
    description: "Create a user",
    response: userResponse,
  })
  .build();

apiBuilderaddEndpointでエンドポイントを定義の生成、追加しています。 エンドポイントごとにaliasで自分の好きなメソッド名を定義できます。 リクエストするクエリやボディに対しても、zodのスキーマ定義を利用できるだけでなく、descriptionを記載しておくことができます。 API定義をimportしてZodiosのクラスを作成します。

const apiClient = new Zodios(API_ENDPOINT, [...userApi]);

実際に利用する際にはalias で書いたメソッド名を利用し、パスを記述する必要はなく、クエリ、レスポンスは型補完が効きます。

const data = await apiClient.getUser({ params: { id: 1 } });

レスポンスの型補完が効いている

さらにZodのスキーマ定義を使ってリクエストやレスポンスに対してバリデーションが効きます。 開発段階では、スキーマ定義通りじゃないリクエストやレスポンスにZodiosのエラーで気づくことができるので有用です。

Zodのバリデーションエラーをthrowできる

Zodios から Open APIをアウトプットすることで、ドキュメントとしても活用できる...と思いましたが、 現時点で適切なライブラリが存在しないようです(残念)。

github.com (空のレポジトリ)

Zod から Open API 自体は可能なので、工夫すればできるかもしれません(やってない)。

www.npmjs.com

この記事のコードサンプルが書かれているレポジトリはこちらです。

github.com

余談: MSW

フロントエンド開発では言わずと知れたモックツールです。 mswのハンドラにはレスポンスを直接型付けできます。 ただこれでは、パスとレスポンスの型は人間が正しく付けてあげる必要があるので、ミスを防げません。 そこでzodios の型を流用してパスとレスポンスの型付けを行うことができます。

export function restGet<Path extends Paths<Api, "get">>(
  path: Path,
  resolver: ResponseResolver<
    RestRequest,
    RestContext,
    Awaited<Response<Api, "get", Path>>
  >
) {
  return rest.get(`${API_ENDPOINT}${path}`, resolver);
}

これにより、パスが補完され、パスに応じたレスポンスの型に補完が効くようになります。

  restGet("/user/:id", (req, res, ctx) => {
    const id = req.params.id;
    const data = users.find((u) => u.id === Number(id));

    if (!data) {
      return res(ctx.status(404));
    }

    return res(ctx.status(200), ctx.json(data));
  }),

takepepeさんの記事を参考にしました。

zenn.dev

またzodiosの型がとてもよく作られているので、そこまで複雑な型パズルをすることなく、型補完が適用できました。

まとめ

ZodとZodiosを利用したAPIクライアントへの型付けについて書きました。 MSWに型をつける方法もついでに紹介しました。

Zodiosは特にファイルを自動生成することなく、割と書き心地よくAPIクライアントを型補完した状態で書き進められるライブラリです。

あまりメジャーではないのですが、僕はだいぶ使い心地が気に入ったので、多くのひとに使ってもらえたらいいなと思います。

GraphQL / TypeScript 参考リンク一覧

概要

GraphQL の理解を進める上で参考になったものを載せます。

React.js, TypeScript, Relay が使われるプロダクトを想定しています。

コンセプト

  • GraphQL 公式

GraphQL とは、を正確に知るための公式ドキュメントです。

graphql.org

  • GraphQLとクライアントサイドの実装指針

GraphQL とはどういうものかが読みやすくまとめられています。

GraphQLとクライアントサイドの実装指針.md · GitHub

  • GraphQL との向き合い方 2022 年版

2022 年現在における GraphQL の特徴がスライドでわかりやすくまとめられています。

speakerdeck.com

  • Render as you fetch incremental GraphQL fragments

Fragment Colocation のコンセプトがわかりやすく記載されています。 @defer のことまで語られていて先進的な記事です。

quramy.medium.com

  • React 時代に選ぶ GraphQL

React と GraphQL を重ねたときのコンセプトです(手前味噌)。

oisham.hatenablog.com

  • Relay React Conf 2021

Relay のコンセプトが動画で紹介されているのでわかりやすいです。 GraphQL というより Relay のコンセプトを学べます。

youtu.be

  • Render as you fetch

React 公式の Render as you fetch についての内容が分かります。 GraphQL は関係ありませんが、React で GraphQL を使う上では理解しておくと良さそうです。

17.reactjs.org

  • Relay.dev

Relay の公式ページです。 Relay で開発しない場合でも GraphQL を理解する上で参考になる内容が多分に含まれています。

relay.dev

Apollo の公式ページです。 こちらも Apollo を使う使わないに関わらず GraphQL を理解する上で参考になる内容が多分に含まれています。

www.apollographql.com

  • GraphQL Spec Draft

難しくてあんまり読んでませんが、重要なので置いておきます。

spec.graphql.org

サーバ / Schema 仕様

GraphQL のサーバや Schema 設計は難しいので、これらの内容を参考にして検討を進めています。

  • Relay サーバ仕様

relay.dev

github.com

techbookfest.org

プラグイン

GraphQL の Plugin である Envelop と利用 Plugin 例です。

  • Envelop

www.envelop.dev

N+1 解消の DataLoader の GitHub です。 紹介記事もたまに見ますが、コード量が多くないので直接読んだ方がはやいです。

github.com

Persisted Query

  • GraphQLとPersisted Query

Persisted Query について概要を学べます。

qiita.com

  • Relay Persisted Query

Relay で行う方法です。デフォルトの機能として紹介されているので、Apollo に比べて簡易だと思います。

Apollo はそもそも Persisted Query の概念が少し異なっているというのもあります。

Persisted Queries | Relay

エラー管理

エラーハンドリングはまだどういう形式が適切か判断できていないので、参考程度に読んでいるものたち。

  • エラーハンドリング

blog.logrocket.com

  • エラー型をユニオンに

relay.dev

www.apollographql.com

  • ZOZO Error handling

techblog.zozo.com

  • GraphQLにおけるエラーハンドリングの実践

techblog.gaudiy.com

ブログ記事

  • The Guild Blog

Guild の記事は参考になるものが多いので読んでおくと良いです。

www.the-guild.dev

  • GraphQLを導入する時に考えておいたほうが良いこと

メルカリ shops で GraphQL 利用時に関する記事です。 こちらも一度目を通しておくと良いです。

engineering.mercari.com

サンプル Schema

サンプルとして利用できる Schema です。

Pokemon は簡単なので GraphQL 理解の導入に良いサンプルです。

一方 GitHub はかなり複雑ですが、しっかり作り込まれているので参考になります。

Star Wars もあるようですが、僕は見ていません。

  • Pokemon GraphQL

github.com

  • Pokemon GraphQL Playground

https://graphql-pokemon2.vercel.app/

docs.github.com

サンプルコード

GraphQL Relay のサンプルコードは多くありません。

  • Relay の Examples

github.com

  • Next.js x Relay のサンプル

github.com

その他

blog.logrocket.com

  • Fragment で変数を扱えない

qiita.com

zenn.dev

まとめ

GraphQL / TypeScript / Relay で開発する際に参考になるリンク集をまとめています。

随時更新予定です(きっとやらない)。

他に参考になる記事などありましたら教えていただけると嬉しいです。

広告

男性育休の問題の本質

概要

男性育休を取りづらいという話になるとき、おおよそ「他に取った人がいないから」とか「現場が取れる雰囲気じゃないから」とか、フワッとした理由だけ述べられるケースが散見されます。男性が育児に参加してこなかったから理解がないだけ、という話に片づけられがちです。

その問題の本質に向き合わずに「男性育休を必須化しよう」という話が出ているので、現場から見ると現実と乖離しているように感じるのだと思います。

男性育休が取りづらい問題の背景には、もちろん男性が育児に参加してこなかったことも関係しているとは思いますが、僕は育児どうこうよりも問題視していることがあるので、それを書きたいと思います。

問題の本質は何なのか

たいていの仕事は一人ではできません。組織やチームを作り大きな仕事を分担して進めています。

例えば大規模なシステム開発を例に挙げます。まずは計画を立てます。納期と照らし合わせて何人月の工数でいつまでに作り上げるか計画を練ります。

この時、納期は絶対です。

納期に遅れることは取引先の信用を失うため許されません。納期を守るために緻密な計画を立てます。どの仕事を誰がどのくらいの期間で行うか、カレンダーに線を引いて決めます。

一方、売上の見込みに対してできるだけ多くの利益を生み出すために、予算を切りつめて工数を絞ります。開発期間に対して余剰工数を生み出さないように、効率的な人員配置とスケジュールが組まれます。

そうして出来上がった緻密な工数計画表は、納期までの期間に最低限必要な工数で、きっちり組み上げられるわけです。

これは必然的に個人に仕事が依存してしまい、より休みづらいことになります。

そんな中現場でPMに「育休で1年間休みたいのですが…」と言えば「いや、もう君の工数ありきで計画を組んでしまってるから困る」という返事が返ってくることでしょう。

この納期と工数の緻密さが、育休を取りづらいことに少なからず関係しているのではないかと思います。

これは育休に限った話ではありません。長期休暇だけでなく、単なる有給ですら取りづらい雰囲気なのは、この真面目で計画的な仕事の進め方を好む企業体質、ひいては日本人の国民性によるところが大きいと思います。

じゃあどうすればいいのか

納期を緩めて遅れることを許容すべきだと思っています。 納期が絶対なら工数に余剰を設けましょう。 あるタイミングで一人長期休暇したり転職したりしても問題なく仕事が回る程度に余剰を作るべきなのです。 一時的な利益を減らしてでも工数に余剰を作ることで、結果的に心理的安全性の高いチームとして生産性が高まり将来的なメリットが生まれるはずです。

本質的に評価されるべきなのは高い品質で開発できる生産性であるべきです。

まとめ

育児休業自体は確かに子供が産まれた親にしか関係しませんが、育休の背景にあるのは工数と納期計画が休みづらさに関わっています。育休を推進することは、工数計画に余裕を生みやすみやすい職場に繋がる可能性もあります。

育児休業を推進しましょう〜!

Web エンジニアが育休をもらった話

概要

1ヶ月間育児休業(無給)を取得しました。

育児休業を取るに至った経緯や取り方、また育児休職期間どのような生活を送っていたか記事にして残しておこうと思います。

本投稿に技術的な内容はありません。

また家庭や子供による部分が大きく個人差がありまくるので、参考程度に読んでください。

育児休業とは

育児休業とは、子を養育する労働者が法律に基づいて取得できる休業のことである。育休とも称される。女子に対するあらゆる形態の差別の撤廃に関する条約の第11条は育児休業の取得による解雇と差別を禁止している。

育児休業 - Wikipedia

産前産後休暇は女性に限った休暇ですが、育児休業は男性でも取得できます。 休職扱いになるため、無給状態になります。その代わり半年以内は給与の67%にあたる育児休業給付金が国からもらえます。 さらにパパ育休は、生まれてから8週以内に育休に入り育休を終えるともう一度育休をとることができます。 (僕の場合は育休終了が8週以内に収まらなかったので、その対象ではありませんでした。)

僕は、1ヶ月間「育児休職」を取り、もう1ヶ月間余っていた「有給」で休暇を取りました。 つまり僕が業務から離れた期間は2ヶ月間になります。

育児休業を取った理由

僕が在籍している会社では育児休業を長期でもらう男性社員は多数派とは言えません。 業務都合を考えると、取りづらくなる気持ちはわかります。 そんな中で育児休業を取得しようと思った理由は、3点あります。

  • お互いの両親のサポートが得られない

夫婦二人とも地方出身者で、関東に仕事に出てきて共働きしています。 親も共働きしている状態だったり、都会への移動が負荷になってしまったりと、お互いの両親がすぐ関東まで育児手伝いに来られる環境にはありません。 僕たちが住んでいる近くには似たような子供を持った知り合いがいるわけでもなく、コミュニティによるサポートも見込めませんでした。

  • 育児を妻に任せる考え方に共感できない

どことなく育児は妻がやるものという考え方が存在しているような気がします。 過去からの慣習なのか産んで母乳が出るからなのか、論理的な意味付けはよく分かっていません。 育児と家事を少なくとも半分は行うと考えた時に、業務しながらこなす自信がなかったからです。

  • 子供と触れ合う時間が欲しい

純粋に生まれた子供と触れ合う時間が欲しいと思いました。 その時間が業務後や土日だけでなく、平日の日中も一緒に触れ合いながら育児をして一緒に時間を過ごしたいと考えました。

育児休業を1ヶ月にした理由

どうして半年や1年といった長期ではなく、1ヶ月になったのか、理由はこれも3つあります。

  • どれだけ大変なのか分からないから

まず一つに、「育児がどれだけ大変なのか分からない」ということがあります。 はじめての子供で育児がどれだけ大変か想像できませんでした。 子供がいる家庭の状況は人それぞれで、子供によるところが大きいと思います。 自分たちがどれだけ大変なのか想像できない状態で、取得前に何ヶ月取るか決めなければならないため、半年〜1年間が長いのかどうかわからなかったからです。 今、過去の自分に伝えられるなら1ヶ月では足りないから半年〜できるなら1年取った方がいい、と言うでしょう。

  • 業務都合

上述した通り、僕が在籍している会社では育児休業を長期でもらう男性社員は多数派とは言えません。 そのため、業務上の都合で自分がチームから長期で抜ける調整が必要になります。 具体的には書きませんが、調整の結果2ヶ月が限界だったのではないかと思っています。 さらに言うと、1ヶ月の休職が限界で、もう半分の後半1ヶ月間は有給の合間に働く日を作ることで調整しました。 (なので、休職ではなく休暇という形を取りました。)

  • 金銭面

育児休職期間は給与が出ません。67%の保証はされますが、言い換えれば33%減ります。 これは共働きなので、夫婦ともに33%減ります。 妻は少なくとも1年は育児休職を取ることになり、半年間は67%、その後半年は50%になります。 もともと計画的に貯蓄しておいたわけではなかったので、家賃と生活費を考慮して、二人とも育児休職になるのは金銭面で厳しいと考えました。

生まれた瞬間に必要なものがたくさんあるので、それだけで相当な出費があります。 例えば出産一時金は国から42万円もらえますが(最近増えると話題)、全く足りません。十数万円以上のオーバーは余裕です。 ベビーカー・チャイルドシートは数万円台がデフォ、ハイローチェア、バウンサー、ベビーベッド、あげればキリがない状況です。 さらに育児休業給付金の振り込みは数ヶ月先ときています。 それでも普通に家賃とかあるわけなので手元に何十万という額を用意しておかないと、子供を産んではいけない計算になってしまいます。

これは言い過ぎかもしれませんが、男性育休が進んでいない現時点では相対的に見ると給料は下がり得ます。 なぜならその1年で働いてコミットできていた成果を出せなくなるため、昇給昇格のチャンスを1年先延ばしにしているとも言えるからです。

育児休業を取るまでに困ったこと

  • 業務の調整

業務内容はチームリーディングやいわゆるテックリードと呼ばれるような技術的なディシジョンを決める役割にあります。 僕が抜けることによって、チームを代わりにリードする代役を立てたり、技術的な方針を先んじて決めておくなどの調整が必要でした。 実際僕の場合は、1ヶ月経ったところで状況確認の打ち合わせを1日入れて聞いてみたら、すぐにでも戻って欲しい、という話をされました。

  • 申請周り

会社、役所周りの申請は正直面倒だったなと思います。 だいぶ面倒ではないように工夫してくれているのだとは思いますが、それでも面倒だなと思いました。 特に役所関連では里帰り出産のため、里帰り先でかかった費用を住所地の補助を使って精算する場合に、大変でした。 会社では、人事制度として存在しているはずなんですが、男性育休に慣れていないような手続きの不都合さを感じました。

  • 里帰り

妻の希望で、里帰り出産を選択しました。 妻の両親のサポートが得られて見知った産婦人科で産める方が良いと判断しました。 困ったことは、上述した申請周りもそうなのですが、新生児期に用意するベビーグッズを里帰り先で買うということです。 ありとあらゆるグッズを里帰り先から、関東の居住地まで送るか、新たに買い直す必要がありました。 新生児を連れてたくさんの荷物を持って新幹線等の移動も大変でした。 また、コロナの影響もありました。病院から立ち会い出産をするためには一週間の同居が必須条件だったため、予定日の一週間少し前から義実家への同居をし、結果的に予定日の一週間後に出産だったので、約二週間の義実家同居となったのも少し大変でした。

育児休業中の1日

育児休業期間、何をしているの、と聞かれることがたまにあったので、ある日の育休中の1日を書いてみます。 子供は生後2ヶ月半くらいの頃のことです。日によって違いますが、大体用事がなくて出かけない日はこんな感じでした。 もちろん日によって全く違うリズムの日もあります。

---

前日
02:00 就寝

---

当日
07:00 
 起床
 授乳ミルク
 寝かしつけ
09:00
 朝ご飯
 洗濯(子供)
 洗濯(大人) 
10:00
 コーヒーブレイク
 今週の予定確認
10:30
 調べ物少し
 うつ伏せの練習
 ぐずるのであやす
11:15
 調べ物少し
 ぐずるのであやす
 うんち
12:00
 授乳ミルク
 昼食準備
13:00
 昼食
 寝ぐずりしたのであやす
15:00
 昼寝
16:00
 授乳ミルク
 お風呂準備
 お散歩準備
17:00
 お散歩兼お買い物
18:00
 お風呂
 ぐずるのであやす
19:00
 授乳ミルク
 夕食準備
20:00
 夕食準備
 夕食
21:00
 寝ぐずりしたのであやす
21:15
 テレビ視聴
22:00
 授乳ミルク
 子供の爪切り
 就寝準備
23:00
 寝かしつけ
23:30
 うんち
 寝かしつけ
01:30
 授乳ミルク
02:00
 寝かしつけ
02:30
 就寝

育児休業中の1日の補足

うちの子は授乳に時間がかかるタイプだと思います。 なぜかというと、母乳とミルクを混合にしていることと、授乳中にすぐ寝てしまうからです。 寝てしまうと吸ってくれないのですが、食事に満足しているわけではないので、置くと背中スイッチで起きて泣きます。 飲んで寝て、起きて飲んでまた寝て、を繰り返すので時間がかかってしまい、時間がかかるときはそれだけで1時間かかります。

僕はミルク担当だったので、ミルクを作って冷やします。 寝てしまった子供を妻から受け取って起こして妻に戻す作業をしつつ、ミルクを冷やしすぎないよう適温状態をキープします。 さらにミルクは基本授乳の後なので、作っても時間が経つと悪くなってしまい捨ててしまう可能性もあるので、ミルクを作るタイミングも難しいという問題もあります。

うちの子は夜寝る方だと思います。5、6時間は寝てくれる日が多いので、他の家庭に比べると楽な方なのだと思います。 悪い日だと1時間ごとに起きてしまって、大人からすると実質オールしたような日もあります。 もちろん日によります。

掃除も夫婦だけのときに比べて頻度が高くなりました。子供にとってホコリが良くないからです。 ただ物が増えるので整理整頓はこれまで以上に難しくなっています。 哺乳瓶など子供向けのものを別で配置するためのスペースが必要で、マンションのキッチンやシンクなんて大したサイズないので場所に困りました。 食器に限らず衣服やタオルなど、基本的に大人と分ける形になるので、新たにスペースが必要になりますし、部屋を綺麗に保つのは至難の技です。

うちの場合沐浴はお風呂の浴槽を使っています。正直一人で沐浴はかなり大変です。また、お風呂を綺麗に保つ必要があるのでお風呂掃除が毎日になりました。

家にずっといても子供にとって刺激がないので毎日散歩します。リモートワークで完全に出不精だった僕が毎日外に出て散歩することになったのは、すごいことです。さらにちゃんと買い物して帰って料理しているので、ちゃんと人間らしい生活を送ることができています。

一番時間がかかるのが寝ぐずりでした。寝ぐずりは寝るまで抱っこして揺らしていないといけないので、手が空かず基本それ以外のことが何もできません。 長いときは、1時間以上泣き続けることも余裕でありますし、腕はパンパンになるし、腰は痛くなります。 そのため交代交代で抱き上げるのが必須でした。 妻は育休中にコロナワクチンを打ったのですが、腕が痛くなってしまい、抱き上げるのに苦労していました。 ちなみに僕は育休中にぎっくり腰になりました。

調べ物をしている時間は、子供に関する情報収集をしています。 コロナ禍ということもあり、ママ会や両親学級などは開催されていないし、近くに友人が住んでいるわけでもないので、子育て情報の収集がほとんどネットか本オンリーで、たまに職場や友人にラインで聞く程度になっていました。 そこまで親しげに聞ける友達がそもそもいないという話かもしれないですが・・・手が空いたタイミングで、よく調べものしていました。

無理やり作ろうとしないと、大人のまとまった自由時間はありませんでした。

これは僕だけかもしれませんが、朝起きるのが厳しいです。朝起きなくていいように転職してフルフレックスの会社にしたんですが、朝は必然的に起きる必要が出てきたので、朝好きな時間まで寝られない厳しさを味わっています。

あとは、毎日同じことを繰り返していたりすると、エンジニアの性で、自動化したくなりますね。

振り返り

振り返ってみると、正直夫婦二人で取り組んでも育児と家事は本当に大変で自分の時間はあまりなかったです。 その代わりほとんどストレスがない生活を送ることができたと思います。

二人なので、大変だ、疲れた、もう抱いていられない、トイレに行きたい、みたいなタイミングで、「ちょっと代わってもらって良い?」ができるかできないかがめちゃめちゃ大きかったと思います。

個人的には育児休業は取った方が良いと思います。 もしできるなら半年〜1年間は取りたかったです。 時短では育児の時間が足りないが、皆無だと金銭的に苦しいので、 気持ち的には最初半年間は33%給与分、次半年は50%分だけ働かせてもらえるオプションがあるとありがたいなと思いました。

一番大事なことなんですが、子供は死ぬほど可愛いです。目に入れても痛くないというのは本当です。寝ぐずりで寝なくても、寝る直前の新生児微笑で帳消しどころかプラスになりますし、泣き喚いていても泣き止んだあとの寝顔で帳消しどころか親は笑顔になれます。子供のためにこれから仕事頑張ろうとは思わなかったですが、子供のためにできることはなんでもしてあげようとは思いました。

最後に買ってよかったものや参考にして買った本を置いておきます。 もう少し経ったら別でブログにするかもしれません。

React 時代に選ぶ GraphQL

概要

先日新規の Web サービス開発でフロントエンド側の技術選定を行いました。 利用する技術の中で GraphQL を提案した際に、RESTful な API で呼び出す方法と比較して納得感がないという意見があがりました。 そこで、なぜ、どういうときに GraphQL を選定すべきだと思うか、文章にして自分なりにまとめておこうと思います。

前提

構成が BFF か BE かで意見は大きく変わりません。 例えば BFF として利用されるケースでは、バックエンド側には BE チームとマイクロサービス的な API が存在しており、 BFF として GraphQL を配置するようなケースです。GraphQL のリゾルバは API を叩きます。 一方、 BE として利用されるケースとは、リゾルバが直接 DB を叩くような形です。 今回はフロントエンドのチームが管理する BFF として、JS のみで GraphQL が利用されることを想定して記載します。

先に結論

まず結論から述べておくと、以下のような条件が重なった際に GraphQL が良いと思っています。

  • Web フロントエンド側の UI がリッチで複雑
  • UI 要件をアジリティ高く柔軟に変更したい
  • Web に精通した技術力の高いエンジニアがいる

この理由を順を追って説明していきます。

Web UI の複雑性

静的な UI から動的な UI へ

従来 Web ページといえば、1 URL に 1 画面の UI がカッチリ決まっているものがほとんどでした。 より UI がリッチで複雑な Web アプリが登場し、 jQuery から React へと技術的な進化が起きました。 現在 Web は多様なサービスを提供しており、ニュース記事を配信するような静的なサイトから表計算を行う動的なアプリケーションまで様々です。 動的な Web アプリケーションは 1 URL に 1 画面という考え方に限定されず、 1 つの操作や 1 つのコンテキストが URL として定義されることもあります。

例えば、ある画面を表示したままモーダルを表示するような UI も複雑になりやすいものの一つです。 モーダルが表示された状態を URL は別で定義しつつ、同じ画面上に JS でモーダルを動的に表示します。 そのモーダルが表示されている画面とモーダルが表示される前の画面は、別の画面か同じ画面か定義するのは難しいかと思います。

コンポーネントベースの UI 設計で保守性を高く保つ

jQuery から React への進化は、複雑化した UI 要件にまで広く対応できるよう保守性が高く柔軟に利用できる技術として進化しています。 詳しいところは React について述べている記事や本がたくさんあるので、調べてみてもらえればと思います。

一つ特徴を取り上げると、再利用性を考慮しコンポーネントベースに Web アプリケーションを開発しやすい点が挙げられます。 複数の画面で共通して利用される部品を React の UI コンポーネントとして開発し、開発効率と保守性を高めることができます。 React では適切にコンポーネント設計を行うことで、保守性高く Web アプリケーションを開発できます。 例えば、共通のモーダル用のコンポーネントを作っておきます。 そうすることで、複数の画面で同じモーダルが使われる要件だとしても、画面ごとに新たにモーダルコンポーネントを作る必要はありません。

参考: React を深く知るための入り口

コンポーネントベースの設計とRESTful API の相性

ただしコンポーネントベースの設計はあくまで UI 設計であり、バックエンド側の DB に入ったデータ構造とは関係ありません。 バックエンド側の DB に入ったデータを UI に展開するために、大抵の場合 RESTful な API からデータを取得して UI に反映する作業が必要です。

例えば、店舗の名前をトップページのタイトル部分に表示したいとします。 タイトルは表示したいテキストをもらって適切なスタイルで表示する UI コンポーネントなので、おおよそこんな感じです。

type Props = {text: string};

const Title: React.FC<Props> = ({ text }: Props) => {
  return <div className="title">{text}</div>;
};

店舗データとして店名等のデータが DB に含まれており、 GET /shops で店舗情報が取得できる RESTful な APIが用意されているとします。

$ GET /shops/1
{
  name: "店名",
  tel: "0120-XXXX-YYYY",
  owner: { ... }
}

API から返却されたレスポンスに含まれる店名 name を Title の text 引数に渡すことで、店名をトップページのタイトルに表示できます。 リソースごとに定義された RESTful な API と個々のコンポーネントには直接的な関係がない方が健全です。

リソースごとにレスポンスを返す RESTful な API だけで UI を構築しようとすると、大抵の場合 1 画面を表示するために複数の API 呼び出しが必要になります。

例えば、トップページで店舗情報と店舗が販売している商品情報を表示する場合、店舗 API と商品 API を呼び出すことになります。 複数の API をブラウザから直接呼び出すことになるので、その分 NW コストがかかります。

さらに、API が返すレスポンスが UI に必要なデータと必ずしも一致しているとは限らないので、いわゆるオーバーフェッチやアンダーフェッチによる NW コストも発生し得ます。

参考

GraphQLでバックエンドのコードをすっきりさせた話 - LayerX エンジニアブログ

Why not use GraphQL? - WunderGraph

BFF で UI と API リソースを仲介する

画面単位と API の単位が合わないケースに対応するために BFF を利用することができます。 BFF では複数のリソースから UI を意識した API に結合することができます。これは API アグリゲーションと呼ばれます。 一応補足しておくと BFF の利用用途はこれに限りません。

参考: BFF(Backends For Frontends)の5つの便利なユースケース:マイクロサービス/API時代のフロントエンド開発(2) - @IT

複数の API リソースを集約して 1 画面に使われるデータを 1 API にまとめたものをページ API と呼ぶことにします。 ページ API を作ることで、複数個の API 呼び出しが不要でページに必要なデータだけを取得できるので、その分 NW コストがかかりません

ちなみにこの記事ではバックエンド側でページ API が開発されるケースは想定していません。 そのような開発が適しているのは、ユースケースが固定的だったり、小規模なチームで BE / FE を一気通貫で作りきることができるスモールプロダクトに限定されると思っています。 個人的には、この方法ではサービスの拡張性が低いと思っているためです。

コンポーネントベースの設計とページ API の相性

1 画面 1 API としてページ API を用意できたとしても、コンポーネントは複数画面に跨っています。 例えば、極端な例で言うと、全ての画面でヘッダーコンポーネントを共通で利用しているとします。 ヘッダーに新たに API から取得したキャンペーン情報を載せたい場合、 1 画面 1 API で構築されていたら全画面向けのページ API にキャンペーン情報を新たにアグリゲーションする変更を加える必要があります

これは極端な例なので、書き方を工夫すればある程度解消はできる問題ではあります。 ただし根本的な考え方として、1 画面 1 API という考え方と、コンポーネントベースに積み上げられた複雑な UI 構成がミスマッチになりつつあることを意識しておくと良いと思います。

コンポーネント API の可能性

UI がコンポーネントベースであるならば、 API も 1 画面 1 API の代わりに 1 コンポーネント 1 API にできるように思います。 しかし、これは別の問題が発生します。

まず、"1 コンポーネント" と言われたときに "コンポーネント" の粒度が様々であるということです。 Atomic Design などコンポーネント入れ子になった考え方で作られる以上、コンポーネントの設計時に API からデータを取得するべきコンポーネントとそうでないコンポーネントを分けて考える必要があります。 これは API の粒度もマチマチになってしまう可能性があるということです。 どの粒度以上のコンポーネントには API を用意し、どの粒度以下のコンポーネントには用意しないのか、ルールの定義が難しく保守性の観点でデメリットがあります。

さらに、1 画面内に配置された複数のコンポーネントで同じデータを使うことがあり得るため、同じデータを重複して取得し、重複して保持することになります。 もともとリソースごとの RESTful API の時に懸念した NW コストが高い問題が出てきます。

例えば、ヘッダーコンポーネントとフッターコンポーネントが同じ画面に配置されていたとします。 ヘッダーでもフッターでも、API から取得した店名を表示したい場合、ヘッダー API とフッター API の両方で店名を取得する必要が出てしまいます。 コンポーネントごとに API を用意するのも、あまり得策とは言えません。 RESTful な API で設計する場合は、ページ API で 1 画面 1 API をベースにしつつ、さらに画面での操作やコンテキストを元にユースケースごとに API を定義することで、設計することが適切になりそうです。 これをユースケース API と呼ぶことにします。

これまでの API と GraphQL

GraphQL は、UI 側で必要なデータをクエリとして記述し、その記述のまま取得して扱うことができます。 ユースケース API はあくまでもバックエンド側(上記の例では BFF)のものなので、バックエンドでユースケースを意識する必要があります。 どの画面にどのデータが必要か定義した上で、そのユースケースごとに API を開発します。

一方 GraphQL は極端に言うと、バックエンド側では /graphql という一つのエンドポイント API だけ開発すれば良く、バックエンド側でユースケースを意識する必要はありません。 実際には GraphQL のリゾルバで API でデータ呼び出しを行います。 GraphQL で改めて Schema を定義しておくことで、 UI で Schema として定義したプロパティを自由に組み合わせて呼び出すことができます。 UI 側、つまり UI コンポーネントでは必要なデータを必要な場所でクエリとして定義します。 これは Fragment Colocation と呼ばれます。 個々のコンポーネントで必要なデータを取得する部分的なクエリ(Fragment)を、コンポーネントに近い場所へ配置する(Colocation)ものです。

参考

GraphQL の Fragment Colocation について

GraphQLとクライアントサイドの実装指針.md · GitHub

使い方としては、上記で示したコンポーネント API に似ています。 コンポーネント API と異なる点は2点です。

1点目は、コンポーネントを意識してバックエンド (BFF) 側で API を用意する必要がない点です。 コンポーネントの粒度を気にして API 設計のためにルールを用意する必要はなく、コンポーネントで必要なデータを必要なだけクエリとして定義するだけで良いです。

2点目は、コンポーネントごとに同じリソースを使う場合でも API 呼び出しに重複が起きない点です。 例えば GraphQL の Relay というライブラリでは、コンポーネントごとに定義したクエリを実際にページから呼び出す際に 1 つの大きなクエリにアグリゲーションした上で重複を排除して呼び出す仕組みがデフォルトで機能に含まれています。

これまでの API よりも GraphQL の方が現代の React で培われているコンポーネント設計に合っていることがわかると思います。

閑話休題: 他のフレームワーク

GraphQL で実現していることを簡単に言うと、

となります。

他の対応策やフレームワークでの対応状況を軽く触れておきます。

React

React Server Components も、似た問題点を解決する方法となっています。 React は UI フレームワーク内の仕組みとして、 React Server Components を実装中です。 これは、あらかじめ UI コンポーネントがサーバー側 (BFF) でレンダリングするものかどうかを指定できるものです。 React UI コンポーネントAPI のデータフェッチが必要なコンポーネントは、React Server Components として定義しておくことで、API フェッチを BFF で済ませた状態にでき、ブラウザ側からのデータフェッチを不要とすることができます。

参考

Introducing Zero-Bundle-Size React Server Components – React Blog

RFC: React Server Components by josephsavona · Pull Request #188 · reactjs/rfcs · GitHub

React Server Components 総まとめ

Next.js

React を使った Web ページを簡単に構築できる Next.js は、サーバとしての機能も備えた一気通貫フレームワークとして人気を博しています。 All in One の仕組みをほぼ設定のチューニングなしに JavaScript のみで書き上げることができます。 Next.js の登場により Web フロントエンドは React でブラウザで動作する Web ページを構築するだけでなく、Web ページを配信するサーバ機能までをフロントエンドエンジニアが簡単に扱えるようになりました。 近年では Node.js での BFF の導入も Next.js をデプロイサービスの Vercel や GCP の Cloud Function にデプロイするだけで、簡単に使えるようになってきています。

Next.js はどちらかというと静的な Web ページを構築するものとして利用されることが想定されたフレームワークになっており、デフォルトで提供されているバックエンドの API データ取得機能に柔軟性が高いとは言えませんでした。

例えば、 Next.js の getServerSidePropsgetStaticProps などの API は、簡単に言うと 1 画面に必要なデータをまるごと取得しページに渡す機能となっており、ページ API 的な考え方になっています。 これは、記事のような 1 画面にコンテンツが集約され、名の通り Static 、つまりあらかじめ静的に用意しておけるような画面には有用だと思います。

今後は Nested Layout の RFC が登場し、 GraphQL で実現できていたことが Next.js でも近いことができるようになります。 これまで Next.js の画面遷移は、異なるページへの遷移時に画面に必要なデータ、コンポーネントを全て取得し直す形になっていました。 Nested Layout は名の通り Layout を入れ子に定義できるものになります。 これにより画面上の遷移や操作時に必要最低限のコンポーネントを UI 上で変化させることができるようになります。 つまり、コンポーネント API 的な API を不要に呼び出してしまうことはなくなります。

参考 Blog - Layouts RFC | Next.js

Remix

Remix について詳しくないのですが、 Remix でも近しいことが考えられているようです。 Remix は Next.js と同様の Web 開発用のフレームワークです。 Next.js ほど多く普及しているわけではありません。 詳しい人はぜひ教えてください。

参考 Remix | Data Loading

フレームワーク状況のまとめ

これらのように GraphQL で先進的だったコンポーネント指向の API データ取得方法の改善が進められ始めています。 GraphQL の利点はそれに限定されませんが、 GraphQL を選択する強い理由とは言えなくなるかもしれません。

ただエコシステムとしての成熟度は GraphQL の方が進んでいることは変わらない事実です。

アジャイルな開発と GraphQL

ユーザーが求める UI の進化に追いつくためのアジャイル

Web フロントエンドの UI がリッチで複雑なものになってきているのは、ユーザーがそれを求めているからに他なりません。 さらに開発手法としてアジャイルな考え方が広がっている一つの理由は、ユーザーが求める UI に日々変化があるからです。 ユーザーに提供される UI は様々なサービスで UX が進化し、ユーザーにとっての当たり前は日々アップデートされ、Web フロントエンド開発者は更なる UI 改善を日々求められるようになっています。 このようなよりリッチで複雑な UI へのアジリティ高く改善を進めるために、フロントエンドチーム単体で改善を主導しリリースできることが重要になっていくものと考えています。

リリースサイクルを高める GraphQL

GraphQL のメリットは UI 側で必要なクエリを定義することができる点です。 コンポーネントに対するクエリの変更だけで修正が済みます。 つまり不必要に API 側に手を入れる必要がなく UI に対する変更量を必要最低限に抑えることができます。 フロントエンドチームだけで改善のための開発からリリースまでのサイクルをより速く回すことができます。

バックエンド側の DB カラムや API の変更も必要となるケースでは、コンポーネントだけでは修正は留まらず BFF 側のリゾルバの変更も必要になります。 実際には上記のような変更量が大きいものよりも、 UI に対する細かい修正や A/B テストのようなケースの方が多いのが現実です。 例えば、ヘッダーで店名を表示するかフッターで店名を表示するべきか A/B テストをする場合であったり、ヘッダーに店名に加えて電話番号を表示すべきかどうかであったりといったケースが考えられます。

チームで工数管理が行われている現場なら、変更量がイコール工数にあたることになり、与えられた工数から修正要件のスプリント管理が行われていると思います。 変更量が少ないということは、スプリント期間内に含めることができるタスクの量が増やせることになります。

GraphQL と型生成

最近では、 Web フロントエンド開発で保守性を高めるために TypeScript で型付けすることがスタンダードになりつつあります。 GraphQL はあらかじめ Schema を定義しておくことで、BFF でのリゾルバと呼び出すクエリの型を自動生成することができます。

RESTful な API では、 API のパス、リクエスト、レスポンスに型を定義する必要があります。 ライブラリによるサポートはありますが、現時点ではエコシステムとして成熟しているとは言い難い状況です。 一方、 GraphQL では型の自動生成が GraphQL のエコシステムとして組み込まれた状態で登場し発展しています。 TypeScript スタンダードな時代に対しても、 GraphQL は開発効率と保守性で比較して優位な状態と言えます。

参考: TypeScript GraphQL Code Generator – Generate GraphQL Types with Apollo Codegen Tutorial - Apollo GraphQL Blog

GraphQL とサービスのエンハンス

将来的なサービスのエンハンスにも有用であると考えています。 例えば、 Web だけでなくネイティブアプリにも展開するケースです。 ネイティブアプリではアプリ向けに UI やサービス要件が異なっていたとしても、 GraphQL で必要なデータをクライアント側で定義することができれば、新たにバックエンドを修正する必要がないかもしれません。 または、パブリックな API としての提供にも GraphQL は有用です。

つまり基本的な考え方として、クライアントやユースケースによってバックエンド側の修正が必要ないため、将来的なクライアント側の変更に強いという特徴があります。

技術力の必要性

GraphQL はすでに世界中の様々なサービスで使われています。 開発元の Facebook、パブリックな API を GraphQL で公開している GitHub、国内ではメルカリや Yahoo など、利用シーンが増えてきています。 そうは言っても、圧倒的に従来の RESTful な API で Web サービスが構築されることの方が多いのは言うまでもありません。 つまり技術者がまだ多くないため、世の中に出ている記事やサンプルも多くありません。

ただ、この点に関して2点だけ注意して考えてもらいたいことがあります。

  • 問題の本質は記事を読めばわかるとは限らないこと
  • 要件が複雑なら技術力が高いエンジニアが必要ということ

問題の本質は記事を読めばわかるとは限らないこと

1点目は、世の中に出ている記事に書かれた内容がすべて正しいとは限らないということです。 Web 開発をしているとすんなりコードが動作するケースの方が稀です。 大抵の場合問題点にぶちあたります。 それを解決するために似たような問題を解決した人がいないかググる、ということが多く見られます。

例えば、 Qiita の記事が出てきて、何らかのプラクティスが書き示されているものを手放しで信用してコピペする人がいます。 僕は自分のメンバーに対してそのような対応を推奨していません。 問題が解消されたように見えたとしても、別の問題に置き換えているだけかもしれません。 実際に問題の本質は何なのか、紹介されているプラクティスでなぜ解決できているのか理解しなければ、解決策として認めません。

この時実際に問題の本質は何かを調べるときどうすればいいかというと、結局問題が発生しているコードを読むしかありません。 必要であればライブラリやフレームワークの中身までです。 そうなると、GraphQL が使われている記事やサンプルコードが RESTful API が使われているものより少ないというようなことは、重要な問題ではありません。 ライブラリやフレームワークの信頼性や保守性が高いコードであることの方が重要です。

要件が複雑なら技術力が高いエンジニアが必要ということ

もう一点は、Web UI の要件が複雑なものはいずれにせよ技術力が高いエンジニアが必要ということです。 要件が複雑なものに GraphQL は向いていると書きましたが、それ以前に複雑性の高い UI 要件をメンテナブルな状態で維持するためには、保守性高く設計する必要があります。 言うまでもなくこれにはフロントエンドに精通した技術力が必要です。

「リッチで複雑な UI 要件にはしたいが、 GraphQL を使えるほど優秀なエンジニアがいない」状態なら、 GraphQL を選ばない選択をするのではなく UI 要件を削り簡素なものにする選択をすべきです。

「リッチで複雑な UI 要件にはしたくて GraphQL を使える優秀なエンジニアもいる」状態であるとき、はじめて GraphQL か RESTful API かどちらを選ぶべきかの議論ができると思っています。

さいごに

GraphQL に対する個人的な考えをまとめました。

もし認識が間違っている点があればご指摘ください。

1ヶ月お休みをもらってやってみたこと

会社から1月の1ヶ月間公式にお休みを頂きました。 やってみたことを挙げていきます。

自身の備忘録で、技術的な話はほぼ出てこないので、読んでもらっても基本得るものはないと思います。

ただ、もしかしたら誰かの何かに役立つかもしれないので、書き残しておきます。

前提

  • 自分:会社員。エンジニア。
  • 妻:会社員。妊娠後期。
  • 結婚しています。
  • 夫婦ともに在宅ワークをしています。
  • 妻は妊娠しており、春頃出産予定です。
  • 両実家が遠く現在は二人のみで賃貸マンションに住んでいます。
  • 時期が近づけば妻実家にて里帰り出産予定です。

専業主夫

まず一番大きな時間をかけたのは専業主夫業です。 炊事、洗濯、掃除、買い物等、 妊娠しながら働いている妻をサポートするため、 基本的な家事はほとんど行うようにしました。

正直、思ったよりも大変でした。

これまでも家事は分担していたし、 たかが大人二人分だけなので、 仕事がなければ一人で全部やるのも楽勝だろうと高を括っていました。

妻が在宅ワークなので、両方のお昼ごはんの準備が必要になること。 妊娠後期になってくると、食事の量は減るが回数は増えること。 妊婦が食べてはいけないものの考慮する必要があること。 洗濯、ゴミ捨て、日用品の整備、食料調達のタイミングと順序などなど。

結局妻に手伝ってもらいながらやることになってしまいました。

ただ1ヶ月も経つとルーティンの効率化ができて、 少しだけ可処分時間を増やすことができました。

個人的には専業主夫やってとてもよかったです。 自分は向いてるなとさえ思いました。 退職して専業主夫として生きていく道も今後の選択肢に入れました。

ちなみに何が楽しいと感じたかと言うと、家事を個々のタスクに分解したサービス設計だと捉えると、 フロー効率を高める作業を永遠にやり続けられるから だと思います。

もう少し具体的に言うと、例えば料理がわかりやすいです。 毎日カレーライスだけを作ることにしてしまえば、作業もルーティンワークになるし、作り置きもできます。 しかし、ユーザーは毎日カレーでは飽きてしまいますし、栄養が偏ります。 その日によって嗜好の変化がある不確実性が高いユーザーに対して最大限の UX を提供しつつ、 フロー効率を高めて自身の可処分時間を増やすにはどうすればいいか考えるのは、僕は楽しかったです。

奥さんにほぼ任せているエンジニアは、一度全部やってみた上でフロー効率を改善してあげると喜ばれるのでは?と思いました。

世の中の子供がいる主婦様方を本当に尊敬しました。 今後は子供が増えるのでもう少し働き方を考えないといけないなと思いました。

英会話

オンライン英会話をはじめてみました。DMM英会話です。 毎日1コマ(25分)はやるようにしようと心がけました。

結果 750 分間やることができました。30日分ですね。 f:id:ka2jun8:20220131131831p:plain

お察しの通り、毎日はできませんでした。 何とかこれでも自分を鼓舞した方ですが、モチベーションが続かなかったです。 これは人それぞれだと思うのですが、たかだか1ヶ月間では、 英会話を楽しいとは思えませんでした。

初対面の人と話すのが苦手なので、英語かどうか関係なく、 緊張してしまってうまく思ったことを話すことができません。 さらにその上英語になるので、より一層会話することが億劫でした。

先生が良かれと思って教材から外れたことをラフに聞いてくれるのですが、 その瞬間、パニックになって思考停止してしまっていました。

何日間かやっていると「教材のこなし方」に慣れてしまって、 25 分を無難にこなすための英語力 は身につきました。

もちろん良かったこともあります。

中学英語レベルくらいは 思い出すことができました。 逆に言うと、中学英語レベルですら忘れていたことに驚きました。

また、日常会話的に言おうと思って言えないのは単語を知らないからなんだなと思いました。 毎回1フレーズくらいは「ああ、これってこうやって言うんだ」と思うので、 今後使えるかどうかは置いておいたとして、毎日1単語・1フレーズ学べました。

あとは、自分はそういえばコミュ障だったんだなということを思い出せました。 最近家から出てなくて新しい人とも大してしゃべってなかったので、忘れていました...。

コミュ障という言葉にしてしまうと何がストレスなのかわかりづらいので言語化すると、 コミュニケーションというサービスに対して僕自身の SLO エラーバジェットの許容度が極端に低いのだと思います。

つまり相手からのリクエストに対して 200 OK を返したいのに、 自分ではうまくレスポンスを構築できず 400/500 系が返ってしまうことへのストレスが、 おそらく人よりも大きいのではないかと思います。

他人のことはわかりませんが、エラーバジェットの許容度が高い、つまり 「まあコミュニケーションミスってもよくあるよくある仕方ない」と思えるくらいの SLO なら、 もっと生きやすいだろうと頭ではわかるのですが、心がついていきません。

とにかく相手のがっかりした表情や残念がる表情を見たくないのです。

そのためリアルタイムな同期コミュニケーションだとリアルタイム性が求められ、うまく答えられないときのストレスを感じやすいから避けたいのです。 非同期コミュニケーションの方が SLO を高く保ち続けやすいので、オンラインに逃げがちになっているのだなとわかりました。

ジム

前々から健康のためにジムに通おうと思っていたので、最寄り駅の近くのジムに申し込みました。 誰しもそうなんだと思うのですが、最初は毎日通おうと思っていました。 そのため、コースはいつでもジムに行けるコースにし、最初だけパーソナルトレーナーをつけて器具の使い方を教わりました。

実際に始まってみると、当日から翌日にかけて体が辛くて2、3日おきにしか通えませんでした。 個人差があると思いますが、僕の場合翌日から筋肉痛がつらいのはもちろん、頭痛や腹痛が出てしまいました。 原因は不明ですが、そこまでハードワークにしなくても起きてしまったので、体質なのかなと思っています。

あとはコロナ拡大によって、ジム施設に行くのが怖くなってしまいました。 筋肉痛だけが問題なら社会復帰してからも通えるかなと思ったのですが、 それ以外の問題があるので2月以降はちょっと難しいかもしれないなと思いました。

ちなみに、入会時と1ヶ月後の体組成計を見比べましたが、変化はありませんでした。 あんなにしんどかったのに...。

新居探し

子供が産まれることもあり、もう少し広い家に住み替えることを検討しました。

賃貸か購入か、購入する場合はマンションか戸建てか、戸建ての場合建売か注文住宅か、があります。 あとは、場所をどのあたりにするか、お金や今後の生活を考えながら、検討しました。

ハウスメーカーの注文住宅相談会やモデルルームの見学など、不動産には10社程度1〜2回話をしました。

まだ結論は出ていません。

分かったことは、

  • 家が高すぎて買うの難しい
  • 考えることが複雑すぎて難しい

家買うの難しすぎるということでした。

何が難しいのかというと、 賃貸と購入を比べると購入の方が良いことは自明なのに、 購入になると不確実性の高く将来的な負債が残る可能性が高い からです。

いろいろ考えたことを忘れないうちに別で記事に残しておこうかなと思っています。

ちなみに、ローンシミュレーションは他のサイトでいいのがなかったので自分で試算ツールを作りました。

life-planner.vercel.app

カジュアル面談

せっかくの機会なので、カジュアル面談を行いました。 現時点で直近の転職の意思はないことを前提に、カジュアルに他社の話を 10 社ほど聞かせていただきました。 小さめのところからある程度大きいところまで、できる限り幅広く聞きました。

分かったことは、僕は 事業への魅力よりも技術的な裁量の大きさに魅力を感じるタイプ なのだなと思いました。

こちらもいろいろ考えたことを忘れないうちに、 別で記事にしようかなと思っていますが、書いていいのか分からないこともあるので、 パブリックにはせずに興味がある方に共有する形にするかもしれません。

興味がある方は別で教えてください。

開発

本当は普段使わない、育てることができない開発スキルを強めたい気持ちがありましたが、 上に書いた通り、ほかに優先してトライしたことがあったので、なかなか時間を有効に使えませんでした。

本当に多少やったことで言えば、

  • deno + aleph.js の素振り
  • remix + tailwind の素振り

くらいです。

ざっと触ったくらいですが、 remix はプロダクションレディなものに感じました。 deno はもう少しという感じですが、だいぶ迫ってきているなと感じています。

趣味

本当は、もっと本を読みたかった!!!...です。 積読がたくさんあるので、 どのくらい消化できるか楽しみだったのですが、 全然ダメでした…。

これは僕自身の気持ちの問題で、 「やることAとやることBがまだ終わってないのに、読書なんてしてていいんか?」 と、もう一人の自分に詰められるので、 心をリラックスさせて本に向き合うことができませんでした。

悔しい...。

その他

あとは子供が産まれるための準備をしていました。 そうは言っても、名前を考えたり、必要なものを買い揃えはじめる程度です。オムツください。

==

以上です!

仕事から離れたら仕事がやりたくなるとか、開発がしたくなるとか言われていましたが、 実際には「開発はしたくなったけど仕事はしたくはならなかった」です!

もっといろいろやりたかったことはありましたが、 1ヶ月で十分お休みをいただいたので、 社会復帰したらまた頑張って働こうと思います!

Next.js にコントリビューションする方法

はじめに

Next.js を使ったことがある人はたくさんいると思いますが、 コントリビュートしたことがある人は少ないのではないでしょうか?

Next.js は Vercel がメンテしてくれていますが、 OSS なのでコントリビューションできます。

この記事では、 Next.js に PR がマージされるまでに僕がやったことを紹介します。 実際の修正内容は詳しく述べません。 修正が取り込まれるまでの流れを紹介し、 「全然大したことしてないじゃん、僕でもできそう」と思ってもらえることがゴールです。

Next.js

github.com

またこの記事は Recruit Advent Calendar 2021 の 3 日目の記事です。

明日は MaxMEllon さんの記事の予定です。

adventar.org

TL;DR

GitHub のコントリビューションガイドか YouTube を見てコントリビューションしてみましょう!

僕の記事を読むより公式の記事や動画の方が正しいと思います。

僕が具体的にどのようにコントリビューションしたか少しでも興味がある方は本編をご覧ください。

github.com

www.youtube.com

本編

前提: この記事を書こうと思ったきっかけ

11/27 の JSConf に登壇しました。

jsconf.jp

その際に @__sosukesuzuki さんの基調講演を聞き、 OSS の貢献の重要性を再認識させられたからです。

JavaScript エコシステムを維持する OSS の努力と課題 - Speaker Deck

発表はおそらく YouTube に上がると思うので、配信されたらぜひ見てみてください。 きっかけはこの発表だけではないですが、一つのきっかけになったのは確かです。

つい先日僕が Next.js に出した小さい修正 PR がマージされました。 ログ出力のフォーマットを変更する PR です。

fix amp validator message format by ka2jun8 · Pull Request #31018 · vercel/next.js · GitHub

特に大した内容ではないので、こういう小さなところから コントリビューションできることを示す良い題材かなと思いました。

まず Issue から

普段業務で Next.js を使って開発を行なっています。 Next.js はアップデートが多く機能追加も多いのでありがたいのですが、 アップデートされた変更によってバグが発生することもあります。

今回僕が出した Issue の場合は、 Next.js ログ出力がなぜか縦書きになってしまうというものでした。

AMP Validator error logs are shown vertically · Issue #31012 · vercel/next.js · GitHub

OSS のバグに出会った際に僕が行うことは、おおよそ以下のような流れです。

  1. 問題に直面したら既存の Issue を探す。
  2. Issue がなければ原因を自分なりに調査する。
  3. OSS 側に問題がありそうだと判断する。
  4. 実際に Issue を出す。

バグは大抵自分が書いたコードのせいで発生します。 しかし今回のケースでは Next.js 側が出力しているログ出力なので、Next.js 側を怪しみました。

まずこういう場合は既存の Issue を探して同じことに困っている人がいないか確認するようにしています。 今回同じような問題を報告している Issue を見つけることはできませんでした。

この時点で Issue をあげる人もいると思いますが、 僕の場合は Next.js のソースコードを読み該当のエラーがなぜ起きるのかを確認するようにしています。 もし自分のコードや環境が問題だった場合に、 OSS のメンテナに余計な負荷をかけたくないからです。

特に今回は Next.js のアップデート後に発生していたので、 Next.js が一つ前のバージョンから何を変えたのか確認しました。

今回のアップデートで、問題が起きた該当のログ出力機能に変更が加えられていることがわかりました。

https://github.com/vercel/next.js/pull/29753/files

Next.js の examples にある最小構成でも発生することを確認しました。

next.js/examples/amp-first at canary · vercel/next.js · GitHub

前のバージョンでは発生しないことを再度確認し、 この辺りで Next.js 側に Issue を立てることにします。

ちなみに Issue を見てもらえればわかると思いますが、 英語はほぼ 2〜3 行しか書いていません。

伝わればヨシ!の精神です。 僕が Issue をあげる場合は、英語の代わりにスクリーンショットや実際のコードを貼ることが多いです。

Issue を出すだけでも、一つのコントリビューションです。 ちゃんと GitHub に草も生えます。

コードの修正

ここからは、 PR を出すことを目標にします。 実際にコードを修正します。

vercel/next.js のコードを clone し、 基本的には contributing.md の通りに進めます。

僕の今回のバグの場合は、該当行に余計な変更が入っていることが問題だったので、 上述した前のバージョンとアップデート後の変更箇所のマージを行いました。

https://github.com/vercel/next.js/pull/31018/commits/34b589febdd3936ac71bd730e22c95c8467de2f9

大した修正ではないことがわかってもらえると思います。

動作確認

書き換えたコードを含む Next をローカルでリビルドします。

ビルドに通ったら、 examples にある最小構成で確認します。 以下のように examples 配下のフォルダを指定すると、ローカルでビルドした Next.js で確認することができます。

yarn next ./examples/amp-first/

便利ですね!

テストの追加

僕は上の時点ではやる気持ちが抑えきれず PR を出してしまいましたが、 テストを追加するのがマナーです。

f:id:ka2jun8:20211129113123p:plain

まずはとりあえず気軽な気持ちでテストを実行してみます。

yarn testonly

そうするとウィンドウがたくさん出てきて何も作業ができなくなるので気をつけてください。

f:id:ka2jun8:20211129115620g:plain

yarn testheadless の方がおすすめですが、こちらもかなり時間がかかるので、 最後に一応確認する程度で寝る前に流すなどの方法が良いかなと思います。

実際の確認は、以下のように該当する追加箇所だけを確認する形になると思います。

yarn testonly --testPathPattern "integration" -t "should detect amp validator warning on invalid amp"

一応 yarn lint も通しておくとよいです。

いざ PR を出して...

PR を出す瞬間は緊張しますが、 まあ間違っていても後から直せるので、出してから考えましょう。

PR Template がすでにあるので、各所に適切な内容を追記する形で出せます。 PR を出すと CI が回ります。

テストに通るか確認することもできますし、 stats も見れます。

stats は、以下のようなコメントで、 変更後にビルドサイズが肥大化していないかなど確認できるようになっています。

f:id:ka2jun8:20211129120827p:plain

メンテナからコメントがついたら指示に従うようにしましょう。 マージされれば完了です。

記念スクショ↓

f:id:ka2jun8:20211129120939p:plain

f:id:ka2jun8:20211129120947p:plain

tim さんなどのメンテナから感謝されると嬉しい気持ちになりますね!

最後に

OSS である Next.js のコントリビューション方法の一例を書きました。

そんなこと言ったってバグなんてないし、という方におすすめなのが、 good first issue です。 Next.js の Issue には good first issue ラベルがあります。

https://github.com/vercel/next.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22

これは簡単な修正をあえて first contribution 用に残しておいてくれているものなので、手取り早くコントリビューションができます。

また examples はメンテナの目が届かないケースが多いので、コントリビューションチャンスが多いです。 ライブラリの組み合わせは無限大なので examples の追加もいいかもしれません。

僕も OSS のハードルはまだまだ高いと思ってしまっていますが、 簡単な修正でも貢献できそうだと伝われば幸いです。

実際に自分が開発していて困っているなら尚更のこと、 OSS でコントリビューションして自分のために直すところからでもいいと思います。

それが同じ境遇の他の誰かのためになるはずです。