개발

OAuth 회원가입, 로그인 유지를 어떻게 해야할까?

whiporithm 2023. 11. 6. 04:43

 


 

OAuth

부스트캠프 학습 스프린트중에 OAuth를 구현할 일이 있었다. 요새 서비스라면 왠만하면 지원하는 기능인데, 나는 처음 구현해 보았다. 우선적으로 학습을 하고 구현을 하기로 했다. 학습에는 생활코딩 강의가 큰 도움이 되었으며 이후에 내 나름대로 흐름을 정리해보았다.

 

https://opentutorials.org/course/3405

 

WEB2 - OAuth 2.0 - 생활코딩

수업소개 사용자가 가입된 서비스의 API에 접근하기 위해서는 사용자로부터 권한을 위임 받아야 합니다. 이 때 사용자의 패스워드 없이도 권한을 위임 받을 수 있는 방법이 필요합니다. 이를 위

opentutorials.org

 

 

조금 부정확 할 순 있으나 학습후에 나름대로 정리해서 이해를 높이려 했다.

 

 

그렇게 학습후에 간단하게 GitHub 기반 OAuth 로그인을 구현했다.

 

그런데 예상치 못한 문제가 있었고, 그건 "로그인 유지" 였다.

 

로그인 유지

OAuth 로 로그인 하게 된다면 GitHub과같은 Resoure Server에서 토큰을 내려준다. 난 처음에는 이 토큰을 활용하여 로그인 유지를 구현해야 하나? 라고 생각했다. 만약 이를 활용하여 로그인 유지를 한다고 가정하면,

 

- 받아온 토큰을 클라이언트에게 전달함

- 클라이언트는 권한이 필요한 요청에 토큰을 전달함

- 서버는 받은 토큰을 통하여 Resource Server의 특정 API를 통하여 이 토큰이 유효한지 검증함.

- 유효하다면 서비스 제공

 

이런 흐름일거라 생각했다. 

 

근데 이렇게 되면 문제가 몇 개 발생했는데,

 

- Resource Server가 여러개일시 Token이 어느 Resource Server 것인지 기억해야함 (구글, 깃헙, 카카오톡 ... 종류가 많아지면 구분해주어야 한다.)

- 토큰을 검증하기 위해서 서비스, Resource Server 두번의 요청이 발생하므로 부하가 크다.

 

이런 문제들이 발생했다. 특히 2번 문제가 맘에 걸렸는데, 로그인만 체킹하기 위해서 Resource Server로 요청을 보내는게 맞는가 싶었다.

 

로그인은 서비스의 JWT로!

애초에 OAuth의 목적은 특정 서비스에서 Resoucre Server의 API를 사용할 수 있도록 하는것이다. 따라서 내려주는 Token은 Resource Server로의 API 요청이 필요할때 사용하는 것이지, 내 서비스의 로그인 유지에 사용하는게 적절치 않다고 생각했다. 따라서 로그인 유지는 서비스에서 자체적으로 내려주는 JWT를 통해서 유지하는게 좋겠다고 생각했다.

 

회원가입

OAuth를 처음 기능만 구현했을땐 "회원가입" 의 개념이 머릿속에 없었다. 그러나 OAuth로 첫 로그인을 한다면 이는 "회원가입" 과 동일한 행동이라고 생각했고 db에 정보를 저장해주기로 했다.

 

1차적인 구조

 

일반 로그인

 

우선 OAuth 로그인 같은 경우가 아니라, 일반 로그인이라면 JWT를 사용한다.

 

 

OAuth 로그인

 

만약 OAuth라면 위와같이 동작하도록 설계했다.

 

- OAuth로 로그인을 한다

- 만약 DB에 관련 정보가 없다면 이는 회원가입으로 처리해서 DB에 데이터를 저장해준다.

- DB에 저장할 정보는 Resoucre Server에서 내려준 Token으로 API를 요청해 정보를 가져와서 사용한다.

   (중복 문제가 발생할 수 있으나 일단 이는 배제하고 진행했다.)

- userId 같은 경우에는 Resoucre Server의 id를, provider 는 Resoucre Server를 명시했다.

- Resource Server에서 내려준 AccessToken 을 저장했고, 필요시에 꺼내서 사용할려고 저장했다.

  (RefreshToken을 명시하긴 했으나 사용하진 않았다. 여기서는 무시해도 된다.)

 

 

