第132章:ミニ課題:保護ルートの土台完成🔐✨
今回は「/dashboard はログインしてないと入れない」っていう、超よく使う“守り”を作るよ〜!🧤💪 主役はもちろん Middleware 🌐⚡(門番さん)
この章のゴール🎯
- ✅
/dashboardに未ログインで行くと →/loginに飛ばされる - ✅
/loginで「ログイン(仮)」すると →/dashboardに入れる - ✅ 「ログアウト」すると → また
/dashboardは入れなくなる
ちゃんと“サーバー側で”止めるのがポイントだよ🙅♀️🛡️(UIだけで隠すのは弱い!)
まず全体の流れを図で理解しよ🗺️✨(Mermaid)
Middleware は「ルートに入る前」に動くよ〜!というのが超大事ポイント🌟 (Next.js)
作るファイルたち📦
今回はこの4つでOK!
middleware.tsapp/dashboard/page.tsxapp/login/page.tsxapp/api/login/route.tsapp/api/logout/route.ts
Step 1️⃣ /dashboard ページを作る🏠✨
app/dashboard/page.tsx
export default function DashboardPage() {
return (
<main style={{ padding: 24 }}>
<h1>Dashboard 🔐</h1>
<p>ログインできてる人だけが見れるページだよ〜🎉</p>
<form action="/api/logout" method="post">
<button type="submit">ログアウトする🚪</button>
</form>
</main>
);
}
Step 2️⃣ /login ページを作る🔑✨
app/login/page.tsx
type Props = {
searchParams?: { from?: string };
};
export default function LoginPage({ searchParams }: Props) {
const from = searchParams?.from ?? "/dashboard";
return (
<main style={{ padding: 24 }}>
<h1>Login 🔑</h1>
<p>ここはログインページだよ〜😊</p>
<form action={`/api/login?from=${encodeURIComponent(from)}`} method="post">
<button type="submit">ログイン(仮)する🍪</button>
</form>
<p style={{ marginTop: 12, opacity: 0.7 }}>
※本物の認証じゃなくて「Cookieを入れるだけ」の練習だよ🧁
</p>
</main>
);
}
fromは「本当は行きたかったページ」を覚えておくためのやつだよ🧭✨ こうしておくと、ログイン後に元のページへ戻せる〜!
Step 3️⃣ ログインAPI(Cookieをセット)を作る🍪✅
app/api/login/route.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export async function POST(request: NextRequest) {
const url = new URL(request.url);
const from = url.searchParams.get("from") ?? "/dashboard";
const res = NextResponse.redirect(new URL(from, request.url));
// “ログインしたよ”の目印Cookie(仮)
res.cookies.set("demo_session", "ok", {
httpOnly: true,
sameSite: "lax",
path: "/",
});
return res;
}
NextResponse は cookies を操作できるよ〜🍪✨ (Next.js)
Step 4️⃣ ログアウトAPI(Cookieを消す)を作る🧹🚪
app/api/logout/route.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export async function POST(request: NextRequest) {
const res = NextResponse.redirect(new URL("/login", request.url));
// Cookie削除(maxAge: 0 が簡単)
res.cookies.set("demo_session", "", {
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 0,
});
return res;
}
Step 5️⃣ いよいよ本体!Middlewareで /dashboard を守る🧤⚡
プロジェクト直下に middleware.ts を作ってね
(もし src/ を使う構成なら src/middleware.ts でもOKだよ👍)
middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const isLoggedIn = request.cookies.get("demo_session")?.value === "ok";
if (!isLoggedIn) {
const url = request.nextUrl.clone();
url.pathname = "/login";
url.searchParams.set("from", request.nextUrl.pathname);
return NextResponse.redirect(url);
}
return NextResponse.next();
}
// /dashboard 配下だけ門番する🧤(ここ大事!)
export const config = {
matcher: ["/dashboard/:path*"],
};
matcher は「どのパスに Middleware を効かせるか」を指定できるよ🧭 (Next.js)
Step 6️⃣ 動作チェックしよ〜✅🎉
PowerShell で起動(すでに起動してたらそのままでOK)
npm run dev
確認ポイント👇✨
http://localhost:3000/dashboardに行く
- ✅
/loginに飛ばされたらOK!🚦😺
/loginの「ログイン(仮)」押す
- ✅
/dashboardに入れたらOK!🎉🍪
/dashboardの「ログアウト」押す
- ✅
/loginに戻って、また/dashboardが弾かれたらOK!🔁🧤
よくあるつまずき😵💫→こう直す💡
-
middleware.ts を置く場所が違う → プロジェクト直下(
package.jsonと同じ階層)か、構成によってはsrc/の直下に置くよ📌 -
無限リダイレクトになる →
matcherを/dashboard/:path*に絞れてるかチェック!✅ (/loginにまで門番が来ると永久ループ😇) -
Cookie が入ってるのに弾かれる →
demo_sessionの名前が一致してるか、"ok"になってるか確認🍪🔍
ミニ課題クリア条件🏁✨
- ✅ 未ログインで
/dashboard→/loginへ - ✅ ログイン(仮)で
/dashboardへ - ✅ ログアウトでまた弾かれる
ここまでできたら、もう「保護ルートの土台」は完成だよ〜🔐🎉 次はこれを“本物の認証”に置き換えるだけ!(やることが一気に現実っぽくなる😎)