Torihaji's Growth Diary

Little by little, no hurry.

2025年3月の振り返り(実務 8ヶ月目)

はじめに

みなさん、こんにちは torihaziです

4月が始まり、新生活が始まりました。

ちなみに私はそんな感じは一切ないです。

電車にスーツ着慣れて無さそうな子がいっぱいいるなぁと

感じたくらいですね。初々しくてどこか懐かしかったです。

さて、そんな私ですが、この職についてから早いもので8ヶ月になりました

もうそろそろ力ついてきてほしいなと思う今日この頃ですが、

ちゃんとついているか棚卸しをしていきたいと思います。

(なんかタイピング力落ちた気がしなくもないような)

それではいきましょう

総括

いつも通りメンタル面から行きましょう。

心構えに関することですね。

結局は根性と気合い。

どの仕事においてもそうだと思います。

がしかし。

負けそうな時にそれでもなんとか最善を尽くすこと。

粘ること。

勝てなくても負けなければいい、みたいな。

その辺りの心構えというのは最低限この職を続ける上で必要だと感じました。

世間ではやたら「イッセンマン。」の踊り文句でたちまち話題となる未経験エンジニア界隈、

そこでイッセンマンにいかなくても成功するか否かは

結局のところ当人の精神力次第だとこの1ヶ月で強く感じました。

プログラミングは教えられても、根っこの心の部分までは無理。

自分も決してアスリート並みに強い精神力はあいにく持ち合わせていませんが、

それでも中高のサッカー部での経験や死に物狂いの受験勉強を頑張り抜いてきたので

そこらへんは今となっては良かったなと感じています。

ただ、本当にもったいないなと思っています。

人と仲良くするって大切。

こんなこと書くと、「計算して人と接してるの」みたいに思われそうですが、

職場の人間と仲良くなることはエンジニアにとってかなり大事かと思います。

まぁ仲良ければ楽しいことも増えますしね。

あと久保建英くんも言ってましたが、

普段から仲良くしてれば、あとはゴール決めるだけようなパスを

そうではない他人よりも僕にくれるかもしれないし。

逆もまた然りです。

そうやっていろんな社会で成果を積み上げて信頼を勝ち得ていく。

そういうことも技術以外で習得していくべきスキルかと、最近は感じています。

もちろん面白そうだな、とか

言葉にできないような相性とかも本能的に判断した上でです。

誰彼構わず仲良くする必要はないかと思います。

そんなことしてたら疲れるので。

だからってそうしてない相手が嫌いなわけではないですよ笑

それだけは勘違いしないでください😆

Frontend

いつも通りまずは箇条書きから。

  • app router 難しい
  • app routerの cookieのセット方法
  • Set-Cookieヘッダの仕様
  • JWTとJWSについて(これについてはfrontendではないかも)
  • 型定義方法の現段階のベストプラクティス
  • 同一画面における複数modalの開閉状況管理するカスタムフック
  • HTTP認証のあれこれ

あれ、意外と書くこと薄いかもしれない。

まぁいっか。

approuter大変。

ええと最近実務でapp routerを触り始めました。

結論、pages routerよりも考えることが増えて大変です。

ただ最近知った感動は、

速度が早いし、サーバコンポーネントが非同期のasync await対応なので

データ取得の関数をそのままtsxとかのファイルに書けるってことですね。

前まで useSWRとかでカスタムフックとか酷い時は useEffectとか使ってやっていたものが

const {data: hogehoge} = awita hgoeFetcher()

みたいにするだけで良くなったのはおーとなりました。

あとはCookieのセット方法ですね。

これどん詰まりしました。

例えばRails側でcookiesを添えて、レスポンスとして返す際に

レスポンスヘッダに"Set-Cookie"というヘッダを添えてそこにセットしたものを返します。

それをブラウザが受け取ると、その値をブラウザがCookieをセットしてくれます。

これはブラウザの機能です。

これにより 開発ツールのApplicationタブから見れるCookieにおいてその値を見ることができます。

で、App routerなんですが 以下のような通信をとっています。

ブラウザ <=>front server(App router) <=> Back Server(Rails)

