Next 22장 - 소셜 로그인 (2) kakao
포스트
취소

Next 22장 - 소셜 로그인 (2) kakao

Next

next-auth

참고자료


  • 간편한 소셜 로그인을 위한 라이브러리이다.
  • 이전 게시글에서 작성했던 kakao APIcode, access token을 사용하여 해당 유저의 정보를 가져오는 로직을 단순화하게 사용할 수 있다.
  • 또한 구글, 애플, 네이버, 카카오 등 라이브러리가 지원하는 auth가 많기 때문에 사용이 간편하다.
  • 유저의 정보를 받아와 session에 저장하고 이를 사용하여 유저의 정보를 활용한다.

사용법

1
npm i next-auth
  • next-authsession을 활용하기 떄문에, 이를 전 범위적으로 사용할 Provider라는 개념이 존재한다.
  • 또한 각 social 로그인마다 로직을 간편하게 줄여줄 Provider가 존재하며 이 두 가지 개념은 다르다.
  • 우선 카카오의 Redirect URI라는 개념이 존재하듯, next-auth에도 이 개념이 존재한다.
  • page route를 활용한 방식이기 때문에 주솟값은 api/auth/callback/kakao(사용할 소셜 로그인 ex: kakao)로 고정된다.
  • 카카오의 Redirect URI에 해당하는 부분에 본인의 도메인과 next-auth의 주솟값을 작성한다.
    1
    
    http://localhost:3000/api/auth/callback/kakao
    
  • Next 13 버전은 pages 디렉토리에서 app으로 바뀌었기 때문에, 버전에 따라 […nextauth].ts 안에 작성하는지, app/api/auth/[…nextauth]/route.ts 를 작성하는지가 나뉜다.

Next auth Provider

  • 파일을 생성했다면 라이브러리에서 제공하는 NextAuth를 활용하여 provider를 작성한다.
  • NextAuth는 요청과 응답을 담당한다.
  • 각 사용하려는 social login에 맞는 Provider를 불러와 발급받은 REST API값과 클라이언트를 확인할 ID를 작성한다.
  • 카카오의 경우 clientId로는 REST API 키와 clientSecret으로는 [ 내 애플리케이션 > 앱 설정 > 요약 정보 > 기본 정보 ] 에 해당하는 앱 ID 를 작성한다.
    • 타입 스크립트의 경우 타입 검사를 하기 때문에 undefined error를 방지하고자 타입 단언을 사용했다.

1
2
3
4
5
6
7
8
9
10
11
12
// api/auth/[...nextauth].ts
import NextAuth from "next-auth";
import KakaoProvider from "next-auth/providers/kakao";

export default NextAuth({
  providers: [
    KakaoProvider({
      clientId: process.env.KAKAO_CLIENT_ID!,
      clientSecret: process.env.KAKAO_CLIENT_SECRET!,
    }),
  ],
});

Credentials Provider

  • 로그인을 하거나 회원 가입을 할 때, form에 작성한 사용자 입력 정보를 서버에 전송하듯이 동작한다.
  • label, name, type 등의 res 값으로 지정한 서버 주소로 전송하는 역할을 한다.
  • 마찬가지로 여려가지 옵션을 사용하거나 page, authorize 할 수 있다.
  • 경우에 따라 headers, body 를 작성하거나 작성한 값을 그대로 전송해도 된다.
1
2
3
4
5
6
7
8
9
10
11
12
export default function Credentials<
  C extends Record<string, CredentialInput> = Record<string, CredentialInput>
>(options: UserCredentialsConfig<C>): CredentialsConfig<C> {
  return {
    id: "credentials",
    name: "Credentials",
    type: "credentials",
    credentials: {} as any,
    authorize: () => null,
    options,
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// api/auth/[...nextauth].ts
import NextAuth from "next-auth";
import KakaoProvider from "next-auth/providers/kakao";
import CredentialsProvider from "next-auth/providers/credentials";

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: "example", //여기에 작성한 name으로 signIn을 보냅니다.
      credentials: {
        username: {
          label: "이메일",
          type: "text",
          placeholder: "이메일 주소 입력 요망",
        },
        password: { label: "비밀번호", type: "password" },
      },

      async authorize(credentials, req) {
        const res = await fetch(`${process.env.NEXTAUTH_URL}/api/login`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            username: credentials?.username,
            password: credentials?.password,
          }),
        });
        const user = await res.json();
        console.log(user);

        if (user) {
          // Any object returned will be saved in `user` property of the JWT
          return user;
        } else {
          // If you return null then an error will be displayed advising the user to check their details.
          return null;

          // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
        }
      },
    })
    ,
    KakaoProvider({
      clientId: process.env.KAKAO_CLIENT_ID!,
      clientSecret: process.env.KAKAO_CLIENT_SECRET!,
    }),
  ],
});

credentials => signIn

  • 위에서 작성한 credential name example을 이용하여 지정한 주소로 작성한 메일과 비밀번호를 전송하여 로그인 처리를 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Login.tsx
'use client'
...
import { signIn } from "next-auth/react";

export default function Login(){
    const mailRef = useRef<HTMLInputElement>(null);
    const passRef = useRef<HTMLInputElement>(null);

    const signInHandler = async () => {
        const result = await signIn("credentials", {
            username: mailRef.current,
            password: passRef.current,
            redirect: true,
            callbackUrl: "/",
        });
    }

    return(
        <React.Fragment>
            <input id="mail" type="text" placeholder="이메일 입력" ref={mailRef}/>
            <input id="pass" type="password" placeholder="비밀번호 입력" ref={mailRef}/>
            <button type="button" onClick={signInHandler}>전송</button>
        </React>
    )
}

Session Provider

  • 유저의 요청에 next-auth의 실행 결과는 session에 담긴다.
  • 담긴 session을 사용하기 위해, redux, recoil... 등의 전역 상태 라이브러리와 동일하게 Provider로 제공해주어야 한다.
  • Provider로 제공하고 나면 다른 컴포넌트에서 useSession을 사용해 저장된 회원 정보로 접근이 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// _app.tsx
import type { AppProps } from "next/app";
import { SessionProvider } from "next-auth/react";

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}: AppProps) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
// example.tsx
import { useSession } from "next-auth/react";

export default function Example(){
    const {data: session} = useSession();

    return(
        <React.Fragment>
            {data && <image src={data.profileImg}/>}
        </React>
    )
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.