logo

DowanKim

12. 로그인, 회원가입 authorization code 재사용 문제

2025년 10월 4일

이게머니

TODO : 회원가입 api 구현

회원가입에 대한 현재 api 문서(swagger)는 다음과 같이 작성되어 있습니다.

image.png

회원가입 시에 authorization code와 input의 닉네임을 전달하면 됩니다.

이에 로직 구상을 다음과 같이 했습니다.

현재 로직은 이러합니다

로그인 → 신규 유저라서 401 오류 발생 → 회원가입 페이지(닉네임 설정)

이에 처음에는 다음과 같이 구상하였습니다.

  1. 처음 로그인 때 사용한 authorization code를 로컬 스토리지에 잠시 저장
  2. 이때 정상 로그인 되면 스토리지에서 삭제
  3. 401 오류 뜨면 다시 회원가입 Post할때 authorization code 재사용

처럼 생각하고, 실제로 구현도 진행을 했습니다.

다만 오류가 뜨고, 백엔드 로그인 기능 관리자에게 연락해 왜 문제가 생기는지 모르겠다.. 라고 하였고 둘이 얘기하며 순간 authorization code 재사용을 하면 안된다는 것을 망각하고 정말 대놓고 재사용하고 있는 문제를 알 수 있었습니다.

이에 백엔드 개발자와 얘기하며, 결국에는 로그인 실패 이후 회원가입 때도 카카오로그인을 다시한번 하는 로직이 필수임을 파악하였습니다. 다만, 개발 과정에서 한번 로컬호스트를 키고 처음 카카오 로그인(이메일 입력, 비번입력)을 하고 나서 그 이후로 다시 작업을 할때는 자동 로그인이 되는 것을 알고 있기에, 사용자의 ux 측면에서 다시 로그인을 한다는 것이 ux를 해치지는 않는다고 판단하였습니다.

결과적으로, KakaoCallbackPage에서, 회원가입인지 로그인인지 판단하고 다르게 로직을 진행하는 콜백페이지 내 분기처리가 필요하다고 생각하였습니다.

해결

1. 캐릭터 생성 페이지

회원가입 로직은, 로그인에서 401이 뜨고 캐릭터 생성 페이지로 이동함으로써 시작합니다.

const handleConfirm = async () => { try { localStorage.setItem('temp_nickname', name); localStorage.setItem('needRegister', 'true'); console.log('닉네임 저장:', name); const loginUrl = getKakaoLoginUrl(); window.location.href = loginUrl; } catch (error) { console.error('에러:', error); } };

캐릭터 생성페이지에서 기존에 상태값트로 가지던 name을 바탕으로, 확인 버튼을 누를 때 일단 localStorage에 temp_nickname이라는 키로 닉네임을 저장합니다. 또한 needRegister라는 키로 true를 저장합니다.

그리고 getKaKaoLoginUrl()을 이용해 카카오 로그인 으로 로직이 이동하게 진행합니다.

2. KaKaoCallBackPage

const handleLogin = async (authorizationCode: string) => { if (isProcessing.current) { return; } try { isProcessing.current = true; setStatus('loading'); setMessage('로그인 처리 중...'); const savedNickname = localStorage.getItem('temp_nickname'); const needRegister = localStorage.getItem('needRegister'); if (needRegister === 'true' && savedNickname) { const result = await registerWithCode({ code: authorizationCode, nickname: savedNickname, }); if (result.accessToken) { setAccessToken(result.accessToken, 7); localStorage.setItem('userId', 'temp-user-id'); localStorage.removeItem('temp_nickname'); localStorage.removeItem('needRegister'); setStatus('success'); setMessage('회원가입이 완료되었습니다!'); timeout.current = setTimeout(() => { navigate('/home'); }, 2000); } } else { const result = await loginWithCode(authorizationCode); setStatus('success'); setMessage('로그인이 완료되었습니다!'); if (result.accessToken) { setAccessToken(result.accessToken, 7); localStorage.setItem('userId', 'temp-user-id'); } timeout.current = setTimeout(() => { navigate('/home'); }, 3000); } isProcessing.current = false; processedCode.current = null; } catch (error) { isProcessing.current = false; processedCode.current = null; const axiosError = error as AxiosError; if (axiosError?.response?.status === 401) { setStatus('success'); setMessage('새로운 계정이 생성되었습니다!'); timeout.current = setTimeout(() => { navigate('/character-create'); }, 2000); } else { handleError('로그인에 실패했습니다. 다시 시도해주세요.', true, error); } } };

kakaoCallbackPage를 이제 분기처리 합니다.

흐름을 설명하면, 앞서 저희는 로컬스토리지에 nickname을 설정하고 카카오 로그인을 다시 진행했습니다.

이때, 기존 로직처럼 다시 authorization code를 받아오고,

이때 로컬스토리지에서 temp_nickname과 needRegister를 찾습니다. 둘다 찾는것에 성공하면, 이 상황은 회원가입 상황이므로 회원가입 api를 진행합니다. 그리고 응답을 받아오는것에 성공하면 로컬스토리지에서 temp_nickname과 needRegister를 삭제합니다.

결과

그렇게 회원가입을 진행하고 백엔드에게 연락해 디비에 제가 정상적으로 유저로 가입되었는지 물어보았습니다.

image.png

image.png

드디어 저희 서비스에 첫 사용자가 생겼습니다.

이후 다시 끄고 로그인이 (기존 로그인이므로 바로 200 떠야함) 잘 실행되는지 확인해보았습니다.

200을 받아오며 로그인도 잘 됩니다.