확장성 있는 OAuth 인증 시스템 구축하기

SeokHwan An·2024년 11월 10일
0

shook

목록 보기
18/18

소셜 로그인(OAuth) 도입기 글에 이어서 작업한 내용으로 확장성 있는 소셜 로그인 시스템을 구축한 과정에 대해서 정리하고자 합니다.

문제 상황

이 코드는 당시 구글 소셜 로그인을 구축할 때 구현할 코드입니다. 이후 카카오 소셜 로그인을 추가하려다 보니 기존 구조에 문제가 있음을 깨달았습니다. 먼저 현재 코드를 보면 구글 소셜 로그인에 의존하는 부분이 존재합니다. 그렇기에 새로운 소셜 로그인 플랫폼이 추가될 때마다 OAuthService 코드를 수정하는 상황이 발생합니다. 두 가지 상황을 보겠습니다.

소셜 로그인 플랫폼 별 로그인 메소드를 구현하는 방법

위 방안은 소셜 로그인 플랫폼마다 로그인 메소드를 구현하는 방안입니다. 소셜 로그인 플랫폼 별로 로그인 메소드를 관리하고 있기에 각 플랫폼 별로 로그인 과정을 커스텀할 수 있지만 OAuth 2.0은 모든 소셜 로그인 플랫폼에서 로그인 과정이 동일하게 동작하는 것이 큰 장점이자 특징이기에 이를 분리해서 관리할 필요가 없다고 느꼈습니다. 즉, 사용자 정보를 불러오는 요청만 플랫폼 별로 다를 뿐 동일한 로그인 흐름을 가진 메소드들이 중복된다는 것을 볼 수 있습니다.

소셜 로그인 플랫폼 별 if문을 추가해서 처리하는 방법

위 방안은 동일한 로그인 흐름이 중복되는 것을 제거한 방법으로 플랫폼 정보을 인자로 받고 해당 정보를 바탕으로 알맞은 소셜 로그인을 처리하는 방식입니다. 해당 방안은 소셜 로그인의 공통된 흐름이 중복되지 않는다는 장점이 있지만 여전히 새로운 소셜 로그인 플랫폼이 추가될 때마다 의존필드가 추가된다는 점과 if문이 추가되어야 한다는 문제점이 있습니다.

두 가지 방안 모두 기능이 동작하는데에는 큰 문제가 있는 것은 아니지만 코드를 관리하는 입장에서 새로운 변화가 발생했을 때 관리 point가 발생한다는 점이 큰 아쉬움이라고 느껴졌습니다.

해결 방안

문제를 해결하기에 앞서서 먼저 s-hook의 소셜 로그인 흐름을 살펴보겠습니다.

  1. 클라이언트 측에서 Authorization Server로 부터 Authorization Code를 발급받는다.
  2. Authroization Code로 로그인 요청을 보낸다.
  3. Authroization Code를 통해 사용자 resource에 접근할 수 있는 accessToken을 발급받는다.
  4. accessToken을 통해 사용자 resource를 발급받는다.
  5. 자체 token을 발급해서 클라이언트에 제공한다.

s-hook의 소셜 로그인은 5단계로 수행됩니다.

여기서 살펴볼 것은 3번과 4번과 과정은 특정 소셜 로그인 플랫폼에서만 동작하는 것이 아닌 모든 플랫폼에서 동일하게 동작하는 부분입니다.(단 플랫폼 별로 api 엔드 포인트나 요청 매개 변수는 내부적으로 다를 수 있습니다.) 그 부분에 해당 하는 OAuthService의 코드는 다음과 같습니다.

OAuthService 기준으로 보았을 때 빨간 네모 부분이 구글 기반인지 카카오 기반인지 페이스북 기반인지에 대해서 알필요가 없다는 것이 느꼈습니다. 대신 중간 매개체를 두어 OAuthService가 직접 플랫폼 Provider에 의존하는 것 대신 중간 매개체(OAuthProviderFinder)에서 원하는 소셜 로그인 플랫폼 Provider를 불러오는 구조가 더 좋다고 생각했습니다.

제가 생각한 구조는 위의 그림과 같습니다.

  1. OAuthInfoProvider 인터페이스를 두고 각 소셜 로그인은 해당 구현체를 가진다.
  2. OAuthProviderFinder는 Map 자료구조로 구성해 OAuthInfoProvider의 각 구현체들을 가지고 있는다.
  3. OAuthService는 소셜 로그인 구현체에 의존하는 것이 아닌 OAuthProviderFinder에 의존해 원하는 소셜 로그인 구현체를 불러와서 이용한다.

OAuthInfoProvider 인터페이스 구성

OAuth 2.0의 공통 부분인 authorizationCode를 통해 accessToken을 불러오는 것과 accessToken을 기반으로 사용자 resource를 불러오는 것을 추상화 했습니다.

OAuthProviderFinder 구성

이후 Map 자료 구조를 활용해 인터페이스의 구현체를 관리하는 OAuthProviderFinder를 구성했습니다.

OAuthProviderFinderOAuthService와 각 플랫폼별 OAuthInfoProvider 구현체 간의 매개체로, OAuthService가 원하는 플랫폼의 구현체를 불러오는 것이 핵심 역할입니다. OAuthProviderFinder는 초기 빈으로 초기 등록될 때 init()메소드가 호출되어 map을 초기화 하도록 구성했습니다. map의 key는 플랫폼의 이름이고 value에는 플랫폼 별로 구현한 OAuthInfoProvider 인터페이스의 구현체로 구성됩니다. getOAuthProvider() 메소드를 통해 OAuthService에서 원하는 소셜 로그인 플랫폼의 구현체를 이용할 수 있게 구성했습니다.

OAuthService 코드의 최종 변화

기존에는 특정 소셜 로그인 플랫폼의 Provider에 직접 의존했지만, 중간 매개체를 의존하는 방식으로 변경되었습니다. 이로 인해 OAuthService는 필요에 따라 특정 플랫폼의 Provider를 불러와 처리할 수 있게 되어, 새로운 소셜 로그인 플랫폼이 추가되더라도 OAuthService의 코드를 수정할 필요가 없어 OCP를 만족하는 만족하는 구조를 구축했습니다.

0개의 댓글