NextAuth.js
https://next-auth.js.org/getting-started/example
몽고 DB 설정
1) next.config.js
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
module.exports = (phase) => {
if (phase === PHASE_DEVELOPMENT_SERVER) {
return {
env: {
mongodb_username: "macaronics",
mongodb_password: "jVPUh2t43T34324jjP5MuQ5",
mongodb_clustername: "mongo-test.test1234.mongodb.net",
mongodb_database: "test123",
appName: "my-site-dev"
},
};
}
return {
env: {
mongodb_username: "macaronics",
mongodb_password: "jVPUh2t43T34324jjP5MuQ5",
mongodb_clustername: "mongo-test.test1234.mongodb.net",
mongodb_database: "test123",
appName: "macaronics-blog"
},
};
};
2)lib/db.js 설정
import { MongoClient } from "mongodb";
async function connectToDatabase() {
let client;
let clientPromise;
if (!clientPromise) {
const connectionString = `mongodb+srv://${process.env.mongodb_username}:${process.env.mongodb_password}@${process.env.mongodb_clustername}/${process.env.mongodb_database}?retryWrites=true&w=majority&appName=${process.env.appName}`;
client = new MongoClient(connectionString);
clientPromise = client.connect();
}
await clientPromise;
return {
client,
db: client.db(),
};
}
export { connectToDatabase };
3)사용 contact.js
import { connectToDatabase } from '../../lib/db';
async function handler(req, res) {
if (req.method === "POST") {
const { email, name, message } = req.body;
//console.log(email, name, message);
if (
!email ||
!email.includes("@") ||
!name ||
name.trim() === "" ||
!message ||
message.trim() === ""
) {
return res.status(422).json({ message: "Invalid input" });
}
const newMessage ={
email,
name, message
}
const dbConnection = await connectToDatabase();
let client = dbConnection.client;
let db = dbConnection.db;
try {
const result=await db.collection('messages').insertOne(newMessage);
newMessage.id=result.insertedId;
} catch (error) {
res.status(500).json({ message: error.message});
return;
}finally{
if(client){
await client.close();
client = null; // 클라이언트 객체 초기화
}
}
//console.log("success======>",newMessage);
return res.status(200).json({ message: "Success", insertMessage:newMessage });
}
}
export default handler;
1. NextAuth.js를 이용한 인증 구현 요약
nextAuth3 버전
NextAuth.js는 Next.js 애플리케이션에 인증 기능을 쉽게 추가할 수 있는 라이브러리입니다. 다양한 인증 방법을 지원하며, 이를 통해 사용자 로그인 기능을 구현할 수 있습니다.
NextAuth.js 설치 및 설정
NextAuth.js 설치:
- 터미널에서 개발 서버를 종료한 후 다음 명령어를 입력하여 NextAuth.js 패키지를 설치합니다.
npm install next-auth
API 라우트 생성:
- NextAuth.js 설정을 위한 API 라우트를 생성합니다. pages/api/auth/[...nextauth].js 파일을 생성하고 다음과 같이 설정합니다.
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
export default NextAuth({
providers: [
Providers.Credentials({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
authorize: async (credentials) => {
// 사용자 인증 로직 구현
const user = { id: 1, name: "John Doe", email: "john@example.com" }; // 예시 사용자
if (user) {
return user;
} else {
return null;
}
}
})
],
// 추가 설정 옵션
});
사용자 로그인 및 인증 확인:
- NextAuth.js는 서버 사이드와 클라이언트 사이드 모두에서 인증을 확인할 수 있습니다.
- 서버 사이드 예제
import { getSession } from "next-auth/client";
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session) {
return {
redirect: {
destination: "/api/auth/signin",
permanent: false
}
};
}
return {
props: { session }
};
}
클라이언트 사이드 예제
import { useSession } from "next-auth/client";
export default function Page() {
const [session, loading] = useSession();
if (loading) return <p>Loading...</p>;
if (!session) return <p>You are not logged in</p>;
return <p>Welcome, {session.user.name}</p>;
}
사용자 인터페이스 업데이트:
- 사용자 인증 상태에 따라 다른 인터페이스를 표시할 수 있습니다.
- 로그인 상태에 따라 표시되는 컴포넌트 예제:
import { signIn, signOut, useSession } from "next-auth/client";
export default function NavBar() {
const [session, loading] = useSession();
return (
<nav>
{session ? (
<>
<p>Welcome, {session.user.name}</p>
<button onClick={() => signOut()}>Sign out</button>
</>
) : (
<button onClick={() => signIn()}>Sign in</button>
)}
</nav>
);
}
사용자 관리:
- NextAuth.js는 사용자 생성을 관리하지 않으므로 자체 회원가입 API 라우트와 사용자 인증 로직을 구현해야 합니다.
// pages/api/auth/signup.js
import { hash } from "bcryptjs";
export default async function handler(req, res) {
if (req.method === "POST") {
const { email, password } = req.body;
const hashedPassword = await hash(password, 12);
// 데이터베이스에 사용자 저장 로직 구현
res.status(201).json({ message: "User created" });
} else {
res.status(405).json({ message: "Method not allowed" });
}
}
위의 단계를 통해 Next.js 애플리케이션에 NextAuth.js를 이용한 인증 기능을 추가할 수 있습니다.
이를 통해 다양한 인증 방법을 지원하고, 사용자 로그인 상태에 따라 다른 인터페이스를 표시하는 등의 작업을 쉽게 구현할 수 있습니다.
NextAuth4 버전