Railsからのレスポンスを第一に受け取るのは frontend側のserverです。

そしてApp routerから導入された server componentにおいて

そのようなデータやり取りの関数は server actionsとして実装するのですが。

このままだと cookieはセットされてくれません。

確かにこの時のresponseヘッダ覗いてみると set-cookieヘッダあるのですが、

ブラウザからはそれがセットされているところは確認できません。

ここでどん詰まりましたね。

なんか cookiesとかいうNext.jsの機能があるからやってみたら

server componentでは cookiesのセットはできないとか言われるし。

で結論はこれですね。

【Next.js】Server ActionsでブラウザにCookieをセットする方法 #Next.js - Qiita

それかclient componentで セットするかですね。

この手の問題に詰まって原因究明していたqiitaの記事があったのですが、

見当たらなかったのでひとまず。

その方は レスポンスヘッダの User-Agentの値から "node"だからこれはブラウザじゃなくて node.jsだ!

とかいうふうに原因究明されてた気がします。


あとはなんだ。あーJWTとJWSですか。

JWTはJson Web TokenでJWSはJson Web Signatureですね。

JWTはJson形式のデータを httpヘッダとかurlとかに組み込めるようにJsonを文字列化したものです。

文字列化にはBase64形式でエンコードします。

JWTはヘッダとペイロードからなります。

ここら辺はまだ知識が浅いのですが、ヘッダはメタ情報(形式とか)で

実際必要なデータとしては ペイロードに含めます。

ヘッダとペイロードは 「 . 」で区切られており、デコード自体は簡単にできます。

そのため盗聴しようと思えば秒です。jwtをデコードしてくれるものもググればすぐ出ます。

なのでpayloadとかにpasswordとか載せちゃダメですよ。

で、それじゃまずいやろってことで"改ざん"を検知する仕組みとしてJWSがあります。

JWSはそのJWTを秘密鍵とある暗号化方式?によって暗号化することによってできる署名です。

header . payload をbase64エンコードしたものを 上記で署名し、それをくっつけます。

header.payload.署名

これですね。


次はベストプラクティスとカスタムフックですか。

カスタムフックからですね。個人的にはよう考えたと思ってるのですが。

使用場面としては同一画面に複数modalを出そうとしている時に、それらの開閉状況を

いちいちuseDisclosure実行するなんて面倒なことしなくて良くなります。

useDisclosureはmodalの開閉状況管理するフックとして有名なやつです。

isOpen, handleOpen, handleClose, onOpenChangeを返すやつですね。

chakra uiとかでも確かあった気がします。

で、どうやるか。

import { useCallback, useState } from "react";
export type ModalType =
  | "modalの名称をユニオン型で"
  | ...

// モーダルの状態をより型安全に管理
export interface ModalState {
  isOpen: boolean;
  type: ModalType;
  previousType: ModalType;
}

// home画面の各種モーダルの開閉状況を管理するフック
// Modalの開閉状況は単一のstateで管理し、typeでどのModalを開くかを管理する
// previousTypeは別Modalを開く前のModalのtypeを保持する
export const useModalManager = (initialType: ModalType = "none") => {
  const [state, setState] = useState<ModalState>({
    isOpen: false,
    type: initialType,
    previousType: initialType
  });

  const handleOpen = useCallback((type: ModalType) => {
    setState((prev) => ({
      isOpen: true,
      type,
      previousType: prev.type
    }));
  }, []);

  const handleClose = useCallback(() => {
    setState((prev) => ({
      ...prev,
      isOpen: false,
      previousType: prev.type
    }));
  }, []);

  // shadcn/dialogのonOpenChangeに対応するため追加
  const onOpenChange = useCallback((open: boolean) => {
    setState((prev) => {
      // クリックされた時(openがfalseになる時)の処理
      if (!open) {
        if (prev.previousType === "none") {
          return {
            isOpen: false,
            type: "none",
            previousType: "none"
          };
        }
        return {
          isOpen: true,
          type: prev.previousType,
          previousType: "none"
        };
      }
      return {
        ...prev,
        isOpen: open,
        previousType: prev.type
      };
    });
  }, []);

  return {
    isOpen: state.isOpen,
    currentType: state.type,
    previousType: state.previousType,
    handleOpen,
    handleClose,
    onOpenChange
  };
};

