1. 구글 클라이언트 아이디, 시크릿 얻기 (주제에 살짝 벗어나므로 접어두었다.)
1. 새프로젝트 생성
2. API 및 서비스 > OAuth 동의 화면
3. 유저타입에서 ‘외부’ 선택
4. 각 필드에 해당사항 입력
5. 사용자 인증 정보 > 사용자 인증 정보 만들기 > OAuth 클라이언트 ID
일단 로컬에서 돌릴거여서 브라우저 요청에 사용되는 URL은 localhost:3000 으로 하고, 리디렉션 URI는 /api/auth/callback/google 로 해야 정상적으로 작동한다. (약속된 거라고 함)
6. 다하면 뜨는 모달에서 아이디 비밀번호를 env에 담아둔다
2. Next Auth 사용하기
1) 설치
yarn add next-auth 혹은 npm i next-auth
2) Initialization
Next-Auth 공식 문서 바로가기
공식문서에 잘 나와있으니 따라해보면 된다.
pages 폴더에 넣는 방법과, app 폴더에 넣는 방법이 각각 존재하는데,
사용성에 있어서 전혀 다르지 않고, pages폴더에서 진행하는 법은 이전 블로그 때 해보았기 때문에
새로 업데이트된 app폴더에 넣는 방법으로 진행하였다.
/app/api/auth/[...nextauth]/route.ts
아주 폴더 깊숙한 곳에 라우트 파일을 생성해줘야 한다.
// /app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
const handler = NextAuth({
providers: [
// 프로바이더 넣는 곳
GoogleProvider({
clientId: // 발급받은 clientId
clientSecret: // 발급받은 clientSecret
})
],
// 디비에 넣고 싶을 경우만!
// adapter: MongoDBAdapter(connectDB as Promise<MongoClient>)
});
export { handler as GET, handler as POST };
꼭 구글이 아니더라도 공식문서에 보면, 네이버, 카카오, 깃헙, 애플, 디스코드 등등 굉장히 다양하게 있으니 원하는걸로 사용해줘도 되고, 여러개 쓰고싶으면 여러개 배열에 집어넣어서 사용하면 된다!
3) 로그인, 로그아웃, 세션 정보 받아오기
로그인 정보, 로그인 여부에 따라 화면을 다르게 보여주고 싶다면 세션 정보를 받아오면 된다.
구차한 코드를 작성하고 상태관리를 따로 해줄 필요 없이 SessionProvider로 정보를 받아올 컴포넌트만 감싸주면 된다.
export default function AuthSession({ children }: Props) {
return <SessionProvider>{children}</SessionProvider>;
}
모든 페이지에서 가져다 쓰고 싶으면, layout.ts 파일에다가 해주면 되는데 SessionProvider는 클라이언트 렌더링에서만 사용할 수 있기 때문에 별도로 클라이언트 컴포넌트를 만들어서 layout에 넣어주었다.
이제 SessionProvider로 감싸진 컴포넌트에서는 아래와 같이 제공되는 hook을 통해 session 정보를 갖다 쓸 수 있고, 로그인/로그아웃 해서 세션정보가 변경되는 즉시 업데이트되서 따로 뭐 해줄 것도 없다.
import { useSession, signIn, signOut } from 'next-auth/react';
export default function GuestBook () {
const { data: session } = useSession();
...
return (
// 이런식으로 세션이 있을 때는 로그아웃, 없을 때는 로그인 버튼으로 렌더링 하면 바로바로 반영된다.
<>
{
session
? <button onClick={() => signOut()}>로그아웃</button>
: <button onClick={() => signIn('google')}>로그인</button>
}
</>
)
}
signIn, signOut 함수도 제공하는데, 말그대로 로그인, 로그아웃 시켜주는 함수다.
로그인할 때는 signIn(’google’) 처럼 매개변수로 어떤 로그인을 할건지를 넣어주면 되는데,
구글 로그인 버튼을 누르면 구글 로그인 화면을 제공하고, 애플 로그인 버튼을 누르면 애플 로그인 화면을 제공해주고 싶은 경우에 매우 유용하다.
4) 새창으로 로그인 하기
나는 게스트북 이라는 특정 모달에서만 로그인을 사용할 것이기 때문에 모달에 로그인버튼을 만들어줬다.
그랬더니… 로그인 버튼을 누르면 화면 이동이 되면서 모달이 닫혀버렸는데 사용성이 매우 좋지 못하여 로그인 화면은 새창으로 띄워보았다.
1. 로그인 화면 만들기
어차피 구글 로그인 화면만 제공할 것이므로, 렌더링은 아무것도 하지 않고 세션정보만 받아와서
- 로그인 되어 있으면: 창 닫기
- 로그인 되어있지 않으면: 구글 로그인 화면
- 로그인 하고 난 후: (세션정보가 업데이트 되어 로그인이 된 상태이므로 창 닫아짐)
- 팝업으로 열지 않고 브라우저에 url 입력해서 진입할 경우: 루트페이지로 이동
이정도 코드만 작성해주었다.
// app/login/page.ts
'use client'
import { signIn, useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
export default function Login() {
const { data: session } = useSession();
const [ isPopup, setIsPopup ] = useState();
const router = useRouter();
useEffect(() => {
// popup인지 여부 확인
if(!window.opener) {
setIsPopup(false)
} else {
setIsPopup(true)
}
}, [])
useEffect(() =>{
if(isPopup) {
// 1. 팝업인 경우 세션정보를 확인
if(!!session) {
// 2. 로그인 성공 시 닫기
self.close()
} else {
// 3. 세션 없을 경우 로그인
signIn('google')
}
} else {
// 4. 팝업 아닌 경우 루트페이지로
router.push('/')
}
}, [session, isPopup, router])
return null
}
2. 로그인 버튼 클릭 시 로그인 화면으로 새창 띄우기
로그아웃할때 signOut({ redirect: false })} 매겨변수로 {redirect: false} 를 해주어야 페이지가 새로고침 되지 않고, 상태변경만 이루어진다.
import { useSession, signIn, signOut } from 'next-auth/react';
const goToLogin = () => {
// 여기서 로그인 화면 경로 적어주기
window.open('/login', 'window_name', 'width=430, height=500, location=no, status=no, scrollbars=yes')
}
export default function GuestBook () {
const { data: session } = useSession();
...
return (
<>
{
session
? <button onClick={() => signOut({ redirect: false })}>로그아웃</button>
: <button onClick={goToLogin}>로그인</button>
}
</>
)
}