Rain Lag

종이 인시던트 스토리 노면전차 스위치야드: 작은 오류를 본선에 부딪히기 전에 손으로 우회시키기

‘스위치야드’ 관점, 종이 인시던트 보드, 그리고 팀 간 훈련을 통해 복잡한 소프트웨어 시스템에서 작은 실패가 대형 장애로 연쇄 확대되기 전에 막는 방법을 다룹니다.

종이 인시던트 스토리 노면전차 스위치야드: 작은 오류를 본선에 부딪히기 전에 손으로 우회시키기

현대의 프로덕션 시스템은 빽빽한 도시 철도망과 비슷합니다. 수십 개의 노선, 수백 대의 차량, 촘촘한 일정, 그리고 끊임없는 움직임. 이런 세계에서 가장 위험한 일은 떠돌이 차량이 아무 통제 없이 본선에 올라타게 두는 것입니다.

그런데 많은 엔지니어링 조직이 바로 그걸 **작은 인시던트(small incident)**에 대해서 하고 있습니다. 사소한 데이터 불일치, 간헐적으로 터지는 연동 오류, 살짝 수상한 지연(latency) 스파이크 같은 것들이죠. 이런 것들은 추적도, 관리도, 주인도 없이 시스템 이곳저곳을 떠돌다가, 핵심 사용자 플로우라는 “본선(main line)”에 도달했을 때 비로소 발견됩니다. 그때쯤이면 이미 너무 늦습니다. **완전한 장애(major outage)**가 되어버렸기 때문입니다.

이 글에서는 다른 접근법을 제안합니다. 인시던트 관리를 **노면전차 스위치야드(streetcar switchyard)**처럼 다루는 것입니다. 아주 작은 실패도 프로덕션에서 서로 충돌하기 한참 전에 **손으로 우회(hand‑routing)**하도록 만드는 거죠. 실제 재난 대응에서 사용하는 T‑Card / ICS 219, 네트워크 이론, 팀 빌딩 기법에서 아이디어를 빌려, 작은 문제를 초기에 포착하고 — 그리고 계속 작게 유지하는 — 실용적인 모델을 만들어 보겠습니다.


스위치야드 비유: 실패가 여정을 시작하는 곳

철도 시스템에서 **스위치야드(switchyard)**는 개별 차량이 다음을 위해 모이는 곳입니다.

  • 입고(Received)
  • 검사(Inspected)
  • 분류(Classified)
  • 적절한 선로로 배치(Routed onto appropriate tracks)

반대로 **본선(main line)**은 고속·고가치 열차가 오가는 구간입니다. 여기에는 반쯤 고장난 차량이 갑자기 끼어드는 일을 절대 허용하면 안 됩니다.

여러분의 프로덕션 시스템도 마찬가지입니다.

  • 스위치야드: 로그, 알람, 불안정한 테스트, 고객지원 티켓, 이상한 메트릭, 작은 에러 스파이크, 1회성 데이터 이상
  • 본선: 결제 플로우, 검색, 가입, 콘텐츠 전송, 핵심 SLA — 비즈니스를 정의하는 주요 경로

의도적인 인시던트 스위치야드를 운영하지 않으면, 작은 결함들이 통합 지형을 천천히 굴러다니다가 결국 중요한 지점에 도달합니다. 그러는 동안 이런 일들을 일으킬 수 있습니다.

  • 대시보드를 왜곡해, 잘못된 의사결정을 유도
  • 여러 서비스가 의존하는 캐시를 오염
  • 작은 데이터 조각을 망가뜨려, 그걸 사용하는 다운스트림 배치 작업을 깨뜨림
  • 재시도, 타임아웃, 서킷 브레이커가 예기치 않은 조합으로 작동하도록 유도

핵심 아이디어는 이것입니다. 작은 실패가 전파되기 전에 반드시 통과해야 하는 장소와 프로세스를 만든다.


종이 인시던트 보드: 실제 재난 대응의 T‑Card에서 빌려오기

소방·구급 같은 긴급 서비스는 수십 년 동안 비슷한 조정 문제를 다뤄 왔습니다. 인시던트 커맨드 시스템(ICS)에서는 **T‑Card(ICS 219)**라는 작은 색깔 카드로 인력과 자원, 작업의 상태를 추적합니다. 이 카드는 다음과 같은 특징을 갖습니다.

  • 단순함: 카드보드, 펜, 꽂는 슬롯만 있으면 됨
  • 높은 가시성: 모두가 볼 수 있는 보드에 꽂혀 있음
  • 풍부한 상태 표현: 색상, 위치, 메모만으로도 현재 상태를 한눈에 파악 가능

엔지니어링에서도 종이 인시던트 스위치야드 보드를 통해 비슷한 방식을 도입할 수 있습니다.

