본문 바로가기
[ 프로젝트 ]/여기 여기 붙어라

카카오 로그인 구현하기 [React]

by 디디 ( DD ) 2023. 7. 27.

 

 

  카카오 api를 많이 사용하는 이번 프로젝트의 특성상 카카오 로그인을 먼저 구현하게 되었다. 카카오 로그인이 선행되어야 톡캘린더와 같은 기능을 이용할 수 있기 때문이다. 관련 로직을 이해하고 적용하는 데 거의 2주가 걸린 것 같은데, 물론 모든 시간을 프로젝트에 쏟은 건 아니지만 생각보다 꽤 오래 고생을 했다. 분명 프론트단은 어렵지 않다는 이야기를 들었었는데...! 

 

 

  우선, 카카오에서 제공하는 공식 문서를 찾아보았다. 상당히 꼼꼼하고 친절하게 사용 방법이 안내되어 있었다. 

 

https://developers.kakao.com/docs/latest/ko/kakaologin/common#intro-login-process 

 

Kakao Developers

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

developers.kakao.com

 

해당 문서를 보면, 로그인 과정을 한눈에 볼 수 있는 시퀀스 다이어그램들이 있는데, 그중 우리가 이용할 방식인 REST API의 로직은 아래와 같다. 

 

 

 

  내용이 잘 정리되어 있는 것과는 별개로 처음 이 다이어그램과 Step별 설명을 읽고 나는 한 가지 의문이 들었다. '왜 우리 서버를 거쳐야 하는 거지?'라는 의문이었다. 프론트단에서 로그인 요청을 해서 인가 코드를 받고, 그 인가 코드로 토큰을 발급받아 필요할 때마다 꺼내 이용하면 될 것 같은데 왜 이러한 과정이 필요한 것인지 이해가 되지 않았다. (아마 우리 서비스의 경우 카카오 api 호출용 토큰 발급을 위해 카카오 로그인을 도입한 것이라 이런 고민을 했었던 것 같다.) 관련해서 백단을 맡은 친구와 한참 고민을 해보았는데, 우리의 결론은 '클라이언트와 서버의 역할을 분리할 필요가 있기 때문에 서버를 거치는 과정이 필요하다.'라는 것이었다. 이후에라도 유저 정보를 데이터 베이스에 저장할 필요가 있는 경우, 즉 특정 데이터와 유저 정보가 연결되어야 할 때, 프론트단에서 정보를 받아 서버에 넘겨주는 것은 아무래도 좀 어색하다. 유저의 요청에 따라 데이터를 받아와 화면을 그리는 것은 프론트단에서, 인증 관련 로직과 요청에 맞게 데이터를 관리하고 가공하는 것은 백단에서 맡는다고 한다면, OAuth 서비스 제공자와 소통하는 역할은 백엔드가 맡는 것이 논리적으로 알맞다. 그리고 자체 토큰을 함께 이용하면 카카오 토큰은 프론트단에서 아예 다루지 않을 수 있기 때문에 보안상의 이점도 있다. 

 

 

  그렇게 위 다이어그램 순서에 따라 기능을 구현하기 시작했는데, 다시 막히는 부분이 나왔다. 카카오 로그인 요청을 보내면 화면이 내 손에서 떠나게 되는데 어떻게 유저정보를 받아올 수 있는 건지 알 수가 없었다. 그래서 Redirect URI을 프론트쪽으로 설정해서 인가 코드를 받은 후, 이 인가 코드를 우리 서버에 넘겨주며 유저 정보를 받아올 수 있는 api를 만들기로 했다. 즉, Redirection이라는 (기능만을 위한) 페이지를 만들어 이 페이지로 들어오는 순간 post 요청을 보내도록 만들어 준 것이다. 

 

참고로 아래의 코드가 내가 작성한 Redirection 페이지의 코드이다. URL에서 인가 코드를 추출하는 부분과 useEffect를 이용해 페이지로 들어오는 순간 유저 정보를 받아오는 요청을 보내는 부분을 중점적으로 보면 된다.  

import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { setUserInfo } from "../store";

import { getUserInfo } from "../api/user";
import decodeJwtToken from "../utils/decodeJwtToken";

