NextAuth는 다양한 소셜 로그인 서비스를 지원하는 라이브러리로, 이를 통해 사용자 인증 기능을 손쉽게 구현할 수 있습니다.
하지만 Next.js가 13 버전으로 업데이트 되면서 최신 버전에 대한 업데이트가 이뤄지지 않아 최신 버전에서 NextAuth를 사용하는 방법에 대해 정리하고자 합니다.
먼저, 소셜 로그인 연동을 위해서는 clientId와 clientSecret가 필요합니다. 해당 키는 연동할 소셜의 개발자 콘솔에서 발급 받을 수 있습니다.
카카오 : https://developers.kakao.com/console/app
다음으로, Redirect URI을 설정해 주어야합니다. Redirect URI는 다음과 같은 형식을 따라야합니다. 해당 경로 또한 개발자 콘솔에서 설정할 수 있습니다.
[origin]/api/auth/callback/[provider]
ex. http://localhost:3000/api/auth/callback/google
위의 준비가 완료되었으면 이제 Next.js 애플리케이션에 NextAuth를 설치하기 위해 다음 명령어를 실행합니다.
npm install next-auth
또는
yarn add next-auth
공식문서를 확인해보면 pages/api/auth/[...nextauth].js 파일을 생성하도록 작성되어 있지만 Next.js 13버전에서는 구조가 변경되었기 때문에 다음과 같은 경로에 작성 합니다.
/app/api/auth/[...nextauth]/route.ts
구글 로그인 예시
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
export const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
// ...add more providers here
],
}
export default NextAuth(authOptions)
NextAuth에서 제공하는 useSession의 user에는 id가 없기 때문에 id 값을 사용하기 위해선 임의로 추가해주어야 합니다.
jwt 토큰으로 생성된 id 값을 user의 id에 저장합니다.
...
export const authOptions: NextAuthOptions = {
...
callbacks: {
async session({ session, token }) {
const user = session?.user;
if (user) {
session.user = {
...user,
id: token.id as string
}
}
return session
},
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
}
},
}
...
그럼 id에 type 오류가 발생하게 되는데, nextauth에 설정된 session 타입에 id가 지정되어있지 않기 때문이다. next-auth.d.ts 파일을 만들고 기본 타입을 변경해주도록 합니다.
// src/types/next-auth.d.ts
import { User } from '@/model/user';
declare module 'next-auth' {
interface Session {
user: User
}
}
// src/model/user.ts
export type User = {
id: string;
name: string;
email: string;
image?: string;
}
그럼 오류가 사라지고 session 데이터에 id값이 추가된 것을 확인할 수 있습니다.
모든 컴포넌트에서 로그인과 관련된 유저 정보를 받아올 수 있게 하기 위해 최상위에 SessionProvider를 감싸야한다. Next.js 13버전의 최상위는 layout.tsx 파일이지만 Provider는 상태를 가지고 있기 때문에 SSG에서는 사용할 수 없다.
layout.tsx는 SSG로 사용하기 위해서 별도의 새로운 context를 만들어 준다.
// src/context/AuthContext.tsx
'use client';
import { SessionProvider } from "next-auth/react"
type Props = {
children: React.ReactNode;
}
export default function AuthContext({ children }: Props) {
return <SessionProvider>{children}</SessionProvider>
}
그다음, layout.tsx를 만들어둔 AuthContext로 감싸준다.
// src/app/layout.tsx
import AuthContext from '@/context/AuthContext'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<AuthContext>
{children}
</AuthContext>
</body>
</html>
)
}
NextAuth에서 제공해주는 함수와 커스텀 Hook을 사용하여 손쉽게 사용자 화면에 로그인 기능을 연동할 수 있다.
import { useSession, signIn, signOut } from "next-auth/react"
export default function Component() {
const { data: session } = useSession()
if (session) {
return (
<>
Signed in as {session.user.email} <br />
<button onClick={() => signOut()}>Sign out</button>
</>
)
}
return (
<>
Not signed in <br />
<button onClick={() => signIn()}>Sign in</button>
</>
)
}
간단하게 소셜 로그인이 연동된 것을 확인할 수 있다.