이번 주는 실전 프로젝트가 본격적으로 시작되는 한 주였다. 실질적으로 처음으로 다른 사람들과 함께 개발을 진행해본 것이다. 지금까지 혼자서 이것저것 만져가며 주먹구구식으로 해왔던 것들이 얼마나 문제가 있었던 것인지 알게 되었다. 지금까지는 그냥 과제가 있으면 정해진 것들을 보고, 그 활용 방식을 찾아보거나 강의를 통해 습득한 후에 내가 이해한 바를 검증하는 과정에 불과했던 것 같기까지 하다. 실전프로젝트는 정말로 내가 원하는 것을 만들어보는 경험을 하며 내가 원하는 것을 진짜로 구현해보는 경험이라는 것에서 전혀 별개의 경험인 것 같다.
내가 지금까지 꺠달은 팀 프로젝트에 대해서 말해보자면. 프로젝트는 최초에 원하는 것을 구상하고, 이것을 점점 더 구체화시켜 나가며 시작된다. 서비스에 대해서 명확하고 명료한 하나의 목적을 가지게 되면, 그것을 구현하기 위해서 다시 조금씩 살을 붙여나가기 시작한다. 이런 과정을 거쳐 서비스가 다룰 영역에 대해서 확정되면, 구체화된 서비스가 요구하는 기능을 구현하기 시작한다. 이 때, 백엔드의 입장에서는 각 기능에 맞춰 효율적이라고 생각되는 구조로 DB를 먼저 설계하고, API를 설계한다. API를 설계할 때에는 비즈니스 로직에 대해서 명확하게 이해하고 있어야 하며, 각각의 API는 모두 각각의 이유가 있어야 하며, 그렇지 않으면 목적이 흐려진다는 걸 배운 것 같다. 이런 논의 과정은 생각보다 매끄럽게 진행되기 힘들었다. 실제로 실전 프로젝트를 진행하면서 회의에 소요한 시간이 거의 만 하루는 되는 것 같다.
이렇게 구현할 서비스를 구체화하는 데에 성공했다면, 그 때부터는 실제로 구현에 들어갈 시간이다. 나는 이번 프로젝트에서 우선 로그인과 회원가입 기능을 맡았다. 로그인은 소셜 로그인을 이용하였으며, 카카오, 구글, 깃허브 총 3가지 소셜 로그인 지원 업체를 이용하기로 하였다. 나는 검색 능력이 부족하여 각 업체들이 제공하는 API들의 URI를 찾는 데에 많은 고생을 했다. 각 업체에서 제공하는 문서의 링크는 다음과 같다. 단, 이는 모두 웹서버 기준이다.
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#get-token-info
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps
Authorizing OAuth Apps - GitHub Docs
GitHub's OAuth implementation supports the standard authorization code grant type and the OAuth 2.0 Device Authorization Grant for apps that don't have access to a web browser. If you want to skip authorizing your app in the standard way, such as when test
docs.github.com
https://developers.google.com/identity/protocols/oauth2/web-server
웹 서버 애플리케이션에 OAuth 2.0 사용 | Google ID 플랫폼 | Google Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 웹 서버 애플리케이션에 OAuth 2.0 사용 이 문서에서는 웹 서버 애플리케이션이 Google API 클라이언트 라이브러리 또는 Google OAuth
developers.google.com

