Keycloak, Auth0, Zitadel의 아키텍처와 기능을 비교하고, 셀프 호스팅과 SaaS 간의 트레이드오프, 렐름/테넌트 설계, 페더레이션 전략을 정리합니다.
현대 애플리케이션에서 인증과 사용자 관리를 처음부터 직접 구축하는 것은 바퀴를 다시 발명하는 것과 같습니다. 아이덴티티 플랫폼(Identity Platform), 또는 IdP(Identity Provider)는 인증, 사용자 관리, 토큰 발급, 페더레이션 등을 표준화된 방식으로 제공합니다. 이번 장에서는 대표적인 세 가지 플랫폼인 Keycloak, Auth0, Zitadel을 비교 분석합니다.
직접 구현 대비 아이덴티티 플랫폼 도입의 이점은 명확합니다.
인증 시스템을 직접 구현하면 초기에는 간단해 보이지만, MFA, 패스키, 소셜 로그인, 보안 감사, 브루트포스 방지, 비밀번호 정책 등이 추가되면서 복잡도가 급격히 증가합니다. 보안 전문 팀이 없다면 검증된 플랫폼을 사용하는 것이 훨씬 안전합니다.
Keycloak은 Red Hat이 후원하는 오픈소스 아이덴티티 플랫폼으로, Java(Quarkus) 기반으로 동작합니다. 온프레미스 환경에서 가장 널리 사용되는 IdP입니다.
Keycloak의 멀티테넌시는 렐름(Realm)으로 구현됩니다. 각 렐름은 독립적인 사용자 저장소, 클라이언트, 역할, 인증 플로우를 가집니다.
{
"realm": "my-service",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": true,
"loginWithEmailAllowed": true,
"duplicateEmailsAllowed": false,
"bruteForceProtected": true,
"maxFailureWaitSeconds": 900,
"minimumQuickLoginWaitSeconds": 60,
"maxDeltaTimeSeconds": 3600,
"failureFactor": 5,
"passwordPolicy": "length(12) and upperCase(1) and lowerCase(1) and digits(1) and specialChars(1) and notUsername",
"accessTokenLifespan": 300,
"ssoSessionIdleTimeout": 1800,
"ssoSessionMaxLifespan": 36000,
"clients": [
{
"clientId": "my-web-app",
"protocol": "openid-connect",
"publicClient": true,
"redirectUris": ["https://app.example.com/callback"],
"webOrigins": ["https://app.example.com"],
"standardFlowEnabled": true,
"directAccessGrantsEnabled": false,
"attributes": {
"pkce.code.challenge.method": "S256"
}
}
],
"authenticationFlows": [
{
"alias": "browser-with-passkey",
"description": "패스키 우선 인증 플로우",
"providerId": "basic-flow",
"topLevel": true,
"builtIn": false
}
]
}Auth0은 Okta에 인수된 SaaS 기반 아이덴티티 플랫폼입니다. 개발자 경험에 초점을 맞추어 빠른 통합이 가능합니다.
Auth0의 확장 메커니즘인 Actions는 인증 파이프라인의 특정 지점에 JavaScript 코드를 삽입할 수 있는 서버리스 함수입니다.
exports.onExecutePostLogin = async (event, api) => {
// 회사 도메인 이메일로 로그인한 경우 직원 역할 부여
const emailDomain = event.user.email?.split("@")[1];
if (emailDomain === "mycompany.com") {
// 커스텀 클레임으로 역할 추가
api.accessToken.setCustomClaim(
"https://myapp.com/roles",
["employee"]
);
// 첫 로그인 시 추가 처리
if (event.stats.logins_count === 1) {
api.accessToken.setCustomClaim(
"https://myapp.com/onboarding",
true
);
}
}
// 특정 국가에서의 로그인 차단
const blockedCountries = ["XX", "YY"];
if (blockedCountries.includes(event.request.geoip?.countryCode)) {
api.access.deny("이 지역에서는 로그인이 제한됩니다.");
}
};Zitadel은 Go로 작성된 오픈소스 아이덴티티 플랫폼으로, Keycloak과 Auth0 사이의 포지션을 차지합니다. 이벤트 소싱 아키텍처를 채택하여 감사 추적이 내장되어 있습니다.
| 기준 | Keycloak | Auth0 | Zitadel |
|---|---|---|---|
| 라이선스 | Apache 2.0 | 상용 (무료 티어 있음) | Apache 2.0 |
| 호스팅 | 셀프 호스팅 | SaaS | 셀프 호스팅 / Cloud |
| 언어 | Java (Quarkus) | N/A (SaaS) | Go |
| 프로토콜 | OIDC, SAML, OAuth | OIDC, SAML, OAuth | OIDC, SAML, OAuth |
| 패스키 지원 | 지원 (v23+) | 지원 | 지원 |
| 확장 메커니즘 | SPI (Java) | Actions (JS) | Actions v2 (JS) |
| 관리 UI | 웹 콘솔 | 대시보드 | 웹 콘솔 |
| 이벤트 소싱 | 미지원 | 미지원 | 기본 아키텍처 |
| 메모리 요구 | 높음 (JVM) | N/A | 낮음 |
| 커뮤니티 | 매우 활발 | 상용 지원 | 성장 중 |
| 학습 곡선 | 높음 | 낮음 | 중간 |
의사결정의 핵심은 "운영 역량"과 "데이터 주권"입니다. 운영 팀이 강하고 데이터 주권이 중요하면 셀프 호스팅, 빠른 출시가 중요하고 운영 부담을 줄이고 싶으면 SaaS를 선택합니다. 두 가지를 절충하고 싶다면 Zitadel Cloud처럼 관리형 오픈소스 옵션도 있습니다.
멀티테넌트 SaaS를 구축할 때, 테넌트(고객사)를 IdP에서 어떻게 격리할지가 중요한 설계 결정입니다.
| 기준 | 테넌트별 렐름 | 단일 렐름 |
|---|---|---|
| 테넌트 수 | 수십 개 이하 | 수백~수천 개 |
| 커스터마이징 요구 | 높음 | 낮음 |
| 규제 요구 | 높음 (데이터 격리) | 낮음 |
| 운영 복잡도 | 높음 | 낮음 |
엔터프라이즈 환경에서는 고객사의 기존 IdP와 페더레이션(Federation)을 구성해야 하는 경우가 많습니다. "고객사 직원이 기존 회사 계정으로 우리 서비스에 로그인"하는 시나리오입니다.
페더레이션의 핵심은 사용자 아이덴티티 매핑입니다. 고객사 IdP에서 전달하는 클레임(이메일, 이름, 그룹 등)을 내부 사용자 모델에 어떻게 매핑할지 사전에 정의해야 합니다. Keycloak의 "Identity Provider Mappers"나 Auth0의 "Connection Rules"가 이 역할을 합니다.
실전에서 Keycloak을 빠르게 시작하기 위한 Docker Compose 설정입니다.
services:
keycloak:
image: quay.io/keycloak/keycloak:25.0
command: start-dev
environment:
KC_DB: postgres
KC_DB_URL_HOST: postgres
KC_DB_URL_DATABASE: keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
KC_HOSTNAME: auth.example.com
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:위 설정의 start-dev 명령은 개발용입니다. 프로덕션 환경에서는 start 명령을 사용하고, TLS 설정, 프로덕션용 DB 연결, 클러스터링, 적절한 캐시 설정을 반드시 적용해야 합니다.
아이덴티티 플랫폼 선택은 단순한 기능 비교가 아니라, 조직의 운영 역량, 데이터 주권 요구, 확장 계획, 예산을 종합적으로 고려하는 전략적 결정입니다. Keycloak은 완전한 제어가 필요할 때, Auth0은 빠른 출시와 개발자 경험이 중요할 때, Zitadel은 두 가지의 균형을 찾을 때 좋은 선택입니다.
다음 장에서는 인증된 사용자에게 "무엇을 할 수 있는지"를 결정하는 권한 모델(Authorization Model)의 진화를 다룹니다. RBAC의 한계를 넘어 ReBAC로 나아가는 여정을 살펴보겠습니다.
이 글이 도움이 되셨나요?
RBAC의 한계를 분석하고, ABAC과 ReBAC(관계 기반 접근 제어)의 개념, Google Zanzibar 모델, OpenFGA와 SpiceDB의 구현 방식을 비교합니다.
WebAuthn 프로토콜과 FIDO2 표준의 구조, 패스키의 등록 및 인증 플로우, 플랫폼 인증기와 크로스 디바이스 인증의 동작 원리를 설명합니다.
JWT의 클레임과 서명 알고리즘을 깊이 분석하고, 리프레시 토큰 로테이션, 토큰 저장 전략, 세션 관리 패턴, BFF 패턴을 실전 코드와 함께 설명합니다.