웹 애플리케이션을 개발하면서 사용자 인증은 핵심적인 요소 중 하나입니다. 이 글에서는 Next.js 애플리케이션에서 NextAuth.js를 활용하여 간편하게 자체 로그인을 구현하고 로그인이 필요한 페이지에서 세션을 효과적으로 관리하는 방법에 대해 알아보겠습니다.
NextAuth.js는 Next.js 애플리케이션에서 사용자 인증을 손쉽게 구현할 수 있도록 도와주는 라이브러리입니다. 다양한 인증 공급자(Providers)를 지원하며, 자체 로그인 또한 구현할 수 있도록 도와줍니다.주로 웹 애플리케이션 개발에서 사용되며, 다음과 같은 주요 특징을 제공합니다:
다양한 인증 공급자 지원: NextAuth.js는 다양한 인증 공급자(예: Google, Facebook, GitHub, Twitter 등)와 함께 사용할 수 있습니다. 이를 통해 사용자는 웹 애플리케이션에 다양한 방법으로 로그인하거나 가입할 수 있습니다.
세션 관리: NextAuth.js는 사용자 세션을 관리하고 보안적으로 유지합니다. 사용자 로그인 상태를 추적하고 세션을 관리하여 애플리케이션 내에서 사용자 인증을 유지합니다.
간단한 설정: NextAuth.js를 설정하는 것은 상대적으로 간단하며, 대부분의 설정은 설정 파일을 통해 수행됩니다. 이를 통해 빠르게 인증 시스템을 설정할 수 있습니다.
확장성: NextAuth.js는 확장 가능한 아키텍처를 제공하여 사용자 지정 로직 및 필요한 기능을 추가하거나 수정할 수 있습니다.
TypeScript 지원: TypeScript를 사용하여 NextAuth.js를 구현할 수 있으며, 타입 안정성을 확보할 수 있습니다.
NextAuth.js는 Next.js와의 통합이 원활하며, Next.js 애플리케이션 내에서 간편하게 사용할 수 있습니다. 이를 통해 웹 애플리케이션에서 사용자 인증 및 세션 관리를 구현하는 과정을 간단하게 만들어줍니다.
먼저 NextAuth.js를 설정하고 사용자 정의 로그인 프로세스를 구현하기 위해 pages/api/auth/[...nextauth].js
파일을 만듭니다. 이 파일은 사용자 인증 관련 엔드포인트를 정의합니다. 설정 파일에서는 다음과 같이 NextAuth.js를 초기화합니다.
app directory인 경우 의 파일 구조
https://codevoweb.com/setup-and-use-nextauth-in-nextjs-13-app-directory/
그러나 next13은 app directory 와 pages directory 를 함께 사용하는것을 지원하기 때문에 app directory를 사용할 경우에도pages/api/auth/[...nextauth].js
로 설정이 가능합니다.
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import axios from 'axios';
export default NextAuth({
providers: [
Providers.Credentials({
// 이 부분은 자체 로그인 로직을 구현합니다.
credentials: {
username: { label: 'Username', type: 'text' },
password: { label: 'Password', type: 'password' }
},
async authorize(credentials) {
// 외부 서버와 통신하여 유저 정보와 토큰을 가져오는 로직을 여기에 구현합니다.
const { username, password } = credentials;
// 외부 서버와의 통신을 통해 유저 정보와 토큰을 가져옵니다.
const response = await axios.post('https://your-external-server.com/api/login', {
username,
password
});
const data = response.data;
if (data) {
// 유저 정보와 토큰을 NextAuth.js 세션에 저장합니다.
return {
name: data.name,
email: data.email,
token: data.token
};
} else {
// 로그인 실패 시 null을 반환합니다.
return null;
}
}
})
],
callbacks: {
async session(session, token) {
// 세션에 토큰 정보를 추가합니다.
session.token = token.token;
return session;
}
},
session: {
jwt: true
}
});
Credentials Provider를 사용하여 자체 로그인 구현.
클라이언트에서 입력한 자격 증명을 서버로 전송하고 토큰을 받아옵니다.
credentials 속성은 사용자가 로그인할 때 필요한 자격 증명(예: 사용자 이름과 비밀번호)을 정의하는 데 사용됩니다.
여기에서 credentials 객체의 역할을 자세히 설명합니다:
username: 이 속성은 사용자 이름(또는 다른 식별자)을 입력할 수 있는 필드를 정의합니다. 사용자는 로그인할 때 이 필드에 사용자 이름을 입력해야 합니다. label 속성을 사용하여 필드 레이블을 정의할 수 있습니다.
password: 이 속성은 비밀번호를 입력할 수 있는 필드를 정의합니다. 사용자는 로그인할 때 이 필드에 비밀번호를 입력해야 합니다. label 속성을 사용하여 필드 레이블을 정의할 수 있습니다. type 속성은 입력 필드의 타입을 지정하는 데 사용되며, 여기서는 password로 설정되어 비밀번호 입력 필드임을 나타냅니다.
Providers.Credentials를 사용하여 사용자 정의 자격 증명 로그인을 구현하면 다음과 같은 작업이 가능합니다:
credentials 객체를 정의하여 사용자 이름과 비밀번호와 같은 필요한 자격 증명 필드를 설정합니다.
로그인 화면에서 signIn 함수를 호출하여 자체 로그인을 실행하고, signIn 함수의 첫 번째 인자로는 credentials 로그인 공급자(provider)의 이름을 전달하고, 두 번째 인자로는 사용자가 입력한 자격 증명 정보(예: 사용자 이름과 비밀번호)를 전달합니다.
이 때, signIn 함수를 호출하면 authorize 콜백 함수가 실행됩니다. authorize 함수에서는 외부 서버와 통신하여 사용자 정보와 토큰을 가져오는 로직을 구현합니다.
외부 서버와의 통신 결과를 통해 로그인이 성공하면 해당 사용자 정보와 토큰을 NextAuth.js 세션에 저장하고, 그렇지 않으면 로그인 실패를 처리할 수 있습니다.
import { signIn } from 'next-auth/react';
function LoginPage() {
const handleSubmit = async (e) => {
e.preventDefault();
// 사용자가 입력한 사용자 이름(username)과 비밀번호(password)를 가져옵니다.
const username = e.target.username.value;
const password = e.target.password.value;
// signIn 함수를 사용하여 자체 로그인 요청을 보냅니다.
const result = await signIn('credentials', {
username,
password,
// 필요한 경우 다른 필드도 추가할 수 있습니다.
});
// 로그인이 성공하면 다음 페이지로 이동할 수 있습니다.
if (result.error) {
// 로그인 실패 시 오류 메시지를 처리할 수 있습니다.
console.error(result.error);
}
};
return (
<div>
<h1>로그인</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">사용자 이름</label>
<input type="text" id="username" name="username" />
</div>
<div>
<label htmlFor="password">비밀번호</label>
<input type="password" id="password" name="password" />
</div>
<button type="submit">로그인</button>
</form>
</div>
);
}
export default LoginPage;
NextAuth.js는 클라이언트와 서버 간의 통신을 담당하는 라이브러리입니다. 이 라이브러리는 서버 측에서 실행되며, 서버에서 사용자 인증 및 세션 관리와 관련된 작업을 수행합니다. 클라이언트는 NextAuth.js를 사용하여 서버에서 제공하는 API 엔드포인트를 호출하고 세션을 관리할 수 있습니다.
NextAuth.js를 사용하면 클라이언트는 서버 측 세션에 직접 접근하거나 조작할 수 없으며, 사용자 인증 및 세션 관리가 서버 측에서 안전하게 처리됩니다. 이것이 NextAuth.js를 사용하는 주요 이점 중 하나입니다. 사용자 정보 및 세션 관리는 서버에서 안전하게 보호되며, 클라이언트는 이러한 데이터에 대한 직접적인 액세스를 허용하지 않습니다.
따라서 NextAuth.js를 사용하면 클라이언트와 서버 간의 보안 통신을 효과적으로 처리할 수 있으며, 사용자 정보와 세션 데이터를 안전하게 관리할 수 있습니다. 다만, NextAuth.js를 사용하는 서버 자체의 보안 설정과 모범 사례를 따르는 것이 중요하며, 서버 측 보안에도 신경을 써야 합니다.
NextAuth.js에서 사용자 정보와 토큰을 세션에 저장하는 방법은 다음과 같이 두 가지입니다.
const response = await axios.post('https://your-external-server.com/api/login', {
username,
password
});
const data = response.data;
if (data) {
// 유저 정보와 토큰을 NextAuth.js 세션에 저장합니다.
return {
name: data.name,
email: data.email,
token: data.token // 이 토큰 정보는 세션에 자동으로 저장됩니다.
};
}
위의 코드에서 authorize 함수 내에서 반환한 객체에 token 정보가 포함되어 있으며, 이 정보는 NextAuth.js에서 자동으로 세션에 저장됩니다. 이 방법은 대부분의 경우에 사용하기 적합하며 간단하고 편리합니다.
callbacks: {
async session(session, token) {
// 세션에 토큰 정보를 추가합니다.
session.token = token.token; // 이 부분에서 세션에 토큰 정보를 수동으로 추가합니다.
return session;
}
},
위의 코드에서는 session 콜백 함수를 사용하여 세션 객체에 직접 토큰 정보를 추가하는 방법을 사용합니다. 이 방법은 특별한 사용 사례나 커스터마이징이 필요한 경우에 유용합니다. 예를 들어 세션 객체에 토큰 이외의 사용자 지정 데이터를 추가하거나, 세션에 특정 조건에 따라 다른 정보를 추가해야 할 때 사용합니다.
따라서 프로젝트의 요구 사항과 사용자 인증 및 세션 관리의 세부 사항에 따라 두 가지 방법 중 하나를 선택할 수 있습니다. 일반적으로 자동 방식이 간단하고 효율적이며 대부분의 경우에 적합합니다. 다만 특별한 커스텀 요구 사항이 있는 경우 수동 방식을 사용하여 세션을 더욱 커스터마이즈할 수 있습니다.
session: { jwt: true } 설정은 필수적으로 사용해야 하는 것은 아닙니다. NextAuth.js를 사용하는 프로젝트에 따라 JWT를 사용할 필요가 없는 경우도 있을 수 있습니다. JWT를 사용하느냐 마느냐는 프로젝트의 요구 사항과 보안 관련 결정에 따라 다를 수 있습니다.
JWT를 사용하지 않을 경우에는 기본적인 세션 관리 방식을 사용하게 됩니다. 이 경우, 세션 데이터가 서버 측에서 안전하게 저장되며 NextAuth.js가 세션을 처리합니다. 이 방법도 보안적으로 충분하며, JWT를 사용할 필요가 없는 경우에는 session: { jwt: true } 설정을 제외하고 설정할 수 있습니다.
따라서 JWT를 사용할지 여부는 프로젝트의 요구 사항과 보안 정책에 따라 결정해야 합니다. 일부 프로젝트에서는 JWT를 사용하여 추가적인 보안과 효율성을 얻을 수 있지만, 다른 프로젝트에서는 기본 세션 관리 방식을 사용하여 간단하게 구현할 수도 있습니다.
하지만 일반적으로 session: { jwt: true } 설정을 사용하는 것이 좋습니다. 이 설정은 다음과 같은 이점을 제공합니다:
보안: JWT는 서버에서 서명되어 클라이언트에서 수정할 수 없습니다. 이로써 세션 데이터의 무결성이 보장됩니다.
효율성: JWT는 클라이언트 측에서 저장되므로 서버에 저장된 세션과 비교하여 서버 부하를 줄일 수 있습니다.
쉬운 전달: JWT는 HTTP 헤더 또는 쿠키와 같은 방법으로 클라이언트와 서버 간에 쉽게 전달할 수 있습니다.
먼저 세션을 사용하려면 애플리케이션의 최상위 수준에서 세션 컨텍스트인 <SessionProvider />
를 사용해야 합니다.
import { SessionProvider } from "next-auth/react"
export default function App({
Component,
pageProps: { session, ...pageProps },
}) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
)
}
import { useSession, signIn, signOut } from 'next-auth/react';
function ProtectedPage() {
const { data: session } = useSession();
if (!session) {
return (
<div>
<p>이 페이지는 로그인이 필요한 페이지입니다.</p>
<button onClick={() => signIn('credentials')}>로그인</button>
</div>
);
}
return (
<div>
<p>안녕하세요, {session.user.name}님!</p>
<button onClick={() => signOut()}>로그아웃</button>
</div>
);
}
export default ProtectedPage;
signOut() 함수를 호출하는 것만으로 세션을 종료하고 로그아웃을 수행합니다. NextAuth.js는 이를 처리하고 현재 세션을 삭제하여 로그아웃 상태로 전환합니다. 따라서 signOut() 함수를 호출할 때 별도의 설정이나 세션 삭제 코드를 작성할 필요가 없습니다.
간단하게 signOut() 함수를 호출하여 로그아웃을 실행할 수 있으며, 사용자가 다시 로그인해야 합니다. 이것은 세션을 관리하는 NextAuth.js의 기능 중 하나로, 로그아웃을 처리하려면 별도의 로직을 작성할 필요가 없습니다.
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.GitHub({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
Providers.Naver({
clientId: process.env.NAVER_CLIENT_ID,
clientSecret: process.env.NAVER_CLIENT_SECRET,
}),
// 다른 소셜 로그인 공급자 설정 가능
],
// ...
});
import { signIn } from 'next-auth/react';
function LoginPage() {
return (
<div>
<button onClick={() => signIn('github')}>GitHub 로그인</button>
</div>
);
}
export default LoginPage;
소셜 로그인 후 자체 로그인처럼 토큰이나 유저정보를 별도로 세션에 저장하는 로직을 구현할 필요가 없습니다. NextAuth.js 가 알아서 세션에 저장해주며
const { data: session } = useSession();
를 이용해 사용하면 됩니다.
만일 서버가 다운된다면 session도 초기화가 되는거고
그렇다면 사용자들 모두 재로그인을 해야하게 되는걸까요??