내가 구현한 소셜 로그인이 진행되는 로직은 위 그림과 같다. 다만, 헷갈릴 수 있는 부분은 2번 인증 코드 전달과 3번 인증 코드 전송이 사실상 동시에 진행된다는 것이다. 소셜 로그인을 구현하기 위해서는, 소셜 로그인을 제공하는 서비스 업체에 나의 웹사이트의 호스트 이름과 redirect uri를 등록하여야 한다. 이것을 토대로 소셜 로그인 제공 업체는 사용자가 로그인을 한 후, 해당 uri로 되돌려보낼 수 있는 것이다. 핵심은, 여기서 이 uri와 호스트를 모두 백엔드 서버로 지정하는 것이다. 이렇게 하면, 유저가 로그인을 하는 순간, 그것이 백엔드 서버로 GET 요청을 보내는 것이 되며, 이렇게 GET 요청이 들어갈 때, 인가 코드를 함께 전송하여 백엔드 서버에서 이것으로 소셜 로그인 서비스 제공 업체와 상호작용을 할 수 있는 것이다. 이런 작동 원리를 적용한 이유는 구글에 있었다. 카카오와 깃허브 소셜 로그인을 구현할 때에는, 클라이언트에서 인가 코드를 받은 후, POST 요청으로 백엔드 서버에 이것을 전송하기로 했었는데, 구글에서 문제가 생겼다. redirect uri mismatch, invalid grant 등의 온갖 에러가 튀어나오기 시작한 것이다. 몇시간 정도 에러에 시달리며 의심하게 된 문제는 바로 인가코드를 요청하기로 되어있는 등록된 Host와 토큰을 요청하는 Host가 상이하여 문제가 발생하는 것이었다. 실제로 최초 서술하였던 방식으로 작동 방식을 바꾼 후에는 문제가 발생하지 않았다.
로그인과 회원가입을 구현하는 일은 여전히 끝내지 못했고, 쉽지 않은 일인 것 같다. 소셜 로그인을 구현함에 있어 단순히 해당 서비스 제공 업체에서 유저에 대한 정보를 받아오는 것이 끝이 아니고, 오히려 시작이라는 것을 알게 되었다. 해당 유저가 로그인을 유지하는 것부터가 난관이었는데, 바로 처음으로 리프레시 토큰과 엑세스 토큰을 함께 이용하게 되면서 고민거리가 늘어났다. 정확히 언제 엑세스 토큰을 이용해야 하는가? 언제 리프레시 토큰을 이용해야 하는가? 정확히 언제 이것들을 발급하고 정확히 언제 클라이언트에 전달하는가? 등등의 고민거리가 존재했다. 내가 구체화시켰다고 생각한 프로젝트는 다시금 모호한 안개처럼 느껴졌다.
그래도 한단계 한단계 밟아가면서 앞으로 나아가고 있는 기분이다. 프로젝트가 진행되면 될수록 기록과 가독성, 그리고 깔끔한 정리가 왜 필요한 것인지 깨닫고 있다. 사람은, 최소한 나는 망각의 동물이며 어제 한 말도 까먹고, 방금 작성한 코드도 새로우며 구글링을 하면서 검색 기록에서 정확히 같은 주제를 보는 일이 잦은 그런 존재라는 것을 알게 되었다. 이럴 때 내가 해야 할 일과 구현해야 할 서비스에 대해서 최대한 빠르게 그러나 자세하게 읽을 수 있는 정리된 파일이 없다면, 잘못된 방식으로 API를 구현할 수 있고, 이 과정에서 너무 많은 시간을 낭비하게 된다. 이런 깨달음을 뒤늦게 얻고 정리한 소셜 로그인과 관련된 로직은 다음과 같다.
👇소셜 로그인 작동 원리
- 사용자가 소셜 로그인을 위한 URL을 클릭한다.
- 이 URL은 소셜 로그인 기능을 제공하는 업체의 로그인 페이지이며, 사용자가 정상적으로 로그인한 경우, 기입된 redirect URI로 사용자를 리디렉션 시켜주며, Query parameter의 형태로 인가 코드를 함께 전송한다.
- 기입된 URI는 백엔드 서버로 되어있다.
- 사용자는 기입된 redirect URI에 따라 백엔드 서버로 요청을 보내게 되며, 백엔드 서버에서 인가 코드를 인식한다.
- 벡엔드 서버에서는 인식된 인가 코드를 바탕으로, 각 업체에 맞는 방식으로 access 토큰을 요청한다.
- 백엔드 서버에서 요청 결과로 받은 access 토큰을 이용하여 사용자 정보를 조회한다.
- 백엔드 서버에서 전달받은 사용자 정보를 바탕으로, 사용자를 3가지 케이스로 분류한다.
- 우리 사이트에 최초로 로그인 한 경우
- 우리 사이트에 최초로 로그인 한 경우, 임의의 UUID를 userID로하여 DB에 등록한 후, access 토큰을 발급한다.
- 이는 소셜 로그인 서비스 제공 업체에서 제공한 사용자 정보를 외부로 노출시키지 않기 위함이다.
- 우리 사이트에 최초로 로그인 한 경우, 임의의 UUID를 userID로하여 DB에 등록한 후, access 토큰을 발급한다.
- 우리 사이트에서 소셜 로그인은 진행하였지만, 사용자 상세 정보를 작성하지 않은 경우
- 우리 사이트에 소셜 로그인을 진행하여 임의의 UUID로 userID와 소셜 로그인 서비스 제공 업체에서 제공받은 사용자 정보가 등록되어 있지만, 우리 사이트에 유저 상세 정보가 작성되어 있지 않은 경우
- 유저 상세 정보를 작성할 때, nickname은 필수적으로 입력되고, DB에 저장되는 값이다.
- 따라서, nickname이 존재하지 않는다면, 상세 정보를 작성하지 않은 유저로 판단한다.
- 이 경우, 상세 정보 작성에 이용할 수 있도록 access 토큰을 발급한다.
- 우리 사이트에 소셜 로그인을 진행하여 임의의 UUID로 userID와 소셜 로그인 서비스 제공 업체에서 제공받은 사용자 정보가 등록되어 있지만, 우리 사이트에 유저 상세 정보가 작성되어 있지 않은 경우
- 우리 사이트에서 사용자 상세 정보를 제공한 경우
- 우리 사이트에 유저 상세 정보가 저장되어 있는 경우, 기존 사용자로 판단하여 유저가 작성한 userID를 이용해 access 토큰과 refresh 토큰을 발급한다.
- 우리 사이트에 최초로 로그인 한 경우
👇모든 요청에 대해서 access 토큰을 함께 전송한다.
- access 토큰이 변조된 경우
- 서버가 사용자를 특정할 수 없으므로, 403 error를 발생시킨다.
- errorMessage: Access Token Maliciously Modified
- 기대하는 반응 - 사용자를 로그아웃 시키고, 모든 사용자 정보를 제거한다.
- access 토큰이 만료되었을 경우
- 서버의 응답
- 서버가 사용자를 특정할 수 있으므로, 401 error를 발생시킨다.
- errorMessage: Access Token Expired
- 기대하는 반응 - 클리어인트가 access 토큰과 refresh 토큰을 함께 담아서 요청한다.
- 서버의 응답
- access 토큰이 정상적인 경우
- 서버가 들어온 요청에 대해서 정상적으로 처리한다.
- access 토큰을 새로 발급하여 전송한다.
- 이 때, response의 header에 authorization : Bearer ${accessToken}의 형식으로 전송한다.
- 유저의 정보에 대해서 함께 전송한다.
- 이 때, response의 body에 컬럼명과 그 정보를 담아서 전송하며, 전송할 정보에 대해서는 추후 협의를 통해 결정한다.
👇access 토큰이 만료되면 Refresh 토큰과 함게 다시 요청을 전송한다.
- refresh 토큰이 변조된 경우
- 서버가 사용자를 특정할 수 없으므로, 403 error를 발생시킨다.
- errorMesage: Refresh Token Maliciously Modified
- 기대하는 반응 - 사용자를 로그아웃 시키고, 모든 사용자 정보를 제거한다.
- refresh 토큰이 만료되었을 경우
- 서버가 사용자를 특정할 수 있으므로, 401 error를 발생시킨다.
- errorMessage: Refresh Token Expired
- 기대하는 반응 - 사용자를 로그아웃 시키고, 모든 사용자 정보를 제거한다.
- refresh 토큰이 정상정긴 경우
- access 토큰이 정상적인 경우와 마찬가지로 반응한다.
👇추가적으로 고려하고 있는 사항들
- access 토큰을 발급할 때, 토큰이 expire될 때까지 남은 시간을 함께 전송하여, 클라이언트에서 토큰이 만료되기 전에 미리 갱신을 요청하고, 갱신받을 수 있도록 하여 유저 편의성을 극대화시킨다.
- 기대 효과
- 유저가 임의적으로 요청을 보낼 때, access 토큰이 만료되어 있는 경우가 발생하지 않게 되어 요청이 서버를 한번 더 거칠 필요가 없어지고, 따라서 유저가 더 빠른 시간 안에 적절한 응답을 받을 수 있다.
- 기대 효과
'항해 99' 카테고리의 다른 글
항해 99 11주차를 끝내며 (0) | 2022.03.28 |
---|---|
항해 99 10주차를 끝내며 (0) | 2022.03.21 |
항해 99 8주차를 끝내며 (0) | 2022.03.07 |
항해 99 7주차를 끝내며 (0) | 2022.02.28 |
항해 99 6주차를 끝내며 (0) | 2022.02.20 |