애플리케이션의 규모가 커질수록 최적화는 중요한 고려 사항이 된다. 현재 구현된 인증 로직은 MainNavigation 등 여러 컴포넌트에서 useSession 훅을 사용하고 있다. 하지만 특정 상황에서 중복된 인증 확인 과정이 발생하는 문제가 있어 이를 최적화하는 방법을 다룬다.
1. 중복된 세션 확인 문제
사용자가 로그인된 상태에서 프로필 페이지(profile)에 접속하거나 새로고침을 한다고 가정해보자.
- 서버 사이드 확인: 페이지가 로드되기 전 getServerSideProps 함수가 실행되어 사용자의 인증 여부를 확인한다.
- 클라이언트 사이드 확인: 페이지 로드 후, MainNavigation 컴포넌트 내의 useSession 훅이 실행되어 다시 한번 세션 존재 여부를 파악한다.
개발자 도구의 Network 탭을 확인해보면, 페이지 새로고침 시 /api/auth/session으로 요청이 전송되는 것을 볼 수 있다. 이 요청은 useSession 훅이 자동으로 보내는 것이다.
/api/auth/session 요청이 필요한 이유 (보안 및 기술적 배경)
이 요청이 발생하는 이유는 보안상의 제약 때문이다. NextAuth는 세션 관리 방식에 따라 다르게 동작한다:
- JWT 전략 (기본값): 세션 토큰이 암호화된 JWT 형태로 쿠키에 저장된다. 이 쿠키는 보안을 위해 HttpOnly 옵션으로 설정된다.
- Database 전략: 세션 정보는 데이터베이스에 저장되고, 쿠키에는 세션 ID만 저장된다.
- HttpOnly 쿠키의 특성: 브라우저의 자바스크립트(클라이언트 사이드 코드)로는 이 쿠키의 내용을 직접 읽거나 조작할 수 없다.
- 검증 과정: 따라서 클라이언트(브라우저)가 현재 유효한 세션을 가지고 있는지 확인하려면, 쿠키를 포함하여 서버의 엔드포인트(/api/auth/session)로 요청을 보내야 한다. 서버만이 쿠키를 읽고 유효성을 검증한 뒤, "인증됨" 또는 "인증되지 않음"이라는 결과를 클라이언트에 알려줄 수 있다.
이 요청 자체는 잘못된 것이 아니며 필수적인 보안 절차다. 하지만 이미 getServerSideProps에서 인증 확인을 마친 상황이라면, 클라이언트에서 또다시 확인 요청을 보내는 것은 불필요한 중복 요청(Redundant Request)이 된다.
2. Provider를 이용한 최적화
이러한 중복 요청을 방지하고 성능을 최적화하기 위해 NextAuth는 Provider 컴포넌트를 제공한다. 이를 적용하기 위해 모든 페이지 컴포넌트를 감싸고 있는 _app.js 파일을 수정해야 한다.
_app.js 수정 (NextAuth v3 기준)
next-auth/client에서 Provider를 import 하여 전체 애플리케이션을 감싸준다.
import { Provider } from 'next-auth/client';
import Layout from '../components/layout/layout';
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return (
// Provider로 감싸고 pageProps.session을 전달
<Provider session={pageProps.session}>
<Layout>
<Component {...pageProps} />
</Layout>
</Provider>
);
}
export default MyApp;
동작 원리 상세 설명
위 코드에서 Provider 컴포넌트에 session={pageProps.session}을 전달하는 것이 핵심이다.
- 데이터 흐름: _app.js의 MyApp 컴포넌트는 렌더링 될 페이지의 정보(Component)와 해당 페이지의 props(pageProps)를 받는다.
- 서버 사이드 데이터 전달: 프로필 페이지처럼 getServerSideProps를 사용하는 페이지는, 서버에서 이미 세션 검증을 마치고 그 결과(session 객체)를 props에 담아 반환한다. 이 데이터가 pageProps.session에 들어있다.
- 초기화: Provider는 전달받은 pageProps.session 값을 이용해 내부적으로 세션 상태를 즉시 초기화한다.
- 초기 렌더링 최적화: Provider가 이미 유효한 세션 데이터를 가지고 있기 때문에, 하위 컴포넌트(예: MainNavigation)에서 useSession 훅이 호출될 때 초기 로딩 상태 없이 즉시 세션 정보를 사용할 수 있다. 이를 통해 초기 렌더링 시의 불필요한 /api/auth/session 요청을 방지하고 사용자 경험을 개선할 수 있다.
결과 및 이점
- 세션이 있는 페이지 (예: Profile): getServerSideProps에서 가져온 세션이 Provider에 주입되므로, 초기 렌더링 시 추가적인 클라이언트 사이드 확인 요청 없이 즉시 세션 정보를 사용할 수 있다.
- 세션이 없는 페이지 (일반 페이지): pageProps.session은 undefined가 된다. 이 경우 useSession은 평소처럼 동작하여 직접 세션 확인 요청을 보낸다.
이러한 패턴을 적용하면 NextAuth가 내부적으로 최적화를 수행할 수 있게 되며, 초기 로딩 경험을 개선하여 애플리케이션의 성능을 향상시킬 수 있다. NextAuth는 클라이언트와 서버 간 세션 동기화를 위해 백그라운드에서 세션을 재검증할 수 있지만, 초기 렌더링 시의 사용자 경험은 크게 개선된다.
3. NextAuth v4에서의 변경 사항
강의 내용은 NextAuth v3를 기준으로 설명되어 있으나, 최신 버전인 NextAuth v4에서는 컴포넌트 이름과 import 경로가 변경되었다. v4를 사용할 경우 아래와 같이 코드를 작성해야 한다.
주요 변경점
- import 경로가 next-auth/client에서 next-auth/react로 변경되었다.
- Provider 컴포넌트의 이름이 SessionProvider로 변경되었다.
_app.js 수정 (NextAuth v4 기준 예시)
import { SessionProvider } from 'next-auth/react';
import Layout from '../components/layout/layout';
import '../styles/globals.css';
// 구조 분해 할당으로 pageProps에서 session을 명시적으로 추출하여 사용하면 더 명확함
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<SessionProvider session={session}>
<Layout>
<Component {...pageProps} />
</Layout>
</SessionProvider>
);
}
export default MyApp;
v4에서도 동작 원리는 동일하다. getServerSideProps 등을 통해 서버에서 미리 가져온 session 정보를 SessionProvider에 주입함으로써, 클라이언트 로딩 시 발생하는 초기 렌더링의 지연과 불필요한 세션 체크 요청을 방지한다.
4. Next.js App Router에서의 적용
Next.js 13 이상에서 App Router를 사용하는 경우, SessionProvider 적용 방식이 달라진다. App Router는 기본적으로 서버 컴포넌트를 사용하지만, SessionProvider는 클라이언트 컴포넌트이기 때문에 별도의 래퍼 컴포넌트를 만들어야 한다.
4.1 SessionProvider 래퍼 생성
먼저 클라이언트 컴포넌트로 SessionProvider를 감싸는 파일을 생성한다.
// app/providers.js (또는 components/providers.js)
'use client';
import { SessionProvider } from 'next-auth/react';
export function Providers({ children, session }) {
return (
<SessionProvider session={session}>
{children}
</SessionProvider>
);
}
4.2 Root Layout에 적용
app/layout.js에서 위에서 만든 Providers 컴포넌트를 사용한다.
// app/layout.js
import { Providers } from './providers';
import './globals.css';
export default function RootLayout({ children }) {
return (
<html lang="ko">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
4.3 서버 컴포넌트에서 세션 가져오기
App Router에서는 서버 컴포넌트에서 직접 세션을 가져올 수 있다.
// app/profile/page.js
import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
import { redirect } from 'next/navigation';
export default async function ProfilePage() {
const session = await getServerSession(authOptions);
if (!session) {
redirect('/api/auth/signin');
}
return (
<div>
<h1>프로필 페이지</h1>
<p>환영합니다, {session.user.name}님!</p>
</div>
);
}
4.4 클라이언트 컴포넌트에서 세션 사용
클라이언트 컴포넌트에서는 기존과 동일하게 useSession 훅을 사용한다.
// components/UserMenu.js
'use client';
import { useSession, signOut } from 'next-auth/react';
export default function UserMenu() {
const { data: session, status } = useSession();
if (status === 'loading') {
return <div>로딩 중...</div>;
}
if (!session) {
return <a href="/api/auth/signin">로그인</a>;
}
return (
<div>
<span>{session.user.name}</span>
<button onClick={() => signOut()}>로그아웃</button>
</div>
);
}
4.5 서버 세션을 Provider에 전달하기 (고급)
Pages Router처럼 서버에서 가져온 세션을 SessionProvider에 초기값으로 전달하려면, 다음과 같이 구성할 수 있다:
// app/layout.js
import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
import { Providers } from './providers';
import './globals.css';
export default async function RootLayout({ children }) {
const session = await getServerSession(authOptions);
return (
<html lang="ko">
<body>
<Providers session={session}>
{children}
</Providers>
</body>
</html>
);
}
이렇게 하면 Pages Router와 유사하게 서버에서 가져온 세션 정보가 클라이언트의 SessionProvider에 초기값으로 전달되어, 초기 렌더링 시의 불필요한 요청을 최소화할 수 있다.
App Router의 장점
App Router 방식에서는:
- 서버 컴포넌트에서 직접 getServerSession을 호출하여 세션을 가져올 수 있어 더 간단하다
- 클라이언트 번들 크기가 줄어든다 (서버 컴포넌트는 번들에 포함되지 않음)
- 필요한 곳에만 'use client'를 사용하여 클라이언트 사이드 인터랙션을 추가할 수 있다
Pages Router의 getServerSideProps와 달리, App Router에서는 컴포넌트 내에서 직접 비동기 데이터 페칭이 가능하므로 더 직관적인 코드 작성이 가능하다.
'NextJS > NextAuth' 카테고리의 다른 글
| [NextAuth] API 라우트 보호 - 9 (0) | 2025.11.20 |
|---|---|
| [NextAuth] 서버 사이드 페이지 가드 (라우트 보호) 추가하기 - 7 (0) | 2025.11.18 |
| [NextAuth] 클라이언트 사이드 페이지 가드 (라우트 보호) 추가하기 - 6 (0) | 2025.11.18 |
| [NextAuth] 클라이언트 컴포넌트 인증 : 로그인, 로그아웃 -5 (0) | 2025.11.17 |
| [NextAuth] 로그인 API 구현 [...nextauth].js - 4 (0) | 2025.11.16 |