들어가며 — 전환기는 이벤트가 아니라 시대다
앞선 두 글에서 SiteMinder(사이트마인더)의 아키텍처와 Keycloak으로의 마이그레이션 전략을 다뤘습니다. 이번 글의 주제는 그 사이에 놓인 가장 긴 구간, **하이브리드 공존기**입니다.
현장의 진실 하나를 먼저 인정하고 시작하겠습니다. 수백 개 앱을 가진 조직에서 레거시 WebSSO와 모던 IAM의 공존 기간은 분기 단위가 아니라 **연 단위**입니다. 2~5년이 보통이고, 더 길어지는 조직도 흔합니다. 그렇다면 공존은 "잠깐 버티는 임시 상태"가 아니라 **그 자체로 설계, 운영, 모니터링되어야 할 아키텍처**입니다. 2026년의 관점에서 보면 더욱 그렇습니다 — 전환기 동안에도 passkeys 도입, Zero Trust 정책, AI 에이전트 신원 관리 같은 새 요구사항은 멈추지 않고 들어오기 때문입니다.
이 글은 공존기를 버티는 것이 아니라 **잘 설계해서 통과하는** 방법을 다룹니다. 네 가지 핵심 패턴, 세션과 로그아웃 동기화 문제, 보안 리스크, 그리고 "언제 끝났다고 선언할 수 있는가"의 기준까지 정리합니다.
공존기의 지형도
먼저 전형적인 하이브리드 환경의 전체 그림입니다.
사용자 (브라우저)
|
+----------------+----------------+
| |
v v
+--------------------+ +--------------------+
| 모던 IAM 영역 | 브리지 | 레거시 WebSSO 영역 |
| (Keycloak) | <========> | (SiteMinder) |
| | SAML/OIDC | |
| - OIDC 앱 | | - SM_USER 헤더 앱 |
| - SPA + API | | - 에이전트 보호 앱 |
| - passkeys | | - FCC 로그인 |
+---------+----------+ +---------+----------+
| |
v v
+--------------------+ +--------------------+
| 리버스 프록시 레이어 | | 공유 User Store |
| (oauth2-proxy 등) |----------->| (AD/LDAP) |
| 레거시 앱 무수정 수용 | | 양쪽이 함께 참조 |
+--------------------+ +--------------------+
이 그림에서 도출되는 설계 원칙이 세 가지 있습니다.
1. **사용자 스토어는 가능한 한 하나로**: 계정/비밀번호의 진실 공급원이 둘로 갈라지는 순간 공존기의 난이도는 배가됩니다.
2. **인증의 진실 공급원은 단계적으로 한쪽으로**: 두 IdP가 각자 로그인 화면을 갖는 기간을 최소화하고, 한쪽이 다른 쪽에 위임하는 구조를 빨리 만듭니다.
3. **모든 브리지는 한시적임을 명시**: 브리지 인프라에는 만든 날짜와 폐기 목표일을 함께 기록합니다.
패턴 1: 표준 프로토콜 브리지 (SAML/OIDC 페더레이션)
가장 정석적인 패턴입니다. 두 시스템이 모두 말할 수 있는 표준 프로토콜(현실적으로는 [SAML 2.0](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf))로 **양방향 신뢰**를 구성합니다.
- **레거시를 IdP로**: 전환 초기. 기존 로그인 경험 유지, 신규 앱만 Keycloak(브로커) 경유로 개발.
- **모던을 IdP로**: 전환 중후반. 로그인 자체를 Keycloak으로 옮기고, 레거시 영역은 SAML SP로 강등.
이 패턴의 결정적 장점은 **양쪽 모두 벤더 지원 범위 안의 표준 기능**만 쓴다는 점입니다. 커스텀 코드가 거의 없으므로 운영 리스크가 낮습니다.
전환 중후반(모던이 IdP)의 인증 흐름은 다음과 같습니다.
사용자 → 레거시 앱 접근
→ SM Agent: SMSESSION 없음 → SiteMinder 페더레이션 SP 개시
→ SAML AuthnRequest → Keycloak
→ Keycloak: SSO 세션 있으면 즉시, 없으면 로그인 (passkey 가능)
→ SAML Response (assertion) → SiteMinder
→ SiteMinder: assertion 검증 → SMSESSION 발급
→ 사용자: 레거시 앱 진입 (SM_USER 헤더는 기존 그대로)
운영 시 주의점은 다음과 같습니다.
- **속성 매핑 계약을 문서화**: assertion에 어떤 속성(사번, 그룹, 이메일)을 어떤 형식으로 싣는지가 양 시스템 간의 API 계약입니다. 한쪽의 무심한 변경이 반대편 전체 앱을 깨뜨립니다.
- **인증서 수명 관리**: SAML 서명 인증서 만료는 하이브리드 환경의 단골 전면 장애 원인입니다. 만료 90/30/7일 전 알림을 자동화하십시오.
- **클럭 스큐**: assertion의 NotBefore/NotOnOrAfter 검증은 양쪽 서버의 NTP 동기화를 전제로 합니다.
패턴 2: 리버스 프록시 헤더 주입 — 레거시 앱 무수정 보호
수정 불가능한 헤더 기반 앱을 모던 IdP 밑으로 옮기는 패턴입니다. 이전 글에서 구성을 다뤘으므로 여기서는 공존 관점의 포인트와 함께 최소 구성을 다시 싣습니다.
nginx: 레거시 앱 앞단의 인증 프록시
server {
listen 443 ssl;
server_name legacy-crm.example.com;
location /oauth2/ {
proxy_pass http://oauth2-proxy:4180;
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/start?rd=$scheme://$host$request_uri;
auth_request_set $user $upstream_http_x_auth_request_preferred_username;
auth_request_set $groups $upstream_http_x_auth_request_groups;
레거시 앱이 기대하는 정확한 헤더 이름으로 변환 주입
proxy_set_header SM_USER $user;
proxy_set_header SM_USERGROUPS $groups;
proxy_pass http://legacy-crm.internal:8080;
}
}
oauth2-proxy 핵심 설정 (Keycloak OIDC)
provider: keycloak-oidc
client_id: legacy-crm-proxy
oidc_issuer_url: https://keycloak.example.com/realms/enterprise
cookie_secure: true
cookie_samesite: lax
session_store_type: redis # 다중 인스턴스 시 세션 공유
redis_connection_url: redis://redis.internal:6379
skip_provider_button: true
공존 관점에서 이 패턴의 진짜 의미는 **앱의 소속을 라우팅으로 바꿀 수 있다**는 것입니다. DNS가 레거시 게이트웨이를 가리키면 그 앱은 레거시 영역 소속이고, 프록시를 가리키면 모던 영역 소속입니다. 앱 코드는 어느 쪽이든 동일하므로, 전환과 롤백이 모두 라우팅 변경 한 번입니다.
주의점은 세 가지입니다.
- **프록시 세션 스토어**: 다중 인스턴스 프록시는 반드시 Redis 같은 공유 세션 스토어를 써야 합니다. 그렇지 않으면 로드밸런싱 시 무작위 재로그인이 발생합니다.
- **WebSocket과 장수명 연결**: 레거시 앱이 WebSocket이나 SSE를 쓰면 auth_request 경로의 타임아웃/업그레이드 설정을 별도로 검증해야 합니다.
- **업로드 큰 요청**: auth_request는 본문 없이 인증만 확인하지만, 프록시 버퍼링 설정에 따라 대용량 업로드가 깨지는 사례가 있습니다.
패턴 3: Identity Orchestration 레이어
브리지와 프록시를 앱마다 수작업으로 구성하는 대신, **추상화 레이어가 신원 흐름 전체를 정책으로 관리**하는 패턴입니다. [Strata Maverics](https://www.strata.io/)가 대표적이고, 클라우드 벤더의 유사 기능들도 있습니다.
+------------------------------------------+
| Identity Orchestration Layer |
| 앱별 정책: "이 앱은 Keycloak, 저 앱은 SM" |
| 세션 변환 / 헤더 주입 / 로그아웃 팬아웃 처리 |
+-----+--------------------+----------------+
| |
v v
+-----------+ +-----------+
| Keycloak | | SiteMinder|
+-----------+ +-----------+
장점은 명확합니다.
- 앱 단위 IdP 전환이 **설정 변경**이 되어, 웨이브 전환 속도가 빨라집니다.
- 다중 레거시 IdP 환경(인수합병으로 SiteMinder와 Oracle Access Manager가 둘 다 있는 경우 등)에서 위력이 큽니다.
- 로그아웃 팬아웃, 세션 수명 정렬 같은 까다로운 문제를 제품 기능으로 흡수합니다.
단점도 분명합니다.
- 라이선스 비용이 들고, **신원 경로의 모든 트래픽이 통과하는 새로운 단일 장애점**이 생깁니다 — HA 설계가 필수입니다.
- 오케스트레이션 레이어 자체에 대한 종속이 생깁니다. 전환 완료 후 이 레이어를 걷어낼 수 있는 출구 설계를 처음부터 포함해야 합니다.
엔지니어링 여력이 충분한 조직은 패턴 1+2 조합 자체 구축으로 같은 효과를 낼 수 있습니다. 요컨대 이 패턴은 기술이 아니라 **조달과 역량의 의사결정**입니다.
패턴 4: Strangler Fig — 앱 단위 점진 이관
[Strangler Fig](https://martinfowler.com/bliki/StranglerFigApplication.html)는 마틴 파울러가 정리한 레거시 현대화의 고전 패턴으로, 무화과나무가 숙주를 서서히 감싸며 대체하듯 새 시스템이 옛 시스템을 앱 단위로 잠식하는 접근입니다. IAM 공존기에 적용하면 다음과 같습니다.
시점 t0: [SM: 200개 앱] [KC: 0개]
시점 t1: [SM: 180개 앱] --Wave 1(20개)--> [KC: 20개]
시점 t2: [SM: 120개 앱] --Wave 2~3------> [KC: 80개]
시점 t3: [SM: 30개 앱] --Wave 4~6------> [KC: 170개]
시점 t4: [SM: 0개. 디커미션] [KC: 200개]
핵심 규칙은 세 가지입니다.
1. **신규 앱은 무조건 새 시스템으로**: 레거시 영역에 신규 진입을 금지하는 정책(architecture fitness function)을 거버넌스로 강제합니다. 이것이 없으면 잠식이 아니라 영구 공존이 됩니다.
2. **이관 단위는 사용자 여정 묶음으로**: 사용자가 한 업무 흐름에서 오가는 앱들을 같은 웨이브로 묶어야 브리지 경유 횟수가 줄고 체감 품질이 좋아집니다.
3. **진행률을 공개 지표로**: 남은 앱 수, 레거시 경유 인증 비율을 대시보드로 공개하면 조직의 추진력이 유지됩니다.
세션 수명과 로그아웃 동기화 — 공존기의 최대 난제
세션 계층의 불일치
하이브리드 환경에서 한 사용자의 "로그인 상태"는 최소 세 계층에 존재합니다.
| 계층 | 세션 | 수명 제어 |
| --- | --- | --- |
| 모던 IdP | Keycloak SSO 세션 | SSO Session Idle/Max |
| 브리지/프록시 | oauth2-proxy 쿠키, SAML 세션 | 쿠키 만료, 토큰 갱신 |
| 레거시 | SMSESSION | realm idle/max timeout |
이 세 계층의 수명이 어긋나면 두 가지 증상이 나타납니다.
- **유령 세션**: 상위 계층(IdP)은 죽었는데 하위 계층(프록시 쿠키, SMSESSION)이 살아 있어, "로그아웃했는데 아직 들어가진다"는 보안 문제.
- **재인증 폭풍/리다이렉트 루프**: 하위가 먼저 죽고 상위 갱신이 꼬이면, 사용자가 무한 리다이렉트를 만나는 가용성 문제.
실무 권고는 단순한 규칙으로 수렴합니다. **수명은 위에서 아래로 갈수록 같거나 짧게.** IdP 세션이 가장 길고, 브리지 세션은 그보다 짧거나 같고, 레거시 세션은 가장 짧게 잡으면, 만료 시 항상 상위로 거슬러 올라가 조용히 재발급받는 안전한 방향으로 동작합니다.
로그아웃 동기화
로그아웃은 더 어렵습니다. 사용자가 어느 쪽에서 로그아웃하든 **모든 계층의 세션이 종료**되어야 합니다. 표준 도구는 다음과 같습니다.
- OIDC 측: [OpenID Connect RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html), [Back-Channel Logout](https://openid.net/specs/openid-connect-backchannel-1_0.html)
- SAML 측: SAML Single Logout (SLO) — 현실에서는 구현 편차가 커서 신뢰도가 낮습니다.
- 레거시 측: SiteMinder 로그아웃 URL (SMSESSION 쿠키 무효화)
현실적인 해법은 **중앙 로그아웃 오케스트레이션 엔드포인트**입니다. 모든 앱의 로그아웃 버튼이 이 엔드포인트를 가리키게 하고, 여기서 각 계층의 로그아웃을 순차 실행합니다.
GET /global-logout
1. SiteMinder 로그아웃 호출 (SMSESSION 무효화)
2. oauth2-proxy 세션 삭제 (/oauth2/sign_out)
3. Keycloak RP-Initiated Logout으로 리다이렉트
(id_token_hint + post_logout_redirect_uri)
4. "로그아웃되었습니다" 랜딩 페이지
백채널이 가능한 구간(Keycloak과 프록시 사이)은 back-channel logout으로 보강하고, 불가능한 구간(SMSESSION)은 프런트채널 체이닝으로 처리하는 혼합 구성이 보통입니다. **전 계층 로그아웃의 완결성은 패턴 선택과 무관하게 반드시 테스트 시나리오에 포함**되어야 합니다.
사용자 경험 — 이중 로그인 방지
공존기의 UX 실패는 곧 헬프데스크 폭주입니다. 점검 항목을 정리합니다.
- **로그인 화면은 하나만**: 사용자가 두 가지 로그인 화면을 무작위로 만나는 상태를 최단기간으로 끝내야 합니다. 브리지를 통해 인증의 진실 공급원을 한쪽으로 모으는 것이 최우선 과제인 이유입니다.
- **딥링크 보존**: 브리지를 거치는 모든 흐름에서 사용자가 처음 요청한 URL(RelayState, rd 파라미터)이 보존되는지 검증합니다. "로그인했더니 홈으로 떨어졌다"는 사소해 보여도 가장 많은 불만을 만듭니다.
- **세션 만료의 우아한 처리**: AJAX 요청이 만료를 만나면 302 HTML이 아니라 401 JSON을 받도록 프록시에 분기 설정을 둡니다. 그렇지 않으면 SPA 화면 한가운데 로그인 폼 HTML이 박히는 고전적 버그가 생깁니다.
- **시간대 공지**: 웨이브 전환일에는 해당 앱 사용자에게 "오늘부터 로그인 화면이 바뀝니다" 공지를 보내는 것만으로 티켓이 절반 이하로 줄어듭니다.
보안 리스크 — 공존기에 늘어나는 공격 표면
공존기는 본질적으로 공격 표면이 **두 시스템의 합집합 + 브리지**로 늘어나는 기간입니다.
헤더 스푸핑
프록시 패턴의 고질적 리스크입니다. 방어 체크리스트는 다음과 같습니다.
[ ] 레거시 앱은 프록시/게이트웨이에서만 도달 가능 (네트워크 정책)
[ ] 프록시는 인바운드 SM_USER 류 헤더를 무조건 덮어씀
[ ] 게이트웨이-앱 구간에 mTLS 또는 공유 시크릿 헤더
[ ] 앱 직접 포트(8080 등)가 사내망에서라도 열려 있지 않은지 정기 스캔
[ ] 헤더 위조 시도를 테스트 시나리오(T7)로 상시 자동화
특히 마지막 항목이 중요합니다. 공존기에는 네트워크 변경이 잦아서, 처음에 막아 둔 직접 접근 경로가 어느 순간 다시 열리는 사고가 실제로 일어납니다.
세션 불일치 악용
유령 세션은 UX 문제이자 보안 문제입니다. 퇴직자 계정을 IdP에서 비활성화해도 살아 있는 SMSESSION이나 프록시 쿠키로 한동안 접근이 가능하다면 감사 지적 사항입니다. 대응은 다음과 같습니다.
- 레거시/브리지 세션 수명을 짧게 유지 (위의 "위에서 아래로 짧게" 규칙)
- 계정 비활성화 이벤트를 훅으로 받아 관련 세션을 능동 파기 (Keycloak Event Listener SPI + SiteMinder Session Store 연동)
- 고위험 앱은 세션 검증 시 매번 사용자 상태를 재확인하는 옵션 적용
브리지 자체의 보안
- SAML 브리지: 서명 검증 미설정, 약한 다이제스트 알고리즘, audience 미검증이 단골 취약점입니다. assertion 암호화와 서명 알고리즘을 점검하십시오.
- 프록시: 쿠키 시크릿 로테이션, Redis 세션 스토어 접근 통제, 토큰을 로그에 남기지 않는 설정을 확인합니다. [RFC 9700](https://datatracker.ietf.org/doc/html/rfc9700)의 권고를 기준선으로 삼으면 좋습니다.
모니터링과 전환 완료 기준
공존기 핵심 지표
| 지표 | 의미 | 목표 방향 |
| --- | --- | --- |
| 레거시 경유 인증 비율 | 전체 인증 중 SiteMinder가 처리한 비율 | 단조 감소 |
| 브리지 인증 비율 | 브리지를 거친 크로스 도메인 인증 비율 | 중반 증가 후 감소 |
| 인증 성공률 (양쪽 각각) | 가용성 베이스라인 | 99.9 이상 유지 |
| 로그인 p95 소요 시간 | 브리지 홉 추가로 인한 지연 감시 | 베이스라인 + 1초 이내 |
| 미이관 앱 수 | strangler 진행률 | 웨이브 계획 대비 추적 |
| 유령 세션 감지 수 | 로그아웃 불완전성 | 0 수렴 |
로그 통합도 중요합니다. SiteMinder 감사 로그와 Keycloak 이벤트를 같은 SIEM으로 모아, **사용자 단위로 양쪽 인증 이력을 하나의 타임라인**으로 볼 수 있어야 공존기의 사고 조사가 가능해집니다.
Keycloak 이벤트를 SIEM으로 보내는 구성 개념 (예: syslog 리스너)
spi-events-listener-jboss-logging-success-level: info
spi-events-listener-jboss-logging-error-level: warn
운영에서는 커스텀 EventListener SPI로 Kafka/SIEM 전송이 일반적
전환 완료를 선언하는 기준
"끝났다"는 선언에는 객관적 기준이 필요합니다. 권장 기준은 다음과 같습니다.
1. 레거시 경유 인증 비율이 0이 되고 N주(예: 8주) 유지됨 — 월말/분기말 주기 업무 포함
2. 미이관 앱 0개 — 또는 잔여 앱의 공식 폐기 결정 완료
3. 레거시 의존 DNS/라우팅 항목 0건
4. 브리지/오케스트레이션 레이어 제거 계획 승인 — 공존 인프라도 함께 걷어내야 진짜 완료입니다
5. SiteMinder 정책 아카이브 보존 및 라이선스 종료 처리
6. 감사 로그 보존 의무 이관 (규제 산업의 경우 수년치 레거시 감사 로그 보존 방안 확정)
사례 시나리오 — 가상 은행 인트라넷 200개 앱 이관
마지막으로 패턴들을 종합한 가상 사례입니다. 설정은 다음과 같습니다: 중견 은행, 인트라넷 앱 200개, SiteMinder 12.8(EOS 임박), 직원 1만 2천 명, 목표 기간 30개월.
**인벤토리 결과**: 표준 프로토콜 앱 25개(A), 수정 가능 헤더 앱 60개(B), 수정 불가 헤더 앱 105개(C), SDK 의존 앱 10개(D).
**아키텍처 결정**:
- Keycloak HA 클러스터 구축, 기존 AD를 User Federation으로 연결 (패턴 공통 기반)
- 1단계(0~6개월): Keycloak을 브로커로, SiteMinder를 IdP로 (패턴 1, 방향 2). 신규 앱 동결 정책 시행 — 모든 신규 앱은 Keycloak OIDC로만 (패턴 4 규칙 1)
- 2단계(6~12개월): 로그인 진실 공급원 역전 — Keycloak이 IdP, SiteMinder는 SAML SP로 강등 (패턴 1, 방향 1). 이 시점부터 전 직원이 passkey 등록 가능
- 3단계(6~24개월, 병행): A그룹 25개는 엔드포인트 교체로 즉시 이관. B그룹 60개는 정기 릴리스에 OIDC 전환을 끼워 점진 이관. C그룹 105개는 oauth2-proxy 팜(패턴 2)으로 웨이브당 15~20개씩 이관
- 4단계(24~30개월): D그룹 10개 개별 재설계 — 3개는 재개발, 5개는 프록시 수용 가능 판명, 2개는 폐기 결정. SiteMinder 디커미션
**겪은 문제들** (현실에서 그대로 만나게 될 것들):
- Wave 3에서 C그룹 한 앱이 SM_USERGROUPS의 캐럿 구분을 LDAP DN 형식과 함께 파싱하고 있어 그룹 매핑 전면 재작업 — 헤더 "값 형식"까지 인벤토리에 포함해야 한다는 교훈
- 월말 결산 배치가 서비스 계정으로 헤더 인증을 흉내 내고 있던 것이 발견됨 — 사람 아닌 신원(non-human identity)은 별도 트랙으로 분리, client credentials 플로우로 전환
- SAML 인증서 만료로 2단계 중 47분 전면 로그인 장애 — 이후 인증서 만료 알림과 이중화 키 롤오버 절차 도입
- 30개월 시점 잔여 앱 4개 — 오너 부서 과금 거버넌스 발동 후 3개월 내 정리 완료
결과적으로 33개월에 디커미션을 완료했고, 공존기 동안에도 passkey 도입(14개월 차)과 Zero Trust 네트워크 정책 적용을 병행할 수 있었습니다. **공존 아키텍처를 제대로 설계했기 때문에 현대화가 전환 완료를 기다릴 필요가 없었다**는 것이 이 시나리오의 요점입니다.
패턴 비교 요약과 선택 가이드
네 가지 패턴은 배타적이 아니라 조합되는 것이지만, 의사결정을 돕기 위해 한 표로 비교합니다.
| 항목 | 패턴 1 브리지 | 패턴 2 프록시 | 패턴 3 오케스트레이션 | 패턴 4 strangler |
| --- | --- | --- | --- | --- |
| 해결하는 문제 | IdP 간 SSO 연결 | 무수정 앱 수용 | 전환 복잡도 흡수 | 이관의 방향성/거버넌스 |
| 구축 난이도 | 낮음 (표준 기능) | 중간 (앱별 구성) | 낮음 (구매) | 기술 아닌 조직 문제 |
| 운영 리스크 | 인증서/속성 계약 | 프록시 SPOF, 세션 스토어 | 신규 SPOF, 벤더 종속 | 추진력 상실 |
| 비용 | 인건비 위주 | 인프라 + 인건비 | 라이선스 | 거버넌스 비용 |
| 적용 시기 | 공존기 전 구간 | C군 앱 이관 구간 | 일정 빠듯/멀티 IdP | 공존기 전 구간 |
간단한 선택 흐름은 다음과 같습니다.
시작
├─ 두 IdP 간 SSO가 필요한가? ──────────────→ 항상 패턴 1 (기반)
├─ 수정 불가 헤더 앱이 있는가? ─ 예 ─┐
│ ├─ 엔지니어링 여력 충분 → 패턴 2 자체 구축
│ └─ 여력 부족/멀티 IdP → 패턴 3 도입 검토
└─ 앱이 30개 이상인가? ── 예 ──────────────→ 패턴 4 거버넌스 필수
트러블슈팅 — 공존기에 자주 만나는 장애
마지막으로, 하이브리드 환경 운영 중 실제로 자주 접수되는 증상과 1차 대응을 정리합니다.
증상 1: 특정 앱에서만 간헐적 재로그인 요구
- **흔한 원인**: 프록시 다중 인스턴스가 공유 세션 스토어 없이 동작 (로드밸런서가 다른 인스턴스로 보낼 때마다 세션 미스), 또는 Agent Key 롤오버 직후의 SMSESSION 검증 실패.
- **1차 확인**: 해당 앱 경로의 프록시 인스턴스 수와 session_store_type 설정, 레거시 쪽이면 Policy Server의 키 롤오버 시각과 장애 시각의 상관관계.
증상 2: 리다이렉트 루프
- **흔한 원인**: 계층 간 세션 수명 역전 (프록시 쿠키는 살아 있는데 IdP 세션 만료), 또는 redirect_uri 와 쿠키 도메인 불일치.
- **1차 확인**: 브라우저 개발자 도구로 302 체인을 캡처해 어느 계층이 어느 계층으로 던지는지 식별합니다. curl로 재현하면 다음과 같습니다.
리다이렉트 체인 추적 (쿠키 보존, 최대 10홉)
curl -s -o /dev/null -L --max-redirs 10 \
-b cookies.txt -c cookies.txt \
-w '%{num_redirects} redirects, final: %{url_effective}\n' \
https://legacy-crm.example.com/
각 홉의 Location 헤더 확인
curl -s -D - -o /dev/null https://legacy-crm.example.com/ | grep -i '^location'
증상 3: 로그아웃했는데 다른 앱에 여전히 접근 가능
- **흔한 원인**: 로그아웃 오케스트레이션 체인에서 한 계층 누락 (특히 SMSESSION 무효화 단계), 또는 back-channel logout 미수신 클라이언트.
- **1차 확인**: 글로벌 로그아웃 후 계층별 쿠키 상태를 순서대로 덤프합니다. SMSESSION, 프록시 쿠키, Keycloak 세션 쿠키 중 무엇이 살아남는지가 누락 지점을 알려줍니다.
증상 4: 전환 후 그룹/권한이 비어 보임
- **흔한 원인**: 그룹 클레임 형식 불일치 (캐럿 구분 문자열 vs JSON 배열), 클레임 누락 (scope에 groups 미포함), 또는 LDAP group mapper의 동기화 지연.
- **1차 확인**: 토큰 자체를 디코드해 클레임이 있는지부터 확인하고, 있다면 프록시의 헤더 변환을, 없다면 Keycloak mapper 설정을 봅니다.
access token의 클레임 확인 (jq 사용)
TOKEN=$(curl -s -X POST \
-d "client_id=debug-cli" -d "grant_type=password" \
-d "username=testuser" -d "password=..." \
https://keycloak.example.com/realms/enterprise/protocol/openid-connect/token \
| jq -r .access_token)
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '.groups'
증상 5: 월말/분기말에만 발생하는 인증 실패
- **흔한 원인**: 배치/서비스 계정이 사람용 인증 경로(헤더 모방, 화면 스크래핑)를 쓰고 있다가 전환에서 누락된 경우.
- **1차 대응**: non-human identity 전수 조사 트랙을 별도로 만들고, client credentials 플로우로 이관합니다. 사례 시나리오의 은행도 정확히 이 문제를 겪었습니다.
마치며
하이브리드 공존기는 마이그레이션의 부산물이 아니라, 그 자체로 설계 대상인 아키텍처입니다. 정리하면 다음과 같습니다.
- **패턴 1(프로토콜 브리지)** 로 인증의 진실 공급원을 빠르게 모던 쪽으로 옮기고,
- **패턴 2(프록시 헤더 주입)** 로 수정 불가 레거시 앱을 무수정 수용하며,
- **패턴 3(orchestration)** 은 역량과 일정의 트레이드오프로 선택하고,
- **패턴 4(strangler fig)** 의 거버넌스로 잠식의 방향성을 강제합니다.
그리고 세션 수명 정렬, 로그아웃 오케스트레이션, 헤더 스푸핑 방어, 전환 완료 기준 — 이 네 가지를 공존기 운영의 상시 점검 항목으로 삼으면, 수년의 전환기를 사고 없이, 그리고 현대화를 멈추지 않으면서 통과할 수 있습니다.
참고 자료
- [SAML 2.0 Core 사양 (OASIS)](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf)
- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
- [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html)
- [OpenID Connect RP-Initiated Logout 1.0](https://openid.net/specs/openid-connect-rpinitiated-1_0.html)
- [Keycloak 공식 문서](https://www.keycloak.org/documentation)
- [Keycloak 릴리스 노트](https://www.keycloak.org/docs/latest/release_notes/index.html)
- [Broadcom SiteMinder Tech Docs](https://techdocs.broadcom.com/siteminder)
- [oauth2-proxy 공식 문서](https://oauth2-proxy.github.io/oauth2-proxy/)
- [Martin Fowler — Strangler Fig Application](https://martinfowler.com/bliki/StranglerFigApplication.html)
- [RFC 6749 — The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
- [RFC 8693 — OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693)
- [RFC 9700 — Best Current Practice for OAuth 2.0 Security](https://datatracker.ietf.org/doc/html/rfc9700)
- [Strata — Identity Orchestration](https://www.strata.io/)
- [W3C WebAuthn Level 3](https://www.w3.org/TR/webauthn-3/)
현재 단락 (1/233)
앞선 두 글에서 SiteMinder(사이트마인더)의 아키텍처와 Keycloak으로의 마이그레이션 전략을 다뤘습니다. 이번 글의 주제는 그 사이에 놓인 가장 긴 구간, **하이브리드...