안동민 개발노트

안동민 개발노트

Auth.js 설정로그인과 로그아웃보호된 라우트역할 기반 접근 제어
본문 시작
  1. 홈
  2. 문서
  3. Next.js
  4. 10장 : 인증 및 권한 관리
  5. 로그인과 로그아웃
  1. Next.js
  2. 로그인과 로그아웃

로그인과 로그아웃

Auth.js 서버 액션과 클라이언트 API로 로그인·로그아웃 UI를 구현하고 세션 경계를 구분합니다.

인증 설정을 마쳤다면 사용자가 로그인과 로그아웃을 시작할 수 있는 화면을 만듭니다.

Auth.js에서는 서버 액션과 클라이언트 API 두 방식이 있습니다.

기본 선택은 서버 액션입니다.

브라우저 상태에 따라 즉시 바뀌는 UI가 필요할 때만 클라이언트 API를 사용합니다.


서버 액션으로 로그인하기

src/auth.ts에서 내보낸 signIn을 서버 액션에서 호출합니다.

src/components/sign-in-button.tsx
import { signIn } from '@/auth';

export function SignInButton() {
  return (
    <form
      action={async () => {
        'use server';
        await signIn('github', { redirectTo: '/account' });
      }}
    >
      <button type="submit">GitHub로 로그인</button>
    </form>
  );
}

버튼을 누르면 서버 액션이 GitHub 인증 흐름을 시작합니다.

인증이 끝나면 redirectTo에 지정한 /account로 이동합니다.

기본 redirect 콜백은 상대 경로와 같은 출처의 URL로 이동을 제한합니다. 이 예제는 고정된 내부 경로를 사용합니다. 목적지를 입력으로 받거나 콜백을 바꿀 때도 허용할 내부 경로를 명시적으로 제한합니다.


서버 액션으로 로그아웃하기

로그아웃도 중앙 인증 모듈의 signOut을 호출합니다.

src/components/sign-out-button.tsx
import { signOut } from '@/auth';

export function SignOutButton() {
  return (
    <form
      action={async () => {
        'use server';
        await signOut({ redirectTo: '/' });
      }}
    >
      <button type="submit">로그아웃</button>
    </form>
  );
}

signOut()은 현재 브라우저의 세션 쿠키를 지우고 지정한 경로로 이동합니다. 앞 절의 JWT 전략에서 이 동작이 이미 복사된 토큰까지 서버에서 일괄 폐기하는 것은 아닙니다.

브라우저 저장소의 값을 직접 지우는 방식으로 세션을 흉내 내지 않습니다.


세션에 따라 버튼 바꾸기

서버 컴포넌트에서 세션을 읽으면 브라우저가 별도로 세션을 조회하기 전에 알맞은 버튼을 서버 렌더 결과에 포함할 수 있습니다. 서버의 세션 조회 자체에는 대기 시간이 있을 수 있습니다.

src/components/auth-menu.tsx
import { auth } from '@/auth';
import { SignInButton } from './sign-in-button';
import { SignOutButton } from './sign-out-button';

export async function AuthMenu() {
  const session = await auth();

  if (!session?.user) {
    return <SignInButton />;
  }

  return (
    <div>
      <span>{session.user.name ?? session.user.email}</span>
      <SignOutButton />
    </div>
  );
}

사용자 이름이 null 또는 undefined이면 이메일을 사용합니다. 두 필드 모두 없거나 빈 문자열인 경우까지 표시 이름을 보장하는 코드는 아닙니다.


클라이언트 API 사용하기

모달 안에서 로그인 버튼을 제어하거나 세션 갱신 상태를 바로 보여줘야 한다면 클라이언트 API가 필요할 수 있습니다.

src/components/client-auth-button.tsx
'use client';

import { signIn, signOut, useSession } from 'next-auth/react';

export function ClientAuthButton() {
  const { data: session, status } = useSession();

  if (status === 'loading') {
    return <button disabled>확인 중</button>;
  }

  if (session?.user) {
    return <button onClick={() => signOut({ redirectTo: '/' })}>로그아웃</button>;
  }

  return <button onClick={() => signIn('github', { redirectTo: '/account' })}>로그인</button>;
}

이 방식은 상위 트리에 SessionProvider가 있어야 합니다.

클라이언트의 세션 상태는 화면 표현을 위한 값입니다.

데이터 저장이나 관리자 기능 같은 보안 판단은 서버에서 다시 검사해야 합니다.


로그인 실패 다루기

OAuth 공급자가 오류를 반환하거나 사용자가 동의를 취소할 수 있습니다.

/login 페이지를 직접 만든 경우 설정에 경로를 지정합니다. 다음은 설정 추가 예시이며 기존 NextAuth, GitHub import와 필요한 세션·콜백 설정은 유지합니다. 경로를 설정하는 것만으로 페이지 파일이 생성되지는 않습니다.

src/auth.ts
export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [GitHub],
  pages: {
    signIn: '/login',
    error: '/login',
  },
});

오류 화면에는 공급자의 원문 오류나 비밀 정보를 그대로 노출하지 않습니다.

사용자에게는 다시 시도할 방법을 안내하고, 상세 원인은 서버 로그에서 확인합니다.


구현 기준

화면에서 버튼을 숨겼다는 사실만으로 권한이 보호되지는 않습니다.

다음 절에서는 auth()를 서버 경계에 적용해 실제 라우트를 보호합니다.

Auth.js 설정

이전 페이지

보호된 라우트

다음 페이지

이 페이지의 목차

서버 액션으로 로그인하기서버 액션으로 로그아웃하기세션에 따라 버튼 바꾸기클라이언트 API 사용하기로그인 실패 다루기구현 기준