위와 같이 구조를 설계하니 일반 로그인과 OAuth로그인을 구분할 수 있었고, 문제였던 로그인 유지도 잘 할 수 있었다. 그러나 문제가 있었는데 OAuth로 로그인할 경우에는 userId는 별명이라고 대체할 수 있으나, password가 null인것이였다.

 

password를 비워두는게 맞나?

위 테이블 구조는 명확하지가 않다. OAuth로 회원가입 하게 된다면 password가 비게 된다. userId는 Resoure Server에서 가져온 값으로 대체할 수 있다고 하더라도 password는 존재하지 않는다. 애초에 검증은 Resource Server에서 하게 되니 우리쪽에서 password를 저장할 일이 없는것이다.

password 부분을 null로 처리해야했다.

 

반대로 일반 회원가입(서비스 자체 회원가입)을 하게 된다면 AccessToken 필드를 사용할 일도 없다. 해당 필드는 OAuth에서 내려준 Token 을 저장하기 위하여 만든 필드고, Resource Server로 API 요청이 필요할때 꺼내서 사용하기 위한 목적으로 만들어졌다. 따라서 일반 유저는 필요없는 필드이다.

 

 

Token필드를 nullable 하는건 큰 문제가 있을까? 하다고 생각했으나 비밀번호를 nullable하게 두는건 마음에 들지 않았다. 따라서 구조나 기능을 바꿀 필요가 있었다.

 

혼자 고민도 해보고 질문도 해보며, 결론적으로 두가지 방법으로 추렸다.

 

두 가지의 경우

테이블 하나로 관리

우선 위와같이 테이블 하나로 관리하려면 어떤 추가적인 기능이 필요할까?

OAuth로 첫 로그인(회원가입)을 하게 된다면 회원가입 페이지를 추가적으로 제공해서 userId나 password를 명시받게 구현하는 것이다. 실제로 첫 소셜 로그인을 했는데 회원가입 페이지로 리다이렉트 되는 기억이 나도 많았다. 이런식으로 구현하게 된다면 password가 null이 되는 경우 없이 한 테이블로 관리가 가능할 것이다.

 

그러나 나는 이 흐름이 정말 싫다. 물론 이 과정을 한 번 거치면 로그인이야 편하겠지만, 회원가입을 결국 시킨다는 그 느낌이 싫고, 사용자 입장에서 항상 귀찮았다. 따라서 이 방법은 접었다.

 

테이블 분리

위와 같이 password가 명시되어있는 서비스 회원 테이블과, OAuth로 회원가입 하는 경우의 테이블을 구분하면 null로 되는 문제를 막을 수 있다. 처음에 이 생각을 했으나, 테이블이 늘어나기 때문에 서비스내의 로직이 늘어난다는 점이 맘에 걸려 구현하지 않았다. 그러나 테이블이 명확한 목적을 가지는것이 맞다고 생각했고, 확장성에 있어서는 테이블 분리가 필수라고 느꼈기에 이 방식을 채택하였다.

 

최종

아주 간단하게 구현하여 아래와 같은 구조로 구현했다.

 

 

두 테이블로 나누어서 역할을 확실하게 만들었다.

 

지금은 굉장히 빈약하기에 보완점을 추가적으로 말해보자면,

 

위에는 github_member라고 테이블을 만들었으나 구글이나 카카오 등 여러 OAuth를 구현하게 된다면 provider와 같은 필드를 추가하여 어느 Resource Server의 회원인지 구분하면 될 것이다.

 

그리고 위에서 잠시 언급했던 userId나 회원 닉네임에 대한 중복이 발생할 수 있는데 이는 OAuth를 통한 회원가입 이후 추가적인 정보를 받아 검증을 통해 중복을 피할 수 있겠다. OAuth를 통한 회원가입 이후 정보를 받는다는게 조금 껄끄럽다고 위에서 말했으나, 닉네임과 같이 커스텀한 부분은 입력이 필수 불가결인 거 같다고 생각했다.

 

참고

ERDCloud에 OKKY 사이트의 ERD가 공개되어 있는데 여기도 테이블을 분리해서 사용하고 있었다.

 

 

user테이블과 oauthid를 구분해서 저장하는 걸 확인할 수 있다. 또한 OAuth 로 회원가입을 해보니,

 

 

위와 같이 닉네임 정도를 유저에게 입력받고 있었다. 개인적으로 닉네임 하나 입력하는 정도는 회원가입의 느낌도 나지 않으며 불편하다고 생각하지 않기에 괜찮다고 생각한다.