NextAuth.js 설치
npm install next-auth@beta
기본 설정
// auth.ts (루트에 위치)
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import GitHub from "next-auth/providers/github";
import Credentials from "next-auth/providers/credentials";
import { db } from "@/lib/db";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
GitHub({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
Credentials({
credentials: {
email: { label: "이메일", type: "email" },
password: { label: "비밀번호", type: "password" },
},
async authorize(credentials) {
const user = await db.users.findByEmail(credentials.email as string);
if (!user) return null;
const valid = await verifyPassword(
credentials.password as string,
user.hashedPassword
);
if (!valid) return null;
return { id: user.id, name: user.name, email: user.email };
},
}),
],
callbacks: {
async session({ session, token }) {
if (token.sub) session.user.id = token.sub;
return session;
},
},
});
API Route Handler 연결
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;
환경 변수
# .env.local
AUTH_SECRET=your-secret-key # openssl rand -base64 32
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
로그인/로그아웃 버튼
// components/AuthButtons.tsx
import { signIn, signOut, auth } from "@/auth";
// 서버 컴포넌트에서 세션 확인
export default async function AuthButtons() {
const session = await auth();
if (session?.user) {
return (
<div>
<p>안녕하세요, {session.user.name}님!</p>
<form action={async () => {
"use server";
await signOut();
}}>
<button type="submit">로그아웃</button>
</form>
</div>
);
}
return (
<div>
<form action={async () => {
"use server";
await signIn("google");
}}>
<button>Google로 로그인</button>
</form>
<form action={async () => {
"use server";
await signIn("github");
}}>
<button>GitHub로 로그인</button>
</form>
</div>
);
}
미들웨어로 라우트 보호
// middleware.ts
import { auth } from "@/auth";
export default auth((req) => {
const isLoggedIn = !!req.auth;
const pathname = req.nextUrl.pathname;
if (!isLoggedIn && pathname.startsWith("/dashboard")) {
return Response.redirect(new URL("/login", req.url));
}
});
export const config = {
matcher: ["/dashboard/:path*"],
};
클라이언트에서 세션 사용
"use client";
import { useSession } from "next-auth/react";
function UserMenu() {
const { data: session, status } = useSession();
if (status === "loading") return <span>로딩 중...</span>;
if (!session) return <a href="/api/auth/signin">로그인</a>;
return (
<div>
<img src={session.user.image ?? ""} alt="아바타" />
<span>{session.user.name}</span>
</div>
);
}
정리
flowchart LR
USER["사용자"]
GOOGLE["Google OAuth"]
GITHUB["GitHub OAuth"]
NEXTAUTH["NextAuth.js"]
SESSION["세션/JWT"]
APP["보호된 페이지"]
USER -->|"로그인 클릭"| NEXTAUTH
NEXTAUTH --> GOOGLE
NEXTAUTH --> GITHUB
GOOGLE & GITHUB -->|"콜백"| NEXTAUTH
NEXTAUTH --> SESSION
SESSION -->|"인증됨"| APP
| 항목 | 설명 |
|---|---|
handlers | API Route에 연결 |
auth() | 서버 컴포넌트 세션 확인 |
signIn() | 로그인 |
signOut() | 로그아웃 |
useSession() | 클라이언트 세션 훅 |
다음 편에서는 환경 변수와 배포 — .env 설정과 Vercel 배포 방법을 배웁니다.