NextAuth.js를 사용하여 로그인 인증을 구현하려면, Next.js 애플리케이션에서 NextAuth를 설정하고, 제공되는 인증 공급자(예: Google, GitHub 등)를 사용하여
로그인 프로세스를 관리할 수 있습니다. 아래는 NextAuth.js 버전 4를 사용하여 로그인 인증을 설정하는 예시입니다.
1) 프로젝트 설정: Next.js 프로젝트를 초기화하고 필요한 패키지를 설치합니다.
npx create-next-app@latest my-next-app cd my-next-app npm install next-auth@latest
2) API 라우트 설정: NextAuth를 위한 API 라우트를 설정합니다. pages/api/auth/[...nextauth].js 파일을 생성합니다.
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import CredentialsProvider from 'next-auth/providers/credentials';
import { verifyPassword } from '../../../lib/auth';
import { connectToDatabase } from '../../../lib/db';
export const authOptions = {
//jwt 사용 설정
session: {
jwt: true,
// // Defaults to `session.maxAge`.
//maxAge: 60 * 60 * 24 * 30,
},
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
CredentialsProvider({
async authorize(credentials) {
const { email, password } = credentials;
const dbConnection = await connectToDatabase();
let client = dbConnection.client;
let db = dbConnection.db;
try {
const user = await db.collection("users").findOne({ email: email });
if (!user) {
throw new Error("해당 이메일 정보를 가진 유저가 존재하지 않습니다.");
}
const isValid = await verifyPassword(password, user.hashedPassword);
if (!isValid) {
throw new Error("비밀번호가 유효하지 않습니다.");
}
return {
name: user.email,
email: user.email ,
id:user._id,
role: user.role
};
} catch (error) {
console.error("Authorization error: ", error.message);
// 오류 메시지를 반환
throw new Error(error.message);
} finally {
if (client) {
await client.close();
}
}
},
}),
],
callbacks: {
async session({ session, token }) {
if (token.email) {
session.user.id = token.id;
session.user.email = token.email;
session.user.role = token.role; // 사용자 역할 추가
console.log("callbacks callbacks session : ",session);
}
return session;
},
async jwt({ token, user }) {
if (user) {
token.id = user.id;
token.email = user.email;
token.role = user.role; // 사용자 역할 추가
console.log(" token : ",token);
}
return token;
},
},
// /auth/signin 경로에 로그인 페이지가 있다고 가정하면, NextAuth.js는 인증이 필요한 사용자를 이 경로로 리디렉션합니다.
// 이 경로에 로그인 폼을 만들고, 사용자가 로그인을 시도할 때 해당 페이지에서 인증을 처리하게 됩니다.
pages: {
signIn: '/auth',
},
};
export default NextAuth(authOptions);
3) 비밀번호 검증 함수 추가:
lib/auth.js 파일에 비밀번호 검증 함수를 추가합니다.
import { hash, compare } from 'bcryptjs';
export async function hashPassword(password) {
const hashedPassword = await hash(password, 12);
return hashedPassword;
}
export async function verifyPassword(password, hashedPassword) {
const isValid = await compare(password, hashedPassword);
return isValid;
}
4) 회원가입
pages/api/auth/ signup.js
import { hashPassword } from "../../../lib/auth";
import { connectToDatabase } from "../../../lib/db";
async function handler(req, res) {
if (req.method === "POST") {
const data = req.body;
const { email, password } = data;
console.log(email, password);
// 이메일과 비밀번호 유효성 검사
if (!email || !email.includes("@") || !password || password.trim().length < 4) {
res.status(422).json({ message: "유효하지 않은 이메일 또는 비밀번호입니다." });
return;
}
let client;
let db;
try {
// 데이터베이스에 연결
const dbConnection = await connectToDatabase();
client = dbConnection.client;
db = dbConnection.db;
// 기존 사용자인지 확인
const existingUser = await db.collection("users").findOne({ email: email });
if (existingUser) {
res.status(422).json({ message: "이미 회원 가입처리된 이메일입니다." });
return;
}
// 비밀번호 해시 처리
const hashedPassword = await hashPassword(password);
let role="user";
if(email==="admin@gmail.com"){
role="admin"; // admin일 경우 role="admin"로 설정합니다.
}
// 새로운 사용자 삽입
const result = await db.collection("users").insertOne(
{ email, hashedPassword ,
role: role, // 기본 역할 설정
});
res.status(201).json({ message: "성공", data: result.insertedId });
} catch (error) {
console.error(error); // 에러 로그 출력
res.status(500).json({ message: "서버 내부 오류" });
} finally {
// 데이터베이스 연결을 닫습니다.
if (client) {
await client.close();
}
}
} else {
res.status(405).json({ message: "허용되지 않은 메소드입니다." });
}
}
export default handler;
5) 로그인 폼:
auth/auth-form.js
import { useRef, useState } from "react";
import { signIn } from 'next-auth/react';
import classes from "./auth-form.module.css";
import { useRouter } from "next/router";
async function createUser(email, password) {
const res = await fetch(`/api/auth/signup`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
password,
}),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.message || "Something went wrong!");
}
return data;
}
async function signInHandler(email, password) {
const result = await signIn('credentials', {
redirect: false,
email,
password,
});
return result;
}
function AuthForm() {
const [isLogin, setIsLogin] = useState(true);
const emailInputRef = useRef();
const passwordInputRef = useRef();
const router=useRouter();
function switchAuthModeHandler() {
setIsLogin((prevState) => !prevState);
}
async function submitHandler(event) {
event.preventDefault();
const emailInput = emailInputRef.current.value;
const passwordInput = passwordInputRef.current.value;
if (isLogin) {
const result=await signInHandler(emailInput, passwordInput);
emailInputRef.current.value="";
passwordInputRef.current.value="";
if (result.error) {
// 로그인 실패
alert('로그인 실패: ' + result.error);
} else {
// 로그인 성공
alert('로그인 성공');
router.push("/");
}
} else {
try {
const result = await createUser(emailInput, passwordInput);
emailInputRef.current.value="";
passwordInputRef.current.value="";
setIsLogin(true);
alert("회원 가입을 축하 합니다.");
} catch (error) {
alert(error.message);
}
}
}
return (
<section className={classes.auth}>
<h1>{isLogin ? "로그인" : "회원가입"}</h1>
<form onSubmit={submitHandler}>
<div className={classes.control}>
<label htmlFor="email">이메일</label>
<input type="email" id="email" required ref={emailInputRef} />
</div>
<div className={classes.control}>
<label htmlFor="password">비밀번호</label>
<input
type="password"
id="password"
required
ref={passwordInputRef}
/>
</div>
<div className={classes.actions}>
<button>{isLogin ? "로그인" : "회원가입"}</button>
<button
type="button"
className={classes.toggle}
onClick={switchAuthModeHandler}
>
{isLogin ? "계정생성" : "로그인"}
</button>
</div>
</form>
</section>
);
}
export default AuthForm;
6) 데이터베이스 연결 설정:
MongoDB 연결을 위한 lib/db.js 파일을 설정합니다.
import { MongoClient } from "mongodb";
async function connectToDatabase() {
let client;
let clientPromise;
if (!clientPromise) {
const connectionString = `mongodb+srv://${process.env.mongodb_username}:${process.env.mongodb_password}@${process.env.mongodb_clustername}/${process.env.mongodb_database}`;
client = new MongoClient(connectionString);
clientPromise = client.connect();
}
await clientPromise;
return {
client,
db: client.db(),
};
}
export { connectToDatabase };
7)환경 변수 설정:
.env.local 또는 next.config.js 파일을 생성하고 필요한 환경 변수를 설정합니다.
next.config.js
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
module.exports = (phase) => {
if (phase === PHASE_DEVELOPMENT_SERVER) {
return {
env: {
mongodb_username: "macaronics",
mongodb_password: "23213",
mongodb_clustername: "mongo-test.test.mongodb.net",
mongodb_database: "nextjsEx3Auth",
appName: "my-site-dev",
GITHUB_ID:"test1",
GITHUB_SECRET:'12324'
},
};
}
return {
env: {
mongodb_username: "macaronics",
mongodb_password: "1232",
mongodb_clustername: "mongo-test.test.mongodb.net",
mongodb_database: "nextjsEx3Auth",
appName: "test-blog",
GITHUB_ID:"test1",
GITHUB_SECRET:'12324'
},
};
};
8) 로그아웃
_app.js
import Layout from "../components/layout/layout";
import "../styles/globals.css";
import { SessionProvider } from "next-auth/react";
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<SessionProvider session={session}>
<Layout>
<Component {...pageProps} />
</Layout>
</SessionProvider>
);
}
export default MyApp;
main-navigation.js 로그아웃 버튼을 포함하는 페이지를 설정합니다.
// components/main-navigation.js
import Link from "next/link";
import { useSession, signOut } from "next-auth/react";
import classes from "./main-navigation.module.css";
function MainNavigation() {
const { data: session } = useSession();
let isLoginContent;
if (session) {
isLoginContent = (
<>
<li>
<span className="text-white">{session.user.email} 님</span>
</li>
<li>
<button onClick={() => signOut()}>로그아웃</button>
</li>
</>
);
} else {
isLoginContent = (
<>
<li>
<Link href="/auth" legacyBehavior>
<a>로그인</a>
</Link>
</li>
</>
);
}
return (
<header className={classes.header}>
<Link href="/" legacyBehavior>
<a>
<div className={classes.logo}>Next Auth</div>
</a>
</Link>
<nav>
<ul>
<li>
<Link href="/profile" legacyBehavior>
<a>프로파일</a>
</Link>
</li>
{isLoginContent}
</ul>
</nav>
</header>
);
}
export default MainNavigation;
9) NextAuth 설정 확인: 이제 애플리케이션을 실행하고 NextAuth 설정이 제대로 작동하는지 확인합니다.
npm run dev
브라우저에서 http://localhost:3000으로 이동하여 로그인 및 로그아웃 기능을 테스트할 수 있습니다.
이 기본 예제는 Google , GITHUB 인증 공급자를 사용합니다. 다른 인증 공급자를 사용하려면 Providers 객체에 해당 공급자를 추가하면 됩니다. 또한, 데이터베이스 설정 및 세션 관리 등 추가적인 옵션도 NextAuth 문서를 참고하여 설정할 수 있습니다.
2. 페이지 접근 제어를 설정
- a 페이지: 모든 사람이 접근 가능
- b 페이지: 로그인한 사람만 접근 가능
- c 페이지: 로그인한 사람 중 admin만 접근 가능
A 개별적 설정 방법
1) a 페이지 설정
pages/a.js 파일을 생성합니다. 이 페이지는 모든 사람이 접근할 수 있습니다
export default function PageA() {
return (
<div>
<h1>Page A</h1>
<p>This page is accessible by everyone.</p>
</div>
);
}
2) b 페이지 설정 (로그인한 사람만 접근 가능)
pages/b.js 파일을 생성합니다. 이 페이지는 로그인한 사람만 접근할 수 있습니다.
NextAuth.js의 getSession을 사용하여 세션을 확인합니다.
import { getSession } from 'next-auth/react';
export default function PageB() {
return (
<div>
<h1>Page B</h1>
<p>This page is only accessible by logged-in users.</p>
</div>
);
}
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session) {
return {
redirect: {
destination: '/auth/signin',
permanent: false,
},
};
}
return {
props: { session },
};
}
3) c 페이지 설정 (admin만 접근 가능)
pages/c.js 파일을 생성합니다. 이 페이지는 로그인한 admin만 접근할 수 있습니다.
사용자 역할을 확인하기 위해 NextAuth.js의 getSession을 사용합니다.
import { getSession } from 'next-auth/react';
export default function PageC() {
return (
<div>
<h1>Page C</h1>
<p>This page is only accessible by admin users.</p>
</div>
);
}
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session || session.user.role !== 'admin') {
return {
redirect: {
destination: '/auth/signin',
permanent: false,
},
};
}
return {
props: { session },
};
}
B. _middleware.js를 이용하여 일괄적 설정 방법
참고 : https://nextjs.org/docs/app/building-your-application/routing/middleware

