들어가며
인증 시스템은 장애가 나는 순간 모든 서비스의 장애가 됩니다. 로그인이 안 되면 사용자 입장에서는 서비스 전체가 죽은 것과 다름없고, 더 무서운 것은 "조용한 보안 사고" — credential stuffing이 며칠간 진행되는데 아무도 모르는 상황입니다. 그래서 Keycloak 운영의 성숙도는 결국 관측성(observability)의 성숙도로 수렴합니다.
다행히 2026년의 Keycloak은 관측성 측면에서 과거와 비교할 수 없을 만큼 좋아졌습니다. 26.x 라인은 micrometer 기반 메트릭, 사용자 이벤트 메트릭, OpenTelemetry tracing을 내장하고 있으며, 26.6에서는 zero-downtime rolling update 같은 운영 기능까지 더해졌습니다. 이 글에서는 Keycloak의 이벤트 시스템과 보존 정책, EventListener SPI를 통한 외부 전송, Prometheus + Grafana 모니터링 스택 구성, tracing과 로그 수집, brute force/credential stuffing 탐지, 알림 룰, 그리고 ISMS-P/SOC 2 감사 대응까지 관측성의 전체 그림을 그려보겠습니다.
Keycloak 이벤트 시스템
Login Events와 Admin Events
Keycloak은 두 종류의 이벤트를 발생시킵니다.
| 구분 | Login Events (User Events) | Admin Events |
| --- | --- | --- |
| 발생 주체 | 최종 사용자 행위 | 관리자/Admin API 행위 |
| 대표 이벤트 | LOGIN, LOGIN_ERROR, LOGOUT, REGISTER, UPDATE_PASSWORD, TOKEN_REFRESH | CREATE/UPDATE/DELETE (user, client, role, realm 설정) |
| 핵심 용도 | 보안 모니터링, 사용자 행동 분석 | 변경 감사 (who changed what) |
| 페이로드 | 사용자, client, IP, 결과, 에러 코드 | 리소스 경로, 변경 전후 representation |
이벤트 수집은 realm 단위로 켭니다. Admin Console의 Realm Settings에서도 가능하지만, 코드로 관리합시다.
이벤트 설정 (kcadm.sh)
./kcadm.sh update events/config -r myrealm \
-s eventsEnabled=true \
-s 'eventsExpiration=2592000' \
-s 'enabledEventTypes=["LOGIN","LOGIN_ERROR","LOGOUT","REGISTER","UPDATE_PASSWORD","UPDATE_EMAIL","REMOVE_TOTP","UPDATE_TOTP","TOKEN_EXCHANGE","REFRESH_TOKEN_ERROR"]' \
-s adminEventsEnabled=true \
-s adminEventsDetailsEnabled=true
- `eventsExpiration`은 초 단위 보존 기간입니다. 위 예시는 30일.
- `adminEventsDetailsEnabled`를 켜면 변경된 representation(JSON)이 함께 저장되어 "무엇이 어떻게 바뀌었는지"까지 추적할 수 있습니다. 감사 요건이 있다면 필수입니다.
- 26.x에서는 admin events에도 별도 만료 설정이 적용되므로, 두 종류의 보존 기간을 모두 명시하세요.
조회는 Admin API로 가능합니다.
최근 로그인 실패 이벤트 조회
./kcadm.sh get events -r myrealm \
-q type=LOGIN_ERROR -q max=50
특정 사용자의 admin 변경 이력
./kcadm.sh get admin-events -r myrealm \
-q resourcePath=users/USER_UUID -q max=20
이벤트 보존과 DB 부하 — 흔한 운영 사고
이벤트는 Keycloak의 메인 DB에 저장됩니다(EVENT_ENTITY, ADMIN_EVENT_ENTITY 테이블). 여기서 두 가지 사고 패턴이 반복됩니다.
사고 패턴 1: 무한 보존
eventsExpiration 미설정 → 테이블이 수억 행으로 비대
→ 이벤트 INSERT 지연 → 로그인 트랜잭션 전체 지연
→ "로그인이 느려요" 티켓 폭주
사고 패턴 2: 일괄 삭제 폭탄
뒤늦게 만료 설정 → 만료 작업이 수억 행 삭제 시도
→ DB lock 경합, WAL/redo 폭증 → 운영 중 DB 마비
실무 가이드라인은 다음과 같습니다.
- 이벤트 보존은 **운영 조회용 단기(7~30일)**로 짧게 잡고, 장기 보관은 외부 시스템(아래 SPI 절)으로 내보냅니다. Keycloak DB는 감사 로그 아카이브가 아닙니다.
- 이미 비대해진 테이블은 만료 설정에 맡기지 말고, 점검 시간에 배치 삭제(파티션 단위, LIMIT 반복)로 정리한 후 만료를 설정합니다.
- `enabledEventTypes`를 비워두면 모든 타입이 저장됩니다. CODE_TO_TOKEN, TOKEN_REFRESH 같은 고빈도 이벤트가 필요한지 검토하고, 필요한 타입만 명시하세요. 토큰 갱신이 잦은 SPA가 많은 환경에서는 이 차이가 수십 배의 행 수 차이로 나타납니다.
EventListener SPI — 이벤트를 밖으로 보내기
DB 폴링 대신 이벤트 발생 시점에 외부 시스템(Kafka, Loki, SIEM)으로 푸시하는 것이 EventListener SPI입니다. 내장 listener로는 `jboss-logging`(로그 출력)과 `email`(사용자 알림)이 있고, 커스텀 구현을 JAR로 배포할 수 있습니다.
Kafka로 전송하는 커스텀 Listener
public class KafkaEventListenerProvider implements EventListenerProvider {
private final KafkaProducer<String, String> producer;
private final String topic;
private final ObjectMapper mapper = new ObjectMapper();
public KafkaEventListenerProvider(KafkaProducer<String, String> producer, String topic) {
this.producer = producer;
this.topic = topic;
}
@Override
public void onEvent(Event event) {
// LOGIN_ERROR 등 보안 이벤트를 비동기 전송
try {
String payload = mapper.writeValueAsString(Map.of(
"category", "USER_EVENT",
"type", event.getType().name(),
"realmId", event.getRealmId(),
"clientId", event.getClientId(),
"userId", event.getUserId(),
"ipAddress", event.getIpAddress(),
"error", event.getError(),
"time", event.getTime(),
"details", event.getDetails()
));
producer.send(new ProducerRecord<>(topic, event.getRealmId(), payload));
} catch (Exception e) {
// 절대 인증 흐름을 깨뜨리지 않는다 — 로깅 후 진행
LoggerFactory.getLogger(getClass()).warn("event publish failed", e);
}
}
@Override
public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
try {
String payload = mapper.writeValueAsString(Map.of(
"category", "ADMIN_EVENT",
"operation", adminEvent.getOperationType().name(),
"resourceType", String.valueOf(adminEvent.getResourceTypeAsString()),
"resourcePath", adminEvent.getResourcePath(),
"realmId", adminEvent.getRealmId(),
"authUserId", adminEvent.getAuthDetails().getUserId(),
"time", adminEvent.getTime()
));
producer.send(new ProducerRecord<>(topic, adminEvent.getRealmId(), payload));
} catch (Exception e) {
LoggerFactory.getLogger(getClass()).warn("admin event publish failed", e);
}
}
@Override
public void close() {
}
}
Factory와 등록 파일도 필요합니다.
public class KafkaEventListenerProviderFactory implements EventListenerProviderFactory {
private KafkaProducer<String, String> producer;
private String topic;
@Override
public EventListenerProvider create(KeycloakSession session) {
return new KafkaEventListenerProvider(producer, topic);
}
@Override
public void init(Config.Scope config) {
Properties props = new Properties();
props.put("bootstrap.servers", config.get("bootstrapServers", "kafka:9092"));
props.put("key.serializer", StringSerializer.class.getName());
props.put("value.serializer", StringSerializer.class.getName());
props.put("acks", "1");
props.put("linger.ms", "20");
this.producer = new KafkaProducer<>(props);
this.topic = config.get("topic", "keycloak-events");
}
@Override
public String getId() {
return "kafka-event-listener";
}
@Override
public void close() {
if (producer != null) producer.close();
}
}
META-INF/services/org.keycloak.events.EventListenerProviderFactory
파일 내용: com.example.keycloak.KafkaEventListenerProviderFactory
JAR를 providers 디렉토리에 넣고 빌드 후, realm의 event listener에 등록합니다.
cp keycloak-kafka-listener.jar /opt/keycloak/providers/
/opt/keycloak/bin/kc.sh build
./kcadm.sh update events/config -r myrealm \
-s 'eventsListeners=["jboss-logging","kafka-event-listener"]'
구현 시 철칙이 하나 있습니다. **listener의 실패가 인증을 실패시키면 안 됩니다.** onEvent는 인증 트랜잭션 경로에서 호출되므로, 외부 전송은 비동기/버퍼링으로 하고 예외는 삼켜야 합니다. Kafka가 죽었다고 전사 로그인이 막히는 설계는 최악입니다.
Loki로 보내는 가벼운 대안
커스텀 JAR 없이 가볍게 시작하려면, `jboss-logging` listener가 남기는 이벤트 로그를 구조화 JSON으로 출력하고 Promtail/Alloy가 Loki로 수집하는 구성이 실용적입니다.
JSON 로그 출력 + 이벤트 로그 레벨 조정
bin/kc.sh start \
--log-console-output=json \
--log-level=INFO,org.keycloak.events:DEBUG
promtail 설정 발췌 — Keycloak 이벤트만 라벨링
scrape_configs:
- job_name: keycloak
static_configs:
- targets: [localhost]
labels:
job: keycloak
__path__: /var/log/keycloak/*.log
pipeline_stages:
- json:
expressions:
logger: loggerName
message: message
- match:
selector: '{job="keycloak"} |= "org.keycloak.events"'
stages:
- labels:
logger:
Kafka 경로는 SIEM/실시간 탐지 파이프라인에, Loki 경로는 운영 조회와 중기 보관에 적합합니다. 둘은 배타적이지 않으며 함께 쓰는 조직이 많습니다.
메트릭 엔드포인트 — Micrometer와 /metrics
Keycloak은 micrometer 기반 메트릭을 관리 포트(기본 9000)의 /metrics로 노출합니다.
bin/kc.sh start \
--metrics-enabled=true \
--event-metrics-user-enabled=true \
--event-metrics-user-tags=realm,clientId,idp \
--http-metrics-histograms-enabled=true
- `metrics-enabled`: JVM, DB 커넥션 풀(Agroal), HTTP, Infinispan 캐시 메트릭을 노출합니다.
- `event-metrics-user-enabled`: 26.x의 사용자 이벤트 메트릭입니다. 로그인 성공/실패 같은 이벤트가 카운터로 집계되어, DB 이벤트 테이블을 긁지 않고도 실시간 통계를 얻을 수 있습니다.
- `event-metrics-user-tags`: 메트릭에 붙는 태그를 제어합니다. clientId, idp 태그는 카디널리티를 키우므로 client 수가 많은 환경에서는 신중히 선택하세요.
Prometheus 스크레이프 설정 예시입니다.
prometheus.yml 발췌
scrape_configs:
- job_name: keycloak
metrics_path: /metrics
static_configs:
- targets: ['keycloak-0.mgmt:9000', 'keycloak-1.mgmt:9000']
scheme: https
tls_config:
insecure_skip_verify: false
봐야 할 핵심 메트릭
| 영역 | 메트릭 (예시) | 의미와 경보 기준 |
| --- | --- | --- |
| 로그인 | keycloak_user_events_total (event 태그 login, login_error) | 실패율 급증 = 공격 또는 장애 |
| 토큰 | keycloak_user_events_total (event 태그 refresh_token, code_to_token) | 발급률 급변 = 클라이언트 이상 |
| HTTP | http_server_requests_seconds (uri, status 태그) | p99 지연, 5xx 비율 |
| DB 풀 | agroal_available_count, agroal_blocking_time_average | 풀 고갈 = 전면 장애 전조 |
| 캐시 | vendor_statistics 계열 (Infinispan sessions, realms 캐시) | 히트율 하락, 클러스터 리밸런싱 |
| JVM | jvm_memory_used_bytes, jvm_gc_pause_seconds | GC pause와 로그인 지연 상관 |
특히 **DB 커넥션 풀(agroal)**은 Keycloak 장애의 최전선 지표입니다. 풀 대기 시간이 늘기 시작하면 수 분 내에 로그인 타임아웃으로 번지는 경우가 많으므로, available_count 고갈과 blocking_time 상승에 경보를 걸어두세요.
Grafana 대시보드 구성
대시보드는 "보안 운영"과 "시스템 건강" 두 장으로 나누는 것을 권장합니다.
+----------------------------------------------------------------+
| Keycloak Security Operations |
+------------------------+---------------------------------------+
| 로그인 성공/실패 (rate) | 실패율 % (성공+실패 대비) |
| realm/client별 스택 | 임계선 표시 (5%, 20%) |
+------------------------+---------------------------------------+
| LOGIN_ERROR 사유 분포 | IP별 실패 Top 10 (Loki 쿼리) |
| invalid_user_credentials| user_not_found 비율 급증 = enumeration |
+------------------------+---------------------------------------+
| 신규 등록/비밀번호 변경 | admin 이벤트 타임라인 |
+------------------------+---------------------------------------+
+----------------------------------------------------------------+
| Keycloak System Health |
+------------------------+---------------------------------------+
| HTTP p50/p95/p99 | 5xx 비율 |
+------------------------+---------------------------------------+
| Agroal 풀 사용률/대기 | Infinispan 캐시 히트율/엔트리 수 |
+------------------------+---------------------------------------+
| JVM 힙/GC pause | 인스턴스별 CPU/스레드 |
+----------------------------------------------------------------+
자주 쓰는 PromQL 몇 가지를 적어둡니다.
5분 윈도우 로그인 실패율 (%)
100 *
sum(rate(keycloak_user_events_total{event="login_error"}[5m]))
/
sum(rate(keycloak_user_events_total{event=~"login|login_error"}[5m]))
토큰 엔드포인트 p99 지연
histogram_quantile(0.99,
sum by (le) (rate(http_server_requests_seconds_bucket{uri=~".*token.*"}[5m])))
DB 풀 대기 평균 (ms)
avg(agroal_blocking_time_average_milliseconds)
OpenTelemetry Tracing
"로그인이 느린데 어디가 느린지 모르겠다"에 대한 답이 분산 추적입니다. Keycloak 26.x는 OpenTelemetry tracing을 내장 지원합니다.
bin/kc.sh start \
--tracing-enabled=true \
--tracing-endpoint=http://otel-collector:4317 \
--tracing-protocol=grpc \
--tracing-sampler-type=traceidratio \
--tracing-sampler-ratio=0.05 \
--tracing-service-name=keycloak-prod
- 트레이스에는 HTTP 요청, DB 쿼리, LDAP 호출 등의 span이 포함되어, "로그인 2.5초 중 LDAP bind가 2.1초"를 한눈에 볼 수 있습니다.
- 프로덕션에서는 샘플링 비율을 낮게(1~5%) 시작하세요. parentbased 샘플러를 쓰면 게이트웨이부터 이어지는 trace context를 존중합니다.
- Collector에서 tail-based sampling으로 "느린 요청과 에러만 전량 보존"하는 구성이 인증 시스템과 특히 궁합이 좋습니다.
- 로그에 trace_id가 함께 찍히도록 JSON 로그와 조합하면, Grafana에서 메트릭 → 트레이스 → 로그로 드릴다운하는 삼위일체가 완성됩니다.
이상 징후 탐지 — Brute Force와 Credential Stuffing
Keycloak 내장 Brute Force Detection
realm 단위로 켜는 내장 방어 기능입니다.
./kcadm.sh update realms/myrealm \
-s bruteForceProtected=true \
-s failureFactor=5 \
-s waitIncrementSeconds=60 \
-s maxFailureWaitSeconds=900 \
-s maxDeltaTimeSeconds=43200 \
-s permanentLockout=false
실패 횟수가 임계치를 넘으면 계정을 점증적으로 잠급니다. 단, 이것만으로는 부족합니다. 내장 기능은 **계정 단위** 방어이기 때문입니다.
Credential Stuffing은 패턴이 다르다
| 공격 | 패턴 | 내장 방어 효과 | 추가 대응 |
| --- | --- | --- | --- |
| Brute force | 한 계정에 다수 비밀번호 | 효과적 (계정 잠금) | 내장 기능 + 알림 |
| Credential stuffing | 다수 계정에 각 1~2회 시도 | 거의 무력 | IP/ASN 단위 분석, WAF, 봇 탐지 |
| Password spraying | 다수 계정에 같은 비밀번호 | 거의 무력 | 시도 비밀번호 패턴 분석, MFA |
credential stuffing은 계정당 실패가 1~2회라 계정 잠금에 걸리지 않습니다. 탐지의 단서는 **전역 패턴**입니다.
- LOGIN_ERROR 전체 비율의 급증 (특히 user_not_found, invalid_user_credentials의 동반 상승)
- 단일 IP/대역에서 다수의 서로 다른 username 시도 (Loki에서 ipAddress 기준 집계)
- 평소와 다른 지역/ASN, 비정상 User-Agent 분포
- 새벽 시간대의 균일한 요청 간격 (봇의 시그니처)
이벤트를 Kafka로 흘리고 있다면 스트림 처리(Flink/ksqlDB)로 "IP별 고유 username 수가 N분에 M개 초과" 같은 룰을 실시간 평가할 수 있습니다. 근본 대응은 결국 MFA/passkeys 확대(26.6의 로그인 폼 passkeys 통합이 도입 장벽을 크게 낮췄습니다)와 유출 비밀번호 차단 정책입니다.
알림 룰 예제 (Prometheus Alertmanager)
groups:
- name: keycloak-security
rules:
- alert: KeycloakLoginFailureRateHigh
expr: |
100 *
sum(rate(keycloak_user_events_total{event="login_error"}[5m]))
/
sum(rate(keycloak_user_events_total{event=~"login|login_error"}[5m]))
> 20
for: 10m
labels:
severity: warning
team: identity
annotations:
summary: "로그인 실패율 20% 초과 (10분 지속)"
description: "공격 또는 인증 경로 장애 가능성. 보안 대시보드를 확인하세요."
- alert: KeycloakLoginErrorSpike
expr: |
sum(rate(keycloak_user_events_total{event="login_error"}[5m]))
> 4 * sum(rate(keycloak_user_events_total{event="login_error"}[5m] offset 1d))
for: 15m
labels:
severity: critical
annotations:
summary: "로그인 실패가 전일 동시간 대비 4배 급증"
- name: keycloak-health
rules:
- alert: KeycloakDbPoolExhausted
expr: min(agroal_available_count) == 0
for: 2m
labels:
severity: critical
annotations:
summary: "DB 커넥션 풀 고갈 — 로그인 전면 장애 임박"
- alert: KeycloakTokenLatencyHigh
expr: |
histogram_quantile(0.99,
sum by (le) (rate(http_server_requests_seconds_bucket{uri=~".*token.*"}[5m])))
> 1
for: 10m
labels:
severity: warning
annotations:
summary: "토큰 엔드포인트 p99가 1초 초과"
- alert: KeycloakInstanceDown
expr: up{job="keycloak"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Keycloak 인스턴스 다운"
알림 설계의 원칙은 "보안 알림과 가용성 알림의 수신자를 분리"하는 것입니다. 실패율 급증은 보안팀 채널로, 풀 고갈은 플랫폼 온콜로 가야 하며, 모든 알림에는 대시보드/런북 링크를 첨부합니다.
감사 컴플라이언스 — ISMS-P / SOC 2 관점
관측성 스택은 그대로 감사 대응 자산이 됩니다. 심사에서 반복적으로 요구되는 항목과 매핑하면 다음과 같습니다.
| 감사 요구 (ISMS-P / SOC 2 공통) | Keycloak에서의 대응 |
| --- | --- |
| 인증 성공/실패 기록 보존 | login events + 외부 장기 보관 (SIEM/오브젝트 스토리지) |
| 관리자 행위 추적 | admin events (details 포함) + 변경 전후 representation |
| 로그의 위변조 방지 | 외부 전송 후 불변 스토리지 (WORM, 버킷 잠금) |
| 보존 기간 정책 | 내부 단기 + 외부 1년 이상 (규제별 상이) 문서화 |
| 접근 권한 검토 (정기) | Admin API로 role/group 멤버십 정기 추출 리포트 |
| 이상 접근 모니터링 | 실패율/지역/IP 기반 알림 룰 + 대응 런북 |
| 시각 동기화 | NTP — 이벤트 타임스탬프 신뢰성의 전제 |
실무 팁 몇 가지를 덧붙입니다.
- 감사인이 요구하는 것은 "로그가 있다"가 아니라 **"로그를 보고 행동한 증적"**입니다. 알림 발생 → 확인 → 조치 기록이 남는 워크플로(티켓 연동)를 만들어 두세요.
- admin events의 representation에는 민감 정보가 포함될 수 있습니다. 외부 전송 파이프라인에서 마스킹 정책을 정의하고, 개인정보 보존 기간 규정과 로그 보존 기간이 충돌하지 않는지 법무와 확인이 필요합니다.
- realm 설정/정책의 변경 자체를 IaC(Terraform, keycloak-config-cli)로 관리하면 "변경 통제" 요건의 상당 부분이 Git 이력으로 증빙됩니다. admin events는 콘솔을 통한 비정상 경로 변경을 잡아내는 보조 수단이 됩니다.
운영 베스트 프랙티스 요약
- 이벤트는 켜되, DB 보존은 짧게 — 장기 보관은 SPI/로그 파이프라인으로 외부화.
- listener 구현은 절대 인증 경로를 막지 않게 비동기 + 예외 격리.
- metrics-enabled + event-metrics-user-enabled를 기본 기동 옵션으로.
- 대시보드는 보안 운영과 시스템 건강을 분리하고, agroal 풀 지표에 경보 필수.
- tracing은 낮은 샘플링으로 시작해 tail-based sampling으로 고도화.
- 계정 단위 brute force 방어와 전역 패턴 기반 stuffing 탐지를 구분해 설계.
- 알림에는 런북 링크, 보안/가용성 수신자 분리, 주기적 알림 피로도 리뷰.
- 감사 대응은 "수집"이 아니라 "대응 증적"까지 — 워크플로와 불변 보관을 갖출 것.
마치며
Keycloak의 관측성은 "이벤트(무슨 일이 있었나), 메트릭(지금 어떤 상태인가), 트레이스(왜 느린가), 로그(상세 맥락)"라는 네 기둥이 서로를 보완하는 구조로 설계할 때 완성됩니다. 26.x에 들어 메트릭과 tracing이 내장된 덕분에 진입 장벽은 크게 낮아졌고, 남은 것은 조직의 운영 규율 — 보존 정책, 알림 설계, 대응 런북 — 입니다.
인증 시스템의 모니터링은 단순한 인프라 운영을 넘어 보안 탐지와 컴플라이언스의 교차점에 있습니다. 이 글의 구성 요소들을 하나씩 쌓아 올리면, "로그인 장애를 사용자보다 먼저 아는" 그리고 "공격을 공격자가 성공하기 전에 아는" 운영 체계에 도달할 수 있을 것입니다.
참고 자료
- [Keycloak Server Administration Guide — Auditing and Events](https://www.keycloak.org/docs/latest/server_admin/index.html#auditing-and-events)
- [Keycloak Guides — Gaining insights with metrics](https://www.keycloak.org/observability/configuration-metrics)
- [Keycloak Guides — Monitoring user activities with event metrics](https://www.keycloak.org/observability/event-metrics)
- [Keycloak Guides — Root cause analysis with tracing](https://www.keycloak.org/observability/tracing)
- [Keycloak Server Developer Guide — Event Listener SPI](https://www.keycloak.org/docs/latest/server_development/index.html#_events)
- [Keycloak 26.6.0 Release Notes](https://www.keycloak.org/docs/latest/release_notes/index.html)
- [Prometheus Documentation — Alerting Rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)
- [Grafana Loki Documentation](https://grafana.com/docs/loki/latest/)
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
- [Micrometer Documentation](https://micrometer.io/docs)
- [OWASP — Credential Stuffing Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Credential_Stuffing_Prevention_Cheat_Sheet.html)
- [AICPA — SOC 2 Trust Services Criteria](https://www.aicpa-cima.com/topic/audit-assurance/audit-and-assurance-greater-than-soc-2)
현재 단락 (1/319)
인증 시스템은 장애가 나는 순간 모든 서비스의 장애가 됩니다. 로그인이 안 되면 사용자 입장에서는 서비스 전체가 죽은 것과 다름없고, 더 무서운 것은 "조용한 보안 사고" — cr...