보드에는 무엇을 올릴까?

인시던트 카드는 스위치야드에 들어온 하나의 작은 실패 “차량”을 의미합니다. 최소한 다음 정보는 담는 것이 좋습니다.

  • ID / 이름 (예: DATA-ROUTER-004: 주문 피드에서 ID 불일치)
  • 발생 신호(source signal) (알람, 로그 패턴, SRE 관찰, 고객지원 티켓, 테스트 실패 등)
  • 추정 영향 범위(blast radius) (영향 가능성이 있는 서비스 / 도메인)
  • Owner (다음 액션에 책임을 가지는 사람 1명)
  • Status (new, triaging, contained, monitoring, closed 같은 간단한 단계)
  • 라우팅 결정(routing decision) (근거를 남기고 무시, 지금 즉시 수정, 가드레일을 두고 연기, 에스컬레이션 등)

디지털 시대에 왜 굳이 종이인가?

물론 이걸 디지털로도(그리고 결국엔 그렇게) 구현할 수 있습니다. 하지만 물리적이고, 눈에 잘 띄는 보드는 의외의 장점이 있습니다.

  • 우선순위를 강제합니다. 공간이 한정되어 있기 때문에, 보드가 가득 차면 무언가를 옮기거나 닫아야 합니다.
  • 사회적으로 부정하기 어렵습니다. 누구든 보드 앞을 지나가면 열려 있는 문제와 그 책임자를 볼 수 있습니다.
  • 링크 덤핑이 아니라, 짧고 사람이 읽을 수 있는 요약을 적도록 유도합니다.
  • 팀 간 조율이 손으로 만질 수 있을 만큼 구체적이 됩니다.

시간이 지나면 이를 Jira, Linear, Notion, 커스텀 대시보드 같은 디지털 도구로 미러링할 수 있습니다. 다만 T‑Card의 정신은 유지해야 합니다. 단순하고, 잘 보이고, 작은 실패의 상태 변화에 초점을 둔 추적 방식이라는 점입니다.


실패 전파를 네트워크 문제로 보기

복잡한 시스템은 도미노처럼 일렬로 쓰러지지 않습니다. 네트워크처럼 실패합니다. 비선형적이고, 의외의 곳에서 터지며, 컴포넌트 사이의 “약한 연결(weak tie)”에서 문제가 생기곤 합니다.

아키텍처를 그래프로 생각해 봅시다.

  • 노드(Node) = 서비스, 데이터베이스, 큐, 배치 잡, 외부 API
  • 엣지(Edge) = 데이터 플로우, 이벤트 스트림, API 호출, 공유 인프라

여기에서 1% 수준의 직렬화(serialization) 불일치 같은 “작은” 실패 하나가 다음과 같은 일을 일으킬 수 있습니다.

  1. 큐에 쌓이는 메시지 중 극히 일부를 오염시킵니다.
  2. 다운스트림 배치 잡이 그 레코드를 건너뛰게 만듭니다.
  3. 일부 사용자가 청구서를 받지 못합니다.
  4. 이를 고치려는 수동 보정 작업이 정상적인 검증 단계를 우회합니다.
  5. 그 결과 로그에 비정상적인 카디널리티가 생겨, 스토리지 사용량이 폭증합니다.

각 단계만 놓고 보면 아무것도 아닌 것처럼 보이지만, 합쳐지면 상당한 장애를 만들어냅니다.

인시던트 스위치야드는 이 초반 단계의 점프들을 발견하고 가로채는 장소입니다. 이를 위해서는 다음이 필요합니다.

  • 핵심 경로를 매핑합니다. 작은 컴포넌트에서 핵심 비즈니스 플로우로 이어지는 경로 중, 특히 위험한 것들을 파악합니다.
  • 인시던트에 그래프 상의 위치 태그를 붙입니다. 어떤 노드/엣지에서 발생했고, 어떤 고가치 경로에 영향을 줄 수 있는지 기록합니다.
  • 클러스터를 관찰합니다. 서로 “무관해 보이는” 작은 인시던트가 특정 연동 지점 주변에 모여 있다면, 숨겨진 전파 채널이 있을 가능성이 있습니다.

목표는 모든 작은 인시던트를 없애는 것이 아니라, 본선에 도달하기 훨씬 전에 전파 사슬을 끊는 것입니다.


대형 폭발 대신 우아한 성능 저하(Graceful Degradation)를 설계하기

탄탄한 스위치야드는 프로세스만으로 완성되지 않습니다. 아키텍처도 뒷받침해야 합니다. 작은 문제가 스위치야드를 빠져나가는 일은 결국 생길 것이므로, 시스템은 그때 **우아하게 성능이 저하(graceful degradation)**되도록 설계되어야 합니다.