1)next.config.js 파일에 NEXTAUTH_SECRET 생성
암호 생성 사이트 : https://ko.pw-gen.com/
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
//암호 생성 사이트 : NEXTAUTH_SECRET
//https://ko.pw-gen.com/
module.exports = (phase) => {
if (phase === PHASE_DEVELOPMENT_SERVER) {
return {
env: {
mongodb_username: "유저네임",
mongodb_password: "비번",
mongodb_clustername: "clustername",
mongodb_database: "nextjsEx3Auth",
appName: "my-site-dev",
NEXTAUTH_SECRET:"%vFT=rII0T/)*&$-maTE1)elt~9nFs=6QIbMnq(2C/-=5t/K&)pmJ1iPe~2R3BAiH!TWLbn3URn+9onIBLkRPB$ZodGCbYv51iX~",
GITHUB_ID:"test1",
GITHUB_SECRET:'12324'
},
};
}
return {
env: {
mongodb_username: "유저네임",
mongodb_password: "비번",
mongodb_clustername: "clustername",
mongodb_database: "nextjsEx3Auth",
appName: "macaronics-blog",
NEXTAUTH_SECRET:"%vFT=rII0T/)*&$-maTE1)elt~9nFs=6QIbMnq(2C/-=5t/K&)pmJ1iPe~2R3BAiH!TWLbn3URn+9onIBLkRPB$ZodGCbYv51iX~",
GITHUB_ID:"test1",
GITHUB_SECRET:'12324'
},
};
};
2) [...nextauth].js 에서 secret 를 추가 한다.
[...nextauth].js
~
export const authOptions = {
secret: process.env.NEXTAUTH_SECRET,
//jwt 사용 설정
session: {
jwt: true,
// // Defaults to `session.maxAge`.
//maxAge: 60 * 60 * 24 * 30,
secret: process.env.NEXTAUTH_SECRET,
},
~
};
export default NextAuth(authOptions);
3) root middleware.ts 이름으로 파일 생성한다.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getToken } from 'next-auth/jwt';
export async function middleware(req: NextRequest) {
//console.log("Request URL: ", req.url);
//console.log("Request Cookies: ", req.cookies);
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
//console.log("middleware= token :", token);
const { pathname } = req.nextUrl;
if (pathname.startsWith('/profile')|| pathname.startsWith('/b')) {
if (!token) {
return NextResponse.redirect(new URL('/auth', req.url));
}
}
if (pathname.startsWith('/admin')|| pathname.startsWith('/c')) {
if (!token || token.role !== 'admin') {
return NextResponse.redirect(new URL('/', req.url));
}
}
return NextResponse.next();
}
//보호하고 싶은 경로들을 설정합니다.
export const config = {
matcher: ['/profile/:path*', '/admin/:path*' , '/b/:path*', '/c/:path*']
};

