<?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>API ‣ てんハロ｜未経験エンジニアのIT学習ログ</title>
	<atom:link href="https://it-bokenki.com/category/api/feed/" rel="self" type="application/rss+xml" />
	<link>https://it-bokenki.com</link>
	<description>Hello Worldから、今日も生きてる</description>
	<lastBuildDate>Mon, 21 Jul 2025 14:30:11 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://it-bokenki.com/wp-content/uploads/2025/06/cropped-ブログ　アイコン-32x32.png</url>
	<title>API ‣ てんハロ｜未経験エンジニアのIT学習ログ</title>
	<link>https://it-bokenki.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>[&#8230;nextauth] とは？Next.jsでログイン認証を実装する方法【App Router対応】</title>
		<link>https://it-bokenki.com/2025/06/27/nextauth/</link>
					<comments>https://it-bokenki.com/2025/06/27/nextauth/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Fri, 27 Jun 2025 03:58:43 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[バックエンド]]></category>
		<category><![CDATA[フロントエンド]]></category>
		<category><![CDATA[nextauth]]></category>
		<category><![CDATA[ログイン機能]]></category>
		<category><![CDATA[認証機能]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=3536</guid>

					<description><![CDATA[<p>※当サイトはアフィリエイト広告を利用しています。商品リンクにはプロモーションを含む場合があります。 Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち 困ってた自分に届けたい話 初めてロ [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/27/nextauth/">[…nextauth] とは？Next.jsでログイン認証を実装する方法【App Router対応】</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><span class="fz-12px">※当サイトはアフィリエイト広告を利用しています。商品リンクにはプロモーションを含む場合があります。</span></p>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NZROH">
<img fetchpriority="high" decoding="async" border="0" width="300" height="250" alt="" src="https://www27.a8.net/svt/bgt?aid=230528653907&#038;wid=001&#038;eno=01&#038;mid=s00000023513004030000&#038;mc=1"></a>
<img decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=3T911P+F006GI+51FE+NZROH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NWRNM">Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">困ってた自分に届けたい話</span></h2>



<p>初めてログイン機能を作ったとき、「ログイン状態をどうやって保持すればいいの？」「セッションって何？」と疑問だらけ。</p>



<p>ググって出てきたコードをコピペしてみても、うまく動かず。<br>認証の仕組みが見えづらくて、「これで本当に安全なのかな？」と不安になることも…。</p>



<p>そんなときに出会ったのが <code>NextAuth.js</code>。</p>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>この記事は、同じように困っていた方への<strong>備忘録兼シェア</strong>として書いています。</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">Next.js をもっと詳しく学びたい人へおすすめの本</span></h2>



<p><span class="keyboard-key">&#x25b6; 動かしながら基礎から学びたい人に</span><br>「まずは手を動かしたい！」という人にぴったりの一冊。基本構造から丁寧に解説されており、Next.jsとReactの連携も自然に身につきます。初学者でもステップを追いやすく、サンプルアプリの構築を通して理解を深められる構成です。</p>



<table cellpadding="0" cellspacing="0" border="0" style=" border:1px solid #ccc; width:300px;"><tr style="border-style:none;"><td style="vertical-align:top; border-style:none; padding:10px; width:140px;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0D9PT5FVJ%2F%3Ftag%3Da8-affi-321185-22"><img decoding="async" border="0" alt="" src="https://m.media-amazon.com/images/I/41IcDiqx7jL._SS160_.jpg" /></a></td><td style="font-size:12px; vertical-align:middle; border-style:none; padding:10px;"><p style="padding:0; margin:0;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0D9PT5FVJ%2F%3Ftag%3Da8-affi-321185-22">動かして学ぶ！Next.js/React開発入門</a></p><p style="color:#cc0000; font-weight:bold; margin-top:10px;">新品価格<br/>￥3,168<span style="font-weight:normal;">から</span><br/><span style="font-size:10px; font-weight:normal;">(2025/7/21 23:18時点)</span></p></td></tr></table>
<img decoding="async" border="0" width="1" height="1" src="https://www10.a8.net/0.gif?a8mat=459I7K+A7CH0Y+249K+BWGDT" alt="">



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x25b6; Reactの経験があり、次のステップへ進みたい人に</span><br>「Reactは少し触ったことがあるけど、Next.jsを仕事で使いこなしたい」という人向け。実践を意識した構成で、Webアプリの構造、レンダリング戦略、SEO対応など、現場で必要なノウハウが詰まっています。</p>



<table cellpadding="0" cellspacing="0" border="0" style=" border:1px solid #ccc; width:300px;"><tr style="border-style:none;"><td style="vertical-align:top; border-style:none; padding:10px; width:140px;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0F9PXRFHD%2F%3Ftag%3Da8-affi-321185-22"><img decoding="async" border="0" alt="" src="https://m.media-amazon.com/images/I/41zubkTAnmL._SS160_.jpg" /></a></td><td style="font-size:12px; vertical-align:middle; border-style:none; padding:10px;"><p style="padding:0; margin:0;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0F9PXRFHD%2F%3Ftag%3Da8-affi-321185-22">React Next.js 実践プログラミング入門: React Next.js モダンWeb開発実践ガイド</a></p><p style="color:#cc0000; font-weight:bold; margin-top:10px;">新品価格<br/>￥3,300<span style="font-weight:normal;">から</span><br/><span style="font-size:10px; font-weight:normal;">(2025/7/21 23:17時点)</span></p></td></tr></table>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=459I7K+A7CH0Y+249K+BWGDT" alt="">



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x25b6; App Routerを本格的に学びたいエンジニアへ</span><br>App Routerを軸に、最新のNext.js開発に対応した本格派。中級者以上の方が「一歩先へ進みたい」と感じたときに読みたい一冊。デザインパターンや設計の視点もあり、長期的に役立つ内容です。</p>



<table cellpadding="0" cellspacing="0" border="0" style=" border:1px solid #ccc; width:300px;"><tr style="border-style:none;"><td style="vertical-align:top; border-style:none; padding:10px; width:140px;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0CW1KC9N8%2F%3Ftag%3Da8-affi-321185-22"><img decoding="async" border="0" alt="" src="https://m.media-amazon.com/images/I/51SeLrEQrQL._SS160_.jpg" /></a></td><td style="font-size:12px; vertical-align:middle; border-style:none; padding:10px;"><p style="padding:0; margin:0;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0CW1KC9N8%2F%3Ftag%3Da8-affi-321185-22">実践Next.js —— App Routerで進化するWebアプリ開発 エンジニア選書</a></p><p style="color:#cc0000; font-weight:bold; margin-top:10px;">新品価格<br/>￥3,665<span style="font-weight:normal;">から</span><br/><span style="font-size:10px; font-weight:normal;">(2025/7/21 23:18時点)</span></p></td></tr></table>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www13.a8.net/0.gif?a8mat=459I7K+A7CH0Y+249K+BWGDT" alt="">



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">[&#8230;nextauth] ってなに？</span></h2>



<p><code>[...nextauth]</code> は、Next.jsの「<strong>キャッチオールルート</strong>」という仕組みを使った特別なファイルです。</p>



<p>これは、<code>/api/auth/</code> 以下の複数のAPIエンドポイント（ログイン・ログアウトなど）を<br><strong>1つのファイルでまとめて管理できる便利な仕組み</strong>です。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc4">なぜ[&#8230;nextauth] は必要なの？</span></h2>



<p>NextAuth.js を使うと、認証に関するさまざまなAPIが自動的に使われます。</p>



<p>たとえば、こんなエンドポイントがあります。</p>



<figure class="wp-block-table is-style-regular"><table class="has-fixed-layout"><thead><tr><th>APIルート</th><th>概要</th></tr></thead><tbody><tr><td>/api/auth/signin</td><td>ログインページ</td></tr><tr><td>/api/auth/signout</td><td>ログアウト処理</td></tr><tr><td>/api/auth/session</td><td>現在のログイン状態確認</td></tr><tr><td>/api/auth/callback/credentials</td><td>ログイン成功後にユーザー情報を処理</td></tr><tr><td>/api/auth/csrf</td><td>ログイン中のユーザーを識別するための電子的な証明書を取得<br>※電子的な証明書＝トークン</td></tr></tbody></table></figure>



<p>これらを<strong>1つ1つ手動で作ると管理が大変</strong>です。</p>



<p>そこで、<code>[...nextauth]</code> というファイル1つにまとめることで、<br><strong>これらすべてのAPIルートを一括で定義・管理できる</strong>ようになります。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc5">App Router で実装</span></h2>



<p>Next.js 13 以降では、新しく登場した <code>app/</code> ディレクトリを使う構成（App Router）が推奨されています。これが <strong>新規プロジェクトやモダンな構成に適した形</strong>です。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">App Router のファイル構成</span></h3>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="992" height="378" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.42.png" alt="[...nextauth] とは？Next.jsでログイン実装" class="wp-image-3684" style="width:644px;height:auto" title="[...nextauth] とは？Next.jsでログイン実装" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.42.png 992w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.42-300x114.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.42-768x293.png 768w" sizes="auto, (max-width: 992px) 100vw, 992px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">App Router の実装内容</span></h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>// app/api/auth/[&#8230;nextauth]/route.ts
// 認証のエントリーポイント
import NextAuth from &#8220;next-auth/next&#8221;;
import { authOptions } from &#8220;@/lib/auth&#8221;;
// NextAuthに設定を渡して処理を作成
const handler = NextAuth(authOptions);
// App RouterではGETとPOSTのメソッドを個別にエクスポートする必要あり
export { handler as GET, handler as POST };
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// app/api/auth/[...nextauth]/route.ts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 認証のエントリーポイント</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextAuth</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/next</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">authOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@/lib/auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// NextAuthに設定を渡して処理を作成</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">NextAuth</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">authOptions</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// App RouterではGETとPOSTのメソッドを個別にエクスポートする必要あり</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">GET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">POST</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>// lib/auth.ts
import CredentialsProvider from &#8220;next-auth/providers/credentials&#8221;;
import type { Session } from &#8220;next-auth&#8221;;
import type { JWT } from &#8220;next-auth/jwt&#8221;;　// NextAuth.jsが使っているトークン（認証情報）の型名
import type { NextAuthOptions } from &#8220;next-auth&#8221;;
// JWTにカスタム情報（role）を追加する場合の型定義
interface Token extends JWT {
  role?: string;
}
export const authOptions: NextAuthOptions = {
  // ① ログイン方法の指定（ここではIDとパスワードによる認証）
  providers: [
    CredentialsProvider({
      name: &#8220;Credentials&#8221;,
      credentials: {
        username: { label: &#8220;ユーザーID&#8221;, type: &#8220;text&#8221; },
        password: { label: &#8220;パスワード&#8221;, type: &#8220;password&#8221; },
      },
      // 認証処理：環境変数のユーザー名・パスワードと一致すれば成功
      async authorize(credentials) {
        if (
          credentials?.username === process.env.ADMIN_USERNAME &amp;&amp;
          credentials?.password === process.env.ADMIN_PASSWORD
        ) {
          return { id: &#8220;1&#8221;, name: &#8220;管理者&#8221;, role: &#8220;admin&#8221; };
        }
        return null; // 認証失敗
      },
    }),
  ],
  // ② カスタムページ（ログイン画面など）を指定
  pages: {
    signIn: &#8220;/auth/signin&#8221;, // 独自ログインページを使う場合
  },
  // ③ セッションの設定（JWT方式を利用）
  session: {
    strategy: &#8220;jwt&#8221;,  // サーバーセッションではなくトークンで管理
    maxAge: 100,      // トークンの有効期限（秒）
  },
  // ④ コールバック処理（トークンやセッションに追加情報を含める）
  callbacks: {
    // JWT作成時：ユーザー情報からroleをtokenに追加
    async jwt({ token, user }: { token: Token; user?: unknown }) {
      if (user &amp;&amp; typeof user === &#8220;object&#8221; &amp;&amp; &#8220;role&#8221; in user) {
        token.role = (user as { role?: string }).role;
      }
      return token;
    },
    // セッション作成時：tokenからroleを取り出してsessionに追加
    async session({ session, token }: { session: Session; token: Token }) {
      session.user = {
        &#8230;session.user,
        role: token.role ?? null,
      } as typeof session.user &amp; { role?: string | null };
      return session;
    },
  },
};
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// lib/auth.ts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">CredentialsProvider</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/providers/credentials</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Session</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">JWT</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/jwt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">　</span><span style="color: #616E88">// NextAuth.jsが使っているトークン（認証情報）の型名</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextAuthOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// JWTにカスタム情報（role）を追加する場合の型定義</span></span>
<span class="line"><span style="color: #81A1C1">interface</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Token</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">JWT</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">string</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">authOptions</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextAuthOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ① ログイン方法の指定（ここではIDとパスワードによる認証）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">providers</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">CredentialsProvider</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Credentials</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">username</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ユーザーID</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">type</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">パスワード</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">type</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 認証処理：環境変数のユーザー名・パスワードと一致すれば成功</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">authorize</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">username</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ADMIN_USERNAME</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">password</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ADMIN_PASSWORD</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">管理者</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">role</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">admin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 認証失敗</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ]</span><span style="color: #ECEFF4">,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ② カスタムページ（ログイン画面など）を指定</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">pages</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">signIn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/auth/signin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 独自ログインページを使う場合</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ③ セッションの設定（JWT方式を利用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">strategy</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">jwt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// サーバーセッションではなくトークンで管理</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">maxAge</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">      </span><span style="color: #616E88">// トークンの有効期限（秒）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ④ コールバック処理（トークンやセッションに追加情報を含める）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">callbacks</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// JWT作成時：ユーザー情報からroleをtokenに追加</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">jwt</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> token</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Token</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> user</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unknown</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">object</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">role</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// セッション作成時：tokenからroleを取り出してsessionに追加</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">session</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> session</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Session</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> token</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Token</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">role</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">null</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc8">Pages Router で実装</span></h2>



<p><code>pages/</code> ディレクトリを使う旧構成で、Next.js 初期から使われていたルーターです。<br>今でも既存プロジェクトではよく使われていますが、<strong>新規プロジェクトではあまり使われません</strong>。<br>今後は <code>app/</code> を使う App Router が推奨されています。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc9">Pages Router のファイル構成</span></h3>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="1054" height="234" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.58.png" alt="[...nextauth] とは？Next.jsでログイン実装" class="wp-image-3685" style="width:644px;height:auto" title="[...nextauth] とは？Next.jsでログイン実装" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.58.png 1054w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.58-300x67.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.58-1024x227.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-27-11.36.58-768x171.png 768w" sizes="auto, (max-width: 1054px) 100vw, 1054px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc10">Pages Router の実装内容</span></h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>// pages/api/auth/[&#8230;nextauth].ts
import NextAuth from &#8220;next-auth&#8221;;
import CredentialsProvider from &#8220;next-auth/providers/credentials&#8221;;
import type { Session } from &#8220;next-auth&#8221;;
import type { JWT } from &#8220;next-auth/jwt&#8221;;
// JWTにカスタム情報（role）を追加する場合の型定義
interface Token extends JWT {
  role?: string;
}
export default NextAuth({
  // ① ログイン方法の指定（ここではIDとパスワードによる認証）
  providers: [
    CredentialsProvider({
      name: &#8220;Credentials&#8221;,
      credentials: {
        username: { label: &#8220;ユーザーID&#8221;, type: &#8220;text&#8221; },
        password: { label: &#8220;パスワード&#8221;, type: &#8220;password&#8221; },
      },
      // 認証処理：環境変数のユーザー名・パスワードと一致すれば成功
      async authorize(credentials) {
        if (
          credentials?.username === process.env.ADMIN_USERNAME &amp;&amp;
          credentials?.password === process.env.ADMIN_PASSWORD
        ) {
          return { id: &#8220;1&#8221;, name: &#8220;管理者&#8221;, role: &#8220;admin&#8221; };
        }
        return null; // 認証失敗
      },
    }),
  ],
  // ② カスタムページ（ログイン画面など）を指定
  pages: {
    signIn: &#8220;/auth/signin&#8221;, // 独自ログインページを使う場合
  },
  // ③ セッションの設定（JWT方式を利用）
  session: {
    strategy: &#8220;jwt&#8221;,  // サーバーセッションではなくトークンで管理
    maxAge: 100,      // トークンの有効期限（秒）
  },
  // ④ コールバック処理（トークンやセッションに追加情報を含める）
  callbacks: {
    // JWT作成時：ユーザー情報からroleをtokenに追加
    async jwt({ token, user }: { token: Token; user?: unknown }) {
      if (user &amp;&amp; typeof user === &#8220;object&#8221; &amp;&amp; &#8220;role&#8221; in user) {
        token.role = (user as { role?: string }).role;
      }
      return token;
    },
    // セッション作成時：tokenからroleを取り出してsessionに追加
    async session({ session, token }: { session: Session; token: Token }) {
      session.user = {
        &#8230;session.user,
        role: token.role ?? null,
      } as typeof session.user &amp; { role?: string | null };
      return session;
    },
  },
});</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// pages/api/auth/[...nextauth].ts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextAuth</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CredentialsProvider</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/providers/credentials</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Session</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">JWT</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/jwt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// JWTにカスタム情報（role）を追加する場合の型定義</span></span>
<span class="line"><span style="color: #81A1C1">interface</span><span style="color: #D8DEE9FF"> Token </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">JWT</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">NextAuth</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ① ログイン方法の指定（ここではIDとパスワードによる認証）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">providers</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">CredentialsProvider</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Credentials</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">credentials</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">username</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ユーザーID</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">type</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">パスワード</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">type</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 認証処理：環境変数のユーザー名・パスワードと一致すれば成功</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">authorize</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">username</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ADMIN_USERNAME</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">password</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ADMIN_PASSWORD</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">管理者</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">role</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">admin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 認証失敗</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ]</span><span style="color: #ECEFF4">,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ② カスタムページ（ログイン画面など）を指定</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">pages</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/auth/signin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 独自ログインページを使う場合</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ③ セッションの設定（JWT方式を利用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">session</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">strategy</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">jwt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// サーバーセッションではなくトークンで管理</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">maxAge</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">      </span><span style="color: #616E88">// トークンの有効期限（秒）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ④ コールバック処理（トークンやセッションに追加情報を含める）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">callbacks</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// JWT作成時：ユーザー情報からroleをtokenに追加</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">jwt</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> token</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Token</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> user</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">unknown</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">object</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">role</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// セッション作成時：tokenからroleを取り出してsessionに追加</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">session</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> session</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Session</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> token</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Token </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">role</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">null</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc11">まとめ</span></h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>比較項目</th><th>Pages Router</th><th>App Router</th></tr></thead><tbody><tr><td>構成</td><td>1ファイル完結型</td><td>設定分離型（lib/auth.ts）推奨</td></tr><tr><td>エクスポート方法</td><td>export default</td><td>export { GET, POST }</td></tr><tr><td>推奨</td><td>App Router移行前<br>既存プロジェクト</td><td>Next.js 13以降の新規開発</td></tr></tbody></table></figure>



<p>ファイル1つ（または2つ）で以下すべてのAPIが動く！そのため、それぞれを個別に作る必要なし！</p>



<ul class="wp-block-list">
<li><code>/api/auth/signin</code></li>



<li><code>/api/auth/signout</code></li>



<li><code>/api/auth/session</code></li>



<li><code>/api/auth/callback/credentials</code></li>



<li><code>/api/auth/csrf</code></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/26/azure-blob/" title="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-14-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-14-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-14-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-14-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</div><div class="blogcard-snippet internal-blogcard-snippet">Next.jsとAzure Blob Storageを使ったファイルアップロードの実装方法を初心者向けに解説。設定手順からReactでのUI作成、APIの書き方まで！</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div><p>The post <a href="https://it-bokenki.com/2025/06/27/nextauth/">[…nextauth] とは？Next.jsでログイン認証を実装する方法【App Router対応】</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/27/nextauth/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応</title>
		<link>https://it-bokenki.com/2025/06/26/nextauth-js/</link>
					<comments>https://it-bokenki.com/2025/06/26/nextauth-js/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Thu, 26 Jun 2025 09:12:47 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[バックエンド]]></category>
		<category><![CDATA[フロントエンド]]></category>
		<category><![CDATA[ログイン機能]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=3615</guid>

					<description><![CDATA[<p>※当サイトはアフィリエイト広告を利用しています。商品リンクにはプロモーションを含む場合があります。 Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち 困ってた自分に届けたい話 初めてロ [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/26/nextauth-js/">Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><span class="fz-12px">※当サイトはアフィリエイト広告を利用しています。商品リンクにはプロモーションを含む場合があります。</span></p>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NZROH">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www27.a8.net/svt/bgt?aid=230528653907&#038;wid=001&#038;eno=01&#038;mid=s00000023513004030000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=3T911P+F006GI+51FE+NZROH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NWRNM">Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">困ってた自分に届けたい話</span></h2>



<p>初めてログイン機能を作ったとき、「ログイン状態をどうやって保持すればいいの？」「セッションって何？」と疑問だらけ。</p>



<p>ググって出てきたコードをコピペしてみても、うまく動かず。<br>認証の仕組みが見えづらくて、「これで本当に安全なのかな？」と不安になることも…。</p>



<p>そんなときに出会ったのが <code>NextAuth.js</code>。</p>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>この記事は、同じように困っていた方への<strong>備忘録兼シェア</strong>として書いています。</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">Next.js をもっと詳しく学びたい人へおすすめの本</span></h2>



<p><span class="keyboard-key">&#x25b6; 動かしながら基礎から学びたい人に</span><br>「まずは手を動かしたい！」という人にぴったりの一冊。基本構造から丁寧に解説されており、Next.jsとReactの連携も自然に身につきます。初学者でもステップを追いやすく、サンプルアプリの構築を通して理解を深められる構成です。</p>



<table cellpadding="0" cellspacing="0" border="0" style=" border:1px solid #ccc; width:300px;"><tr style="border-style:none;"><td style="vertical-align:top; border-style:none; padding:10px; width:140px;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0D9PT5FVJ%2F%3Ftag%3Da8-affi-321185-22"><img decoding="async" border="0" alt="" src="https://m.media-amazon.com/images/I/41IcDiqx7jL._SS160_.jpg" /></a></td><td style="font-size:12px; vertical-align:middle; border-style:none; padding:10px;"><p style="padding:0; margin:0;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0D9PT5FVJ%2F%3Ftag%3Da8-affi-321185-22">動かして学ぶ！Next.js/React開発入門</a></p><p style="color:#cc0000; font-weight:bold; margin-top:10px;">新品価格<br/>￥3,168<span style="font-weight:normal;">から</span><br/><span style="font-size:10px; font-weight:normal;">(2025/7/21 23:18時点)</span></p></td></tr></table>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www10.a8.net/0.gif?a8mat=459I7K+A7CH0Y+249K+BWGDT" alt="">



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x25b6; Reactの経験があり、次のステップへ進みたい人に</span><br>「Reactは少し触ったことがあるけど、Next.jsを仕事で使いこなしたい」という人向け。実践を意識した構成で、Webアプリの構造、レンダリング戦略、SEO対応など、現場で必要なノウハウが詰まっています。</p>



<table cellpadding="0" cellspacing="0" border="0" style=" border:1px solid #ccc; width:300px;"><tr style="border-style:none;"><td style="vertical-align:top; border-style:none; padding:10px; width:140px;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0F9PXRFHD%2F%3Ftag%3Da8-affi-321185-22"><img decoding="async" border="0" alt="" src="https://m.media-amazon.com/images/I/41zubkTAnmL._SS160_.jpg" /></a></td><td style="font-size:12px; vertical-align:middle; border-style:none; padding:10px;"><p style="padding:0; margin:0;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0F9PXRFHD%2F%3Ftag%3Da8-affi-321185-22">React Next.js 実践プログラミング入門: React Next.js モダンWeb開発実践ガイド</a></p><p style="color:#cc0000; font-weight:bold; margin-top:10px;">新品価格<br/>￥3,300<span style="font-weight:normal;">から</span><br/><span style="font-size:10px; font-weight:normal;">(2025/7/21 23:17時点)</span></p></td></tr></table>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=459I7K+A7CH0Y+249K+BWGDT" alt="">



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x25b6; App Routerを本格的に学びたいエンジニアへ</span><br>App Routerを軸に、最新のNext.js開発に対応した本格派。中級者以上の方が「一歩先へ進みたい」と感じたときに読みたい一冊。デザインパターンや設計の視点もあり、長期的に役立つ内容です。</p>



<table cellpadding="0" cellspacing="0" border="0" style=" border:1px solid #ccc; width:300px;"><tr style="border-style:none;"><td style="vertical-align:top; border-style:none; padding:10px; width:140px;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0CW1KC9N8%2F%3Ftag%3Da8-affi-321185-22"><img decoding="async" border="0" alt="" src="https://m.media-amazon.com/images/I/51SeLrEQrQL._SS160_.jpg" /></a></td><td style="font-size:12px; vertical-align:middle; border-style:none; padding:10px;"><p style="padding:0; margin:0;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=459I7K+A7CH0Y+249K+BWGDT&#038;a8ejpredirect=https%3A%2F%2Fwww.amazon.co.jp%2Fdp%2FB0CW1KC9N8%2F%3Ftag%3Da8-affi-321185-22">実践Next.js —— App Routerで進化するWebアプリ開発 エンジニア選書</a></p><p style="color:#cc0000; font-weight:bold; margin-top:10px;">新品価格<br/>￥3,665<span style="font-weight:normal;">から</span><br/><span style="font-size:10px; font-weight:normal;">(2025/7/21 23:18時点)</span></p></td></tr></table>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www13.a8.net/0.gif?a8mat=459I7K+A7CH0Y+249K+BWGDT" alt="">



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">全体構成</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="602" height="750" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-14.50.51.png" alt="Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応" class="wp-image-3618" style="width:425px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-14.50.51.png 602w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-14.50.51-241x300.png 241w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc4">認証設定（lib/auth.ts）</span></h2>



<p><strong>Next.js でログイン機能を作るときに必要な「設定ファイル」</strong>です。<br>NextAuth.js というライブラリが、このファイルを見てどう動くか決めます。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">NextAuthの設定内容</span></h3>



<h4 class="wp-block-heading"><span><strong>ログイン方式の設定（providers）</strong></span></h4>



<p>「どんな方法でログインできるようにするか」を決める場所です。</p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">例</span>：</p>



<ul class="wp-block-list">
<li>ユーザー名とパスワードでログイン ： <strong>CredentialsProvider</strong> を使う</li>



<li>Google アカウントでログイン ： <strong>GoogleProvider</strong><code> </code>を使う</li>



<li>GitHub アカウントでログイン ： <strong>GitHubProvider</strong> を使う</li>
</ul>



<p>→ 今回は「IDとパスワードでログインしたい」ので <code>CredentialsProvider</code> を使います。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><span><strong><strong>セッション管理（session）</strong></strong></span></h4>



<p>「ログイン状態（セッション）をどうやって覚えておくか？」を設定します。</p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">例</span>：</p>



<ul class="wp-block-list">
<li><strong>jwt</strong>：サーバーに保存せず、ログイン情報をクッキーに入れて管理（軽い）</li>



<li><strong>database</strong>：ログイン情報をデータベースに保存（管理がしっかり）</li>
</ul>



<p>→ 今回は簡単な構成にしたいので、<code>jwt</code> を使っています。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><span><strong><strong><strong>コールバック関数（callbacks）</strong></strong></strong></span></h4>



<p>「ログインしたあと、追加で何かしたいとき」に使います。</p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">例</span>：</p>



<ul class="wp-block-list">
<li>ログインしたユーザーの「名前」や「権限（adminなど）」をセッションに保存する</li>



<li>トークンに extra 情報を加える</li>
</ul>



<p>→ 今回は、「管理者かどうか（role）」をセッションに入れています。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">lib 配下にファイルを配置する理由</span></h3>



<p><code>lib</code> は「<strong>ロジック（処理）や設定などの共通ユーティリティを置く場所</strong>」という意味で使われます。<code>auth.ts</code> は<strong>アプリ全体で使う「認証の設定」</strong> をまとめたものなので、<code>lib</code> に置くのが自然です。</p>



<p><span class="keyboard-key">&#x1f5c2; <strong>配置イメージ</strong></span></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ディレクトリ</th><th>役割</th></tr></thead><tbody><tr><td><code><strong>lib/</strong></code>配下</td><td>アプリ全体で使い回すロジックや設定の置き場</td></tr><tr><td><strong><code>app/api/</code></strong>配下 　もしくは<strong><code>　　　　　pages/api/</code></strong>配下</td><td>APIルート（APIエンドポイント）</td></tr><tr><td><strong><code>components/</code></strong>配下</td><td>UIの部品化されたパーツの置き場</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">実装内容</span></h3>



<p><strong><span class="keyboard-key">&#x26a0;&#xfe0f; 実装時の注意点</span></strong></p>



<ul class="wp-block-list">
<li>ログインID・パスワードは直書き厳禁。今回は.envに直書きしてしまっています。<br>（次回、Azure SQL Databaseにパスワード等を格納する記事出します！）</li>



<li><code>role</code> など独自の情報をセッションやトークンに保持したい場合は、<code>callbacks</code> を使って明示的に追加します。</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import CredentialsProvider from &#8220;next-auth/providers/credentials&#8221;;
import type { Session } from &#8220;next-auth&#8221;;
import type { JWT } from &#8220;next-auth/jwt&#8221;;
interface Token extends JWT {
  role?: string;
}
export const authOptions = {
  // CredentialsProvider の設定（IDとパスワードでログインさせるための仕組み）
  providers: [
    CredentialsProvider({
      // ログイン画面のフォームの中身
      credentials: {
        username: { label: &#8220;ユーザーID&#8221;, type: &#8220;text&#8221; },
        password: { label: &#8220;パスワード&#8221;, type: &#8220;password&#8221; },
      },
      // 入力されたIDとパスワードが正しいかチェックする関数
      async authorize(credentials) {
        if (
          credentials?.username === process.env.[&#8220;任意の環境変数名&#8221;] &amp;&amp;
          credentials?.password === process.env.[&#8220;任意の環境変数名&#8221;]
        ) {
          return { id: &#8220;1&#8221;, name: &#8220;管理者&#8221;, role: &#8220;admin&#8221; };
        }
        return null;
      },
    }),
  ],
  // 独自に作成するページのパス
  pages: {
    signIn: &#8220;/auth/signin&#8221;,
  },
  // セッションの設定
  session: {
    strategy: &#8220;jwt&#8221; as const,
    maxAge: 100,
  },
  // ログイン後、ユーザー情報（user）やトークン（token）に追加情報（ここでは role）を保持・共有
  callbacks: {
    // useSession() で取り出すとき、session.user にも role を渡す
    async session({ session, token }: { session: Session; token: Token }) {
      session.user = {
        &#8230;session.user,
        role: token.role ?? null,
      } as typeof session.user &amp; { role?: string | null };
      return session;
    },
    // ログイン後、トークンに role を保存
    async jwt({ token, user }: { token: Token; user?: unknown }) {
      if (user &amp;&amp; typeof user === &#8220;object&#8221; &amp;&amp; &#8220;role&#8221; in user) {
        token.role = (user as { role?: string }).role;
      }
      return token;
    },
  },
};
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CredentialsProvider</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/providers/credentials</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Session</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">JWT</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/jwt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">interface</span><span style="color: #D8DEE9FF"> Token </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">JWT</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">authOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// CredentialsProvider の設定（IDとパスワードでログインさせるための仕組み）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">providers</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">CredentialsProvider</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// ログイン画面のフォームの中身</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">credentials</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">username</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ユーザーID</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">type</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">パスワード</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">type</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">},</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 入力されたIDとパスワードが正しいかチェックする関数</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">authorize</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">username</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">[</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">任意の環境変数名</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">] </span><span style="color: #81A1C1">&amp;&amp;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #D8DEE9">credentials</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">password</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">[</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">任意の環境変数名</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">管理者</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">role</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">admin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ]</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 独自に作成するページのパス</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">pages</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/auth/signin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// セッションの設定</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">session</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">strategy</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">jwt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">maxAge</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ログイン後、ユーザー情報（user）やトークン（token）に追加情報（ここでは role）を保持・共有</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">callbacks</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// useSession() で取り出すとき、session.user にも role を渡す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">session</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> session</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Session</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> token</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Token </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">role</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">null</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// ログイン後、トークンに role を保存</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">jwt</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> token</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Token</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> user</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">unknown</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">object</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">role</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">token</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> role</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">role</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc8">認証エンドポイント（app/api/auth/[...nextauth]/route.ts）</span></h2>



<p><strong>APIルートの設定</strong>です。NextAuth の設定を使ってログイン処理などを実行できるようにします。</p>



<p><strong><span class="keyboard-key">&#x26a0;&#xfe0f; 実装時の注意点</span></strong></p>



<ul class="wp-block-list">
<li><code>route.ts</code> では NextAuth をラップし、GET/POST 両方のメソッドをエクスポートする必要があります。</li>



<li><code>authOptions</code> を正しく <code>lib/auth.ts</code> からインポートできていることを確認してください。</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import NextAuth from &#8220;next-auth/next&#8221;;
import { authOptions } from &#8220;@/lib/auth&#8221;;
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextAuth</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/next</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">authOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@/lib/auth</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">NextAuth</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">authOptions</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">GET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">POST</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc9">アプリ全体で認証を使う（components/Providers.tsx）（app/layout.tsx）</span></h2>



<p><strong><strong>セッション（ログイン状態）をアプリ全体で共有</strong></strong>できるようにするためのコンポーネントです。</p>



<p><strong><strong>アプリのレイアウト全体を </strong><code><strong>Providers</strong></code><strong> で囲み、どのページでもログイン情報を使える</strong></strong>ようにします。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc10">SessionProviderとは</span></h3>



<p><code>SessionProvider</code> は、<strong>ログイン状態（セッション）をアプリ全体で使えるようにするための部品</strong>です。</p>



<p>囲んでいないと、<br><code>useSession()</code>（ログイン状態を取得する関数）<br><code>getSession()</code>（セッション情報を取得する関数）<br>などが正しく動作しません。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc11">SessionProvider を直接 layout.tsxに記載しない理由</span></h3>



<p>Next.js App Router では <strong><code>layout.tsx</code> はサーバーコンポーネントであることが前提</strong> です。なので、以下のような理由で <code>SessionProvider</code> を直接 <code>layout.tsx</code> に書けないことがあります。</p>



<p><span class="keyboard-key">&#x2757;問題の背景</span></p>



<ul class="wp-block-list">
<li><code>SessionProvider</code> は <strong>クライアントコンポーネント（<code>"use client"</code> が必要）</strong></li>



<li>一方で、<code>layout.tsx</code> はデフォルトで <strong>サーバーコンポーネント</strong></li>



<li>サーバーコンポーネントに <code>use client</code> をつけると、<code>metadata</code> などのサーバー専用機能が使えなくなる</li>
</ul>



<p><code>SessionProvider</code> を直接 <code>layout.tsx</code> に書くと、<code>layout.tsx</code> 全体がクライアントコンポーネントになり、Next.js 本来の機能（<code>metadata</code> など）が使えなくなる。</p>



<p>そのため、<strong>別のクライアントコンポーネントに切り出してラップする</strong>のが推奨されているわけです。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc12">実装内容</span></h3>



<p><span class="keyboard-key">&#x2705; 解決策</span>：<code>&lt;Providers&gt;</code> コンポーネントを別に作成して、ラップする！！</p>



<p><code>Providers.tsx</code> を作って <code>"use client"</code> をつけることで、</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>&#8220;use client&#8221;;
import { SessionProvider } from &#8220;next-auth/react&#8221;;
export function Providers({ children }: { children: React.ReactNode }) {
  return &lt;SessionProvider>{children}&lt;/SessionProvider>;
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">use client</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SessionProvider</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Providers</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">children</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> children</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">ReactNode </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #8FBCBB">SessionProvider</span><span style="color: #81A1C1">&gt;{</span><span style="color: #D8DEE9">children</span><span style="color: #81A1C1">}&lt;/</span><span style="color: #8FBCBB">SessionProvider</span><span style="color: #81A1C1">&gt;;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>これを <code>layout.tsx</code> 側で下記のようにラップする。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import type { Metadata } from &#8220;next&#8221;;
import { Providers } from &#8220;@/components/Providers&#8221;;
import &#8220;./globals.css&#8221;;
export const metadata: Metadata = {
  title: &#8220;Create Next App&#8221;,
  description: &#8220;Generated by create next app&#8221;,
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    &lt;html lang=&#8221;en&#8221;>
      &lt;body>
        &lt;Providers>{children}&lt;/Providers>
      &lt;/body>
    &lt;/html>
  );
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Metadata</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Providers</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@/components/Providers</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./globals.css</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">metadata</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Metadata </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Create Next App</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">description</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Generated by create next app</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">RootLayout</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">children</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> children</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">ReactNode </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;html</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">lang</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">en</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;body&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;</span><span style="color: #8FBCBB">Providers</span><span style="color: #81A1C1">&gt;{</span><span style="color: #D8DEE9">children</span><span style="color: #81A1C1">}&lt;/</span><span style="color: #8FBCBB">Providers</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/body&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/html&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc13">ログインフォーム（app/auth/signin/page.tsx）</span></h2>



<p><strong><strong>ユーザーがログインIDとパスワードを入力</strong></strong>し、ログイン処理を実行するフォームです。</p>



<p><strong><span class="keyboard-key">&#x26a0;&#xfe0f; 実装時の注意点</span></strong></p>



<ul class="wp-block-list">
<li><code>signIn("credentials")</code> の引数 <code>redirect: false</code> を忘れると、自動遷移してしまいエラー処理が難しくなります。</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>&#8220;use client&#8221;;
import { useState } from &#8220;react&#8221;;
import { signIn } from &#8220;next-auth/react&#8221;;
export default function SignInForm() {
  const [userId, setUserId] = useState(&#8220;&#8221;);
  const [password, setPassword] = useState(&#8220;&#8221;);
  const [errorMsg, setErrorMsg] = useState(&#8220;&#8221;);
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setErrorMsg(&#8220;&#8221;);
    const res = await signIn(&#8220;credentials&#8221;, {
      redirect: false,
      username: userId,
      password,
    });
    if (res?.error) {
      setErrorMsg(&#8220;ログインIDまたはパスワードが間違っています&#8221;);
    } else if (res?.ok) {
      window.location.href = &#8220;/download&#8221;;
    } else {
      setErrorMsg(&#8220;不明なエラーが発生しました&#8221;);
    }
  };
  return (
    &lt;div className=&#8221;flex items-center justify-center min-h-screen bg-cover bg-center&#8221; style={{ backgroundImage: &#8220;url(&#8216;/images/white-background.png&#8217;)&#8221; }}>
      &lt;form onSubmit={handleSubmit} className=&#8221;bg-white p-8 rounded shadow-md w-[500px] mx-auto&#8221;>
        &lt;h1 className=&#8221;text-2xl font-bold mb-6 text-center&#8221;>ログイン&lt;/h1>
        &lt;label className=&#8221;block mb-4&#8243;>
          &lt;span className=&#8221;block text-gray-700 font-semibold mb-1&#8243;>ログインID&lt;/span>
          &lt;input type=&#8221;text&#8221; value={userId} onChange={(e) => setUserId(e.target.value)} required className=&#8221;w-full px-3 py-2 border border-gray-300 rounded&#8221; />
        &lt;/label>
        &lt;label className=&#8221;block mb-4&#8243;>
          &lt;span className=&#8221;block text-gray-700 font-semibold mb-1&#8243;>パスワード&lt;/span>
          &lt;input type=&#8221;password&#8221; value={password} onChange={(e) => setPassword(e.target.value)} required className=&#8221;w-full px-3 py-2 border border-gray-300 rounded&#8221; />
        &lt;/label>
        {errorMsg &amp;&amp; &lt;p className=&#8221;text-red-600 text-center mb-4&#8243;>{errorMsg}&lt;/p>}
        &lt;button type=&#8221;submit&#8221; className=&#8221;w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700&#8243;>ログイン&lt;/button>
      &lt;/form>
    &lt;/div>
  );
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">use client</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">useState</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">signIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SignInForm</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">userId</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setUserId</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">password</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setPassword</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">errorMsg</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setErrorMsg</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleSubmit</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">FormEvent</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">preventDefault</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setErrorMsg</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">signIn</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">credentials</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">redirect</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">username</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">userId</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">password</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setErrorMsg</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ログインIDまたはパスワードが間違っています</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">?.</span><span style="color: #D8DEE9">ok</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">location</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">href</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/download</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setErrorMsg</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">不明なエラーが発生しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">flex items-center justify-center min-h-screen bg-cover bg-center</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">style</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">backgroundImage</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">url(&#39;/images/white-background.png&#39;)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">}&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;form</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">onSubmit</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleSubmit</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bg-white p-8 rounded shadow-md w-[500px] mx-auto</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;h1</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text-2xl font-bold mb-6 text-center</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">ログイン</span><span style="color: #81A1C1">&lt;/h1&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;label</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block mb-4</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;span</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block text-gray-700 font-semibold mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">ログインID</span><span style="color: #81A1C1">&lt;/span&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;input</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">userId</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setUserId</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">target</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">required</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">w-full px-3 py-2 border border-gray-300 rounded</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/label&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;label</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block mb-4</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;span</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block text-gray-700 font-semibold mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">パスワード</span><span style="color: #81A1C1">&lt;/span&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;input</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">password</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setPassword</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">target</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">required</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">w-full px-3 py-2 border border-gray-300 rounded</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/label&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">errorMsg</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;p</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text-red-600 text-center mb-4</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;{</span><span style="color: #D8DEE9">errorMsg</span><span style="color: #81A1C1">}&lt;/p&gt;}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;button</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">submit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">ログイン</span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/form&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc14">未ログイン時のログイン誘導（components/Login.tsx）</span></h2>



<p><strong><strong>ユーザーが未ログインだった場合に、ログインボタンを表示してログイン画面に誘導</strong></strong>する処理です。</p>



<p><strong><span class="keyboard-key">&#x26a0;&#xfe0f; 実装時の注意点</span></strong></p>



<ul class="wp-block-list">
<li><code>useSession()</code> を使うには、呼び出し元が <code>SessionProvider</code> 配下である必要があります。</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>&#8220;use client&#8221;;
import { signIn } from &#8220;next-auth/react&#8221;;
export default function Login() {
  return (
    &lt;div className=&#8221;mt-8 text-center&#8221;>
      &lt;button
        className=&#8221;text-blue-600 underline&#8221;
        onClick={() => signIn(undefined, { callbackUrl: &#8220;/auth/signin&#8221; })}
      >
        &#x1f512; 管理者ログインはこちら
      &lt;/button>
    &lt;/div>
  );
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">use client</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">signIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next-auth/react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Login</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-8 text-center</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text-blue-600 underline</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">signIn</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">undefined</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">callbackUrl</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/auth/signin</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        &#x1f512; 管理者ログインはこちら</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc15">実行画面</span></h2>



<p>下記のリンク押下で、ログイン画面が表示される</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="86" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.06-1024x86.png" alt="Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応" class="wp-image-3668" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.06-1024x86.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.06-300x25.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.06-768x64.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.06.png 1360w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ログインID・パスワードを入力</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="595" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.20-1024x595.png" alt="Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応" class="wp-image-3669" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.20-1024x595.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.20-300x174.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.20-768x447.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.20.png 1424w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ログイン完了したら、管理者画面が表示される（この画面のコードはなし）</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="282" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.39-1024x282.png" alt="Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応" class="wp-image-3670" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.39-1024x282.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.39-300x83.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.39-768x212.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.39-1536x424.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-18.09.39.png 1646w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/16/use-client/" title="Next.jsの「use client」とは？サーバーコンポーネントとクライアントコンポーネントの違い" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">Next.jsの「use client」とは？サーバーコンポーネントとクライアントコンポーネントの違い</div><div class="blogcard-snippet internal-blogcard-snippet">Next.jsの &quot;use client&quot; の意味と、サーバー・クライアントコンポーネントの違いを初心者向けに解説します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div><p>The post <a href="https://it-bokenki.com/2025/06/26/nextauth-js/">Next.jsでログイン機能を実装｜NextAuth.jsでID・パスワード認証対応</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/26/nextauth-js/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</title>
		<link>https://it-bokenki.com/2025/06/26/azure-blob/</link>
					<comments>https://it-bokenki.com/2025/06/26/azure-blob/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Thu, 26 Jun 2025 04:07:14 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[クラウド]]></category>
		<category><![CDATA[開発事例]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=3579</guid>

					<description><![CDATA[<p>Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち 全体構成 Azure 側の準備 ファイルを保存する先として Azure Blob Storage を使うために、まずはAzure側での [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/26/azure-blob/">Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>どもども<br>今回は「Azure Blob Storage にファイルをアップロードする方法」について解説します。</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-r sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/5-1-150x150.png" alt="バグヲ" class="speech-icon-image"/></figure><div class="speech-name">バグヲ</div></div><div class="speech-balloon">
<p>Azureって最近よく使われてるやつだよね？<br>でも、いまだによくわかってない…</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>こんなあなたにピッタリな記事&#x1f447;</p>



<div style="height:15px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>Azure側の設定がわからない</li>



<li>APIはどのように記載するの？</li>



<li>とりあえず実装だけ知りたい！</li>
</ul>



<p>がまぁまぁわかります！</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NZROH">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www27.a8.net/svt/bgt?aid=230528653907&#038;wid=001&#038;eno=01&#038;mid=s00000023513004030000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=3T911P+F006GI+51FE+NZROH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NWRNM">Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/24/npm-vs-pnpm-vs-yarn/" title="npm・yarn・pnpmの違いとは？速度や特徴をざっくり比較！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">npm・yarn・pnpmの違いとは？速度や特徴をざっくり比較！</div><div class="blogcard-snippet internal-blogcard-snippet">JavaScriptやTypeScriptのプロジェクトでは、依存関係の管理に「パッケージマネージャー」が欠かせません。この記事では、代表的な3つのパッケージマネージャー「npm」「yarn」「pnpm」の違いを比較し、それぞれのメリット・デメリットを表にまとめて解説します。npmはNode.js公式が提供している標準的なツールで、ほとんどのプロジェクトで使われています。対してyarnはMeta（旧Facebook）が開発したもので、npmよりも速くて安定していると評価されています。そして近年注目されているのがpnpmで、圧倒的なインストール速度とディスク効率の良さから、徐々にシェアを伸ばしています。選び方に迷ったときは、プロジェクトの規模や開発チームの方針を基準にするとよいでしょう。たとえば、モノレポ構成や高速なCI/CDを重視する場合はpnpmが向いています。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">全体構成</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="784" height="432" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3587" style="width:483px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26.png 784w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26-300x165.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26-768x423.png 768w" sizes="auto, (max-width: 784px) 100vw, 784px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">Azure 側の準備</span></h2>



<p>ファイルを保存する先として <code>Azure Blob Storage</code> を使うために、まずはAzure側での初期設定を行いましょう。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc3">STEP1：ストレージアカウントを作成</span></h3>



<ol class="wp-block-list">
<li><a class="" href="https://portal.azure.com/">Azureポータル</a> にログイン</li>



<li>左上の「≡」メニューから「<strong>ストレージアカウント</strong>」を選択し、「<strong>＋作成</strong>」をクリック</li>



<li>以下の<span class="bold-red">※</span><span class="red">1</span>ように項目を入力（最低限でOK）</li>



<li>下部の「<strong>確認および作成</strong>」→「<strong>作成</strong>」を押す</li>



<li>デプロイ完了後、「<strong>リソースに移動</strong>」ボタンをクリック</li>
</ol>



<p><span class="bold-red">※</span><span class="red">1</span></p>



<figure class="wp-block-table is-style-regular"><table class="has-fixed-layout"><thead><tr><th>項目</th><th>設定値</th></tr></thead><tbody><tr><td>サブスクリプション</td><td>任意（通常は1つ）</td></tr><tr><td>リソースグループ</td><td>任意（新規作成でもOK）</td></tr><tr><td>ストレージアカウント名</td><td>例：myuploadstorage123（一意な名前）</td></tr><tr><td>リージョン</td><td>お住まいに近い場所（東日本ならJapan East）</td></tr><tr><td>パフォーマンス</td><td>standard</td></tr><tr><td>冗長性</td><td>ローカル冗長ストレージ (LRS)</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc4">STEP2：Blobコンテナーを作成</span></h3>



<ol class="wp-block-list">
<li>ストレージアカウントの画面左メニューから「<strong>データストレージ</strong>」→「<strong>コンテナー</strong>」を選択</li>



<li>上部の「＋<strong>コンテナー</strong>」ボタンを押して以下の<span class="bold-red">※</span><span class="red">2</span>ように項目を入力</li>



<li>「<strong>作成</strong>」ボタンを押す</li>
</ol>



<p><span class="bold-red">※</span><span class="red">2</span></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>項目</th><th>設定値</th></tr></thead><tbody><tr><td>名前</td><td>例：upload</td></tr><tr><td>パブリックアクセスレベル</td><td>プライベート（推奨）</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">STEP3：接続文字列を取得して .env.local に設定</span></h3>



<ol class="wp-block-list">
<li>ストレージアカウントの左メニューをスクロールし、「<strong>セキュリティとネットワーク</strong>」セクションにある「<strong>アクセスキー</strong>」をクリック</li>



<li>「key1」の項目にある「<strong>接続文字列</strong>」をコピー</li>



<li>プロジェクト直下の <code>.env.local</code> ファイルに以下を追加</li>
</ol>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=（コピーした内容）
AZURE_CONTAINER_NAME=（設定した名前）</code></pre></div>



<p><code>.env.local</code> を変更したあとは、<strong>Next.js の開発サーバーを再起動</strong>してください。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc6">ファイルアップロード用UIの作成（React）</span></h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>&#8220;use client&#8221;;
import { useState, useRef } from &#8220;react&#8221;;
export default function DragAndDropUploader() {
  // ドラッグ中かどうかの状態
  const [isDragging, setIsDragging] = useState(false);
  // メッセージ表示用（成功・エラー）
  const [message, setMessage] = useState(&#8220;&#8221;);
  // input要素の参照（隠してボタンから開く用）
  const fileInputRef = useRef&lt;HTMLInputElement>(null);
  // 選択されたファイル一覧
  const [selectedFiles, setSelectedFiles] = useState&lt;File[]>([]);
  // ファイルがドロップされた時の処理
  const handleDrop = (e: React.DragEvent&lt;HTMLDivElement>) => {
    e.preventDefault();
    setIsDragging(false);
    const files = Array.from(e.dataTransfer.files); // FileList → 配列に変換
    if (files.length === 0) return;
    setSelectedFiles(files);
  };
  // ドラッグ中（枠が赤くなる）
  const handleDragOver = (e: React.DragEvent&lt;HTMLDivElement>) => {
    e.preventDefault();
    setIsDragging(true);
  };
  // ドラッグが外れたとき（赤枠を戻す）
  const handleDragLeave = () => setIsDragging(false);
  // ファイル選択時の処理
  const handleFileSelect = (e: React.ChangeEvent&lt;HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    if (files.length === 0) return;
    setSelectedFiles(files);
  };
  // 一括取消（ファイルクリア）
  const handleClearFiles = () => {
    setSelectedFiles([]);
    setMessage(&#8220;&#8221;);
  };
  // ボタンクリック → input をクリックさせる
  const handleButtonClick = () => {
    fileInputRef.current?.click();
  };
  // アップロード処理
  const handleUpload = async () => {
    if (selectedFiles.length === 0) {
      setMessage(&#8220;&#x274c; ファイルが選択されていません&#8221;);
      return;
    }
    const formData = new FormData();
    selectedFiles.forEach((file) => formData.append(&#8220;files&#8221;, file));
    try {
      // APIにPOST
      const res = await fetch(&#8220;/api/upload&#8221;, {
        method: &#8220;POST&#8221;,
        body: formData,
      });
      if (res.ok) {
        setMessage(&#8220;&#x2705; アップロード成功しました&#8221;);
      } else {
        const data = await res.json();
        setMessage(&#8220;&#x274c; アップロード失敗: &#8221; + (data.error || res.statusText));
      }
    } catch {
      setMessage(&#8220;&#x274c; アップロード中にエラーが発生しました&#8221;);
    }
  };
  return (
    &lt;div className=&#8221;m-5 mx-auto w-fit&#8221;>
      &lt;h2>&#x1f4e5; スキルシートをアップロード&lt;/h2>
      {/* ドラッグ＆ドロップ枠 */}
      &lt;div
        onDrop={handleDrop}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        className={`w-[620px] h-[320px] border-2 border-dashed rounded p-5 text-center transition-colors duration-300 ${
          isDragging ? &#8220;border-red-500&#8221; : &#8220;border-gray-300&#8221;
        } bg-gray-100 flex items-center justify-center`}
      >
        &lt;div>
          &lt;p>{isDragging ? &#8220;ここにドロップ&#8221; : &#8220;ファイルをドラッグ＆ドロップ&#8221;}&lt;/p>
          &lt;p>または&lt;/p>
          &lt;button
            onClick={handleButtonClick}
            className=&#8221;bg-red-500 text-white px-4 py-2 rounded&#8221;
          >
            ファイルを選択
          &lt;/button>
        &lt;/div>
        {/* ファイル選択用input（非表示） */}
        &lt;input
          type=&#8221;file&#8221;
          multiple
          ref={fileInputRef}
          onChange={handleFileSelect}
          style={{ display: &#8220;none&#8221; }}
        />
      &lt;/div>
      {/* 選択されたファイルの表示 + ✕ボタン */}
      {selectedFiles.length > 0 &amp;&amp; (
        &lt;div className=&#8221;mt-4 text-left&#8221;>
          &lt;div className=&#8221;flex items-start justify-between mb-2&#8243;>
            &lt;p className=&#8221;font-semibold&#8221;>選択中のファイル&lt;/p>
            &lt;button
              onClick={handleClearFiles}
              className=&#8221;font-semibold text-sm text-red-600 hover:underline&#8221;
            >
              一括取消
            &lt;/button>
          &lt;/div>
          &lt;ul>
            {selectedFiles.map((file, index) => (
              &lt;li key={index} className=&#8221;flex items-center justify-between mb-1&#8243;>
                &lt;span>{file.name}&lt;/span>
                &lt;button
                  onClick={() =>
                    setSelectedFiles((prev) =>
                      prev.filter((_, i) => i !== index)
                    )
                  }
                  className=&#8221;text-red-600 font-bold&#8221;
                >
                  ✕
                &lt;/button>
              &lt;/li>
            ))}
          &lt;/ul>
        &lt;/div>
      )}
      {/* アップロードボタン */}
      &lt;div>
        &lt;button
          onClick={handleUpload}
          className=&#8221;bg-green-700 text-white px-4 py-2 rounded mt-5&#8243;
        >
          アップロード
        &lt;/button>
        {message &amp;&amp; &lt;p className=&#8221;mt-2&#8243;>{message}&lt;/p>}
      &lt;/div>
    &lt;/div>
  );
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">use client</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">useState</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">useRef</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">DragAndDropUploader</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ドラッグ中かどうかの状態</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">isDragging</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setIsDragging</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">false</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// メッセージ表示用（成功・エラー）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">message</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setMessage</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// input要素の参照（隠してボタンから開く用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileInputRef</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useRef</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLInputElement</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">null</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 選択されたファイル一覧</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setSelectedFiles</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">File[]</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">([])</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ファイルがドロップされた時の処理</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleDrop</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">DragEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLDivElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">preventDefault</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setIsDragging</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">false</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Array</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">from</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">dataTransfer</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// FileList → 配列に変換</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ドラッグ中（枠が赤くなる）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleDragOver</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">DragEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLDivElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">preventDefault</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setIsDragging</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">true</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ドラッグが外れたとき（赤枠を戻す）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleDragLeave</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setIsDragging</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">false</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ファイル選択時の処理</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleFileSelect</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">ChangeEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLInputElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Array</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">from</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">target</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> [])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 一括取消（ファイルクリア）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleClearFiles</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">([])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ボタンクリック → input をクリックさせる</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleButtonClick</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fileInputRef</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">current</span><span style="color: #ECEFF4">?.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// アップロード処理</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleUpload</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x274c; ファイルが選択されていません</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">FormData</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">forEach</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">append</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">files</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// APIにPOST</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetch</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/api/upload</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">method</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">POST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ok</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x2705; アップロード成功しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x274c; アップロード失敗: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">statusText</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x274c; アップロード中にエラーが発生しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">m-5 mx-auto w-fit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;h2&gt;</span><span style="color: #D8DEE9FF">&#x1f4e5; スキルシートをアップロード</span><span style="color: #81A1C1">&lt;/h2&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* ドラッグ＆ドロップ枠 */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onDrop</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleDrop</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onDragOver</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleDragOver</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onDragLeave</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleDragLeave</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">`</span><span style="color: #A3BE8C">w-[620px] h-[320px] border-2 border-dashed rounded p-5 text-center transition-colors duration-300 </span><span style="color: #81A1C1">${</span></span>
<span class="line"><span style="color: #A3BE8C">          </span><span style="color: #D8DEE9">isDragging</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">?</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border-red-500</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">:</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border-gray-300</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #A3BE8C">        </span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> bg-gray-100 flex items-center justify-center</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;p&gt;{</span><span style="color: #D8DEE9">isDragging</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ここにドロップ</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ファイルをドラッグ＆ドロップ</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">}&lt;/p&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;p&gt;</span><span style="color: #D8DEE9FF">または</span><span style="color: #81A1C1">&lt;/p&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleButtonClick</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bg-red-500 text-white px-4 py-2 rounded</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            ファイルを選択</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* ファイル選択用input（非表示） */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;input</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">file</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">multiple</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">ref</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">fileInputRef</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleFileSelect</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">style</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">display</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">none</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* 選択されたファイルの表示 + ✕ボタン */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-4 text-left</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">flex items-start justify-between mb-2</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;p</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">font-semibold</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">選択中のファイル</span><span style="color: #81A1C1">&lt;/p&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleClearFiles</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">font-semibold text-sm text-red-600 hover:underline</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">              一括取消</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;ul&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">map</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">index</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&lt;li</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">key</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">index</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">flex items-center justify-between mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&lt;span&gt;{</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">}&lt;/span&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">prev</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                      </span><span style="color: #D8DEE9">prev</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">filter</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">index</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    )</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text-red-600 font-bold</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  ✕</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&lt;/li&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            ))</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;/ul&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      )</span><span style="color: #81A1C1">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* アップロードボタン */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleUpload</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bg-green-700 text-white px-4 py-2 rounded mt-5</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          アップロード</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">message</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;p</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-2</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;{</span><span style="color: #D8DEE9">message</span><span style="color: #81A1C1">}&lt;/p&gt;}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc7">ファイルアップロードAPIの作成（Next.js × Azure SDK）</span></h2>



<p><span class="badge-grey">仕様</span><br>ファイル名が一意になるように、アップロードされたファイル名の末尾に<br>「<strong>_YYYYMMDD_hhmm</strong>」と日時を付与してから<code>Azure Blob Storage</code> にアップロードする</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import { NextResponse } from &#8220;next/server&#8221;;
import { BlobServiceClient } from &#8220;@azure/storage-blob&#8221;;
// POSTメソッドで呼び出されたときの処理
export async function POST(req: Request) {
  try {
    // フロントから送られてきたファイル付きのデータ（FormData）を受け取る
    const formData = await req.formData();
    // name=&#8221;files&#8221; という名前で送られてきたファイル全部取得
    const files = formData.getAll(&#8220;files&#8221;) as File[];
    // ファイルが1つもなかったらエラーを返す
    if (!files.length) {
      return NextResponse.json(
        { error: &#8220;ファイルがありません&#8221; },
        { status: 400 }
      );
    }
    // Azure Storage に接続するためのクライアントを作成
    const blobService = BlobServiceClient.fromConnectionString(
      process.env.AZURE_STORAGE_CONNECTION_STRING! // .env.local に設定した接続文字列
    );
    // コンテナー（事前に作成済み）に接続
    const container = blobService.getContainerClient(
      process.env.AZURE_CONTAINER_NAME!
    );
    // ファイルごとに繰り返す
    for (const file of files) {
      // タイムスタンプを付けて、ファイル名の重複を防ぐ
      const now = new Date();
      const timestamp = `${now.getFullYear()}${String(
        now.getMonth() + 1
      ).padStart(2, &#8220;0&#8221;)}${String(now.getDate()).padStart(2, &#8220;0&#8221;)}_${String(
        now.getHours()
      ).padStart(2, &#8220;0&#8221;)}${String(now.getMinutes()).padStart(2, &#8220;0&#8221;)}`;
      // 元のファイル名と拡張子を分離
      const originalName = file.name;
      const ext = originalName.includes(&#8220;.&#8221;)
        ? &#8220;.&#8221; + originalName.split(&#8220;.&#8221;).pop()
        : &#8220;&#8221;;
      const base = originalName.replace(ext, &#8220;&#8221;);
      // 一意なファイル名を作成（例：resume_20250626_1030.pdf）
      const uniqueName = `${base}_${timestamp}${ext}`;
      // Fileオブジェクト → ArrayBuffer に変換
      const arrayBuffer = await file.arrayBuffer();
      // ArrayBuffer → Node.js の Buffer に変換（Azure用）
      const buffer = Buffer.from(arrayBuffer);
      // Azure上の「保存先ファイル（Blob）」を指定
      const blockBlob = container.getBlockBlobClient(uniqueName);
      // ファイルをアップロード（bufferとして保存）
      await blockBlob.uploadData(buffer);
    }
    // 成功レスポンスを返す
    return NextResponse.json({
      message: &#8220;すべてのファイルをアップロードしました&#8221;,
    });
  } catch (error) {
    // エラーが起きた場合はログを出してエラーを返す
    console.error(&#8220;アップロードエラー:&#8221;, error);
    return NextResponse.json({ error: &#8220;アップロード失敗&#8221; }, { status: 500 });
  }
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextResponse</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next/server</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">BlobServiceClient</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@azure/storage-blob</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// POSTメソッドで呼び出されたときの処理</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">POST</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">req</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Request</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// フロントから送られてきたファイル付きのデータ（FormData）を受け取る</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">req</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">formData</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// name=&quot;files&quot; という名前で送られてきたファイル全部取得</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">files</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> File[]</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// ファイルが1つもなかったらエラーを返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">error</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ファイルがありません</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">status</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">400</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Azure Storage に接続するためのクライアントを作成</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blobService</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">BlobServiceClient</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">fromConnectionString</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">AZURE_STORAGE_CONNECTION_STRING</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// .env.local に設定した接続文字列</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// コンテナー（事前に作成済み）に接続</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">container</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blobService</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getContainerClient</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">AZURE_CONTAINER_NAME</span><span style="color: #81A1C1">!</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// ファイルごとに繰り返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">of</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// タイムスタンプを付けて、ファイル名の重複を防ぐ</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">now</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Date</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timestamp</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getFullYear</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">}${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #A3BE8C">        </span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getMonth</span><span style="color: #ECEFF4">()</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">+</span><span style="color: #A3BE8C"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #A3BE8C">      </span><span style="color: #ECEFF4">).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getDate</span><span style="color: #ECEFF4">()).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">_</span><span style="color: #81A1C1">${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #A3BE8C">        </span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getHours</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #A3BE8C">      </span><span style="color: #ECEFF4">).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getMinutes</span><span style="color: #ECEFF4">()).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 元のファイル名と拡張子を分離</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ext</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">split</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">pop</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">base</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">replace</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ext</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 一意なファイル名を作成（例：resume_20250626_1030.pdf）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uniqueName</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">base</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">_</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">timestamp</span><span style="color: #81A1C1">}${</span><span style="color: #D8DEE9">ext</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// Fileオブジェクト → ArrayBuffer に変換</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">arrayBuffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">arrayBuffer</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// ArrayBuffer → Node.js の Buffer に変換（Azure用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">buffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Buffer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">from</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">arrayBuffer</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// Azure上の「保存先ファイル（Blob）」を指定</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blockBlob</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">container</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getBlockBlobClient</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uniqueName</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// ファイルをアップロード（bufferとして保存）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blockBlob</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">uploadData</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">buffer</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 成功レスポンスを返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">すべてのファイルをアップロードしました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// エラーが起きた場合はログを出してエラーを返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">アップロードエラー:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">error</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">アップロード失敗</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">status</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc8">実行画面</span></h2>



<p><code>Azure Blob Storage</code> にまだファイルがアップロードされていない状態</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-1024x180.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3597" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-1024x180.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-300x53.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-768x135.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-1536x270.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-2048x360.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>アップロード画面</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="591" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-1024x591.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3592" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-1024x591.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-300x173.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-768x443.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37.png 1490w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ファイルをアップロードすると、選択中のファイルが表示される</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="694" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-1024x694.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3598" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-1024x694.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-300x203.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-768x521.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35.png 1496w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>アップロードボタンを押下すると、アップロード完了</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="737" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-1024x737.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3599" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-1024x737.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-300x216.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-768x552.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50.png 1518w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>Azure Blob Storage</code> にファイルが格納されていることを確認</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="145" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-1024x145.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3600" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-1024x145.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-300x42.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-768x108.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-1536x217.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-2048x289.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/24/dom-vs-virtual-dom/" title="DOMと仮想DOMの違いをわかりやすく比較！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">DOMと仮想DOMの違いをわかりやすく比較！</div><div class="blogcard-snippet internal-blogcard-snippet">DOMと仮想DOMの違いを、お弁当のたとえや図を使ってやさしく解説！Reactでよく出る用語の理解がグッと深まります。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div><p>The post <a href="https://it-bokenki.com/2025/06/26/azure-blob/">Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/26/azure-blob/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>axiosとは？初心者でもわかる使い方・fetchとの違いを解説</title>
		<link>https://it-bokenki.com/2025/06/16/axios/</link>
					<comments>https://it-bokenki.com/2025/06/16/axios/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Mon, 16 Jun 2025 07:10:02 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[フロントエンド]]></category>
		<category><![CDATA[axios]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=3463</guid>

					<description><![CDATA[<p>Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち axiosとは？ axios は、JavaScriptでAPI通信（サーバーとのやり取り）を簡単に行うための外部ライブラリです。 シン [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/16/axios/">axiosとは？初心者でもわかる使い方・fetchとの違いを解説</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>どもども<br>今回は「axios（アクシオス）」について解説します。</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-r sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/5-1-150x150.png" alt="バグヲ" class="speech-icon-image"/></figure><div class="speech-name">バグヲ</div></div><div class="speech-balloon">
<p>API通信っていろいろあって混乱するんだけど、<br>axiosってなにが便利なの？</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>こんなあなたにピッタリな記事&#x1f447;</p>



<div style="height:15px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>axiosってなに？</li>



<li>axiosとfetchの違いは？</li>



<li>API通信がよくわからない</li>
</ul>



<p>がまぁまぁわかります！</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NZROH">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www27.a8.net/svt/bgt?aid=230528653907&#038;wid=001&#038;eno=01&#038;mid=s00000023513004030000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=3T911P+F006GI+51FE+NZROH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NWRNM">Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち</a></p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/16/async-await-promise-basic/" title="async/awaitとPromiseの違いとは？" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-10-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-10-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-10-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-10-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">async/awaitとPromiseの違いとは？</div><div class="blogcard-snippet internal-blogcard-snippet">非同期処理が難しいと感じている方へ。async/await・Promise・try/catch の基本をやさしく解説します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">axiosとは？</span></h2>



<p><strong><code>axios</code></strong> は、JavaScriptでAPI通信（サーバーとのやり取り）を簡単に行うための<strong>外部ライブラリ</strong>です。</p>



<p><strong>シンプルなコードで通信が実現できます。</strong></p>



<p><span class="keyboard-key">例</span>：</p>



<ul class="wp-block-list">
<li>Webページを開いたときにユーザー情報を取得したい</li>



<li>入力フォームからデータをサーバーに送りたい</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">axiosは何ができるの？</span></h2>



<p><code>axios</code>を使うと、以下のような操作が簡単にできます。</p>



<ul class="wp-block-list">
<li>GETリクエスト（データを取得）</li>



<li>POSTリクエスト（データを送信）</li>



<li>JSONデータの自動変換</li>



<li>エラー処理（HTTPステータスも catch できる）</li>



<li>タイムアウトやリトライの設定</li>



<li>リクエストヘッダーの共通管理</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">axiosとfetchの違いは？</span></h2>



<p>初心者にとって混乱しやすいのがここ。実は、JavaScriptには標準の通信手段として <code>fetch</code> という関数もあります。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc4">同じGET処理をaxiosとfetchで書くと？</span></h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// fetch版
fetch("https://api.example.com/users")
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

// axios版
import axios from "axios";

axios.get("https://api.example.com/users")
  .then(res => console.log(res.data))
  .catch(err => console.error(err));</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// fetch版</span></span>
<span class="line"><span style="color: #88C0D0">fetch</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">https://api.example.com/users</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">log</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">catch</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// axios版</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">axios</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">axios</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">axios</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">https://api.example.com/users</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">log</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">catch</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span></code></pre></div>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>比較項目</th><th>fetch</th><th>axios</th></tr></thead><tbody><tr><td>JSON変換</td><td><code>.json()</code> が毎回必要</td><td>自動で変換、<code>res.data</code> ですぐ使える</td></tr><tr><td>エラー処理</td><td>通信エラーしか catch できない</td><td>HTTPエラー（400, 500番台）も catch 可能</td></tr><tr><td>タイムアウト設定</td><td>自力でAbortControllerが必要</td><td><code>timeout</code> オプションですぐ設定可能</td></tr><tr><td>リクエスト共通化</td><td>毎回ヘッダー設定などを記述</td><td><code>axios.defaults</code> で一括設定可能</td></tr><tr><td>2025年の使用状況</td><td>学習・小規模開発に多い</td><td>チーム・実務では今もaxiosが主流</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">axiosとfetch どっちを使えばいいの？</span></h3>



<p>&#x2705;&#xfe0f; <strong>学習や練習なら fetch でもOK</strong><br>ライブラリ不要で軽い</p>



<p>&#x2705;&#xfe0f; <strong>実務やチーム開発なら axios をおすすめ</strong><br>冗長な処理を減らせて、保守しやすく、失敗しづらい</p>



<p>2025年現在でも、<strong>ReactやVueなどの現場ではaxiosがよく使われています。</strong></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc6">axiosの使い方</span></h2>



<h3 class="wp-block-heading"><span id="toc7">axiosインストール方法</span></h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>npm install axios
# または
yarn add axios</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc8">基本的な使い方（GET/POST）</span></h3>



<p>axiosを使って、サーバーからデータを取得（GET）・送信（POST）する例です。<br><code>res.data</code> でサーバーからのデータにアクセスできます。<br>エラーが起きたときは <code>.catch()</code> で内容を確認できます。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>// GETリクエスト
axios.get(&#8220;/api/user&#8221;)
  .then(res => console.log(res.data))
  .catch(err => console.error(err));
// POSTリクエスト
axios.post(&#8220;/api/user&#8221;, { name: &#8220;太郎&#8221; })
  .then(res => console.log(res.data))
  .catch(err => console.error(err));
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// GETリクエスト</span></span>
<span class="line"><span style="color: #D8DEE9">axios</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/api/user</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">log</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">catch</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// POSTリクエスト</span></span>
<span class="line"><span style="color: #D8DEE9">axios</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">post</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/api/user</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">太郎</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">log</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">catch</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc9">まとめ</span></h2>



<ul class="wp-block-list">
<li>axiosは、JavaScriptでAPI通信を行うための便利なライブラリ</li>



<li>fetchよりも書きやすく、実務で使いやすい工夫がたくさん</li>



<li>初心者はまず「GETでデータ取得 → .then(res.data)」を覚えればOK！</li>
</ul>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div><p>The post <a href="https://it-bokenki.com/2025/06/16/axios/">axiosとは？初心者でもわかる使い方・fetchとの違いを解説</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/16/axios/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SMTPとは？メール送信の仕組みと実装・エラー内容まとめ</title>
		<link>https://it-bokenki.com/2025/06/14/smtp/</link>
					<comments>https://it-bokenki.com/2025/06/14/smtp/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Sat, 14 Jun 2025 14:32:39 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[IT基礎知識]]></category>
		<category><![CDATA[Node.js]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=3293</guid>

					<description><![CDATA[<p>でじぼうです。 開発をすると SMTP って言葉よく聞きますよね。実際よくわかっていない人、集まってください！ご説明します。 Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち SMTP [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/14/smtp/">SMTPとは？メール送信の仕組みと実装・エラー内容まとめ</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>でじぼうです。</p>



<p>開発をすると<strong> <code>SMTP</code></strong> って言葉よく聞きますよね。<br>実際よくわかっていない人、集まってください！ご説明します。</p>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="でじぼう" class="speech-icon-image"/></figure><div class="speech-name">でじぼう</div></div><div class="speech-balloon">
<p>この記事は下記の方がおすすめ！<br></p>



<ul class="wp-block-list">
<li style="font-size:15px">SMTPってなに？</li>



<li style="font-size:15px">どう実装するのかわからない</li>



<li style="font-size:15px">エラーが出ても解読できない</li>
</ul>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NZROH">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www27.a8.net/svt/bgt?aid=230528653907&#038;wid=001&#038;eno=01&#038;mid=s00000023513004030000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=3T911P+F006GI+51FE+NZROH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NWRNM">Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">SMTPとは？</span></h2>



<p>SMTP（Simple Mail Transfer Protocol）とは、<strong>メールを送るときに使われる通信ルール</strong>のことです。</p>



<p>「プロトコル」とは「決まりごと」という意味なので、SMTPは「メール送信用の決まりごと」と覚えるとシンプルです。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">SMTPはどんなときに使われる？</span></h2>



<p>Gmailなどでメールを送るとき、その裏側ではSMTPが働いて、「どこに」「誰が」「何を」送ったかを伝えています。</p>



<p>例えるなら、SMTPは「郵便局の配達員」のような存在です。<br><strong>書いた手紙（メール）を相手の住所（メールアドレス）に届けてくれる役割</strong>です。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">メール送信の流れ</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="512" height="678" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-14-23.24.07.png" alt="SMTPとは？メール送信の仕組みと実装・エラー内容まとめ" class="wp-image-3358" style="width:360px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-14-23.24.07.png 512w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-14-23.24.07-227x300.png 227w" sizes="auto, (max-width: 512px) 100vw, 512px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc4">SMTPでよく出る用語と設定項目</span></h2>



<p>下記に、SMTP関連でよく利用する用語をまとめました。</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>用語</th><th>意味</th></tr></thead><tbody><tr><td>SMTPサーバー</td><td>・メールを送信するための中継サーバー<br>例：Gmailなら <code>smtp.gmail.com</code> </td></tr><tr><td>ポート番号</td><td>・サーバーと通信する入口番号<br>・SMTPでは<span class="marker"> <strong>465（SSL）</strong></span> や <br><strong><span class="marker">587（STARTTLS）</span></strong> がよく使われる</td></tr><tr><td>SSL（465番）</td><td>・通信の最初から最後まで、<strong><span class="marker">ずっと暗号化された状態</span></strong>でやりとりする方式</td></tr><tr><td>STARTTLS（587番）</td><td>・最初は普通の通信で始めて、<strong><span class="marker">途中で暗号化される</span></strong>方式</td></tr><tr><td>認証（Auth）</td><td>・送信者が本人かを確認する仕組み<br>・メールアドレスとアプリパスワードなどでログインする</td></tr><tr><td>host</td><td>・SMTPサーバーのアドレス<br>・<code>smtp.gmail.com</code> や <code>smtp.sendgrid.net</code> のような形</td></tr><tr><td>secure</td><td>・暗号化方式の指定<br>・<span class="marker"><strong><code>true</code> で SSL</strong></span>、<strong><span class="marker"><code>false</code> で STARTTLS</span></strong> を使用</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc5">どっちのポート番号を使えばいいの？【465 or 587】</span></h2>



<p>基本的には <strong><span class="marker">587（STARTTLS）を使うのが推奨</span></strong> です。</p>



<p>多くのメールサービス（Gmail、SendGrid、Outlook など）でも推奨されています。<br></p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-point block-box has-background has-border-color has-white-background-color has-grey-border-color">
<p class="has-small-font-size"><span class="fz-16px">その理由は</span></p>



<ul class="wp-block-list">
<li><span class="fz-16px">セキュリティ標準として <strong>TLS（STARTTLS）が新しい</strong></span></li>



<li><span class="fz-16px">ファイアウォールやISP（ネット回線業者</span>）<span class="fz-16px">でブロックされにくい</span></li>



<li><span class="fz-16px">相互運用性が高く、失敗しづらい</span></li>
</ul>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">465（SSL）を使ってもいいの？</span></h3>



<p>使ってもOKです。ただし注意点もあります。</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>比較項目</th><th>587（STARTTLS）</th><th>465（SSL）</th></tr></thead><tbody><tr><td>推奨度</td><td>推奨されている</td><td>古いがまだ使われている</td></tr><tr><td>安全性</td><td>高い（TLS）</td><td>高い（SSL）※非推奨化の流れ</td></tr><tr><td>対応範囲</td><td>広い</td><td>一部でのサーバーのみ使えることも</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc7">SMTPとPOP/IMAPの違いを比較</span></h2>



<p>メールの送受信には、それぞれ役割の違うプロトコルが使われています。<br>特に、SMTP・POP・IMAPの3つはよく登場しますが、<strong>それぞれ役割がまったく異なります。</strong><br>下の表で、その違いを比べてみましょう。</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>プロトコル</th><th>役割</th><th>使いどころ</th></tr></thead><tbody><tr><td><span class="marker"><strong>SMTP</strong></span></td><td>メールを送る</td><td>・Gmailやアプリから送信するときに使われる</td></tr><tr><td><span class="marker"><strong>POP</strong></span></td><td>メールを受け取る（削除型）</td><td>・サーバーから<strong>PCやスマホにダウンロードして削除</strong>する方式<br>・1台の端末だけでメールを管理したいときに向いている</td></tr><tr><td><span class="marker"><strong>IMAP</strong></span></td><td>メールを受け取る（サーバーに残す）</td><td>・メールをサーバー上に<strong>残したまま</strong>アクセス<br>・複数の端末で同じメールを見たいときに便利</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/06/14/status/" title="404？500? ステータスコードとは？よく見るコード厳選まとめ" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">404？500? ステータスコードとは？よく見るコード厳選まとめ</div><div class="blogcard-snippet internal-blogcard-snippet">開発エンジニアが現場でよく使うHTTPステータスコードを、意味・原因・使われる場面とともにわかりやすく解説！</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc8">SMTPを使った開発実装の例</span></h2>



<p>Web開発では、例えば「お問い合わせフォーム」からの自動返信メールなどにSMTPが使われます。</p>



<p>フォーム入力 → SMTP経由で自動返信メールを送信する流れです。</p>



<ul class="wp-block-list">
<li></li>
</ul>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc9">Node.js + nodemailer の実装例</span></h3>



<p><code>.env</code> にSMTP情報を記載し、それを<code>Node.js</code>で読み込んでメール送信を行うコード例です。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password</code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import nodemailer from &#8220;nodemailer&#8221;;
// .env.local に記載した SMTP情報を読み込む
const transporter = nodemailer.createTransport({
  // 接続先のSMTPサーバー
  host: process.env.SMTP_HOST,
  // 使用するポート番号
  port: Number(process.env.SMTP_PORT),
  // ポートが465ならSSL通信（secure: true）、それ以外ならSTARTTLS（secure: false）
  secure: Number(process.env.SMTP_PORT) === 465,
  // 認証情報（メールアドレスとアプリパスワードなど）
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});
// メールの内容
const mailOptions = {
  from: &#8220;your-email@gmail.com&#8221;,
  to: &#8220;receiver@example.com&#8221;,
  subject: &#8220;お問い合わせありがとうございます&#8221;,
  text: &#8220;これは自動返信メールです。&#8221;,
};
// 送信
async function sendEmail() {
  try {
    await transporter.sendMail(mailOptions);
    console.log(&#8220;メール送信に成功しました&#8221;);
  } catch (error) {
    console.error(&#8220;メール送信に失敗しました:&#8221;, error);
  }
}
sendEmail(); // 関数を実行</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nodemailer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">nodemailer</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// .env.local に記載した SMTP情報を読み込む</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">transporter</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nodemailer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">createTransport</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 接続先のSMTPサーバー</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">host</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SMTP_HOST</span><span style="color: #ECEFF4">,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 使用するポート番号</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">port</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Number</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SMTP_PORT</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ポートが465ならSSL通信（secure: true）、それ以外ならSTARTTLS（secure: false）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">secure</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Number</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SMTP_PORT</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">465</span><span style="color: #ECEFF4">,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 認証情報（メールアドレスとアプリパスワードなど）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">auth</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SMTP_USER</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">pass</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SMTP_PASS</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// メールの内容</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mailOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">from</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">your-email@gmail.com</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">to</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">receiver@example.com</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">subject</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">お問い合わせありがとうございます</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">これは自動返信メールです。</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 送信</span></span>
<span class="line"><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sendEmail</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">transporter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">sendMail</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">mailOptions</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">log</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">メール送信に成功しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">メール送信に失敗しました:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">sendEmail</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 関数を実行</span></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc10">よくある設定ミス：「secureを省略」</span></h3>



<p>SMTP設定で以下のように書いている場合：</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>port: Number(process.env.SMTP_PORT),
secure: Number(process.env.SMTP_PORT) === 465,</code></pre></div>



<p>この <code>secure</code> の指定は<strong>省略せず、明示するのが推奨</strong>です。<br>なぜなら、ポート番号だけでは nodemailer は<strong>どの暗号化方式（SSLかSTARTTLS）を使うか判断できない</strong>からです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>port: 465,
secure: false  // ← ポート465なのにSSLを使わない ⇒ エラーになる可能性大</code></pre></div>



<p>上記のように設定が矛盾していると、<code>connect error</code> や <code>SSL wrong version number</code> のようなエラーになります。</p>



<p> <span class="badge-green">正しい設定例</span></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>// SSL（465番）で送信する場合
port: 465,
secure: true,

// STARTTLS（587番）で送信する場合
port: 587,
secure: false,</code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc11">SMTPエラーと確認方法</span></h2>



<p>エラーの種類・確認方法の例をご紹介します。<br><br><span class="badge-yellow">エラー確認方法</span> </p>



<ul class="wp-block-list">
<li>開発環境：VSCodeのターミナルに表示</li>



<li>本番環境：ログファイルに記載される等</li>
</ul>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>エラー表示</th><th>意味</th></tr></thead><tbody><tr><td>SMTP Authentication error</td><td>メールアドレスやパスワード（アプリパスワード）が間違っている</td></tr><tr><td>Connection timeout</td><td>ポSMTPサーバーに接続できない（ポート番号やネット環境の問題）</td></tr><tr><td>550 Relay not permitted</td><td>外部ドメインへの送信がサーバー側で許可されていない</td></tr><tr><td>getaddrinfo ENOTFOUND</td><td>SMTPサーバー名が間違っている、または存在しない</td></tr><tr><td>SSL wrong version number</td><td>ポートと <code>secure</code> の設定が合っていない</td></tr><tr><td>ECONNREFUSED</td><td>サーバーが接続を拒否している（ホスト名やポートのミス）</td></tr><tr><td>Invalid sender address</td><td>from アドレスの形式が正しくない、または拒否された</td></tr></tbody></table></figure>



<p><span class="badge-yellow">例</span><br>認証エラーの場合、ターミナルに下記のようにエラー文が表示されます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>Error: Invalid login: 535-5.7.8 Username and Password not accepted.</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/06/14/dev-keywords/" title="エンジニアなら押さえたい！基本用語5選" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-6-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-6-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-6-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-6-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">エンジニアなら押さえたい！基本用語5選</div><div class="blogcard-snippet internal-blogcard-snippet">現場でよく聞く「ライブラリ」や「フック」などの用語、意味を説明できますか？初心者向けにわかりやすく解説します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=457GS5+AI29WY+E0Q+1BQ3UP">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www20.a8.net/svt/bgt?aid=250611125635&#038;wid=001&#038;eno=01&#038;mid=s00000001817008016000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www17.a8.net/0.gif?a8mat=457GS5+AI29WY+E0Q+1BQ3UP" alt=""></div><p>The post <a href="https://it-bokenki.com/2025/06/14/smtp/">SMTPとは？メール送信の仕組みと実装・エラー内容まとめ</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/14/smtp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>メール配信の強い味方「SendGrid」を徹底解説！</title>
		<link>https://it-bokenki.com/2025/06/11/what-is-sendgrid/</link>
					<comments>https://it-bokenki.com/2025/06/11/what-is-sendgrid/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Wed, 11 Jun 2025 02:15:57 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[バックエンド]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[SendGrid]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[自動送信メール]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=2872</guid>

					<description><![CDATA[<p>でじぼうです。 メール配信を専門に行なってくれるAPI「SendGrid」についてご説明します。 SendGridって、そもそも何？ SendGridは、メール送信用のクラウドサービス（SaaS）です。RESTful A [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/11/what-is-sendgrid/">メール配信の強い味方「SendGrid」を徹底解説！</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>でじぼうです。</p>



<p>メール配信を専門に行なってくれるAPI「SendGrid」についてご説明します。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="でじぼう" class="speech-icon-image"/></figure><div class="speech-name">でじぼう</div></div><div class="speech-balloon">
<p>この記事は下記の方がおすすめ！<br></p>



<ul class="wp-block-list">
<li style="font-size:15px">SendGridってなに？</li>



<li style="font-size:15px">Makeってなに？</li>



<li style="font-size:15px">API×ローコードツールの実装方法が知りたい！</li>
</ul>
</div></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3TB6RG+C3TBLE+4RIG+BYT9D">
<img loading="lazy" decoding="async" border="0" width="468" height="60" alt="" src="https://www26.a8.net/svt/bgt?aid=230629372732&#038;wid=001&#038;eno=01&#038;mid=s00000022228002010000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www16.a8.net/0.gif?a8mat=3TB6RG+C3TBLE+4RIG+BYT9D" alt=""></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">SendGridって、そもそも何？</span></h2>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/4-1.png" alt="SendGridでのメール配信の仕組みを示す図" class="wp-image-2925" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/4-1.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/4-1-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>SendGridは、<strong>メール送信用のクラウドサービス（SaaS</strong>）です。<br>RESTful API や SMTP を使って、Webサービスやアプリケーションから簡単にメールを送信できます。</p>



<p>たとえば、お問い合わせフォームや会員登録後の自動返信メールを、SendGrid経由で確実にユーザーへ届けることができます。</p>



<p>アプリ側では、宛先や本文をSendGridに渡すだけで、あとはSendGridが送信処理を代行してくれる仕組みです。<br></p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-memo block-box">
<p class="has-small-font-size"><span class="marker">SaaS（サース）</span>：完成されたアプリをインターネット経由で利用できるサービス<br><span class="marker">RESTful API</span>：Webサービス同士がデータをやり取りするための「決まりごと」に基づいた通信方法<br><span class="marker">SMTP</span>：メールを送信するための通信ルール（プロトコル）のひとつ</p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">なぜSendGridを使うの？（エンジニア的メリット）</span></h2>



<p>「メール配信くらい自分でプログラム書けばいいんじゃない？」と思う方もいるかもしれません。でも、実はSendGridを使うことには、エンジニアにとってたくさんのメリットがあるんです。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc3">メリット1：メールの「到達率」が格段に上がる！</span></h3>



<p>自分でメールサーバーを立ててメールを送ろうとすると、送信元IPアドレスの評価が悪くて迷惑メール扱いされてしまったり、受信側のサーバーからブロックされてしまったりすることがよくあります。</p>



<p>SendGridは、長年の運用実績と高い技術力で、<span class="marker-under"><strong>メールが「迷惑メール」として扱われにくく、高い確率でユーザーの受信トレイに届く</strong></span>ように工夫されています。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc4">メリット2：大量のメール配信も楽々！</span></h3>



<p>キャンペーンメールやニュースレターなど、一度に数千、数万といった大量のメールを送りたい場合、自分でシステムを組むのは非常に大変です。処理に時間がかかったり、サーバーがダウンしてしまったりするリスクもあります。</p>



<p>SendGridは、<span class="marker-under"><strong>大量のメールを高速かつ安定して配信できるインフラ</strong></span>を持っています。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc5">メリット3：開発コスト・運用コストが大幅に削減できる！</span></h3>



<p>もし自分でメール配信システムをゼロから作るとしたら、</p>



<p>◆ メールサーバーの構築・設定<br>◆ 迷惑メール対策<br>◆ エラーハンドリング<br>◆ 配信状況の監視<br>◆ セキュリティ対策</p>



<p>など、やるべきことが山積みです。これらをすべて自前で実装・運用するのは、時間も労力も莫大にかかります。</p>



<p>利用すれば、<span class="marker-under"><strong>これらの面倒な作業をすべてSendGridに任せることができます</strong></span><strong>。</strong></p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc6">SendGrid の仕組みをざっくり理解しよう！</span></h2>



<p>SendGridを使ったメール配信の基本的な流れは以下のようになります。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc7">① アプリケーションからSendGridへメールを送信する</span></h3>



<p>SendGridが提供するAPI（HTTPリクエスト）を使ってメールの内容（宛先、件名、本文など）をSendGridに送ります。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc8">② SendGridがメールを受け取る</span></h3>



<p>SendGridは受け取ったメールの内容を解析し、適切な処理を行います。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc9">③ SendGridがユーザーのメールサーバーへメールを送信する</span></h3>



<p>SendGridは、迷惑メールと判断されないように様々な工夫を凝らしながら、ユーザーが使っているメールサービス（Gmail, Outlookなど）のサーバーへメールを送ります。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc10">④ ユーザーのメールサーバーがメールを受信する</span></h3>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading has-ex-e-background-color has-background"><span id="toc11">⑤ ユーザーの受信トレイにメールが届く！</span></h3>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-reference-link is-style-normal-card">
<a href="https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/" title="SendGrid で自動返信メールを送信する方法" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/2-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/2-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/2-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/2-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SendGrid で自動返信メールを送信する方法</div><div class="blogcard-snippet internal-blogcard-snippet">SendGridの基本からAPI連携による自動返信メールの実装方法までを徹底解説。Next.jsとの組み合わせで高到達率なメール配信をご説明します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading has-cocoon-white-background-color has-background"><span id="toc12">API連携の実装方法</span></h2>



<p><span class="marker-under"><strong>簡単にメールを送信できる公式APIが提供</strong></span>されています。Java、Python、Ruby、Node.js、PHPなど、主要なプログラミング言語に対応しています。</p>



<p>今回下記のように、お問い合わせ時の自動返信メールを<code>Node.js</code>で実装しました。<br>環境変数は <code>.env</code> ファイルに定義し、以下の2項目を読み込んで使用しています。</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>環境変数項目</th><th>値</th></tr></thead><tbody><tr><td>SENDGRID_API_KEY</td><td>SendGridで発行したAPIキー</td></tr><tr><td>SENDGRID_FROM_EMAIL</td><td>自動返信メールの送信元メールアドレス</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import { NextResponse } from &#8220;next/server&#8221;;
import sgMail from &#8220;@sendgrid/mail&#8221;;

export async function POST(req: Request) {
  const data = await req.json();
  const { name, email, phone, message } = data;

  // 自動送信メールの件名と本文
  const subject = &#8220;お問い合わせありがとうございます&#8221;;
  const mailBody = `${name}様

  このたびはお問い合わせいただき、誠にありがとうございます。
  以下の内容で受付いたしました。
  
  &#8212;&#8212;&#8212;&#8212;&#8212;

  お名前： ${name}
  メール： ${email}
  電話番号： ${phone}
  メッセージ： 
  ${message}

  &#8212;&#8212;&#8212;&#8212;&#8212;

  担当より折り返しご連絡させていただきます。
  今しばらくお待ちください。`;

  try {
    // 自動送信メール
    sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
    await sgMail.send({
      from: process.env.SENDGRID_FROM_EMAIL!,
      to: email,
      subject,
      text: mailBody,
    });

    return NextResponse.json({ message: &#8220;メール送信成功&#8221; });
  } catch (error) {
    return NextResponse.json(
      { message: &#8220;Slack送信に失敗しました&#8221; },
      { status: 500 }
    );
  }
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextResponse</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next/server</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">sgMail</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@sendgrid/mail</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">POST</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">req</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Request</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">req</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">email</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">phone</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">message</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 自動送信メールの件名と本文</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">subject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">お問い合わせありがとうございます</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mailBody</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">様</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">  このたびはお問い合わせいただき、誠にありがとうございます。</span></span>
<span class="line"><span style="color: #A3BE8C">  以下の内容で受付いたしました。</span></span>
<span class="line"><span style="color: #A3BE8C">  </span></span>
<span class="line"><span style="color: #A3BE8C">  ---------------</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">  お名前： </span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #A3BE8C">  メール： </span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">email</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #A3BE8C">  電話番号： </span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">phone</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #A3BE8C">  メッセージ： </span></span>
<span class="line"><span style="color: #A3BE8C">  </span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">message</span><span style="color: #81A1C1">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">  ---------------</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">  担当より折り返しご連絡させていただきます。</span></span>
<span class="line"><span style="color: #A3BE8C">  今しばらくお待ちください。</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 自動送信メール</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">sgMail</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">setApiKey</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SENDGRID_API_KEY</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sgMail</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SENDGRID_FROM_EMAIL</span><span style="color: #81A1C1">!</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">to</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">email</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">subject</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mailBody</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">メール送信成功</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Slack送信に失敗しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">status</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<p></p><p>The post <a href="https://it-bokenki.com/2025/06/11/what-is-sendgrid/">メール配信の強い味方「SendGrid」を徹底解説！</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/11/what-is-sendgrid/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SendGrid で自動返信メールを送信する方法</title>
		<link>https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/</link>
					<comments>https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Wed, 11 Jun 2025 02:18:18 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[バックエンド]]></category>
		<category><![CDATA[開発事例]]></category>
		<category><![CDATA[make]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[SendGrid]]></category>
		<category><![CDATA[SMTP]]></category>
		<category><![CDATA[ローコードツール]]></category>
		<category><![CDATA[自動送信メール]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=2850</guid>

					<description><![CDATA[<p>でじぼうです。 API：SendGridと、ローコードツール：Makeをかけ合わせた自動返信メールを送信する方法についてご説明します。 SendGridとは？ 「メール配信を専門に行ってくれるクラウドサービス（SaaS） [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/">SendGrid で自動返信メールを送信する方法</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<p></p>



<p>でじぼうです。</p>



<p>API：SendGridと、ローコードツール：Makeをかけ合わせた自動返信メールを送信する方法についてご説明します。</p>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="でじぼう" class="speech-icon-image"/></figure><div class="speech-name">でじぼう</div></div><div class="speech-balloon">
<p>この記事は下記の方がおすすめ！<br></p>



<ul class="wp-block-list">
<li style="font-size:15px">SendGridってなに？</li>



<li style="font-size:15px">Makeってなに？</li>



<li style="font-size:15px">API×ローコードツールの実装方法が知りたい！</li>
</ul>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;">
<a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3TB6RG+C3TBLE+4RIG+BYT9D">
<img loading="lazy" decoding="async" border="0" width="468" height="60" alt="" src="https://www26.a8.net/svt/bgt?aid=230629372732&#038;wid=001&#038;eno=01&#038;mid=s00000022228002010000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www16.a8.net/0.gif?a8mat=3TB6RG+C3TBLE+4RIG+BYT9D" alt="">
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">SendGridとは？</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/4.png" alt="SendGrid" class="wp-image-2870" style="width:445px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/4.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/4-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p><strong>「メール配信を専門に行ってくれるクラウドサービス（SaaS）」</strong> です。</p>



<p>開発したWebサービスやアプリケーションからSendGridにメールを送るように設定するだけで、SendGridがそのメールをユーザーに届けてくれます。</p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-memo block-box">
<p class="has-small-font-size"><span class="marker">SaaS（サース）</span>：完成されたアプリをインターネット経由で利用できるサービス</p>
</div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-reference-link is-style-normal-card">
<a href="https://it-bokenki.com/2025/06/11/what-is-sendgrid/" title="メール配信の強い味方「SendGrid」を徹底解説！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/1-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/1-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/1-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/1-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">メール配信の強い味方「SendGrid」を徹底解説！</div><div class="blogcard-snippet internal-blogcard-snippet">SendGridとは何かを初心者向けにわかりやすく解説。メール配信APIの仕組みや使い方、Webアプリとの連携方法を紹介します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">Makeとは？</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/3.png" alt="make" class="wp-image-2869" style="width:408px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/3.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/3-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>「<strong>ノーコードでさまざまなWebサービスを自動連携できるプラットフォーム（iPaaS）」です。</strong><br><br>Gmail、Slack、Google Sheets、Notionなどと連携し、ワークフローを視覚的に構築可能。<br>エンジニアでなくても効率的な自動化が行えます。<br></p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-memo block-box">
<p class="has-small-font-size"><span class="marker">iPaaS（アイパース）</span>＝いろんなWebサービスをつなぐ橋渡し役</p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">なぜ SendGrid × Makeで実装？</span></h2>



<p>もともと <span class="marker-under"><strong>フロントエンド × バックエンドAPI × Make</strong></span> を使ってお問い合わせフォームを実装していました。次のステップとして「お問い合わせをしてくれた方へ自動返信メールを送る機能」を追加することにしました。</p>



<p class="has-text-align-center"><span class="fz-20px"><span class="fz-22px"><span class="fz-24px">前回までの実装内容</span></span></span></p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/6.png" alt="Make SendGrid" class="wp-image-2899" style="width:704px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/6.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/6-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><span>▼ 当初の選択肢：Make × 自前サーバー</span></h5>



<p>まずは自前で契約しているレンタルサーバーを使って実装しようとしましたが、Make のメール送信元である <span class="marker-under"><code>ie.</code><strong>ドメインをそのサーバーが受け付けない仕様であったため、自動返信が正常に動作しないという問題が発生</strong></span>しました。<br></p>



<h5 class="wp-block-heading"><span>▼ 次の選択肢：Make × Googleメール</span></h5>



<p>次に、Gmailを使う方法を検討しましたが、会社独自のドメインでGmailを使うにはGoogle Workspaceの有料契約が必要です。<br>今回はコストをできるだけ抑えたかったため、固定費が発生するこの案は見送りにしました。<br></p>



<h5 class="wp-block-heading"><span>&#x2705; 最終的な選択肢： SendGrid</span></h5>



<p class="has-text-align-center"><span class="fz-20px"><span class="fz-22px"><span class="fz-24px">これから</span>やる実装内容</span></span></p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/5.png" alt="Make SendGrid" class="wp-image-2900" style="width:700px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/5.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/5-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>上記のような経緯があり、<span class="marker-under">無料で使えて独自ドメインも対応可能なSendGrid</span>を使うことに決定しました。<br>SendGridであれば、MakeからのSMTP連携も簡単で、自動返信メールも安定して送ることができます。</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc4">SendGrid × Make 実装手順</span></h2>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/Make.png" alt="Make SendGrid" class="wp-image-2851" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/Make.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>ここからは、実際にSendGridとMakeを使って自動返信メールを実装する手順を紹介していきます。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">① SendGridアカウント作成と会社情報の登録</span></h3>



<p>まずはSendGridのアカウントを作成し、会社情報を登録します。<br>この登録内容の審査には1〜2営業日程度かかるので、余裕を持って申請しておきましょう。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">② 承認完了後、マイページにログインする</span></h3>



<p>承認されると、登録したメールアドレス宛に通知が届きます。<br>メールに記載されたリンクから送信者認証を完了させましょう。<br>その後、アカウントIDが発行されるので、事前に設定したパスワードでSendGridにログインします。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">③ 送信者情報の登録</span></h3>



<figure class="wp-block-image aligncenter size-full is-resized is-style-default"><img loading="lazy" decoding="async" width="500" height="500" src="https://it-bokenki.com/wp-content/uploads/2025/06/Make-1.png" alt="SendGrid 設定方法" class="wp-image-2853" style="width:478px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/Make-1.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-1-300x300.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-1-150x150.png 150w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-1-100x100.png 100w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>左側のDashbord  ＞「Create sender identity」をクリックし、送信者情報を登録します。<br>このとき、<strong><span class="marker-under">送信先・送信元のメールアドレスは同じでも問題ありません</span></strong>。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc8">④ APIキーの作成</span></h3>



<p>左メニューの「<strong><code>Settings ＞ API Keys</code></strong>」から「<strong><code>Create API Key</code></strong>」を選択し、新しいAPIキーを発行します。</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="726" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-1024x726.png" alt="SendGrid 設定方法" class="wp-image-2855" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-1024x726.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-300x213.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-768x545.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40.png 1390w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>設定項目</th><th>設定内容</th></tr></thead><tbody><tr><td>API Key Name</td><td>任意の名前（例：Make用自動返信）</td></tr><tr><td>API Key Permissions</td><td><code>Full Access</code><br>（または&nbsp;<code>Restricted Access</code>&nbsp;→&nbsp;<code>Mail Send</code>&nbsp;にチェック）</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>「<code>Create &amp; View Key</code>」をクリックするとAPIキーが表示されます。<br><strong><span class="marker-under">このタイミングでしかコピーできない</span></strong>ので、忘れずに控えておきましょう。<br>後ほどMakeのSMTP接続設定で、このAPIキーをパスワードとして使います。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc9">⑤ MakeでのSMTP接続設定方法</span></h3>



<p>Makeで「<code>Send an Email</code>」モジュールを追加し、以下の情報で新規<code>Connection</code>を作成します。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="800" height="882" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46.png" alt="Make Email設定方法" class="wp-image-2856" style="width:376px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46.png 800w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46-272x300.png 272w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46-768x847.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /></figure>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>設定項目</th><th>設定内容</th></tr></thead><tbody><tr><td>Connection type</td><td>Other（SMTP)</td></tr><tr><td>Connection name</td><td>任意の名前（例：SendGrid SMTP）</td></tr><tr><td>Email provider</td><td>Other</td></tr><tr><td>Email address</td><td>自動送信メールを送る主のメールアドレス</td></tr><tr><td>Your full name</td><td>会社名等</td></tr><tr><td>SMTP server</td><td>smtp.sendgrid.netを入力</td></tr><tr><td>Port</td><td><code>587</code>（推奨）または&nbsp;<code>465</code>（SSL）</td></tr><tr><td>Use a secure connection (TLS)</td><td>YES</td></tr><tr><td>Use explicit TLS</td><td>NO</td></tr><tr><td>User name</td><td>apikeyと文字を入力</td></tr><tr><td>Password</td><td>SendGridで生成したAPIキーを入力</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc10">⑥ Fromアドレスの設定（詳細設定）</span></h3>



<p>SMTP設定が完了したら、次に<span class="marker-under"><strong>送信元メールアドレス（From）を設定</strong></span>します。<br>これはMakeの「<strong><code>Show advanced settings</code></strong>」をONにすることで表示される詳細設定欄にあります。<br>ここに正しいFromアドレスを入力しないと、メールが正しく送信できなかったり、送信者名が表示されなかったりします。<br><br>下記の写真は「<code>Show advanced settings</code>」がOFFになっている状態</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="617" height="1024" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-617x1024.png" alt="Make Email設定方法" class="wp-image-2858" style="width:357px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-617x1024.png 617w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-181x300.png 181w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-768x1275.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54.png 884w" sizes="auto, (max-width: 617px) 100vw, 617px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc11">⑦ コードの実装</span></h3>



<p>今回下記のように、<ruby><code><rt>Node.js</rt></code></ruby>で実装しました。<br>環境変数は <code>.env</code> ファイルに定義し、以下を読み込んで使用しています。</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>環境変数項目</th><th>値</th></tr></thead><tbody><tr><td>SLACK_WEBHOOK_URL</td><td>Makeとの連携URL</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Makeとの連携URLを設定して、Makeに送りたいお問い合わせ情報（name , email , phone, message)を設定します。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>await fetch(process.env.SLACK_WEBHOOK_URL!, {
  method: &#8220;POST&#8221;,
  headers: {
    &#8220;Content-Type&#8221;: &#8220;application/json&#8221;,
  },
  body: JSON.stringify({
    name,
    email,
    phone,
    message,
  }),
});
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetch</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SLACK_WEBHOOK_URL</span><span style="color: #81A1C1">!</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">method</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">POST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">headers</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Content-Type</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">application/json</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">JSON</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">stringify</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">email</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">phone</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">message</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc12">⑧ 設定完了</span></h3>



<p>送信者認証とSMTPの接続設定がすべて完了すると、Make上で自動返信メールが送信できるようになります。<br>これで、お問い合わせをしてくれた方に、即座に確認メールを送る仕組みが完成です。</p><p>The post <a href="https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/">SendGrid で自動返信メールを送信する方法</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
