[React]카카오 REST API 로그인_TS

2021. 9. 17. 16:00Web_Programming/React

 

소셜 로그인 중 가장 대표적으로 사용되는 것이 카카오 로그인입니다.

개발 문서도 자세히 나와있어서, 저도 카카오 로그인을 택했습니다 

다만 보안 강화를 위해서 JWT를 추가적으로 이용하였습니다 :-D


카카오 로그인

 

토큰

Access Token    사용자를 인증합니다. Android, iOS : 12시간
JavaScript: 2 시간
REST API : 6시간
Refresh Token 일정 기간 동안 다시 인증 절차를 거치지 않고도 액세스 토큰 발급을 받을 수 있게 해 줍니다. 2달
유효기간 1달 남은 시점부터 갱신 가능

 

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com


로그인 구현

토큰을 이용한 로그인의 전체적인 흐름은 다음과 같습니다.

  1. 로그인 ➞ 인가코드 발급
  2. 인가 코드 ➞ 토큰 발급(access, refresh)
  3. access 토큰을 서버로 넘겨 사용자 인증 후 DB 저장 ➞ JWT 토큰 발급
  4. JWT 토큰 localStorage에 저장 후, 자동 로그인 처리

+ 초기 로그인 시에만, 카카오 user정보를 받고 나머지는 JWT토큰을 이용하였기 때문에 access 토큰만 사용하였습니다.

 

💡 로그인 버튼 생성

+ 로그인 버튼은 kakao Dev 사이트에서 제공되고 있습니다.

 

로그인 버튼의 url은 에 아래와 같이 설정해줍니다.

`https://kauth.kakao.com/oauth/authorize?client_id=${내 애플리케이션 REST KEY}&redirect_uri=${카카오에서 설정한 redirect URI}&response_type=code`
import * as React from 'react';
const Login = () => {
  const kauthUrl=`https://kauth.kakao.com/oauth/authorize?client_id=${process.env.REACT_APP_KAKAO_REST_KEY}&redirect_uri=${카카오에서 설정한 redirect URI}&response_type=code`

  return (
	<Link href={kauthUrl}>
        <Image
        src="/assets/kakao_login.png"
        id="kakao-login-btn"
        width="250"
        height="100"
        alt="kakao"
        />
    </Link>
  );
};

 

💡 코드 받기

로그인 버튼 클릭 후, 인증이 제대로 되었다면, redirect URI 파라미터로 코드값이 생성됩니다.

 

code : 토큰 받기 요청에 필요한 인가 코드

 

인가 코드값만 얻어오기 위해 아래와 같이 전처리를 해줍니다.

query-string 패키지를 이용하여 쿼리를 decode 합니다.

const query = queryString.parse(window.location.search);

 

💡 토큰 받기

redirect로 넘어온 url 파라미터 code값이 있을 경우 👉 getKakaoTokenHandler를 통해 토큰을 발급합니다.

const Login = () => {
  const kauthUrl=`https://kauth.kakao.com/oauth/authorize?client_id=${process.env.REACT_APP_KAKAO_REST_KEY}&redirect_uri=http://localhost:3000/auth&response_type=code`
  const query = queryString.parse(window.location.search);
  
  React.useEffect(() => {  
    if (query.code) {
      getKakaoTokenHandler(query.code.toString()); 
    }
  }, [query.code]);
  
  const getKakaoTokenHandler = async (code:string) => {
  	try{
        const data:any = {
          grant_type: "authorization_code",
          client_id: process.env.REACT_APP_KAKAO_REST_KEY,
          redirect_uri: "redirect URI 입력",
          code: code
        };
        const queryString = Object.keys(data)
          .map((k:any)=> encodeURIComponent(k) + '=' + encodeURIComponent(data[k]))
          .join('&');

        //토큰 발급 REST API
        const {data} = await axios.post('https://kauth.kakao.com/oauth/token', queryString, {
          headers: {
            'Content-type': 'application/x-www-form-urlencoded;charset=utf-8'
          }
        }
        //서버에 전달
        sendKakaoTokenToServer(data.access_token)
    }catch(e){
    	console.error(e)
    }
    
  }
  return (
	<Link href={kauthUrl}>
        <img src="kakao_login.png" id="kakao-login-btn" width="250px" />
    </Link>
  );
};

 

💡 JWT 토큰 받기

발급받은 access 토큰을 서버로 넘기고, 서버에서 JWT 토큰 값(추가로 user정보)을 받아 localstorage에 저장해둡니다.

  const sendKakaoTokenToServer = async (token:string ) => {
    const res = await axios.post('/auth/kakao',{access_token: token})
    if (res.status == 201 || res.status == 200) {
      const user =res.data.user;
      window.localStorage.setItem("token", JSON.stringify({
        access_token: res.data.jwt
      })); 
    }else {
      window.alert("로그인에 실패하였습니다.");
    }  
  }

 

이렇게 JWT 토큰 까지 받았다면,

😉 로그인 완료 😉

 

+ 서버 측 카카오 로그인

 

[Node.js] 카카오 로그인&JWT

이전에 [리액트_ts를 이용하여, 카카오 로그인 구현]에 대해 포스팅했습니다. https://keeper.tistory.com/47 [React]카카오 REST API 로그인_TS 소셜 로그인 중 가장 대표적으로 사용되는 것이 카카오 로그인

keeper.tistory.com

 

 

+ (보완) Next 13, react-query 사용시 로그인 hooks 코드 예시

import { userInfoAtoms } from "@app/GlobalProvider";
import { useMutation } from "@tanstack/react-query";
import axios from "axios";
import { useSetAtom } from "jotai";
import { useResetAtom } from "jotai/utils";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect } from "react";

const useAuthKakao = () => {
  const router = useRouter();

  const searchParams = useSearchParams();
  const query = searchParams?.get("code");

  const setUserAtom = useSetAtom(userInfoAtoms.userAtom);
  const resetUserAtom = useResetAtom(userInfoAtoms.userAtom);

  const getKakaoToken = useMutation({
    mutationFn: async (code: string) => {
      const data = {
        grant_type: "authorization_code",
        client_id: process.env.NEXT_PUBLIC_KAKAO_REST_KEY || "",
        redirect_uri: "http://localhost:3000/auth/login",
        code,
      };
      const queryString = Object.keys(data)
        .map((key) => `${key}=${data[key as keyof typeof data]}`)
        .join("&");
      return await axios.post(
        "https://kauth.kakao.com/oauth/token",
        queryString,
        {
          headers: {
            "Content-type": "application/x-www-form-urlencoded;charset=utf-8",
          },
        }
      );
    },
    onSuccess: (res: any) => {
      putKakaoTokenToServer.mutate(res.data.access_token);
    },
    retry: false,
  });

  const putKakaoTokenToServer = useMutation({
    mutationFn: async (token: string) => {
      return await axios.put("/api/auth/kakao", {
        access_token: token,
      });
    },
    onSuccess: (res: any) => {
      const { jwt, user } = res.data;

      if (res.status == 201 || res.status == 200) {
        setUserAtom(user);
        window.localStorage.setItem(
          "token",
          JSON.stringify({
            access_token: jwt,
          })
        );
        axios.defaults.headers.common["Authorization"] = `${jwt}`;
        router.push("/home");
      } else {
        window.alert("로그인에 실패하였습니다.");
        resetUserAtom();
      }
    },
  });

  useEffect(() => {
    if (query) {
      getKakaoToken.mutate(query);
    }
  }, [query]);
  return {
    isLoading: getKakaoToken.isLoading || putKakaoTokenToServer.isLoading,
  };
};

export default useAuthKakao;
반응형