12. 로그인, 회원가입 authorization code 재사용 문제
2025년 10월 4일
TODO : 회원가입 api 구현
회원가입에 대한 현재 api 문서(swagger)는 다음과 같이 작성되어 있습니다.

회원가입 시에 authorization code와 input의 닉네임을 전달하면 됩니다.
이에 로직 구상을 다음과 같이 했습니다.
현재 로직은 이러합니다
로그인 → 신규 유저라서 401 오류 발생 → 회원가입 페이지(닉네임 설정)
이에 처음에는 다음과 같이 구상하였습니다.
- 처음 로그인 때 사용한 authorization code를 로컬 스토리지에 잠시 저장
- 이때 정상 로그인 되면 스토리지에서 삭제
- 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를 삭제합니다.
결과
그렇게 회원가입을 진행하고 백엔드에게 연락해 디비에 제가 정상적으로 유저로 가입되었는지 물어보았습니다.


드디어 저희 서비스에 첫 사용자가 생겼습니다.
이후 다시 끄고 로그인이 (기존 로그인이므로 바로 200 떠야함) 잘 실행되는지 확인해보았습니다.
200을 받아오며 로그인도 잘 됩니다.