こんな感じですね。

使う時には

const {
    isOpen: state.isOpen,
    currentType: state.type,
    previousType: state.previousType,
    handleOpen,
    handleClose,
    onOpenChange
} = useModalManager()


<Modal
  isOpen={isOpen}
  onOpenChange={onOpenChange}
/>

みたいにして呼び出して、modal開きたいときは handleOpen("指定したtype")で呼び出せば開きます。

多分shadcnとか使ってる人はなんとなくわかるのではと思います。

ちなみにshadncnの場合、このようにやるとonOpenChangeは少し手を加えなくてはなりません。

なぜかというと画面外クリックの時の挙動が制御できないからです。

昔記事に書いたと思いますが、shadcn ui の場合、radix ui の仕様だったかもしれませんが

Modal開いた後の黒背景、つまりモーダル外クリックをするとその時点でModalにセットしたonOpenChangeに

引数falseが渡されて呼ばれます。

普通の場合だとonOpenChangeには

大抵 setIsOpenのような感じで引数にtrue/false与えたらisOpenもtrue/falseとなるような

単純なものを渡すので機能するのですが、今回のような特殊の場合には気を使う必要があります。

github.com


次が型定義に関するベストプラクティスについてですね。

これに関しては自分が思いついたというかみんなで考えて多分そうじゃね、となった結論です。

TypeScriptにおいてデータの型を決める時、

  • データベースのスキーマをもとに基本のデータの型をまず定め、
  • レスポンスごとに色々組み合わさっているものを対応するときはその基本の型を extendsして作る

です。

今までRailsのシリアライザで"そのページのためのAPI"で情報を返し、

そのレスポンスデータを扱うための型をそのまま書いていたので、

拡張性に欠けていました。

ページのデザインが変わったなんて時にはその型を変えないといけません。

ただその型は他のページでも使ってるからあっちも直さないと、

うわこんなに直さないと。そうだ、暫定でこんな型作っちゃおう、、、、

みたいな積み重なりで最近手をつけたのですが、カオスでした。

今、頑張ってみんなで直していますが、修羅の道です。

ただ良い学びとなったのでこれを教訓にしていきたいと思います。

Backend

個人開発を進めている中で一番ためになったのは認証周りについてです。

あとはMVCだけでは賄いきれないと気づけたことくらいですね。

MVCって便利ですよね。

Rails経験者が通るべき道だと思います。

ただ最近それだと不便なことに気づきました。

それはコントローラとかが肥大化しがちってことです。

色々concernとか使ってやるのですが、どうもしっくりこなくて。

実務は全然そんなことないので、わかったつもりになっていたのですが

自分で書き始めるとどうしてもfat なコントローラとなってしまっていました。

そこでAIとかに聞いて調べてみると

新しく アクションおよびサービスを導入した方がいいらしく。

で試しに導入してみるとコントローラが"受け取ったリクエストを

他に回して自分はその結果だけをもらい、返すだけ"の

本来あるべき姿を取り戻せました。

やっぱり色々自分で作ってみることですね。


あとはオートローダについてですか。

Railsにはオートローダがあることはご存知ですか?

というかそもそもRubyにおいて他ファイルで定義した内容を自ファイルで

呼び出す方法ってご存知ですか。

それはそのファイルでrequire使います。

でもRailsではどのファイルにもrequireしないでも User.newとかして使えますよね。

それはなぜかというとオートローダという仕組みがあらかじめ読み込んでくれているからです。

また合わせて名前空間というのもカスタムで定義することができます。

詳しくは記事書いたのでそちらを参照くださいませ。

終わりに

書いていて思いました。

内容が薄い。

ちょっと慣れ始めて書き方が雑になってますね。

来月からもう少しやり方を考える必要があります。

どうしましょうか。

知ることができた技術ともっと初めての人にもわかるような丁寧な説明で書くようにしますか。

これは要反省ものです・・・。

ということでレッツエイプリル。

また来月。