도움이 되는 설계 패턴은 다음과 같습니다.

  1. 벌크헤드(Bulkhead)

    • 한 영역의 폭주나 장애가 전체 시스템을 가라앉히지 않도록 리소스를 격리합니다.
    • 예: 중요한 서비스에 전용 커넥션 풀을 두고, best‑effort 작업과 반드시 전달해야 하는 작업을 다른 큐에 분리합니다.
  2. 서킷 브레이커(Circuit Breaker) + 신중한 폴백(Fallback)

    • 단순히 빠르게 실패(fail fast)하는 데서 멈추지 말고, 똑똑하게 폴백해야 합니다.
    • 예: 개인화 서비스가 실패하면, 전체 페이지를 타임아웃시키는 대신, 빠르게 로딩되는 일반형 페이지를 제공합니다.
  3. 멱등(Idempotent)·재실행 가능(Replayable) 워크플로우

    • 작은 데이터 이슈를 감지했을 때, 해당 플로우를 **안전하게 재실행(replay)**할 수 있어야 합니다.
    • 예: 이벤트 소싱(event sourcing)이나 영속 로그(durable log)를 사용해, 수정 후 일부 이벤트만 다시 처리할 수 있게 합니다.
  4. 명확한 데이터 계약(Data Contract)과 스키마(Schema)

    • 명시적인 호환성 체크를 포함한 스키마 진화 전략을 갖추면, “사소한” 스키마 드리프트가 소비자를 몰래 깨뜨릴 가능성을 줄일 수 있습니다.
  5. Fail‑Open vs Fail‑Closed 의사결정

    • 어디에서 부분적인 데이터라도 받아들이는 것이 더 안전한지(fail open), 어디에서는 차단하는 것이 더 안전한지(fail closed)를 미리 결정해야 합니다.
    • 이런 의사결정을 인시던트 카드에 문서화해 두면, 무언가 깨졌을 때 대응자가 기대되는 시스템 행동을 빠르게 이해할 수 있습니다.

우아한 성능 저하는, 스위치야드를 빠져나간 결함이 지선(siding)이나 완행선에 부딪히게 만들고, 급행 본선에는 올라타지 못하게 만드는 일입니다.


데이터 집약적 연동에서의 가시성(Observability) 공백

데이터 레이크, Kafka 스트림, ETL 파이프라인, CDC(Change Data Capture) 피드 같은 데이터 집약적 연동은 미묘한 옵저버빌리티(Observability) 블라인드 스팟을 만들어 냅니다. 이런 시스템은 대개 다음과 같습니다.

  • 고볼륨: 개별 불량 레코드는 데이터 홍수 속에 묻혀버립니다.
  • 비동기: 문제가 수분, 수시간 뒤에 멀리 떨어진 시스템에서야 드러납니다.
  • 다중 소유자: 한 팀이 생산하고, 여러 팀이 소비합니다.

이건 작은 실패가 조용히 전파되기에 아주 좋은 채널입니다.

스위치야드를 제대로 지원하려면, 이러한 공백을 다음과 같이 메워야 합니다.

  1. 기술 메트릭뿐 아니라, 의미(Semantic)가 있는 메트릭 추가

    • “비즈니스 레벨” 신호를 추적합니다. 예: 배송 지역이 null인 주문 수, 스키마 검증에서 거부된 이벤트 비율 등.
  2. 컨텍스트를 가진 샘플링(Sampling with Context)

    • 대표적인 불량 레코드를 충분한 메타데이터와 함께 로그로 남기거나 별도로 캡처해, 출처를 추적할 수 있게 합니다.
  3. 컨슈머(Consumer) 측 검증

    • 업스트림 데이터를 무조건 신뢰하지 않습니다. 소비자는 자신의 **가정(assumption)**을 검증해야 합니다. (값 범위, 참조 무결성, enum 값 등)
  4. Red / Yellow / Green 파이프라인

    • 데이터 플로우를 신호등처럼 취급합니다. 정상(green), 감내 가능한 저하(yellow), 허용 불가(red) 상태를 정의하고, 각 상태를 서로 다른 인시던트 타입으로 스위치야드 보드에 반영합니다.

이런 관행 덕분에 미묘한 데이터 결함을 초기의 작은 인시던트로 가시화할 수 있고, 뒤늦은 고비용 장애로 커지기 전에 처리할 수 있습니다.


팀 빌딩을 스위치야드 훈련으로: 실패를 손으로 우회하는 연습

보드와 아키텍처를 아무리 잘 만들어도, 최종 성패는 사람들이 어떻게 협력하느냐에 달려 있습니다.