a페이지
export default function PageA() {
return (
<div>
<h1>Page A</h1>
<p>이 페이지는 모든 사람이 접근할 수 있습니다.</p>
</div>
);
}
b페이지
export default function PageB() {
return (
<div>
<h1>Page B</h1>
<p>b 페이지 설정 (로그인한 사람만 접근 가능).</p>
</div>
);
}
c 페이지
export default function PageC() {
return (
<div>
<h1>Page C</h1>
<p>이 페이지는 로그인한 admin만 접근할 수 있습니다.</p>
</div>
);
}
admin 페이지
export default function AdminPage() {
return (
<div>
<h1>Page AdminPage</h1>
<p>이 페이지는 로그인한 admin만 접근할 수 있습니다.</p>
</div>
);
}
2.JWT(JSON Web Token) 방식
JWT 인증을 위한 NextAuth.js 설정
1. NextAuth.js 설치:
- 터미널에서 개발 서버를 종료한 후 다음 명령어를 입력하여 NextAuth.js 패키지를 설치합니다.
npm install next-auth
2. JWT 시크릿 키 생성:
- JWT 시크릿 키는 토큰을 서명하고 검증하는 데 사용됩니다. 프로젝트 루트 디렉토리의 .env.local 파일에 추가합니다.
NEXTAUTH_SECRET=your_secret_key
3.NextAuth.js 설정 파일 생성:
- pages/api/auth/[...nextauth].js 파일을 생성하고 JWT 방식을 설정합니다.
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import jwt from "jsonwebtoken";
export default NextAuth({
providers: [
Providers.Credentials({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
authorize: async (credentials) => {
// 사용자 인증 로직 구현
const user = { id: 1, name: "John Doe", email: "john@example.com" }; // 예시 사용자
if (user) {
return user;
} else {
return null;
}
}
})
],
session: {
jwt: true,
},
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},
callbacks: {
async jwt(token, user) {
if (user) {
token.id = user.id;
}
return token;
},
async session(session, token) {
session.user.id = token.id;
return session;
},
}
});
4.사용자 로그인 및 인증 확인:
- 서버 사이드에서 인증 확인 예제
import { getSession } from "next-auth/react";
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session) {
return {
redirect: {
destination: "/api/auth/signin",
permanent: false
}
};
}
return {
props: { session }
};
}
클라이언트 사이드에서 인증 확인 예제
import { useSession } from "next-auth/react";
export default function Page() {
const { data: session, status } = useSession();
if (status === "loading") return <p>Loading...</p>;
if (!session) return <p>You are not logged in</p>;
return <p>Welcome, {session.user.name}</p>;
}
5.사용자 인터페이스 업데이트:
- 로그인 상태에 따라 다른 인터페이스를 표시하는 컴포넌트 예제
import { signIn, signOut, useSession } from "next-auth/react";
export default function NavBar() {
const { data: session, status } = useSession();
if (status === "loading") return null;
return (
<nav>
{session ? (
<>
<p>Welcome, {session.user.name}</p>
<button onClick={() => signOut()}>Sign out</button>
</>
) : (
<button onClick={() => signIn()}>Sign in</button>
)}
</nav>
);
}
6.사용자 관리:
- NextAuth.js는 사용자 생성을 관리하지 않으므로 자체 회원가입 API 라우트와 사용자 인증 로직을 구현해야 합니다.
- 예: 사용자 등록 API 라우트
// pages/api/auth/signup.js
import { hash } from "bcryptjs";
export default async function handler(req, res) {
if (req.method === "POST") {
const { email, password } = req.body;
const hashedPassword = await hash(password, 12);
// 데이터베이스에 사용자 저장 로직 구현
res.status(201).json({ message: "User created" });
} else {
res.status(405).json({ message: "Method not allowed" });
}
}
위의 단계를 통해 JWT 방식을 사용하여 Next.js 애플리케이션에 NextAuth.js를 이용한 인증 기능을 추가할 수 있습니다.
이를 통해 다양한 인증 방법을 지원하고, 사용자 로그인 상태에 따라 다른 인터페이스를 표시하는 등의 작업을 쉽게 구현할 수 있습니다.
3.middleware.ts 설정
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getToken } from 'next-auth/jwt';
import fetch from 'isomorphic-unfetch';
interface TokenType {
memberId: number;
userId:string;
name: string;
email: string;
sub: string;
role: string;
accessToken: string;
refreshToken: string;
iat: number;
exp: number;
jti: string;
}
// 타입 가드 함수
function isTokenType(token: any): token is TokenType {
return (
token &&
typeof token.memberId === 'number' &&
typeof token.userId === 'string' &&
typeof token.name === 'string' &&
typeof token.email === 'string' &&
typeof token.sub === 'string' &&
typeof token.role === 'string' &&
typeof token.accessToken === 'string' &&
typeof token.refreshToken === 'string' &&
typeof token.iat === 'number' &&
typeof token.exp === 'number' &&
typeof token.jti === 'string'
);
}
export async function middleware(req: NextRequest) {
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
const { pathname } = req.nextUrl;
// 보호하고 싶은 경로들을 설정합니다.
const protectedPaths = [
'/profile',
'/admin',
'/boards/write',
'/boards/update',
];
if (protectedPaths.some(path => pathname.startsWith(path))) {
console.log(" 1protectedPaths 체크 :", token);
if (!token || !isTokenType(token)) {
return NextResponse.redirect(new URL('/auth', req.url));
}
console.log(" 2protectedPaths 체크");
// 토큰이 있고 만료 시간을 체크하여 갱신이 필요한지 확인합니다.
const tokenExpiration = token.exp * 1000; // 토큰 만료 시간 (Unix 타임스탬프를 밀리초로 변환)
const now = Date.now();
const threshold = 5 * 60 * 1000; // 5분 (토큰 갱신 임계시간)
// 토큰 만료 5분 이내에 요청이 들어오면 갱신 시도
if (tokenExpiration - now < threshold) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/refreshToken`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token.accessToken}`,
},
// 갱신 토큰 요청 시 필요한 데이터 (예: refresh token 등)
body: JSON.stringify({ refreshToken: token.refreshToken }),
});
if (!response.ok) {
throw new Error('토큰 갱신 실패');
}
const data = await response.json();
console.log("토큰 갱신 성공: ", data);
// 갱신된 토큰을 설정합니다.
token.accessToken = data.accessToken;
token.exp = data.exp;
} catch (error) {
console.error('토큰 갱신 실패:', error);
// 갱신 실패 시 로그아웃 처리는 클라이언트 측에서 수행되어야 합니다.
}
}
}
// 관리자 페이지 접근 권한 확인
if (pathname.startsWith('/admin')) {
if (!token || !isTokenType(token) || token.role.toUpperCase() !== 'ADMIN') {
return NextResponse.redirect(new URL('/', req.url));
}
}
// 로그인 페이지 및 회원가입 페이지
if (pathname.startsWith('/auth') || pathname.startsWith('/signup')) {
if (token && isTokenType(token)) {
return NextResponse.redirect(new URL('/', req.url));
}
}
return NextResponse.next();
}
// 보호하고 싶은 경로들을 설정합니다.
export const config = {
matcher: [
'/profile/:path*',
'/auth',
'/admin/:path*',
'/boards/write',
'/boards/update',
],
};














댓글 ( 0)
댓글 남기기