어느정도 구조 파악이 됐으니 구글 소셜 로그인을 구현해보자.
next-auth
next-auth는 next 전용 라이브러리로 인증 로직을 보다 쉽게 구현할 수 있도록 해준다. 마치 django의 drf 같은 녀석인가??
설치부터 해주자.
> yarn add next-auth
공식문서를 확인해보면 API route를 만들어주기 위해 pages/api/auth/[...nextauth].js 에 위와 같이 설정할 수 있다고 나와있다.
하지만 지금 나는 Next 13 버전을 쓰고 있으므로 nextjs의 공식문서를 확인해보면 app/api 디렉토리 아래의 route 파일에서 api 경로를 처리할 수 있다고 나와있다.
https://nextjs.org/docs/app/building-your-application/routing/router-handlers
위 그림을 보면 이해할 수 있다.
여기서 Rounting files Convention에 있던걸 생각하면, 가변 동적 라우팅을 사용하면 더욱 편하게 작업할 수 있다.
가변 동적 라우팅은 [...folder]를 의미하는데, 때에 따라 크기가 달라지는 배열을 받고 싶다면, pages/post/[...slug].js 와 같이 경로를 구성하면 된다. slug 는 [...param] 과 같이 원하는 이름으로 바꾸어도 된다. (예를들면 api/user/id)
/app/api/auth/[...nextauth]/route.ts
route.ts 파일에 아래와 같이 원하는 소셜 로그인의 Provider의 초기값을 설정할 수 있다.
client id, secret key를 만드는 법은 wontube때도 간단하게 다뤘고, 잘 모르겠다면 구글링 해주세요~
(참고, redirect_uri는 승인된 리디렉션 URI에 http://localhost:3000/api/auth/callback/google를 넣어주면 된다)
import NextAuth, { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
const handler = NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET_KEY,
}),
],
});
export { handler as GET, handler as POST };
dotenv에 설정되있는 환경변수들을 자동완성으로 사용하고싶다면 아래 파일을 root 디렉토리에 추가하면 된다.
environment.d.ts
declare namespace NodeJS {
export interface ProcessEnv {
readonly GOOGLE_CLIENT_ID: string;
readonly GOOGLE_CLIENT_SECRET_KEY: string;
}
}
다음으로 환경 변수 설정이 더 필요한데, NEXTAUTH_SECRET, NEXTAUTH_URL이 필요하다. 배포할때 필요한거 같깉한데.. 미리 해두자.
NEXTAUTH_SECRET=
NEXTAUTH_URL=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET_KEY=
dotenv 파일에 이런식으로 환경변수를 구성해주면 된다.
NEXTAUTH_SECRET은 터미널에서 $ openssl rand -base64 32 이 명령어로 쉽게 만들 수 있다.
SessionProvider
위 과정이 완료되었으면 이제 SessionProvider를 이용하여 프로젝트에 뿌려주면 되는데, next-auth에 들어있는 SessionProvider는 client 코드에서만 사용할 수 있다. 그럴때 파일 최상단에 'use client'라고 적어주면되는데 RootLayout에는 metadat 설정 같은 서버에서 사용하는 코드들 때문에 'use client'를 사용할 수 없다. 따라서 AuthProvider라는 클라이언트 컴포넌트를 만들어서 사용해야 한다.
'src/components/provider/AuthProvider.tsx'
"use client";
import React, { ReactNode } from "react";
import { SessionProvider } from "next-auth/react";
interface AuthProviderProps {
children: ReactNode;
}
const AuthProvider = ({ children }: AuthProviderProps) => {
return <SessionProvider>{children}</SessionProvider>;
};
export default AuthProvider;
'src/app/layout.tsx'
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "@/components/Header";
import AuthProvider from "@/components/provider/AuthProvider";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "chat-app",
description: "Generated by create next app",
};
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Header />
<AuthProvider>{children}</AuthProvider>
</body>
</html>
);
}
그런 다음 session 값이 필요한 클라이언트 컴포넌트에서 session 값이 잘 들어오는지 확인해보자.
"use client";
import { useSession } from "next-auth/react";
export default function Home() {
const session = useSession();
console.log(session);
return <main>Home</main>;
}
위와 같이 useSession을 이용하면 src/app/api/auth/[...nextauth]/route.ts 에서 설정한 값들을 사용할 수 있다.
useSession은 클라이언트 컴포넌트에서만 사용할 수 있다.
콘솔을 확인해보면 다음과 같이 나온다.
로그인 행동을 취하지 않았기 때문에 unauthenticated가 나오는거고 잘 작동하는 듯하다.
로컬로 작업시 환경변수에 설정을 안해주면 에러가난다.
NEXTAUTH_URL=http://localhost:3000
LoginButton
로그인 버튼을 만들기 위해서 login 페이지를 만들어주고, 아래와 같이 코드를 작성해주자.
src/app/(auth)/login/page.tsx
"use client";
import { signIn, useSession } from "next-auth/react";
import React from "react";
const Login = () => {
const session = useSession();
console.log(session);
return (
<div>
<button onClick={() => signIn("google")}>signin with Google</button>
</div>
);
};
export default Login;
따로 콜백 처리도 없고 토큰 처리도 필요 없다. 너무 간편하지 않은가...
로그인 하기 전
로그인 후
로그아웃
예전에 WONTUBE 하면서 직접 로그인 요청부터 토큰 받는것까지 구현해봤었지만, 그 뒤에 youtube api를 받아오는거에 있어서 헤메다가 결국 라이브러리를 썻던 기억이... ㅠ
직접 로그인 요청부터 다 짜봐도 좋겠지만, next-auth를 공부하는것도 재밌다.
https://next-auth.js.org/providers/google