export default function Redirection() {
  const navigate = useNavigate();
  const dispatch = useDispatch();

  // URL에서 인가 코드 추출
  const code = new URL(window.location.href).searchParams.get("code");

  // 인가 코드 전달 후 (우리 서비스) 토큰 받기
  useEffect(() => {
    getUserInfo(code).then((result) => {
      if (result !== "fail") {
        // 엑세스 토큰 저장
        const accessToken = result.headers.authorization;
        sessionStorage.setItem("accessToken", accessToken);
        // 엑세스 토큰을 디코딩하여 유저 정보 저장
        const userInfo = decodeJwtToken(accessToken);
        dispatch(setUserInfo(JSON.stringify(userInfo)));
        // 리프레시 토큰 저장
        const refreshToken = result.headers.refreshtoken;
        sessionStorage.setItem("refreshToken", refreshToken);
      }
      if (result === "fail") {
        alert("로그인에 실패했습니다. 잠시 후 다시 시도해 주시기 바랍니다.");
      }
      // 이후 페이지 이동
      if (sessionStorage.getItem("cardId")) {
        navigate(`/card/${sessionStorage.getItem("cardId")}`);
      } else {
        navigate("/");
      }
    });
  }, []);

  return <div>로그인 처리 중입니다.</div>;
}

 

 

  그리고 아래 그림은 우리가 카카오 로그인을 구현한 방식을 설명한 시퀀스 다이어그램이다.  

 

(1) 먼저, 로그인 버튼을 누르면,

 `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`

유저를 이 주소로 보내준다. 나는 window.location.href를 이용하였다. (직접 GET 요청을 보내도 됨)

 

client_id[내 애플리케이션] > [앱 키]에서 REST API 키를 확인하여 넣어주면 되고, redirect_uri[내 애플리케이션] > [카카오 로그인] > [Redirect URI]에 등록한 후 넣어주면 된다. 

 

여기서 우리는 Redirect URI를 내가 만든 Redirection 페이지의 주소, 즉 프론트쪽으로 설정하였다.

(예시: http://localhost:3000/login/kakao)

 

 

(2) (3) 그러면 카카오 동의 화면이 출력되고, 유저가 동의 과정을 진행할 수 있게 된다. 기존에 동의 과정을 거친 사용자는 이 과정이 생략된다.

 

 

(4) 유저의 동의 정보를 확인한 카카오가 인가 코드를 전달해 주면, (내가 설정한 Redirect URI 뒤로 인가 코드를 붙여서 보내줌)

 

 

(5) 받은 인가 코드를 서버에 보내주고, 

 

 

(6) (7) 서버에서 전달받은 인가 코드를 이용해 카카오 토큰을 발급받는다. 

 

 

(8) (9) 그다음 카카오 토큰을 이용해 사용자 정보를 받아서 클라이언트로 전달해 주면, (우리 서비스에서 쓸 토큰과 함께)

 

 

(10) 유저 정보와 우리 서비스용 토큰을 받은 클라이언트에서 로그인 처리를 해준다.  

 

 

이후 카카오 토큰이 필요한 요청의 경우 진행 과정은 

이런 식으로 이루어지게 된다. 

 

 

 

 

 

(+) 정리하면서 깨닫게 된 것이 있는데, 우리가 공식 문서의 '카카오 로그인 요청' 부분을 조금 헷갈렸었던 것 같다. 나는 카카오 로그인 요청이 인가 코드 발급 요청과 같은 것인 줄 알았는데, 우리 서버로의 요청을 의미하는 것이었다. 즉, 우리 서비스의 api를 이용해 서버로 로그인 요청을 보내면, 서버에서 카카오로 인가 코드 발급 요청을 보내는 것이 바로 아래 카카오 시퀀스 다이어그램의 의미였던 것이다. 이렇게 되면 앞서 내가 고민했었던 '카카오 로그인 요청을 보내면 화면이 내 손에서 떠나게 되는데 어떻게 유저정보를 받아올 수 있는 건지'에 대한 것도 해결이 된다. 굳이 따로 Redirection 페이지를 만들지 않아도 되는 것이다. 결국 카카오에서 제시하는 방법대로 한다면, 클라이언트에서는 서버로 로그인 요청을 보내고, 그 응답을 받는 부분만 구현하면 되므로 자체 서비스 로그인 로직과 크게 다를 것이 없다.  

 

큰 노란 동그라미는 우리 서버와 클라이언트가 소통하는 부분을 표시한 것

 

 

 

(+) 리액트 strict 모드를 꺼야 개발 모드에서도 카카오 로그인 과정을 테스트할 수 있다. strict 모드가 켜져 있는 경우, 카카오 토큰 발급 시 성공 메시지와 에러 메시지가 함께 오는 기현상을 겪게 되는데, 같은 인가 코드로 두 번 요청이 가서 생기는 현상이다. 

 

 

 

(+) 추가적으로 로그인 후 원래 있었던 페이지로 돌아갈 방법에 대해 고민을 했었는데, 되돌아갈 필요가 있는 페이지의 경우 세션 스토리지에 정보를 저장해두고, 로그인 로직이 완료된 후 이 정보를 이용해 페이지 이동을 시켜주는 방법을 사용하였다. 

 

 

댓글