<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>React &#8211; エンジニア見習い</title>
	<atom:link href="https://otonan-syusyoku.work/archives/tag/react/feed" rel="self" type="application/rss+xml" />
	<link>https://otonan-syusyoku.work</link>
	<description>三流プログラマー</description>
	<lastBuildDate>Tue, 28 Oct 2025 05:24:52 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.3</generator>

<image>
	<url>https://otonan-syusyoku.work/wp-content/uploads/2023/10/cropped-名称未設定のデザイン-16-32x32.png</url>
	<title>React &#8211; エンジニア見習い</title>
	<link>https://otonan-syusyoku.work</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Laravel+Inertia+ReactをECSで動かしたい！ NginxとPHP-FPMのコンテナ分離</title>
		<link>https://otonan-syusyoku.work/archives/2153</link>
					<comments>https://otonan-syusyoku.work/archives/2153#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Tue, 28 Oct 2025 05:24:52 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[ネットワーク]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2153</guid>

					<description><![CDATA[Laravel + Inertia (React/Vue) の組み合わせは、SPA（シングルページアプリケーション）の体験とサーバーサイド（Laravel）の書きやすさを両立できる、非常に強力な構成です。 しかし、ローカ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Laravel + Inertia (React/Vue) の組み合わせは、SPA（シングルページアプリケーション）の体験とサーバーサイド（Laravel）の書きやすさを両立できる、非常に強力な構成です。</p>
<p>しかし、ローカルのDocker Compose環境では問題なく動作しても、AWSのECS Fargateのような本番環境にデプロイしようとすると、いくつかの疑問に直面します。</p>
<ul>
<li>「NginxとPHP-FPMコンテナはどう連携させるのがベスト？」</li>
<li>「<code>fastcgi_pass</code> ってローカルと設定を変える必要ある？」</li>
<li>「InertiaでビルドしたReactのJS/CSSファイルは、結局どのコンテナに置くのが正解？」</li>
</ul>
<p>この記事では、<code>fastcgi_pass</code> の基本から、ECS Fargateでの最適なコンテナ構成、そしてInertia/Reactプロジェクト特有の「静的ファイルの配置場所」問題までを、順を追って解説します。</p>
<h2>fastcgi_pass とは？</h2>
<h3>NginxとPHP-FPMの「橋渡し」</h3>
<p>まず基本のおさらいです。Nginxは高性能なWebサーバーですが、それ自体はPHPコードを実行できません。<br />
一方、PHP-FPMはPHPコードを実行することに特化したプロセス（サーバー）です。</p>
<p>Nginxがクライアント（ブラウザ）から <code>.php</code> ファイルへのリクエストを受け取ったとき、そのリクエストをPHP-FPMに処理してもらう必要があります。</p>
<p>このとき、Nginxの設定ファイル（<code>nginx.conf</code>）で、<b>「どのPHP-FPMに処理を依頼するか」の宛先を指定する</b>のが <code>fastcgi_pass</code> ディレクティブです。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-470 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-470">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-470">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-470"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-470"><span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># ↓ この一行が「橋渡し」の指定</span>
    <span class="hljs-attribute">fastcgi_pass</span> php-fpm-server:<span class="hljs-number">9000</span>; 
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h2>ローカル開発 (Docker Compose) での構成</h2>
<p>ローカル開発では、「1コンテナ1責務」の原則に従い、NginxとPHP-FPMを別々のコンテナとして <code>docker-compose.yml</code> で定義するのが一般的です。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-471 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-471">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-471">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-471"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-471"><span class="hljs-comment"># docker-compose.yml (抜粋)</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">'3'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-comment"># Nginxコンテナ</span>
  <span class="hljs-attr">nginx:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:alpine</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"80:80"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./nginx.conf:/etc/nginx/conf.d/default.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./laravel-project:/var/www/html</span>

  <span class="hljs-comment"># PHP-FPMコンテナ</span>
  <span class="hljs-attr">php:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span> <span class="hljs-comment"># PHP-FPMとLaravelコードを含むDockerfile</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./laravel-project:/var/www/html</span>
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>この構成では、NginxコンテナとPHPコンテナは <b>別々のネットワーク空間</b> に存在します。Docker Composeが提供する内部ネットワークを介して、サービス名で通信します。</p>
<p>したがって、NginxコンテナからPHPコンテナへは、<code>php</code> というサービス名（ホスト名）を使ってアクセスします。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-472 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-472">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-472">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-472"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-472"><span class="hljs-comment"># nginx.conf (Docker Compose用)</span>
<span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># 'php' サービス（コンテナ）の 9000番ポートに転送</span>
    <span class="hljs-attribute">fastcgi_pass</span> php:<span class="hljs-number">9000</span>;
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>（※ このTCP/IP通信によるパフォーマンスやセキュリティの懸念は、コンテナ間通信がDockerの内部ネットワークに限定されていれば、実用上ほとんど問題になりません。）</p>
<h2>【1タスク2コンテナ】本番 (ECS Fargate) での構成：</h2>
<p>さて、本題のECS Fargateです。<br />
ECSでは、<code>docker-compose.yml</code> に相当するものとして「タスク定義 (Task Definition)」を使います。</p>
<p>ここで重要なキーポイントは、<strong>「1つのタスク定義の中で、NginxコンテナとPHP-FPMコンテナの2つを定義する」</strong>ことです。</p>
<p><strong>これら2つのコンテナは、1セットで「Webアプリケーションサーバー」</strong>として機能します。</p>
<h3>ECS Fargateでのfastcgi_passはどうなる？</h3>
<p>ECSの同一タスク内で実行されるコンテナは、<b>同じネットワーク空間（ネットワークモード <code>awsvpc</code>）を共有します</b>。</p>
<p>これは、Nginxコンテナから見ると、PHP-FPMコンテナが「他人」ではなく、「自分自身（localhost）」として見えることを意味します。</p>
<p>したがって、ECS Fargate用のNginxイメージに含める <code>nginx.conf</code> の設定は、以下のようになります。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-473 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-473">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-473">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-473"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-473"><span class="hljs-comment"># nginx.conf (ECS Fargate用)</span>
<span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># サービス名ではなく、localhost (127.0.0.1) を指定する</span>
    <span class="hljs-attribute">fastcgi_pass</span> <span class="hljs-number">127.0.0.1:9000</span>;
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h3>タスク定義のイメージ</h3>
<p>タスク定義では、以下のように設定します。</p>
<ul>
<li><b>コンテナ-A: <code>php-fpm-container</code></b>
<ul>
<li>イメージ: （Laravelコードを含むPHP-FPMイメージ）</li>
<li>ポートマッピング: <b>なし</b> (外部に公開する必要はないため)</li>
</ul>
</li>
<li><b>コンテナ-B: <code>nginx-container</code></b>
<ul>
<li>イメージ: （設定済みのnginx.confと静的ファイルを含むNginxイメージ）</li>
<li>ポートマッピング: <b><code>80:80</code></b> (ALBからのトラフィックを受け取るため)</li>
</ul>
</li>
</ul>
<p>ALB（ロードバランサー）からのトラフィックはNginxコンテナが受け取り、必要に応じて <code>localhost:9000</code> を介してPHP-FPMコンテナに処理を渡します。</p>
<h2>Inertia/Reactのビルドファイルはどこに置く？</h2>
<p>ここで、Inertia/React構成特有の問題に直面します。</p>
<p>「InertiaはLaravel（PHP）がビューを配信する仕組みだから、<code>npm run build</code> で生成された <code>public/build</code> フォルダは、PHP-FPMコンテナにだけ置けば良いのでは？」</p>
<p>これはよくある誤解ですが、<b>パフォーマンスと責務分離の観点から、ビルドした静的ファイル（JS/CSS）はNginxコンテナに配置するのが正解</b>です。</p>
<p>この理由を理解するために、Inertiaのページが読み込まれる2段階のフローを見てみましょう。</p>
<h3>ステップ1: HTMLシェルのリクエスト (PHP-FPMが処理)</h3>
<ol start="1">
<li>ブラウザが <code>https://example.com/dashboard</code> にアクセスします。</li>
<li>ALB → Nginxコンテナがリクエストを受け取ります。</li>
<li>NginxはこれがPHPのリクエストだと判断し、<code>fastcgi_pass 127.0.0.1:9000</code> を使って<b>PHP-FPMコンテナ</b>に転送します。</li>
<li>PHP (Laravel) が起動し、Inertiaは <code>app.blade.php</code> をレンダリングしようとします。</li>
<li>このとき、PHPは <b><code>public/build/manifest.json</code></b> を読み取り、HTMLに含めるべきJS/CSSのファイル名を特定します。</li>
<li>PHPは以下のようなHTMLの「ガワ」を<b>生成</b>し、Nginx経由でブラウザに返します。
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c535465467="" class="ng-tns-c535465467-474 ng-star-inserted"></p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-474 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-474">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-474">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-474"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-474"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/build/assets/app.12345.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/build/assets/app.67890.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">data-page</span>=<span class="hljs-string">"..."</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
</div>
</div>
</div>
<p></code-block></response-element></li>
</ol>
<h3>ステップ2: 静的アセット(JS/CSS)のリクエスト (Nginxが処理)</h3>
<ol start="1">
<li>ブラウザは、ステップ1で受け取ったHTMLを解析します。</li>
<li>「<code>/build/assets/app.12345.js</code> と <code>/build/assets/app.67890.css</code> が必要だ」と判断し、ブラウザは<b>別途2回のリクエスト</b>をサーバーに送信します。</li>
<li><b>このリクエストは、もはやPHPとは全く関係ありません。</b></li>
<li>ALB → <b>Nginxコンテナ</b>がこの2つのリクエストを受け取ります。</li>
<li>Nginxは「自分の管理下（<code>public</code> フォルダ）にそのファイルがあるか？」を探します。</li>
<li><b>Nginxコンテナに <code>public/build/</code> 以下の実体ファイルが存在するため</b>、NginxはPHP-FPMを起動することなく、それらの静的ファイルを<b>超高速で</b>ブラウザに直接返します。</li>
</ol>
<p>もし、NginxコンテナにJS/CSSファイルがなければ、このリクエストもPHP-FPMに転送されてしまい、「JSファイルを取得するためだけにLaravelを起動する」という深刻なパフォーマンスボトルネックが発生します。</p>
<h3>CI/CDでのベストプラクティス</h3>
<p>上記2ステップから、CI/CDパイプラインでDockerイメージをビルドする際は、以下の構成が最適です。</p>
<ol start="1">
<li><code>npm run build</code> を実行し、<code>public/build</code> ディレクトリを生成します。</li>
<li><b>PHP-FPMイメージのビルド:</b>
<ul>
<li>Laravelのコード（<code>app/</code>, <code>routes/</code> など）をコピーします。</li>
<li>ステップ1のために <b><code>public/build/manifest.json</code></b> をコピーします。（リンク生成に必要）</li>
</ul>
</li>
<li><b>Nginxイメージのビルド:</b>
<ul>
<li><code>nginx.conf</code> をコピーします。</li>
<li>ステップ2のために <b><code>public</code> フォルダ全体</b>（<code>index.php</code> と、<code>public/build</code> 以下の <b>全てのJS/CSSファイル</b> を含む）をコピーします。（静的アセットの配信用）</li>
</ul>
</li>
</ol>
<h3><b>結論</b></h3>
<p><code>manifest.json</code> はPHP-FPMコンテナに、JS/CSSの実体ファイルはNginxコンテナに必要です。</p>
<p>両方のイメージに <code>public</code> フォルダ（または <code>public/build</code>）全体をコピーするのが、最もシンプルで確実な方法です。</p>
<h2>まとめ</h2>
<p>ECS Fargateで Laravel + Inertia + React 構成を動かすための要点をまとめます。</p>
<ol start="1">
<li><b>構成</b>: 「1タスク2コンテナ（Nginx + PHP-FPM）」構成を採用します。</li>
<li><b><code>fastcgi_pass</code></b>: NginxからPHP-FPMへの通信は、同一タスク内のため <code>fastcgi_pass 127.0.0.1:9000;</code> を指定します。</li>
<li><b>静的ファイル</b>: ビルドしたJS/CSSは、パフォーマンス最適化のためNginxコンテナに配置し、Nginxから直接配信させます。</li>
<li><b><code>manifest.json</code></b>: HTMLシェル生成のため、PHP-FPMコンテナにも <code>manifest.json</code> が必要です。</li>
</ol>
<p>この構成により、PHP-FPMは動的処理に専念し、Nginxは静的ファイルの高速配信に専念するという、「1コンテナ1責務」のメリットを最大限に活かした、スケーラブルな本番環境を構築できます。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2153/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>RESTful APIとSPAの関係についてReactとPHPで解説します</title>
		<link>https://otonan-syusyoku.work/archives/1674</link>
					<comments>https://otonan-syusyoku.work/archives/1674#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 25 Feb 2024 12:40:20 +0000</pubDate>
				<category><![CDATA[好きではないJS]]></category>
		<category><![CDATA[絶対に必要なIT基礎知識]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Vue]]></category>
		<category><![CDATA[セキュリティ]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1674</guid>

					<description><![CDATA[RESTful APIとは？ RESTful APIは、ウェブアプリケーションやウェブサービスで使われるプログラミングインターフェースです。 REST（Representational State Transfer）の原 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>RESTful APIとは？</h2>
<p><img fetchpriority="high" decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1.png" alt="What&#96;s up" width="1000" height="500" class="aligncenter size-full wp-image-1665" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></p>
<p>RESTful APIは、ウェブアプリケーションやウェブサービスで使われるプログラミングインターフェースです。<br />
REST（Representational State Transfer）の原則に基づいて設計されており、ウェブ上のリソースへのアクセスや操作を統一的な方法で提供します。<br />
リソースはURLで識別され、HTTPメソッド（GET、POST、PUT、DELETEなど）を使ってアクセスされます。</p>
<p>[getpost id=&#8221;1672&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<h2>SPA（シングルページアプリケーション）とは？</h2>
<p>SPAは、ユーザーがアプリケーションを操作する際にページの再読み込みをせずに、必要なデータのみをサーバーから非同期に取得し、動的にページの内容を更新するウェブアプリケーションの形式です。</p>
<p>これにより、<span class="sc_marker blue"><strong>従来のマルチページアプリケーションに比べてユーザーエクスペリエンスが向上します。</strong></span></p>
<p>僕が所属しているチームでも<strong>ページのリロードが嫌だというユーザーの使用感からSPAへの移行が少しづつ始まってきています</strong>。<br />
世間的にもSPAは流行っていますよね。（流行っているから偉いというわけではない。知ることが大事</p>
<h2>RESTful APIとSPAの関係性</h2>
<p><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png" alt="Let&#96;s GO" width="1000" height="500" class="aligncenter size-full wp-image-1680" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></p>
<p>SPAでは、<strong>ページの初回読み込み時にアプリケーションの全てのコード（HTML、CSS、JavaScript）がロード</strong>されます。</p>
<p>その後の<span class="sc_marker blue"><strong>ユーザーの操作によってデータが必要になる場合、SPAはRESTful APIを通じてサーバーからデータを非同期に取得</strong></span>します。<br />
<strong>このデータはJSONやXML形式で返され、JavaScriptを用いてクライアントサイドで処理され、ページの一部分だけが動的に更新</strong>されます。</p>
<p>このアーキテクチャにより、SPAはユーザーにスムーズで反応の良いインターフェイスを提供できます。</p>
<p><span class="sc_marker blue"><strong>RESTful APIはこのプロセスの背後にあるデータ通信の基盤となります。</strong></span></p>
<h2>PHPとReactを使った例</h2>
<p>ここでは、PHPで書かれた簡単なRESTful APIと、Reactを使ったSPAの例を示します。</p>
<h3>PHPによるRESTful API</h3>
<p>PHPで簡単なAPIエンドポイントを作成します。この例では、GETリクエストを受け取り、簡単なJSONデータを返します。</p>
<div class="dark bg-gray-950 rounded-md">
<div>
<pre class="line-numbers"><code class="language-php">&lt;?php 
    header('Content-Type: application/json');
    $response = ['message' =&gt; 'Hello, world!'];
    echo json_encode($response); 

<code>    // Response
    //{"message": "Hello, world!"}</code> ?&gt;</code></pre>
</div>
</div>
<p>このPHPスクリプトは、サーバーに配置してのようにアクセスすると<span style="background-color: #ffffff; color: #333333; font-family: 游ゴシック, YuGothic, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Verdana, メイリオ, Meiryo, Osaka, 'ＭＳ Ｐゴシック', 'MS PGothic', sans-serif;">JSONが返されます。</span></p>
<h3>ReactによるSPA</h3>
<p>Reactで簡単なSPAを作成し、上記のAPIからデータを取得して表示します。</p>
<div class="dark bg-gray-950 rounded-md">
<div class="p-4 overflow-y-auto">
<pre class="line-numbers"><code class="language-js">import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [message, setMessage] = useState('');

  useEffect(() =&gt; {
    axios.get('http://yourserver.com/index.php')
      .then(response =&gt; {
        setMessage(response.data.message);
      })
      .catch(error =&gt; console.error('There was an error!', error));
  }, []);

  return (
    &lt;div&gt;
      &lt;button&gt;{message}&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>このコードは、コンポーネントがマウントされた後にAPIからデータを取得し、取得したメッセージを表示します。<br />
React（クライアントサイド）とPHP（サーバーサイド）を使った、RESTful APIとSPAの基本的な連携を示しています。</p>
</div>
<p>今回のコードでは、データを取得して表示するだけのコードになっていますが、Event処理などによりリロードされることなく動的にページの内容を変更させることが可能です。</p>
<h2>セキュリティとパフォーマンスの考慮</h2>
</div>
<p>SPA（シングルページアプリケーション）とRESTful APIを使用する際には、セキュリティとパフォーマンスの両方に配慮することが重要です。<br />
以下では、これらの観点から基本的なガイドラインをいくつか紹介します。</p>
<h3>セキュリティ</h3>
<ol>
<li><strong>HTTPSを使用する<br />
</strong>データの暗号化を確実に行うため、APIとの通信には常にHTTPSを使用します。（当たり前です）<br />
これにより、中間者攻撃（MITM）のリスクを減らすことができます。</li>
<li><strong>CORSポリシーを適切に設定する<br />
</strong>クロスオリジンリソースシェアリング（CORS）ポリシーを適切に設定し、信頼できるオリジンからのリクエストのみを許可します。<br />
不適切なCORS設定は、セキュリティリスクを招く可能性があります。</li>
<li><strong>認証と認可<br />
</strong>JWT（JSON Web Tokens）やOAuthなどの堅牢な認証メカニズムを使用して、APIへのアクセスをセキュアに管理します。<br />
また、ユーザーの権限に基づいてアクセスを制御する認可も重要です。</li>
<li><strong>入力の検証とサニタイズ<br />
</strong> SQLインジェクションやクロスサイトスクリプティング（XSS）攻撃を防ぐため、サーバー側とクライアント側の両方で入力値の検証とサニタイズを行います。</li>
<li><strong>依存関係のセキュリティ<br />
</strong>使用するライブラリやフレームワークのセキュリティ脆弱性に注意し、定期的に更新を行い脆弱性を修正します。</li>
</ol>
<p>この辺の対策については過去に記事にしていますので時間ある人は読んでみてくださーい</p>
<p>[getpost id=&#8221;1618&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;][getpost id=&#8221;1256&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<h3>パフォーマンス</h3>
<ol>
<li><strong>コードの分割<br />
</strong>ユーザーが必要とするコードのみをダウンロードするように、コード分割（Code Splitting）を実装します。<br />
これにより、初期ロード時間を短縮できます。</li>
<li><strong>キャッシュ戦略の利用<br />
</strong>静的リソースやAPIレスポンスのキャッシュを適切に利用することで、パフォーマンスを向上させることができます。<br />
ブラウザのキャッシュやCDNの利用が有効です。</li>
<li><strong>遅延ローディング（Lazy Loading）<br />
</strong> 画像やコンポーネントなど、必要になるまでロードしないようにします。<br />
これにより、初期ロードのパフォーマンスを向上させることができます。</li>
<li><strong>APIの最適化<br />
</strong>不必要なデータの送受信を避けるため、APIのレスポンスをできるだけ小さく保ちます。<br />
また、必要なデータのみを取得するためのフィルタリングやページネーションをAPI側でサポートします。</li>
<li><strong>フロントエンドのパフォーマンス最適化<br />
</strong>Reactなどのフロントエンドフレームワークを使用する場合は、不要な再レンダリングを避け、メモ化（memoization）や仮想DOMの効率的な利用などのテクニックを活用します。</li>
</ol>
<p>セキュリティとパフォーマンスは、アプリケーションの設計と開発の初期段階から考慮する必要があります。<br />
これらの基本的なガイドラインを実践することで、ユーザーに安全で快適な体験を提供することができます。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1674/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【Tips】Laravel+React-Quillで独自スタイルを適用する方法</title>
		<link>https://otonan-syusyoku.work/archives/1575</link>
					<comments>https://otonan-syusyoku.work/archives/1575#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Fri, 05 Jan 2024 11:53:19 +0000</pubDate>
				<category><![CDATA[好きではないJS]]></category>
		<category><![CDATA[Quill]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[コンポーネント]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1575</guid>

					<description><![CDATA[React-Quill を使用している際にスタイルを少しだけ変更したいといった場面があると思います〜 スタイルを当てる事に焦点を置いているので、UIのダサさは気にしないでください。 そんなときのちょっとしたTipsを紹介 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>React-Quill を使用している際にスタイルを少しだけ変更したいといった場面があると思います〜</p>
<p>スタイルを当てる事に焦点を置いているので、UIのダサさは気にしないでください。</p>
<p>そんなときのちょっとしたTipsを紹介します。</p>
<h2>完成形</h2>
<h3>変更前</h3>
<p><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.47.01.png" alt="Quill 変更前" width="878" height="166" class="aligncenter size-full wp-image-1578" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.47.01.png 878w, https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.47.01-300x57.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.47.01-768x145.png 768w" sizes="(max-width: 878px) 100vw, 878px" /></p>
<h3>変更後</h3>
<p><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.46.19.png" alt="Quill 変更後" width="882" height="251" class="aligncenter size-full wp-image-1577" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.46.19.png 882w, https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.46.19-300x85.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/01/スクリーンショット-2024-01-05-20.46.19-768x219.png 768w" sizes="(max-width: 882px) 100vw, 882px" /></p>
<h2>なぜEditorに独自スタイルを当てるのか？</h2>
<p>ITリテラシーが低いユーザーに使用してもらうのが、一番の理由です。</p>
<p>殺風景なUIだとその時点でユーザーが離れてしまうことに最近気づきました。</p>
<p>プログラマーやエンジニアをやっている人には理解できないかもしれないのですが、リテラシーの低い人はとことん低いです。</p>
<ul>
<li>画面が味気ない</li>
<li>よくわからないボタン</li>
<li>英語</li>
</ul>
<p>少し考えたら分かるだろうと怒りたい気持ちもあるのですが、<span class="sc_marker blue"><strong>使ってもらえなければただのゴミを作ってしまうことになるので</strong></span>、工夫が必要です。</p>
<h2>CSSの定義</h2>
<p>Laravel の指定ディレクトリに Quill 用の CSS を用意します。</p>
<ul>
<li>`resources/css/Quill/style.css`</li>
<li>Quill 用にディレクトリを切って用意したほうが得策な気がします…</li>
</ul>
<pre class="line-numbers"><code class="language-css">.quill {
    .ql-toolbar {
        background-color: rgb(148 163 184); /* bg-slate-400 */
    }

    .ql-editor {
        h1, h2, h3 {
            padding-left: 0.5rem;
            border-radius: 0.5rem; /** rounded-lg */
            background-color: rgb(56 189 248); /* bg-sky-400 */
            color: white;
            margin-bottom: 1.2rem;
        }

        p {
            font-size: 1rem;
            margin-bottom: 1.2rem;
        }
    }
}</code></pre>
<p>&nbsp;</p>
<h2>定義したCSSをQuillコンポーネントで読み込み</h2>
<p>先ほど定義した CSS をQuill を定義しているコンポーネント内でインポートします。</p>
<ul>
<li>Quill のデフォルトのスタイルを上書きするために &#8216;react-quill/dist/quill.snow.css&#8217; の後にインポートするようにします</li>
</ul>
<pre class="line-numbers"><code class="language-php">import React from 'react';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import '@/../css/Quill/style.css';

export default function QuillEditor({ handleValueChange, value }) {
    const customToolbarOptions = [
        ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
        ['link', 'image'],
        ['blockquote', 'code-block'],

        [{ 'header': 1 }, { 'header': 2 }],               // custom button values
        [{ 'list': 'ordered' }, { 'list': 'bullet' }],
        [{ 'script': 'sub' }, { 'script': 'super' }],      // superscript/subscript
        [{ 'indent': '-1' }, { 'indent': '+1' }],          // outdent/indent
        [{ 'direction': 'rtl' }],                         // text direction

        [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
        [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

        [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
        [{ 'font': [] }],
        [{ 'align': [] }],

        ['clean']    
    ];

    // 親コンポーネントから受け取った関数を使って、inputの値を渡す
    const handleContentValueChange = (content) =&gt; {
        handleValueChange(content);
    };

    return (
        &lt;ReactQuill
            theme="snow"
            value={value} // 親コンポーネントから受け取った値を設定
            onChange={handleContentValueChange}
            modules={{
                toolbar: customToolbarOptions,
            }}
        /&gt;
    );
}
</code></pre>
<p>&nbsp;</p>
<h2>ReactQuill素晴らしい</h2>
<p>ReactQuillをメンテナンスしてくれている方たちに感謝を伝えたいです。</p>
<p>すごくお世話になっています。ありがとう。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1575/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【Laravelプロジェクト】react-image-galleryを導入</title>
		<link>https://otonan-syusyoku.work/archives/1396</link>
					<comments>https://otonan-syusyoku.work/archives/1396#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sat, 26 Aug 2023 09:02:54 +0000</pubDate>
				<category><![CDATA[好きではないJS]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[個人開発]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1396</guid>

					<description><![CDATA[技術構成 Laravel Laravel Framework 10.15.0 React react-dom@18.2.0 DaisyUi daisyui@3.5.0 tailwind @tailwindcss/form [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>技術構成</h2>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 50%;">Laravel</td>
<td style="width: 50%;">Laravel Framework 10.15.0</td>
</tr>
<tr>
<td style="width: 50%;">React</td>
<td style="width: 50%;">react-dom@18.2.0</td>
</tr>
<tr>
<td style="width: 50%;">DaisyUi</td>
<td style="width: 50%;">daisyui@3.5.0</td>
</tr>
<tr>
<td style="width: 50%;">tailwind</td>
<td style="width: 50%;">@tailwindcss/forms@0.5.4</td>
</tr>
</tbody>
</table>
<h2>いい感じなフォトギャラリーないのか</h2>
<p>HP作成を行っているとスライダーの要望が度々出てくるはずです。</p>
<p>かくいう僕も今回、そういった要望があってスライダーを導入しなければいけない状態でした。</p>
<p>&nbsp;</p>
<p>一度、jQueryで生実装したことがあるのですが、なんやかんや面倒くさい思いをした苦い経験があったので、今回は生実装を避けたいという思いからReact でいい感じなライブラリないのか調べたところ「<strong>react-image-gallery</strong>」に出会いました。</p>
<p>&nbsp;</p>
<p>天下のReact 様は流石だなと言う感じです。</p>
<p>react-image-gallery以外の選択肢としては以下のようなものが挙げられます。</p>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 36.9214%;" rowspan="2">react-image-gallery</td>
<td style="width: 63.0786%;">https://github.com/xiaolin/react-image-gallery</td>
</tr>
<tr>
<td style="width: 63.0786%;">画像のスライドショーを作成するための高機能なライブラリ</td>
</tr>
<tr>
<td style="width: 36.9214%;" rowspan="2">react-images</td>
<td style="width: 63.0786%;">https://github.com/jossmac/react-images</td>
</tr>
<tr>
<td style="width: 63.0786%;">シンプルなカスタマイズ可能なグリッドスタイルのイメージギャラリーを作成するためのライブラリ</td>
</tr>
<tr>
<td style="width: 36.9214%;" rowspan="2">react-awesome-lightbox</td>
<td style="width: 63.0786%;">https://github.com/photostory/react-awesome-lightbox</td>
</tr>
<tr>
<td style="width: 63.0786%;">モーダルウィンドウ内で画像を表示するためのライブラリ</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h2>実際に導入</h2>
<p>npm でインストール。</p>
<pre class="line-numbers"><code class="language-js">npm install react-image-gallery</code></pre>
<p>&nbsp;</p>
<p>インポート</p>
<pre class="line-numbers"><code class="language-js">import ImageGallery from 'react-image-gallery';
import 'react-image-gallery/styles/css/image-gallery.css';</code></pre>
<p><span class="sc_marker blue"><strong>※2行目のcss もインポートしてください。</strong></span><br />
<span class="sc_marker blue"><strong>　css もインポートすることで梱包されているスタイルを当てることが可能です。</strong></span></p>
<p>&nbsp;</p>
<p>上記の設定で使う準備が完了です。</p>
<p>あとは自由に設定していくだけです。</p>
<p>&nbsp;</p>
<p>今回はLaravel からデータを渡すため、コントローラーから配列を渡します。（本当はModel でデータ定義して渡すべきですが、）</p>
<pre class="line-numbers"><code class="language-php">$images = [
            [
                "original" =&gt; "/img/AsItFlows/asit_team.jpg",
                "thumbnail" =&gt; "/img/AsItFlows/asit_team.jpg",
                "originalAlt" =&gt; "チーム",
                "thumbnailAlt" =&gt; "チーム",
                "onErrorImageURL" =&gt; "/img/AsItFlows/logo.jpg",
            ],
            [
                "original" =&gt; "/img/AsItFlows/sunset.jpg",
                "thumbnail" =&gt; "/img/AsItFlows/sunset.jpg",
                "originalAlt" =&gt; "サンセット",
                "thumbnailAlt" =&gt; "サンセット",
                "onErrorImageURL" =&gt; "/img/AsItFlows/logo.jpg",
            ],
            [
                "original" =&gt; "/img/AsItFlows/surf_member.jpg",
                "thumbnail" =&gt; "/img/AsItFlows/surf_member.jpg",
                "originalAlt" =&gt; "メンバー",
                "thumbnailAlt" =&gt; "メンバー",
                "onErrorImageURL" =&gt; "/img/AsItFlows/logo.jpg",
            ],
            ...
        ];
        shuffle($images);
        return Inertia::render('Public/AsItFlows/Top', [
            'items' =&gt; $images
        ]);</code></pre>
<p>&nbsp;</p>
<p>最後にjsx 側でいい感じに使用します。</p>
<pre class="line-numbers"><code class="language-js">&lt;ImageGallery items={items}
  showNav={true}
  autoPlay={true}
  showFullscreenButton={false}
  useBrowserFullscreen={false}
  showPlayButton={false}
/&gt;</code></pre>
<p>&nbsp;</p>
<p><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2023/08/スクリーンショット-2023-08-26-18.01.40.jpg" alt="" width="640" height="688" class="aligncenter size-full wp-image-1401" srcset="https://otonan-syusyoku.work/wp-content/uploads/2023/08/スクリーンショット-2023-08-26-18.01.40.jpg 640w, https://otonan-syusyoku.work/wp-content/uploads/2023/08/スクリーンショット-2023-08-26-18.01.40-279x300.jpg 279w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p>&nbsp;</p>
<p>んー。素敵。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1396/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[React] useStateとuseEffectってすごく便利じゃん</title>
		<link>https://otonan-syusyoku.work/archives/1382</link>
					<comments>https://otonan-syusyoku.work/archives/1382#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Thu, 17 Aug 2023 16:17:59 +0000</pubDate>
				<category><![CDATA[好きではないJS]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[脱3流プログラマー]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1382</guid>

					<description><![CDATA[今までjQueryで戦ってきたのですが、とうとう見切りをつけて次のステップに進もうとしているコテツです… Reactの考え方 React では、DOM 要素を直接操作する代わりに、React の状態管理やレンダリング方法 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>今までjQueryで戦ってきたのですが、とうとう見切りをつけて次のステップに進もうとしているコテツです…</p>
<h2>Reactの考え方</h2>
<p>React では、DOM 要素を直接操作する代わりに、React の状態管理やレンダリング方法を使用して操作を行うことが推奨されているとのこと。</p>
<h2>useState</h2>
<p>useState フックは、状態（state）を React コンポーネント内で管理するために使用します。状態は、コンポーネント内でのデータの変更を追跡し、自動的に再レンダリングするために使われます。</p>
<p>差分取って差分の分だけレンダリングするっぽいですね。楽だし早いね。</p>
<p>Reactの基本理念？である状態管理で差分を取るってのがuseStateの肝な気がする。</p>
<h2>useEffect</h2>
<p>コンポーネントのレンダリング後に実行される処理(データ)を管理するために使用します。</p>
<p>useEffect フックは第1引数に副作用の関数を受け取り、第2引数には、依存する値の配列を指定します。この配列に指定した値が変更された場合に副作用関数が再実行されます。</p>
<p>空の配列を指定することで、初回レンダリング時にのみ副作用関数が実行されることを意味します。</p>
<pre class="line-numbers"><code class="language-js">    const [csrfToken, setCsrfToken] = useState(null);
    useEffect(() =&gt; {
        // メタタグからトークンを取得
        const metaTag = document.querySelector('meta[name="csrf-token"]');
        if (metaTag) {
            setCsrfToken(metaTag.content);
        }
    }, []);</code></pre>
<p>なんとなくonloadっぽい。。。</p>
<h3>注意点</h3>
<h4>第二引数に何も指定しない</h4>
<p>無限ループ発生します。ローカル端末が燃えます。気をつけましょ。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1382/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Docker+Laravel+React+Viteで環境構築し、npm run dev で画面真っ白</title>
		<link>https://otonan-syusyoku.work/archives/1377</link>
					<comments>https://otonan-syusyoku.work/archives/1377#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sat, 22 Jul 2023 05:19:52 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[好きではないJS]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[React]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1377</guid>

					<description><![CDATA[画面真っ白に 表題の通り、Docker+Laravel+React+Viteで環境構築後にnpm run dev で画面が真っ白になってしまった。 バージョン Docker docker --version Docker [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>画面真っ白に</h2>
<p>表題の通り、Docker+Laravel+React+Viteで環境構築後にnpm run dev で画面が真っ白になってしまった。</p>
<table style="border-collapse: collapse; width: 43.7902%;">
<tbody>
<tr>
<td style="width: 18.4791%;" colspan="2">バージョン</td>
</tr>
<tr>
<td style="width: 18.4791%;">Docker</td>
<td style="width: 25.3122%;">
<pre class="line-numbers"><code class="language-php">docker --version
Docker version 20.10.6, build 370c289</code></pre>
</td>
</tr>
<tr>
<td style="width: 18.4791%;">Laravel</td>
<td style="width: 25.3122%;">
<pre class="line-numbers"><code class="language-php">php artisan --version
[22-Jul-2023 14:05:32 Asia/Tokyo] Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9000 (through xdebug.client_host/xdebug.client_port).
Laravel Framework 10.15.0</code></pre>
</td>
</tr>
<tr>
<td style="width: 18.4791%;">React</td>
<td style="width: 25.3122%;">
<pre class="line-numbers"><code class="language-php">npm list --depth=0
data@ /data
+-- @headlessui/react@1.7.15
+-- @inertiajs/react@1.0.9
+-- @tailwindcss/forms@0.5.4
+-- @vitejs/plugin-react@3.1.0
+-- autoprefixer@10.4.14
+-- axios@1.4.0
+-- laravel-vite-plugin@0.7.8
+-- postcss@8.4.27
+-- react-dom@18.2.0
+-- react@18.2.0
+-- tailwindcss@</code></pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h2>エラー文言</h2>
<p>デベロッパーツール＞Console</p>
<pre class="line-numbers"><code class="language-php">GET http://127.0.0.1:5173/@vite/client net::ERR_CONNECTION_REFUSED
localhost/:25     GET http://127.0.0.1:5173/resources/js/app.jsx net::ERR_CONNECTION_REFUSED
localhost/:25     GET http://127.0.0.1:5173/resources/js/Pages/Welcome.jsx net::ERR_CONNECTION_REFUSED
localhost/:20     GET http://127.0.0.1:5173/@react-refresh net::ERR_CONNECTION_REFUSED</code></pre>
<p>&nbsp;</p>
<h2>portがおかしい</h2>
<p>ConsoleのリクエストURLからportがおかしいことに気づく。</p>
<p>Dockerのnginxで指定していたportは8080:80にしていたのだが、http://<strong>127.0.0.1:5173</strong>となっている。</p>
<p>&nbsp;</p>
<p>うーん。なぜだ。</p>
<h2>解決方法</h2>
<p>Laravel,Reactを導入しているコンテナの設定が漏れている気がしてきた。</p>
<p>Dockerにてアプリケーションコンテナにportをしてみると解決できた。</p>
<p>docker-compose.ymlに以下を追記。</p>
<pre class="line-numbers"><code class="language-php">app:
    build: ./docker/php
    volumes:
      - ./src:/data
    ports:
    - 5173:5173  ←vite用port開通</code></pre>
<p>&nbsp;</p>
<p>そして、viteがホストを参照するようにするため、package.jsonに以下を追記。</p>
<pre class="line-numbers"><code class="language-php">"scripts": {
        "dev": "vite --host",   ←--hostを追記
        "build": "vite build"
    },</code></pre>
<p>&nbsp;</p>
<p>これで解決できた−</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1377/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