이 역량은 **스위치야드 드릴(switchyard drill)**이라는 프레임으로 팀 빌딩을 설계함으로써 의도적으로 기를 수 있습니다.

1. 실패 라우팅 게임(Failure Routing Game)

  • 팀에게 가상의 아키텍처 다이어그램과 인시던트 카드 덱을 줍니다. 카드에는 작은 실패가 적혀 있습니다. (부분 장애, 잘못된 메시지 형식, 클럭 스큐(clock skew), 설정 오류 등)
  • 각 카드에 대해 팀의 과제는 라우팅 결정입니다.
    • 이 실패는 어디에서 처음 관측되는가?
    • 전파 경로는 어떻게 이어질 가능성이 큰가?
    • 최소한의 액션으로 어떻게 차단할 수 있는가?
    • 누가 이 사실을 알아야 하는가?
  • 점수는 얼마큼 초기에 오류를 가로챘는지, 본선 플로우를 얼마나 잘 지켰는지에 따라 매깁니다.

2. 팀 간 시뮬레이션 데이(Cross‑Team Simulation Days)

  • 반나절 정도의 연습 세션을 다음처럼 운영합니다.
    • SRE 팀이 스테이징 환경에 작고 현실적인 장애들을 주입합니다.
    • 각 이슈는 반드시 물리적 혹은 가상 T‑Card 보드에 기록해야 합니다.
    • 팀들은 트리아지, 라우팅, 차단, 커뮤니케이션을 연습합니다.
  • 사후 회고에서는 다음을 다룹니다.
    • 어떤 초기 신호를 놓쳤는가?
    • 소유권이 모호해진 지점은 어디인가?
    • 어떤 아키텍처 결정이 도움이 되었고, 어떤 것은 방해가 되었는가?

3. 순번으로 맡는 스위치야드 컨덕터 역할

  • 주간 혹은 격주로 돌아가며 스위치야드 컨덕터(Switchyard Conductor) 역할을 맡도록 합니다.
    • 인시던트 보드를 총괄합니다.
    • 새로 발견된 작은 실패는 모두 카드로 만들어 오너를 지정하도록 합니다.
    • “오늘 야드에 들어온 차량들”을 중심으로 짧은 스탠드업 미팅을 진행합니다.

이런 연습을 통해 작은 실패를 공개적으로, 함께 다루는 문화를 평범한 일로 만들고, 팀 간 협업에 필요한 근육을 기를 수 있습니다.


모두 합치기: 당신만의 노면전차 스위치야드 만들기

작은 실패를 위한 스위치야드를 구축하려면 다음 단계로 시작할 수 있습니다.

  1. 보드를 만든다

    • 공용 공간에 물리적인 T‑Card 스타일 보드를 하나 두거나, 아주 단순한 디지털 보드를 만듭니다.
    • 무엇을 “스위치야드 인시던트”로 볼 것인지 정의합니다. (작지만 헷갈리거나, 반복되거나, 설명이 잘 안 되는 이상 징후들)
  2. 카드를 표준화한다

    • 소스, 오너, 상태, 추정 영향 범위, 라우팅 결정을 담는 가벼운 템플릿을 만듭니다.
  3. 옵저버빌리티와 연결한다

    • 엔지니어들이 수상한 신호를 발견하면, “충분히 커질 때까지” 기다리지 말고 일찍 인시던트 카드로 올리도록 장려합니다.
  4. 아키텍처를 발전시킨다

    • 카드에 쌓이는 패턴(인시던트가 몰리는 지점)을 분석해, 벌크헤드, 데이터 계약, 우아한 성능 저하 같은 영역에 투자할 우선순위를 정합니다.
  5. 팀을 훈련한다

    • 스위치야드 드릴과 시뮬레이션을 정기적으로 실시합니다.
    • 컨덕터 역할을 순환 배치해, 인시던트 스위치야드 운영 경험을 팀 전반에 퍼뜨립니다.

작은 실패가 나타났을 때, 그것들이 보이지 않는 채로 핵심 사용자 여정 쪽으로 흘러가도록 두어서는 안 됩니다. 대신, 잘 운영되는 노면전차 스위치야드로 들어오게 해야 합니다. 그곳에서 사람과 시스템이 함께 이 실패들을 안전한 선로로 손수 우회시키고, 영향 반경을 제한하며, 그때그때 지나가는 차량 하나하나로부터 배움을 얻어야 합니다.

이걸 잘 해내면, 단지 장애를 피하는 데 그치지 않습니다. 복잡성을 존중하고, 지속적으로 학습하며, 본선을 제시간에 달리게 유지하는 조직을 만드는 것입니다.

종이 인시던트 스토리 노면전차 스위치야드: 작은 오류를 본선에 부딪히기 전에 손으로 우회시키기 | Rain Lag