
나름대로 오랜 시간 고민한, 회원가입 프로세스에 대해 단계별로 정리하고자 합니다. 회원가입 프로세스는 단순한 기능을 넘어 UX, State Management, 보안, API 연동까지 복합적인 상황에 대한 이해를 요구합니다. OAuth와 Twilio SMS까지 반영한 전체 코드의 흐름을 설명하겠습니다.

회원가입 페이지에 위치한 구글 버튼을 클릭하면 동작하는 함수입니다. provider라는 매개변수를 통해, 제공자가 google 인지 naver 인지에 대한 분기 처리를 진행합니다.
구글 버튼을 클릭했으니, 3000/auth/google/login 경로로 사용자가 이동하게 됩니다.

3000/auth/google/login 경로에 도착하면 결국 getGoogleAuthUrl() 함수를 실행하게 됩니다. @Redirect() 데코레이터는 사용자를 특정 url로 이동시킬 때 사용합니다.

구글 서버의 경로에, clientId와 callbackUrl을 설정합니다. 유효한 구글 클라이언트임이 확인되면, 미리 설정한 callbackUrl로 사용자를 이동시킵니다. 최종적으로 해당 url에서 인가 코드를 받을 수 있게 됩니다.
인증은 대상이 누구인지 인지하는 절차라면, 인가는 확인된 대상이 유효한 권한이 있는지 확인하는 절차입니다.
신분증 확인이 되었다면 인증 절차는 통과한 것입니다. 그런데 고등학생도 신분증이 발급되죠. 즉 술 또는 담배와 같은 청소년 유해 상품은 구매할 수 없습니다. 인증은 되었으나 인가가 되었다고 보기는 어려운 상황입니다. 궁극적으로 인가 코드는 access token을 얻기 위한 전 단계입니다. 권한 행사에 사용되는 열쇠가 access token이라고 볼 수 있습니다.

5173/redirect, 즉 프론트엔드로 redirect된 후에 인가 코드와 provider를 통해 3000/auth/google/user로 액세스 토큰 발급 요청을 위한 post 요청을 진행합니다.
RedirectPage에서 위 프로세스를 진행하는 이유는, 응답값을 받기 위함에 있습니다. 요청에 대한 응답은, 요청을 진행한 곳에 반환됩니다. 현재의 요청은 액세스 토큰에 대한 요청이지만, 최종적으로 백엔드 내부의 로직을 거쳐 반환되는 응답은 사용자 정보입니다. 사용자 정보가 프론트엔드로 반환되어야 해당 정보를 가공해서 UI/UX를 개발할 수 있겠지요.

구글에서 지정한 경로로, 인가 코드를 포함한 여러 정보에 대한 post 요청을 진행합니다. 해당 요청이 성공하면 response.data를 반환하게 됩니다.

response.data는 tokens 변수에 저장됩니다. tokens 내부, 그러니까 response.data의 내부에는 access_token이 있습니다.
액세스 토큰은 권한 행사에 사용되는 열쇠라고 비유했습니다. 유저 정보를 얻어야 하겠으니 이제 열쇠(엑세스 토큰)를 구글에 전달하면 되겠습니다.

이번에는 사용자 정보를 얻기 위해 액세스 토큰을 구글 서버에 전달했습니다. 해당 요청이 성공했을 때 반환하는 response.data는 사용자 정보에 해당하겠지요.

이번에는 userData라는 변수에 response.data를 저장했습니다.
구글 회원가입 버튼을 클릭했으나, 이미 구글 provider를 통해 가입한 회원일 수 있습니다.

데이터베이스에서 이미 구글을 통해 등록된 유저가 있는지 찾아봅니다. 여기서 주목해야 할 속성은 isExist와 registrationComplete입니다.
데이터베이스에 유저 정보가 있다면, 이미 존재하는 유저임을 명시하기 위해 isExist 속성을 true로 변경해 줍니다.
한 가지 경우가 더 있습니다. 구글로 회원가입 절차를 진행하다가 최종적인 가입까지는 하지 않은 유저가 있을 수 있습니다. 해당 유저를 구분하기 위해 registrationComplete 속성을 추가합니다.

이미 기존에 가입한 회원이라면, 해당 회원의 정보를 existingUser 변수에 저장합니다. 조건문을 통해 isExist가 true라는 점까지 확실하게 확인한 뒤 해당 유저 정보를 반환합니다.
그리고 프론트엔드에서는 현재 사용자를 로그인 페이지로 이동시킵니다. 당신은 이미 구글로 가입된 유저이기 때문에, 로그인을 하는 것이 맞다고 알려주는 절차라고 볼 수 있습니다.

신규 유저라면, 해당 유저 정보를 google_user 테이블에 저장할 필요가 있습니다.

컨트롤러에서 신규 유저는 createUser() 함수로 전달하고 있는 모습을 확인할 수 있습니다. 이때는 프론트엔드로 신규 유저 정보를 전달합니다.

이제 RedirectPage에는 기존 유저든, 신규 유저든 유저 정보가 반환되었을 것입니다.
이때 1)구글 가입 절차를 한 번도 진행한 적 없는 유저이거나, 2)가입 절차 도중 포기했던 유저는 /phone 경로로 이동시킵니다. 휴대폰 인증 절차를 진행해야 하기 때문입니다.

사용자는 이제 UI를 통해 본인의 휴대전화 번호를 입력하게 됩니다. 휴대전화 번호를 입력한 뒤 버튼을 클릭하면, 3000/api/send-code 경로로 post 요청이 진행됩니다.

현재 인증 절차에 대한 정보를 데이터베이스에 저장합니다. SMS가 클라이언트에게 도달하면, 클라이언트는 해당 verification 번호를 다시 입력할 텐데, 입력한 정보가 저장된 정보와 일치하는지 판단하기 위해서는 당연히 '저장된 정보'가 있어야 할 것이기 때문입니다.

저장 로직이 마무리되면, Twilio라는 외부 서비스에 SMS 발급에 대한 요청을 수행합니다. 해당 로직을 통해 클라이언트는 verification code에 대한 문자를 받게 됩니다.

문자를 받은 사용자는, 해당 verification code를 입력하게 됩니다.

데이터베이스에 존재하는 인증 절차 정보의 인증 코드와 사용자가 전송한 인증 코드가 일치한다면 verified 속성을 true로 변경합니다.

유저는 이제 회원가입 완료하기 버튼을 클릭할 수 있게 됩니다.
해당 버튼을 클릭하면, 이전에 언급했던 registrationComplete 속성이 true가 되며 최종 유저 데이터베이스에 저장됩니다.
기존 가입자든, 신규 가입자든 그 끝은 결국 /sign-in입니다. 즉 로그인 페이지로 도착하기까지의 과정(=회원가입)을 마무리했습니다.
로그인 과정은 HTTP가 Stateless 하다는 것을 인지하는 것에서 시작합니다. JWT의 등장 배경은 세션 방식에 의존하고 있습니다. 따라서, 로그인에 대한 처리는 세션 방식(Session Authentication)으로 먼저 진행하려 합니다. 세션 ID를 쿠키에 담는 방식을 먼저 해보는 것이 궁극적으로 JWT를 잘 이해하는 길임을, 지금까지의 학습을 통해 너무나도 잘 알고 있습니다.