【テクニカル・上級編】React Server Components(RSC)対応!ESLintでサーバー・クライアント境界を強制する設計パターン – デバッグ・コード品質・テストツール生産性向上バイブル

RSC時代の静的解析:境界線をコードで強制する「防壁」の設計思想

Next.jsのApp Routerが登場して以来、我々エンジニアが直面している最大の敵は「ランタイムのブラックボックス化」だ。サーバーコンポーネント(RSC)でHooksを呼んでしまい、ブラウザで不意にクラッシュする。あるいは、意図せず重いライブラリをクライアント側にバンドルしてしまう。

これらを「ドキュメントの遵守」や「個人の注意」に委ねるのは、DevOpsの観点からは怠慢に等しい。システムで防げないエラーは、必ずどこかで顕在化する。今日は、ESLintを単なるリンターではなく、「アーキテクチャの強制執行エンジン」として昇華させる手法を解説する。

—

1. 境界防衛:`eslint-plugin-react-hooks` の限界を超えて

標準的な `react-hooks/rules-of-hooks` だけでは、RSCにおける「サーバー/クライアント境界」の違反は防げない。我々が構築すべきは、物理的に「サーバー側で実行不可能なコード」を静的解析レベルで拒絶する防壁だ。

構成の核となるカスタムルール

`@next/eslint-plugin-next` を活用するのは大前提だが、さらに一歩踏み込み、特定のディレクトリ配下では特定のAPI(`useState`や`useEffect`等)をコンパイル以前に「構文エラー」として弾く設定を導入する。

// .eslintrc.js
module.exports = {
overrides: [
{
// サーバーコンポーネントが配置されるディレクトリを特定
files: [‘src/app//page.tsx’, ‘src/app//layout.tsx’],
rules: {
// Hooksの利用を物理的に封殺する
‘react-hooks/rules-of-hooks’: ‘error’,
// サーバーコンポーネントでのクライアントサイドAPI使用を検知
‘no-restricted-imports’: [‘error’, {
paths: [{
name: ‘react’,
importNames: [‘useState’, ‘useEffect’, ‘useContext’],
message: “RSC内でHooksは利用できません。’use client’が必要です。”
}]
}]
}
}
]
};

—

2. パイプラインにおける「静的解析の最適化」ハック

CI/CDにおいて、ESLintはしばしばボトルネックになる。数万行のコードベースを毎回全走査するのは無駄の極みだ。ここで、「差分のみを解析する」アーキテクチャを導入する。

Git差分ベースの並列実行

単なる `eslint .` ではなく、`lint-staged` をCI環境へ拡張し、かつ `cache` オプションを極限まで活用する。

CI環境での実行コマンド設計
–cache: 変更のないファイルの解析結果をキャッシュ
–cache-strategy content: ファイルの内容ハッシュでキャッシュを判定
–parallel: CPUコアをフル活用して並列解析
eslint –ext .tsx,.ts . –cache –cache-strategy content –parallel –max-warnings 0

この設定により、CIの実行時間はリニアに縮小する。さらに、Docker環境では `node_modules/.cache` をビルドステージ間で共有(`–mount=type=cache`)することで、コールドスタート時を除き、数秒で解析を完了させることが可能になる。

—

3. 境界違反を「可視化」する独自CLIスクリプト

静的解析をすり抜けるような、より抽象度の高い境界違反(例:`use client`を忘れたファイルが、特定のプロトコル経由で呼び出される等)を監視するためには、AST(抽象構文樹)を直接叩く独自スクリプトが有効だ。

// scripts/check-rsc-boundaries.js
const fs = require(‘fs’);
const glob = require(‘glob’);

/

  • ‘use client’が存在しないファイルで、特定の副作用関数を検知する
  • 複雑なルールはESLintのプラグイン作成より、この手のスクリプトの方がメンテナンス性が高い

/
const files = glob.sync(‘src/components//.tsx’);
files.forEach(file => {
const content = fs.readFileSync(file, ‘utf-8’);
if (!content.includes(‘”use client”‘) && content.includes(‘useEffect’)) {
console.error(`[FATAL] Boundary Violation: ${file} に ‘use client’ がありません。`);
process.exit(1);
}
});

これをCIパイプラインの `pre-build` フェーズにフックさせる。これにより、ESLintの柔軟性と、カスタムスクリプトによる「絶対的な禁止事項」の二段構えで防壁を構築できる。

—

4. アーキテクトへの提言:なぜこれが必要か

我々が目指すべきは「エラーが出ないコード」ではなく、「エラーが出ないシステム(仕組み)」だ。

RSCは強力だが、その複雑性はエンジニアの認知負荷を激増させる。しかし、上記のような静的解析の強制と、パイプラインによる自動検知を組み込めば、開発者は「RSCであるか否か」という低レイヤの懸念から解放され、本来注力すべきビジネスロジックに集中できる。

「ツールにルールを教え込むな。ルールをツールの中に埋め込め。」

これが、大規模開発の荒波を乗り越えるための唯一の道だ。この設定を導入した瞬間、あなたのチームのデバッグコストは劇的に低下し、コードベースはより堅牢な「自己防衛するシステム」へと進化するだろう。さあ、今すぐパイプラインにこの楔を打ち込んでほしい。

タイトルとURLをコピーしました