최근 Vizend에서는 ThirdParty 로그인 기능 도입 및 리팩토링을 진행했습니다. ThirdParty 로그인은 Google, Apple, Facebook, Keycloak 같은 외부 인증 수단을 통해 사용자를 확인하고, 그 결과를 Vizend의 내부 사용자 체계와 연결하는 기능입니다. 사용자가 보는 화면에서는 흔히 볼 수 있는 "외부 계정으로 로그인" 버튼처럼 보이지만, 서버 관점에서는 외부 인증 결과를 내부 계정, 가입 정책, 계정 연결 정책, 세션 정책과 어떻게 연결할지가 핵심입니다.
이번 작업에서 중요하게 본 지점은 외부 인증과 내부 로그인을 분리하는 것이었습니다. 외부 provider는 사용자가 특정 계정의 소유자라는 사실을 확인해 줄 수 있습니다. 하지만 그 사용자가 Vizend 내부에서 활성 상태인지, 어떤 기업에 속하는지, 어떤 권한을 가져야 하는지, 서비스 접근 조건을 만족하는지는 provider가 판단할 수 없습니다. 이 판단은 Vizend의 사용자 모델과 인증 정책 안에서 이루어져야 합니다.
따라서 ThirdParty 로그인은 외부 인증 성공을 곧바로 내부 로그인 성공으로 취급하는 기능이 아닙니다. 외부 인증 결과를 표준화하고, 내부 사용자와의 연결 여부를 확인한 뒤, 필요한 경우 신규 가입 또는 기존 계정 연결로 이어 주는 인증 보조 흐름에 가깝습니다. 이 글에서는 Vizend에서 ThirdParty 로그인을 도입하고 리팩토링하면서 정리한 설계 방향, 적용 과정, 주요 고려사항을 공유합니다.
적용 배경
Vizend의 Gate는 여러 서비스에서 공통으로 사용하는 인증 기반입니다. 각 서비스가 Google 로그인, Apple 로그인, Keycloak 로그인을 직접 구현할 수도 있지만, 그렇게 하면 인증 정책이 서비스마다 흩어집니다. 초기에는 빠르게 보일 수 있으나 provider와 서비스가 늘어날수록 운영 비용이 커지고, 정책 변경 시 영향 범위를 파악하기 어려워집니다.
예를 들어 어떤 기업은 Google 로그인을 허용해야 하고, 다른 기업은 사내 Keycloak만 허용해야 할 수 있습니다. 어떤 서비스는 이메일이 검증된 사용자만 자동 가입을 허용해야 하고, 다른 서비스는 외부 로그인은 허용하되 자동 가입은 막아야 할 수 있습니다. 이러한 정책을 각 서비스 코드에 분산시키면 같은 기준을 반복해서 구현하게 되고, 장애가 발생했을 때 원인도 분산됩니다.
또 하나의 배경은 외부 계정과 내부 계정의 관계입니다. 사용자가 Google 인증에 성공했다고 해서 곧바로 Vizend의 내부 사용자라고 볼 수는 없습니다. 이미 내부 계정이 있는 사용자일 수도 있고, 아직 가입하지 않은 사용자일 수도 있습니다. 외부 인증은 성공했지만 내부에서는 비활성화된 사용자일 수도 있습니다. 따라서 외부 인증 결과를 내부 계정과 연결하는 별도 단계가 필요합니다.
ThirdParty 로그인 도입 및 리팩토링의 기준은 다음과 같이 정리할 수 있습니다.
-
provider별 인증 방식 차이를 Gate 내부에서 공통 흐름으로 흡수합니다.
-
기업별로 사용할 provider와 가입 정책을 다르게 설정할 수 있게 합니다.
-
외부 계정 정보는 내부 사용자와 직접 섞지 않고 연결 정보로 관리합니다.
-
신규 가입과 기존 계정 연결을 명확히 구분합니다.
-
외부 인증 이후에도 내부 사용자 상태, 권한, 세션 정책을 유지합니다.
설계 방향
설계의 핵심은 provider별 차이와 내부 인증 정책을 분리하는 것입니다. OAuth라는 이름은 같지만 provider별 실제 동작은 다릅니다. 어떤 provider는 access token으로 사용자 정보를 조회하고, 어떤 provider는 인증 코드를 token으로 교환해야 합니다. Apple처럼 id token 자체에서 사용자 식별 정보를 확인하는 경우도 있습니다.
이 차이를 가입 로직이나 계정 연결 로직이 직접 알게 하면 provider가 하나 추가될 때마다 기존 흐름 전체에 분기가 늘어납니다. 그래서 provider별 인증 결과는 Gate가 이해할 수 있는 공통 사용자 정보로 변환하고, 이후 흐름에서는 provider 종류와 무관하게 동일한 기준으로 처리하도록 구성했습니다.
공통 사용자 정보에는 외부 사용자 ID, 이메일, 이름, 이메일 검증 여부, provider 식별자 등이 포함됩니다. 이후 내부 사용자 매칭, 신규 가입 가능 여부 판단, 기존 계정 연결 여부 판단은 이 공통 정보와 Vizend의 정책을 기준으로 진행합니다. 이렇게 하면 provider별 구현은 앞단에 격리되고, 도메인 정책은 안정적으로 유지됩니다.
정책은 코드에 고정하지 않고 기업별 설정으로 관리하는 방향을 선택했습니다. 외부 로그인 설정은 운영 중에도 바뀔 수 있습니다. redirect URI가 변경될 수 있고, Keycloak realm이 분리될 수 있으며, 특정 provider를 일시적으로 비활성화해야 할 수도 있습니다. 이런 값을 코드에 넣으면 작은 운영 변경에도 배포가 필요합니다. 정책으로 분리하면 기업 단위로 더 유연하게 대응할 수 있습니다.
외부 계정 연결 정보도 별도로 관리합니다. 내부 사용자 정보 안에 provider ID를 직접 넣는 방식은 단순하지만 확장성이 떨어집니다. 한 사용자가 여러 외부 계정을 연결할 수 있고, 특정 외부 계정 연결만 비활성화해야 할 수도 있습니다. 따라서 외부 provider의 사용자 ID와 내부 사용자 ID 사이의 연결을 별도 정보로 남기는 구조가 더 적합합니다.
전체 흐름
ThirdParty 로그인의 전체 흐름은 외부 인증, Gate 확인, 내부 사용자 매칭, 가입 또는 연결의 순서로 정리할 수 있습니다. 먼저 사용자는 클라이언트에서 Google, Apple, Keycloak 같은 provider 인증을 수행합니다. provider 인증이 끝나면 클라이언트는 그 결과로 받은 credential을 Gate에 전달합니다.
Gate는 먼저 해당 기업에서 그 provider를 사용할 수 있는지 확인합니다. provider가 비활성화되어 있거나 정책이 없으면 외부 인증이 성공했더라도 Gate에서는 허용하지 않습니다. 이 단계는 운영 정책을 지키기 위한 첫 번째 방어선입니다.
provider 사용이 허용되면 Gate는 credential을 확인하고 외부 사용자 정보를 가져옵니다. 이때 provider별 차이가 발생합니다. Google, Facebook, Apple, Keycloak은 사용자 정보를 확인하는 방식과 응답 형식이 다릅니다. 하지만 Gate 내부에서는 이를 공통 사용자 정보로 변환해 다음 단계로 넘깁니다.
그다음 Gate는 해당 외부 계정이 이미 내부 사용자와 연결되어 있는지 확인합니다. 연결된 사용자가 있으면 내부 사용자 정보를 반환하고, 이후 서비스 접근은 Gate의 표준 로그인과 토큰 정책을 따릅니다. 연결된 사용자가 없으면 바로 가입시키지 않고, 다음 단계로 이어가기 위한 단기 registration token을 발급합니다.
registration token은 외부 인증 결과를 안전하게 이어 가기 위한 장치입니다. 외부 인증 직후 사용자가 바로 가입을 완료하지 않을 수 있습니다. 사용자 이름을 정하거나, 추가 정보를 입력하거나, 기존 계정에 연결할지 선택해야 할 수 있습니다. 이 사이에 provider user ID나 이메일 같은 중요한 값을 클라이언트가 직접 들고 다니게 하면 조작 위험이 있습니다. 서버가 외부 인증 결과를 잠시 보관하고, 클라이언트에는 짧은 수명의 token만 전달하면 이 위험을 줄일 수 있습니다.
신규 가입을 선택하면 Gate는 registration token을 확인하고 가입 정책을 적용합니다. 해당 기업에서 회원가입이 허용되어 있는지, provider 기반 가입이 활성화되어 있는지, 이메일이 필요한지, 검증된 이메일이 필요한지 등을 확인합니다. 조건을 만족하면 내부 사용자를 만들고 외부 계정 연결 정보도 함께 남깁니다.
기존 계정 연결을 선택한 경우에는 사용자가 내부 계정의 username과 password를 다시 입력하도록 합니다. 외부 인증에 성공했다는 사실만으로 기존 내부 계정에 연결하는 것은 위험합니다. 공유 기기나 계정 전환 상황에서는 잘못된 외부 계정이 내부 계정에 연결될 수 있습니다. 내부 계정의 비밀번호를 다시 확인하면 사용자가 실제로 그 내부 계정의 소유자인지 한 번 더 확인할 수 있습니다.
Provider별 차이 처리
ThirdParty 로그인에서 가장 먼저 정리해야 하는 부분은 provider별 응답 차이입니다. OAuth 또는 OIDC라는 공통 표준이 있더라도, 실제 연동에서는 provider마다 제공하는 정보와 검증 방식이 다릅니다.
Google은 이메일과 이메일 검증 여부를 비교적 명확하게 제공합니다. 이 값은 자동 가입이나 자동 연결 정책에서 중요한 판단 근거가 될 수 있습니다. 반면 Facebook은 이메일 값을 받을 수 있더라도 동일한 방식으로 검증 여부를 신뢰하기 어렵습니다. 이런 경우에는 보수적으로 처리하는 것이 안전합니다.
Apple은 id token 중심의 흐름을 갖습니다. 사용자 식별자는 token의 subject를 기준으로 볼 수 있고, 이메일도 token claim으로 전달될 수 있습니다. 다만 이름 정보는 항상 안정적으로 제공되지 않으며, private relay email 같은 특성도 고려해야 합니다. 따라서 Apple 연동은 사용자 profile 정보를 항상 완전하게 받을 수 있다는 전제로 설계하면 안 됩니다.
Keycloak은 표준 OIDC 흐름에 가깝지만 설정 의존성이 큽니다. realm, client, redirect URI, client authentication method에 따라 token 교환 방식과 userinfo 조회 방식이 달라질 수 있습니다. 특히 고객사나 사내 IdP와 연동할 때는 endpoint와 client 인증 방식이 환경별로 달라질 수 있으므로, 정책 기반 설정이 중요합니다.
이처럼 provider별 차이는 존재하지만, Gate 내부 흐름에서는 공통 사용자 정보로 변환된 뒤 처리됩니다. 이 구조는 provider 추가나 변경 시 영향 범위를 줄여 줍니다. 새 provider가 추가되더라도 가입 정책, 계정 연결 정책, 내부 사용자 조회 흐름을 다시 만들 필요가 없습니다.
신규 가입과 기존 계정 연결의 분리
외부 인증에 성공했지만 내부 연결 정보가 없을 때, 그 사용자가 신규 사용자인지 기존 계정이 있는 사용자인지는 알 수 없습니다. 이 상태에서 서버가 임의로 가입을 진행하면 중복 계정이 생길 수 있습니다. 반대로 이메일이 같다는 이유로 바로 기존 계정에 연결하면 잘못된 계정 연결이 발생할 수 있습니다.
이 문제를 줄이기 위해 외부 인증 확인 결과와 실제 가입 또는 연결을 분리합니다. Gate는 먼저 외부 사용자를 확인하고, 연결된 내부 사용자가 없으면 registration token을 발급합니다. 이후 사용자가 신규 가입을 선택하면 가입 흐름으로 진행하고, 기존 계정 연결을 선택하면 계정 연결 흐름으로 진행합니다.
이 분리는 화면 흐름에도 도움이 됩니다. 클라이언트는 Gate의 응답을 보고 이미 연결된 사용자라면 로그인 흐름을 이어가고, 연결되지 않은 사용자라면 가입 또는 계정 연결 화면을 제공할 수 있습니다. provider별로 화면 로직을 다르게 만들 필요가 줄어듭니다.
가입 시에는 기업의 정책을 확인합니다. 외부 provider 기반 가입이 허용되어 있는지, 이메일이 필요한지, 이메일 검증이 필요한지, 관리자 승인이 필요한지 등을 판단합니다. 이 정책을 통과해야 내부 사용자가 생성되고 외부 계정 연결 정보가 남습니다.
기존 계정 연결 시에는 내부 계정 소유자 확인을 거칩니다. 외부 provider 인증 결과만으로 기존 계정에 연결하지 않고, 내부 계정의 인증 정보를 다시 확인합니다. 이 단계는 잘못된 연결을 막는 중요한 안전장치입니다.
이메일 기반 자동 연결
ThirdParty 로그인에서 이메일 기반 자동 연결은 편리하지만 주의가 필요한 기능입니다. 사용자 경험만 보면 provider에서 받은 이메일과 내부 계정 이메일이 같을 때 자동으로 연결하는 것이 자연스럽습니다. 그러나 이메일 문자열이 같다는 사실만으로 계정 소유권이 검증되었다고 볼 수는 없습니다.
따라서 단순 이메일 기반 연결과 검증된 이메일 기반 연결을 구분할 필요가 있습니다. provider가 이메일 소유권을 검증했다고 명확하게 알려 주는 경우에는 자동 연결의 신뢰도가 높아집니다. 반대로 검증 여부를 확신할 수 없는 provider라면 자동 연결을 제한하는 편이 안전합니다.
또한 같은 이메일을 가진 활성 사용자가 여러 명인 경우에는 자동으로 하나를 선택하면 안 됩니다. 인증 시스템에서 모호한 자동 판단은 보안 사고로 이어질 수 있습니다. 이 경우에는 정책 위반으로 처리하고 사용자가 명확한 절차를 통해 계정을 정리하거나 연결하도록 유도하는 편이 낫습니다.
이메일 기반 연결은 편의성과 보안 사이의 균형이 필요한 영역입니다. Vizend의 ThirdParty 로그인에서는 provider별 이메일 검증 여부와 내부 사용자 중복 상태를 함께 고려하도록 흐름을 나누었습니다.
Registration Token의 역할
registration token은 외부 인증과 내부 가입 또는 연결 사이를 이어 주는 단기 토큰입니다. 외부 provider 인증 결과에는 provider user ID, email, email verified, name 같은 정보가 포함됩니다. 이 값들은 가입과 연결에 필요하지만, 클라이언트가 임의로 수정해서 보내면 안 되는 값입니다.
registration token을 사용하면 서버가 외부 인증 결과를 보관하고, 클라이언트에는 opaque token만 전달할 수 있습니다. 이후 가입이나 연결 요청이 들어오면 서버는 token을 확인해 원래 외부 인증 결과를 복원합니다. 이 방식은 외부 인증 결과의 조작 가능성을 줄이고, 외부 인증과 내부 처리 사이의 시간 차이를 안전하게 다룰 수 있게 합니다.
token은 짧은 시간 동안만 유효해야 하며, 한 번 사용되면 재사용할 수 없어야 합니다. 사용자가 가입 화면에서 오래 머물면 token이 만료될 수 있고, 브라우저 뒤로 가기나 중복 제출로 이미 사용한 token을 다시 보내는 경우도 생길 수 있습니다. 이런 경우에는 다시 외부 인증 단계로 안내하는 것이 자연스럽습니다.
운영 환경에서는 token 저장 방식도 중요합니다. 단일 인스턴스에서는 메모리 기반 저장도 단순하게 동작할 수 있지만, 여러 인스턴스로 확장된 환경에서는 공유 저장소가 필요합니다. token을 발급한 인스턴스와 소비하는 인스턴스가 다를 수 있기 때문입니다. 따라서 확장성을 고려하면 Redis 같은 공유 저장소 기반으로 발전시키는 것이 적절합니다.
적용 결과
ThirdParty 로그인 도입 및 리팩토링 이후 흐름은 provider별 구현과 도메인 정책으로 분리되었습니다. Google, Facebook, Apple, Keycloak은 인증 방식이 다르지만 Gate 내부에서는 같은 흐름으로 다룰 수 있습니다. provider별 차이는 앞단에서 정리되고, 이후에는 내부 사용자 매칭, 가입 정책, 계정 연결 정책이 공통으로 적용됩니다.
운영 측면에서도 장점이 있습니다. 기업별로 provider를 활성화하거나 비활성화할 수 있고, provider별 client 설정이나 redirect URI도 정책으로 관리할 수 있습니다. 특정 provider에 문제가 생겼을 때 전체 인증 구조를 바꾸지 않고 해당 provider 정책만 조정할 수 있습니다.
보안 측면에서는 외부 인증과 내부 로그인 판단을 분리한 것이 가장 중요합니다. 외부 provider 인증에 성공하더라도 내부 사용자 상태와 정책을 통과해야 서비스 접근으로 이어질 수 있습니다. 사용자가 외부 계정은 정상적으로 가지고 있더라도 Vizend 내부에서 비활성 상태라면 접근을 허용하지 않을 수 있습니다.
유지보수 측면에서는 provider 추가 비용이 줄어듭니다. 새 provider가 추가되면 해당 provider의 credential을 확인하고 공통 사용자 정보로 변환하는 부분을 추가하면 됩니다. 가입 정책, 연결 정책, 내부 사용자 조회 흐름은 그대로 사용할 수 있습니다. 인증 수단이 늘어날 가능성이 있는 서비스에서는 이 분리가 장기적으로 중요합니다.
정리
ThirdParty 로그인은 단순한 OAuth API 호출이 아니라 외부 인증 결과를 내부 사용자 모델과 연결하는 인증 흐름입니다. 사용자가 보는 화면에서는 버튼 하나로 보이지만, 서버 내부에서는 외부 인증, 내부 사용자 상태, 가입 정책, 계정 연결, 세션 정책이 모두 연결됩니다. 이 흐름을 명확히 나누지 않으면 구현은 빨라 보여도 운영과 확장에서 어려움이 생깁니다.
Vizend의 ThirdParty 로그인 도입 및 리팩토링에서는 외부 provider의 차이를 앞단에서 정리하고, 기업별 정책은 설정으로 관리하며, 실제 서비스 접근 판단은 내부 사용자와 인증 정책을 기준으로 유지하는 방향을 선택했습니다. 이 구조는 provider가 늘어나도 인증 모델의 일관성을 유지할 수 있게 해 줍니다.
인증 영역에서는 편의성보다 잘못된 연결을 막는 것이 더 중요합니다. 이메일이 같다는 이유만으로 자동 연결하지 않는 것, 검증되지 않은 이메일을 신뢰하지 않는 것, 한 번 사용한 token을 재사용하지 못하게 하는 것은 모두 같은 방향의 결정입니다. 사용자에게 한 단계 더 확인을 요구하더라도 잘못된 계정 연결을 막는 것이 더 안전합니다.
향후에는 registration token 저장소를 운영 확장성에 맞게 공유 저장소 기반으로 전환하고, 수동 연결 승인이나 관리자 승인 같은 정책을 운영 화면과 더 촘촘하게 연결할 수 있습니다. provider별 실패 원인과 처리 시간을 모니터링하면 장애 대응도 더 빨라질 것입니다. ThirdParty 로그인은 외부 로그인 수단을 늘리는 기능을 넘어, 다양한 인증 수단을 내부 사용자 모델과 일관되게 연결하기 위한 기반 기능으로 볼 수 있습니다.
David