
t0mmy
最新の制作課題
最近ブックマークしたページ
直近の学習履歴
リファクタリング 第二版 1章 読了
やったこと リファクタリング 第二版 1章 読了 学んだこと リファクタリングは、人間のために行う。 優れたプログラマは、人間にとってわかりやすいコードを書く。 リファクタリングでやること 小さな単位に分割する フェーズを分割する ポリモーフィズムを駆使して処理を動的に変化させる (順不同だけど、上からやる方がやりやすそう。また、対応済みだったり対応不要だったりする場合はスキップする) 小さな単位に分割する 長い処理を、意味のある小さな単位に分割する。 関数化を駆使するとやりやすい。 ... let thisAmount = ...; // 長い長い処理 ... const amountFor = () => {...}; /// 長い長い処理を関数化 ... let thisAmount = amountFor(); ... ローカル変数は、「小さな単位に分割する」際の障害になりやすい。 ローカル変数の排除を心掛けてリファクタリングしていくと、小さな単位に分割しやすい。 (=> 変数のインライン化) フェーズを分割する 小さな単位に分割したコンポーネントを、取り回しの効くような意味のある単位にまとめていく。 この時、最も大雑把なまとめ方として、「入力処理」「計算」「出力処理」がある。 ポリモーフィズムを駆使して処理を動的に変化させる 成し遂げたいこと(例: 料金を計算する)は同じだけど、分類によって計算ロジックを変化させたい(例: 通常価格、特売価格、会員価格など)ような状況。 計算ロジックをサブコンポーネントに委譲し、メインロジックを親コンポーネントでまとめ上げる... といった設計を行う。 これを実現するキーワードの一つが ' ポリモーフィズム ' 。 リファクタリングの第一歩 テストを作成する リファクタリングの度にコミットする 変数、関数などに、適切な名前を付ける テストを作成する リファクタリングによるコードの変更よって、新たにバグ(≒振る舞いが変化してしまうこと)が混入する事は十分考えられる。 このバグを早期に発見するためには、「振る舞いが変化していないこと」を検証する手段が必要。 テストは、この検証手段に相当する。 テストを作成することで、「振る舞いが変化していないこと」を常に確認できる。 振る舞いが変化していたとしても、それは「テスト失敗」という形である程度検知できる。 リファクタリングの度にコミットする リファクタリングに成功(≒コードを修正して、テストもパス)したタイミングでコミットする。 小さな単位でコミットしておくことで、万が一リファクタリングに失敗した場合でも、動いていた状態に戻すことが出来る。 コミット単位が小さいと、コードのロールバック量が少なくなり、開発効率が上がる。 意味のある単位としてまとまったてから、共有のリポジトリに変更をプッシュすると良い。 変数、関数などに、適切な名前を付ける 適切な名前を付けられた変数は、中身の値が容易に特定できる。 適切な名前を付けられた関数は、中身の処理が容易に想像できる。 名前が適切であれば、「コードを読んで中身を理解する」手間を省く。 開発効率の向上に寄与すると同時に、バグの発生を防ぐことにもつながる。 良いコードとは 好みによる部分も大きいが、「どれだけ変更が容易か」(≒どれだけソフトか)という視点は、共通してそう。
2024年08月25日(日)
2.0時間
記事執筆
やったこと 記事執筆 https://zenn.dev/t0mmy/articles/my-understanding-of-null-in-rdb 学んだこと RDB における Null のあれこれ(詳しくは記事参照)
2024年08月25日(日)
1.0時間
実践Next.js 10章 パフォーマンスとキャッシュ 読了
やったこと 実践Next.js 10章 パフォーマンスとキャッシュ 読了 学んだこと 並列化でパフォーマンス改善 依存関係にないデータの取得は、 並行でデータを取得することで、パフォーマンスを改善できる。 ◆直列取得 const data1 = await getData1(); const data2 = await getData2(); const data3 = await getData3(); ◆Promise.all による並列取得 const [data1,data2,data3] = await Promise.all([ getData1(), getData2(), getData3() ]) ◆子サーバーコンポーネントに分割して並列取得 import { data1_component } from "..." import { data2_component } from "..." import { data3_component } from "..." ... return ( <> <data1_component /> <data2_component /> <data3_component /> </> ) データ取得処理用に 子 Server Component を用意し、データ取得処理を委譲する。 コンポーネント構造でも、データを並列取得するため、パフォーマンス改善が見込める。 可読性向上や再利用性向上といったメリットもある Server Component の動作に必要なデータが依存関係にない場合、子 Server Component に分割すると良い。 fetchCache の設定 fetch 関数のキャッシュデフォルト設定は auto。 これは、「動的関数が使用された」ことを起因に、キャッシュの挙動を切り替えるというもの。 fetchするデータと無関係な動的関数が使用された場合でも、動的データ取得扱いされ、リクエストが発生する恐れがある。 無関係な動的関数の例 : ログインしているユーザーの情報を取得するための getServerSession 関数 この場合、 fetch 関数の キャッシュ設定を force-cache とすると、常に静的データ取得扱いされ、常にキャッシュされるようになる。 静的 Route を増やす実装 無関係な動的関数の使用、静的Route であってほしい Route が、意図せず動的 Route になってしまうことがある。 ログインしているユーザーの情報を取得するための getServerSession 関数など Page のほかに、Layout.tsx で動的関数を使用している場合でも、動的 Route 扱いされる 動的 Route の原因となっている、 動的関数を取り除くために、以下の方法が採用できる。 動的関数を使用する部分を Client Component として 別コンポーネントに切り出す 動的関数を含む処理を CSR にて処理するよう修正することで、Route が 静的 Route を増やし、パフォーマンス改善につなげることが出来る。 ただし、動的データ取得処理が多い場合、 Server Component でまとめて取得する 動的 Route の方が、結果的に高パフォーマンスに繋がることもある。 動的関数 in CSR + 静的Route が、常に最適解というわけではない。 SSG Route の実装 予め Route のレンダリング結果を出力する SSG (またはISR)は、パフォーマンス改善において効果の高い手法。 SSG Route を提供する generateStaticParams 関数を使用する。 SSGしたいパスのパスパラメータを定義する関数 generateStaticParams 関数を export することで、 Next.js に認識される ビルド時にしか実行されない 例) export function generateStaticParams() { return [{ id: '1' }, { id: '2' }, { id: '3' }] } // Three versions of this page will be statically generated // using the `params` returned by `generateStaticParams` // - /product/1 // - /product/2 // - /product/3 export default function Page({ params }: { params: { id: string } }) { const { id } = params // ... } /product/1 、 /product/2 、 /product/3 は、SSGにより、ビルド時に事前レンダリングされる。 他のパラメータ( /product/4 , /product/5 など)は、リクエストが来たらレンダリングされ、レスポンスを返すと同時にSSGが行われる。 generateStaticParams 関数は、非同期として定義することもできる。 非同期関数として定義し、DBから SSGしたいID情報を取得し、return に設定する ... といった使い方が想定される。 generateStaticParams に複数の値を持たせたい場合、パラメータ部分を配列にするだけ。 export function generateStaticParams() { return [{ slug: ['a', '1'] }, { slug: ['b', '2'] }, { slug: ['c', '3'] }] } パスパラメータとなる変数が複数存在する場合、その数だけ key-value を定義する export function generateStaticParams() { return [ { category: 'a', product: '1' }, { category: 'b', product: '2' }, { category: 'c', product: '3' }, ] } // Three versions of this page will be statically generated // using the `params` returned by `generateStaticParams` // - /products/a/1 // - /products/b/2 // - /products/c/3 export default function Page({ params, }: { params: { category: string; product: string } }) { const { category, product } = params // ... } SSG を考慮したURI設計 パスにクエリパラメータを含め、 searchParams()を使用する ... というコードだと、 searchParams() が動的関数のため、動的Route と判断される。 categories/flower?page=1 など これを避けてSSG Route とした扱いたい場合、クエリパラメータではなくパスパラメータへ変更する...といった改善策がある。 categories/flower/1 など 参考 : https://nextjs.org/docs/app/api-reference/functions/generate-static-params#single-dynamic-segment Route Segment Config Route Segment 単位で、レンダリングやキャッシュ設定を一括制御できる機能。 特定の Route Segment に設定すると、 Subtree 全てに、設定内容を反映できる。 以下のように、変数をexport するだけで一括指定出来る。 export const revalidate = false // false | 'force-cache' | 0 | number SSG Route パフォーマンスの定量評価 以下の評価項目がある Time To First Byte (TTFB) サーバーからブラウザに、最初のデータが届くまでの時間 Largetct Contentful Paint (LCP) ブラウザの中で、最も大きなコンテンツが表示されるまでの読み込み時間 Lighthouse スコア https://chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=ja いずれも、開発者ツールで確認できる。 Next.js によるアセットの最適化 画像の最適化 <Image> コンポーネントの使用 Next.js が提供する <Image> コンポーネントを使用する。 これは html の <image> 要素を拡張したコンポーネントであり、以下の最適化が自動で適用される。 容量の最適化 : 配信に最適な画像形式(webpやavifなど)へ変換 視覚上の安定化 : 画像読み込み時、レイアウトずれの発生を自動防止 遅延読み込み : 初期表示ではプレースホルダー を表示し、ビューポートに入った時に読み込みを行う プレースホルダー表示ため、 alt 属性が必須となっている(空文字列でもOK。基本は画像を説明する文字列) 画像サイズ変更 : 画像サイズをオンデマンドで変更する 更に最適化された画像ファイルは、Next.js によってキャッシュされる。 キャッシュの格納先は .next/cache/images フォルダ 画像サイズ指定 画像サイズを指定することにより、 「Layout Shift」 というパフォーマンス問題を回避できる。 ◆Layout Shift 画像読み込みによって、他のHTML要素を動かしてしまう事象。 画像表示に必要なスペースを、事前に設定しておくことで回避できる。 画像サイズの設定必須。 優先読み込みを指定できる priority props priority を追加すると、追加された<Image>の画像は、優先的に読み込むことが出来る。 これにより、LCP向上につなげることが出来る。 画像の読み込み先を制限する 「信頼できる画像読み込み先」を指定し、信頼できない画像読み込み先との通信をブロックする。 これは、next.configにて設定する。 { const nextConfig = { images: { remotePatterns: [ { protocol: "https", hostname: "lh3.googleusercontent.com", }, { protocol: "http", hostname: "127.0.0.1", }, ], }, }; export default nextConfig; } 画像フォーマット指定 next.config にて、画像形式を指定できる。 const nextConfig = { images: { formats:['image/avif','image/webp'] } } 未指定の場合、 image/webp が採用される。 画像のキャッシュ時間を指定する 最適化された画像は Next.js によってキャッシュされる。 このキャッシュ時間を、 next.config にて指定できる。 const nextConfig = { images: { minimumCacheTTL: 60 } } 画像キャッシュを無効化することはできない。 キャッシュ保持期間を可能な限り短くすることで、暫定対応する。 キャッシュ時間は、上記 minimumCacheTTL の値と、サーバーレスポンスの max-age Cache Control ヘッダの内、大きい値が採用される。 フォントの最適化 next/font を使用すると、あらゆる Font ファイルのセルフホスティングが可能。 従来のようにFontファイル とCSSファイルの準備が不要になる。 サブセットの指定 Google Font は、自動的にサブセット化できる。 ◆サブセット化 使用している文字だけ抜き出しフォント化すること サブセット化により、Fontファイルのサイズを削減し、パフォーマンス向上が見込める。 const inter = Inter ({subsets: ["latin"]) CSS Variables の適用 外部CSS を読み込み、一部オプションを追加して使いたい場合は、CSS Variables を使用する。 import { Inter } from "next/font/google"; import styles from "../styles/component.module.css"; const inter = Inter({ variable: "--font-inter", }); スタイルを設定するテキストの「親コンテナのclassName」 をフォントローダーの変数値に設定する。 そして、テキストのclassNameを外部CSSファイルのセレクターが適用されるように設定する。 <main className={inter.variable}> <p className={styles.text}>Hello World</p> </main> そして、外部CSSファイル(component.module.css)で、以下のようにCSS Variables を使用する。 .text { font-family: var(--font-inter); font-weight: 200; font-style: italic; } フォント定義ファイルの使用 フォントファイルは、ロードする度にインスタンス生成処理が走る。 何度もインスタンス生成処理が走るのは非効率。 そのため、フォントファイルのロード処理は一か所にまとめておき、 exportする。 他のコンポーネントは、importするだけにしておく。 サードパーティスクリプト読み込みの最適化 Layout Script 複数のRoute でサードパーティスクリプトを読み込む場合は、 next/script をインポートし、 <Scriptl> コンポーネントを Layoutコンポーネントに含めるよう定義する。 これにより、 Layout側で一度スクリプトをダウンロードするだけで、 Subtree でもスクリプトを使用できる。 Root Layout に含めることで、全ての Route で使用するスクリプトを定義する事もできる。 不必要なスクリプトのダウンロードは、パフォーマンスの悪化につながる。 基本的に、サードパーティスクリプトのダウンロードは、スクリプトが必要な特定のページまたはサブコンポーネントで行う。 全ページで必要なサードパーティスクリプトのみ、 Root Layout でダウンロードする。 Next.js の 4種類のキャッシュ Next.js は、レンダリング結果とデータ取得結果をキャッシュする。 以下の順番でキャッシュする。 Request のメモ化 Data キャッシュ Full Route キャッシュ Router キャッシュ Request のメモ化 fetch 関数の戻り値(取得データ)を一時的にキャッシュする。 サーバー上で重複して呼び出された fetch 関数は、キャッシュした戻り値を再利用する。 同一のfetch関数リクエスト排除を目的としており、これを「メモ化」と呼ぶ。 これは、fetch 関数を使用して同一のリクエストを試みても、リクエストは1回のみに抑えることを意味する。 これにより、コンポーネント単位でデータ取得関数を呼びだしても、キャッシュが機能するようになる。 このメモ化は、レンダリング中のみ有効。 言い換えると、レンダリングが完了すると、メモ化したデータはリセットされる fetch 関数以外で Request をメモ化したい場合は、 React の cache 関数を使用する。 Data キャッシュ ビルド時、fetch 関数の取得データを永続的にキャッシュする。 当然、変更のない "静的データ" がキャッシュの対象となる。 キャッシュの有効期限は、 Time-based Revalidation の設定で設けることが出来る。 また、On-demand Revalidation にて、能動的に無効化する事もできる。 force-cache されたデータは、再ビルド時でもキャッシュは削除されず、ずっと再利用され続ける。 ローカル開発だと、Dataキャッシュを直接削除する( .next/cache/fetch-cache 配下)ことで、キャッシュを削除できる。 以下のように設定することで、プロジェクトを通して「キャッシュを使用しない」ことを設定できる。 // Segment Subtree 配下のデータ取得を、全て動的へ export const dynamic = "force-dynamic"; // Segment Subtree 配下のデータ取得時、一切キャッシュしない export const revalidate = 0; fetch 関数以外で Data キャッシュを作成したい場合、Next.js の unstable_cachce 関数を使用する。 第一引数に渡した非同期関数の戻り値が、キャッシュされるようになる。 Full Route キャッシュ 各 Route のレスポンスを永続的にキャッシュする。 Full Route キャッシュが存在せず、Routeでデータ取得がある場合、「Data キャッシュ」を経由してDataソースにアクセスする。 静的サイト生成(SSG)や静的レンダリングファイルのこと。 キャッシュはビルド時、またはリクエスト時に作成される。 再ビルドするまで、ずっと残る。 言い換えると、Full Route キャッシュを削除するには、再ビルドが必要 ただし、 Dataキャッシュの Revalidate が発生すると、関連する Full Route キャッシュも無効になる。 無効になった Full Route キャッシュは、 Dataキャッシュが作成されるときに、同時に作成される。 Router キャッシュ ブラウザにて保持するキャッシュ。 主な目的は、「戻る」「進む」ボタン処理を高速にしつつ、データアクセスの頻度を削減すること。 一定期間が過ぎると、部分的なRouter キャッシュは自動で無効になる。 キャッシュの有効期間は以下の通り。 動的レンダリングRoute : 30s <Link> コンポーネントの props に prefetch={true} を使用すると、有効期間が5分に延長される 静的レンダリングRoute : 300s (5分) <Link> コンポーネントの props に prefetch={false} を使用すると、有効期間が30秒に短縮される この有効期間は切り替え可能。 能動的にキャッシュを無効化するには、以下のいずれかの手段を取る。 ブラウザで router.refresh を実行する http リクエストが二往復必要になる Server Action 内で coolies.set または cookies.delete を使用する 古い Full Route キャッシュやDataキャッシュにヒットし、意図しない結果につながる可能性がある Server Action 内で on-demand revalidation を実施する 推奨
2024年06月02日(日)
3.0時間
取得資格・スキル名
Scrum
■知識レベル Exin Agile Scrum Foundation 合格レベルの知識 以下を読了 * https://www.amazon.co.jp/dp/4798129712 ■経験レベル Scrum開発による実務経験3年
ドメイン駆動設計
以下を読了 * https://www.seshop.com/product/detail/20675 * https://www.seshop.com/product/detail/17776 * https://www.amazon.co.jp/dp/483997599X
プログラミング技術
■知識レベル 以下を読了 * https://www.amazon.co.jp/dp/4274226298 * https://www.amazon.co.jp/dp/4798046140 * https://www.amazon.co.jp/dp/4873115655 * https://www.amazon.co.jp/dp/4873118867
セキュリティ
■知識レベル 安全確保支援士 合格相当の知識 Certified Ethical Hacker 合格相当の知識 以下を読了 * https://www.amazon.co.jp/dp/4797393165 * https://www.amazon.co.jp/dp/479732158X * https://www.amazon.co.jp/dp/4822284972
プログラミング言語 Java
■知識レベル Oracle Java8 Gold 合格相当の知識 * 基礎文法、クラス設計、I/Fを用いた疎結合設計、lambda・Streaming APIを用いた実装 以下の書籍を読了 * https://www.amazon.co.jp/dp/4621303252 * https://www.amazon.co.jp/dp/4844336673 ■経験レベル 大学院時代の研究用ソフトウェアを、すべてJavaで開発。
AWS - 設計
■知識レベル AWS認定 ソリューションアーキテクト プロフェッショナル 合格レベルの知識 AWS認定 専門知識 セキュリティ 合格レベルの知識 AWS認定 専門知識 データ分析 合格レベルの知識 AWS認定 専門知識 高度なネットワーキング 合格レベルの知識 ■経験レベル AWSサービスを使用したデータ分析基盤構築 1年 * S3、Glue、Redshift、Athena、AWS Batch、Lambda、SAM
Web
■知識レベル 以下を読了 * https://www.amazon.co.jp/dp/4774142042 ■経験レベル
運用・監視・保守
■知識レベル 以下を読了 * https://www.oreilly.co.jp/books/9784873118642/
Linux
■知識レベル 以下を読了 * https://www.amazon.co.jp/dp/4297120240 ■経験レベル cat , grep , awk , sed , uniq , sort 辺りを用いた、基本的なテキスト処理 cron、シェルスクリプトを活用した定期処理の実装 * シェルスクリプトは、必要に応じて適宜書く程度の経験レベル EC2(amazon linux、RHEL 6/7)の構築
ネットワーク
■知識レベル 以下を読了 * https://www.amazon.co.jp/dp/4274224473
AWS - 開発
■知識レベル AWS認定 デベロッパー アソシエイト 合格レベルの知識 AWS認定 Sysops エンジニア アソシエイト 合格レベルの知識 AWS認定 専門知識 データベース 合格レベルの知識 以下を読了 * https://www.sbcr.jp/product/4815607654/