로그인하지 않은 사용자는 /profile과 같은 특정 페이지에 접근할 수 없어야 합니다. 만약 해당 페이지에 머무르는 중에 로그아웃을 클릭한다면, 즉시 다른 페이지로 리디렉션되어야 합니다.
NextAuth를 사용하여 이러한 클라이언트 사이드 페이지 보호를 구현할 수 있습니다.
useSession을 사용시 문제점
NextAuth에서 페이지를 보호하는 가장 먼저 떠오르는 방법은 useSession 훅을 사용하는 것입니다. 이 훅은 세션 데이터와 로딩 상태를 반환합니다. 이론적으로는 로딩 중일 때 로딩 UI를 보여주고, 로딩이 끝난 후 세션이 없으면 리디렉션하면 됩니다.
NextAuth v3 (next-auth/client)
// user-profile.js (NextAuth v3)
import { useSession } from 'next-auth/client';
function UserProfile() {
const [session, loading] = useSession();
if (loading) {
return <p>Loading...</p>; // 로딩 중 UI
}
// 로딩이 끝났지만 세션이 없다면 리디렉션 (이론상)
if (!session) {
// window.location.href = '/auth'; // <- 이 코드가 실행될 것으로 기대
}
return <h1>Your User Profile</h1>;
}
NextAuth v4+ (next-auth/react)
v4+에서는 훅의 반환값이 객체로 변경되었습니다.
// user-profile.js (NextAuth v4+)
import { useSession } from 'next-auth/react';
function UserProfile() {
// 훅의 반환값이 객체로 변경됨
const { data: session, status } = useSession();
const loading = status === 'loading';
if (loading) {
return <p>Loading...</p>;
}
if (status === 'unauthenticated') {
// window.location.href = '/auth';
}
return <h1>Your User Profile</h1>;
}
하지만 이 접근 방식에는 NextAuth v3에서 심각한 문제가 있습니다. 페이지에 처음 접근할 때 로그아웃 상태(세션 없음)라면, v3의 useSession은 loading 상태를 true로 반환한 후, 이 상태가 업데이트되지 않고 계속 true로 머무르는 현상이 발생합니다. loading이 false가 되지 않기 때문에, session이 null인지 확인하고 리디렉션하는 로직을 실행할 수 없게 됩니다. (이 문제는 v4+에서는 해결되었습니다.)
getSession을 이용한 해결 방법 (v3 Workaround)
v3에서 이 문제를 해결하기 위해 useSession 대신 getSession 함수를 사용해야 합니다.
- useSession: 컴포넌트가 렌더링될 때 즉시 현재 세션 상태와 로딩 상태를 반환합니다. (그러나 위에서 언급한 로딩 문제가 있습니다.)
- getSession: 이 함수는 새로운 요청을 보내 서버로부터 최신 세션 데이터를 가져옵니다. Promise를 반환하기 때문에, 응답을 받은 후 우리가 원하는 로직을 실행할 수 있습니다.
getSession을 사용하려면 React의 useState와 useEffect 훅을 함께 사용하여 우리가 직접 로딩 상태를 제어해야 합니다.
NextAuth v3 (next-auth/client)
먼저, 컴포넌트의 자체 로딩 상태를 useState로 만듭니다. 초기값은 true로 설정합니다.
// user-profile.js (NextAuth v3)
import { useState, useEffect } from 'react';
import { getSession } from 'next-auth/client';
function UserProfile() {
// 1. 자체 로딩 상태 관리 (초기값 true)
const [isLoading, setIsLoading] = useState(true);
// ...
}
NextAuth v4+ (next-auth/react)
v4+에서는 useSession이 반환하는 status 값을 사용하면 되므로, 별도의 useState가 필요 없습니다.
// user-profile.js (NextAuth v4+)
import { useSession } from 'next-auth/react';
function UserProfile() {
const { data: session, status } = useSession();
const loading = status === 'loading';
// 'loading' 상태를 직접 사용
}
useEffect를 사용한 세션 확인 및 리디렉션
useEffect 훅을 사용하여 컴포넌트가 마운트될 때(한 번만) getSession()을 호출합니다.
NextAuth v3 (next-auth/client)
getSession()이 반환하는 Promise가 완료되면 .then() 블록이 실행됩니다. 이 session 객체의 유무를 확인하여 리디렉션 로직을 구현합니다.
// user-profile.js (NextAuth v3)
useEffect(() => {
// 2. 컴포넌트 마운트 시 세션 데이터를 가져옴
getSession().then((session) => {
// 3. 세션이 없는 경우 (로그아웃 상태)
if (!session) {
// 4. 로그인 페이지로 강제 리디렉션
window.location.href = '/auth';
} else {
// 5. 세션이 있는 경우 (로그인 상태) -> 로딩 상태를 false로 변경
setIsLoading(false);
}
});
}, []); // 의존성 배열을 비워 한 번만 실행
NextAuth v4+ (next-auth/react)
v4+에서는 status 값이 변경될 때마다 useEffect를 실행하여 리디렉션 여부를 확인합니다. useRouter 훅을 사용하여 리디렉션합니다.
// user-profile.js (NextAuth v4+)
import { useRouter } from 'next/router';
import { useEffect } from 'react';
// ...
const { data: session, status } = useSession();
const router = useRouter();
useEffect(() => {
// 로딩 중이 아니고 (loading이 아니고)
// 인증되지 않은 상태 (unauthenticated)라면 /auth로 리디렉션
if (status !== 'loading' && status === 'unauthenticated') {
router.push('/auth');
}
}, [status, router]); // status가 변경될 때마다 이 효과를 재실행
콘텐츠 깜박임 문제 방지 (v3)
v3 방식을 사용할 때 setIsLoading(false)를 호출하는 위치가 매우 중요합니다.
만약 세션 유무와 관계없이 .then() 블록 마지막에서 setIsLoading(false)를 호출하면, 세션이 없는 사용자가 리디렉션되기 직전에 아주 잠깐 동안 프로필 페이지 콘텐츠가 노출되는 '깜박임(flash)' 현상이 발생할 수 있습니다.
잘못된 코드 (깜박임 발생)
// user-profile.js (NextAuth v3) - 잘못된 예시
useEffect(() => {
getSession().then((session) => {
if (!session) {
window.location.href = '/auth';
}
// !!! 문제: 세션이 없어도 로딩이 false가 되어 잠깐 렌더링됨
setIsLoading(false);
});
}, []);
올바른 코드 (깜박임 방지)
이를 방지하려면, 반드시 세션이 존재하는 else 블록 안에서만 setIsLoading(false)를 호출해야 합니다.
// user-profile.js (NextAuth v3) - 올바른 예시
useEffect(() => {
getSession().then((session) => {
if (!session) {
window.location.href = '/auth';
} else {
// !!! 해결: 세션이 있는 것이 확인된 후에만 로딩을 끈다
setIsLoading(false);
}
});
}, []);
(v4+에서는 status가 'authenticated'가 되기 전까지는 프로필 콘텐츠를 렌더링하지 않으므로 이 문제가 자연스럽게 방지됩니다.)
최종 렌더링 로직
마지막으로, 렌더링 부분에서는 우리가 만든 isLoading 상태(v3) 또는 status 값(v4)을 확인하여 로딩 UI 또는 실제 페이지 콘텐츠를 반환합니다.
NextAuth v3 (next-auth/client)
// user-profile.js (NextAuth v3)
// 7. 로딩 중일 때 로딩 UI 반환
if (isLoading) {
return <p className={classes.profile}>Loading...</p>;
}
// 8. 로딩이 끝났고 세션이 있으면 (리디렉션되지 않았으면) 페이지 콘텐츠 반환
return (
<section className={classes.profile}>
<h1>Your User Profile</h1>
{/* <ProfileForm /> */}
</section>
);
NextAuth v4+ (next-auth/react)
// user-profile.js (NextAuth v4+)
// 1. status가 'loading'일 때 로딩 UI 반환
if (status === 'loading') {
return <p className={classes.profile}>Loading...</p>;
}
// 2. status가 'authenticated'일 때만 페이지 콘텐츠 반환
if (status === 'authenticated') {
return (
<section className={classes.profile}>
<h1>Your User Profile</h1>
{/* <ProfileForm /> */}
</section>
);
}
// 3. 'unauthenticated' 상태이며 리디렉션이 처리되는 동안 로딩 UI 반환
return <p className={classes.profile}>Loading...</p>;
요약
useSession은 헤더의 로그인/로그아웃 UI처럼 실시간으로 상태를 반영하는 데는 유용하지만, (v3 기준) 페이지 로드 시점의 인증 상태를 확인하여 리디렉션하는 데는 loading 상태가 고정되는 문제가 있었습니다. v3에서는 getSession을 useEffect, useState와 함께 사용하여 수동으로 로딩 상태를 제어하는 '우회 방법(workaround)'을 통해 안정적인 클라이언트 사이드 라우트 보호 기능을 구현해야 했습니다.
v4 이상에서는 이 문제가 해결되어 useSession 훅의 status 값을 통해 더 간단하게 동일한 기능을 구현할 수 있습니다.
'NextJS > NextAuth' 카테고리의 다른 글
| [NextAuth] Session Provider 사용하기 - 8 (0) | 2025.11.20 |
|---|---|
| [NextAuth] 서버 사이드 페이지 가드 (라우트 보호) 추가하기 - 7 (0) | 2025.11.18 |
| [NextAuth] 클라이언트 컴포넌트 인증 : 로그인, 로그아웃 -5 (0) | 2025.11.17 |
| [NextAuth] 로그인 API 구현 [...nextauth].js - 4 (0) | 2025.11.16 |
| [NextAuth] 회원가입 컴포넌트 및 API 연동 - 3 (0) | 2025.11.12 |