Trouble Shooting

1 / 1

에러 해결 - 카카오 로그인 KOE320 Error

오류 내용

카카오 소셜 로그인 구현 후 테스트하던 중 다음과 같은 오류가 발생했습니다. 로그인 구현 과정은 다음과 같습니다.

  1. 사용자가 로그인 버튼을 누르면 카카오 로그인 페이지로 이동
  2. 카카오 로그인 완료 시 카카오 서버에서 redirect uri로 인가 코드 발급
  3. 전달 받은 인가 코드를 백엔드 서버로 전송
  4. 백엔드 서버에서는 전달 받은 인가 코드로 카카오 서버에 Access Token 발급 요청, 유저 정보 받아오기
  5. DB 확인해서 회원가입 / 로그인 절차 마친 후에 앱 내 JWT 토큰 발급
  6. 로그인 성공 시 홈으로 이동

하지만 작동 테스트를 위해 쿠키를 지우면서 로그인을 시도했을 때, 몇가지 이상한 점을 발견했습니다.

  • 서버에서 status 500 에러를 반환한다.
  • 에러 상세에는 카카오 서버의 KOE320 Error 라고 나와있다. (동일한 인가 코드를 두 번 이상 사용하거나, 이미 만료된 인가 코드를 사용한 경우, 혹은 인가 코드를 찾을 수 없는 경우)
  • 하지만 에러가 났음에도 서버로부터 JWT 토큰을 받았다. (로그인에는 성공했다)
  • 디버깅을 위해 백엔드 코드에서 오류가 난 시점 이후 콘솔에 찍어봤을 때 정상적으로 나온다.

해결

// /login/kakao-callback
const KakaoCallback: NextPage = () => {
  const router = useRouter();
  const { code, error } = router.query;
  if (typeof code === 'string') {
    (async () => {
      const response = await fetchLoginRequestToServer(code); // 서버에 로그인 요청
      if (response.status === 200) {
        alert('로그인에 성공했습니다.');
        router.push('/');
      }
    })();
  } else {
    // 에러 처리
  }

  return <div></div>;
};

export default KakaoCallback;

서버에 로그인을 요청하는 함수를 useEffect가 아닌 컴포넌트 도입부에 작성했던 것이 문제였습니다. 컴포넌트가 마운팅 되는 것을 기다리지 않고 빠르게 로그인 작업을 수행하기 위해 useEffect 내부에 작성하지 않았던 것인데, 이것 때문에 컴포넌트가 재랜더링 되면서 로그인 요청을 한 번 더 보내는 원인이 되었습니다.

따라서 처음 로그인 요청 시에는 성공했지만 두번째 요청에는 처음과 같은 인가코드를 사용했기 때문에 KOE320 에러가 났던 것입니다.

아래와 같이 코드를 수정하니 오류가 해결되었습니다.

const KakaoCallback: NextPage = () => {
  const router = useRouter();
  const { code, error } = router.query;

  useEffect(() => {
    if (typeof code === 'string') {
      (async () => {
        const response = await fetchLoginRequestToServer(code); // 서버에 로그인 요청
        if (response.status === 200) {
          alert('로그인에 성공했습니다.');
          router.push('/');
        }
      })();
    } else {
      // 에러 처리
    }
  });

  return <div></div>;
};

export default KakaoCallback;

컴포넌트가 다시 한 번 렌더링된 이유

그렇다면 KakaoCallback 컴포넌트가 두 번 렌더링된 이유는 무엇일까요?

next.config.js 파일에는 다음과 같이 설정되어 있습니다.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

module.exports = nextConfig;

공식문서에 따르면 reactStrictMode는 애플리케이션 내의 잠재적인 문제를 알아내기 위한 도구입니다. reactStrictMode는 개발 모드에서만 활성화되며 다음의 검사를 실시합니다.

  • 안전하지 않은 생명주기를 사용하는 컴포넌트 발견
  • 레거시 문자열 ref 사용에 대한 경고
  • 권장되지 않는 findDOMNode 사용에 대한 경고
  • 예상치 못한 부작용 검사
  • 레거시 context API 검사

따라서 이번 경우에는 앱 내에 잠재된 문제를 검사하는 과정에서 컴포넌트가 다시 한 번 렌더링되었고 결국 예상치 못한 부작용을 찾아낼 수 있었습니다.