기술

AI 루프를 완성했다고 믿었는데, 데모에서만 돌고 있었습니다

2026.08.289분 읽기

어떤 AI 운영·관제 플랫폼에 반복 이벤트 기반 사전 경고 기능을 올려뒀습니다. 매년 같은 시기에 되풀이되는 이벤트를 미리 감지하고, 과거 그 시기에 났던 장애를 근거로 점검 대상을 뽑아주는 기능입니다. 설계와 1차 구현이 끝났고 시연도 매끄럽게 돌았습니다.

그래서 완성으로 분류해두고 다음 기능으로 넘어갔습니다. 몇 주 뒤에 이 루프가 실제로 도는지 확인하려고 코드와 운영 DB를 역방향으로 훑었습니다. 결함이 14건 나왔습니다. 그중 에러를 내고 있던 것은 하나도 없었습니다.


에러가 없다는 것은 동작한다는 증거가 아니었습니다

이 기능은 감지에서 격상, 기록, 학습까지 한 바퀴 도는 루프입니다. 루프형 기능의 함정은 마디 하나가 끊겨도 앞뒤 마디가 각자 정상 응답을 돌려준다는 점입니다. 감지는 감지에 성공했고, 격상은 격상 요청을 받아 200을 돌려줬고, 기록 화면은 정상적으로 열립니다.

다만 그 화면에는 이벤트를 연결하는 입력란이 없었습니다. 즉 사람이 아무리 성실하게 기록해도 학습으로 되먹임되지 않는 구조였습니다. 예외가 없으니 로그는 깨끗하고, 대시보드 지표도 초록입니다. "완성했다"와 "실제로 돈다"의 간극은 에러 로그에 흔적을 남기지 않습니다.

다운로드.png


데모만 신버전 화면으로 열려 있었습니다

원인을 따라가 보니 실운영 격상 경로가 사건 유형을 알람 종류에서 파생시키고 있었습니다. 그 결과 이벤트 연결 UI가 없는 구버전 화면이 열렸습니다. 반면 데모 케이스만 신버전 화면을 강제로 지정해둔 코드가 한 줄 들어 있었습니다.

시연을 위해 넣은 그 한 줄이, 실운영은 구버전으로 돌고 있다는 사실을 몇 주간 가려줬습니다. 데모용 특수 경로는 편의가 아니라 부채입니다. 데모와 실운영이 같은 코드 경로를 타는지부터 확인하지 않으면, 시연 성공은 아무것도 보증하지 않습니다.


검증기는 등록되지 않은 대상 앞에서 침묵합니다

두 번째로 나온 것은 검증 자체가 무력화된 상태였습니다. 격상 기록 양식이 필드 스키마에 등록되지 않아 빈 서류도 확정 처리를 통과했습니다. 필드별 재생성은 400으로 실패하고 있었습니다. 검증기가 빠진 것이 아니라 검증할 대상이 등록되지 않은 것이고, 이 경우 기본값은 대부분 통과입니다.

시드 데이터의 키 생성 버그도 두 종류 나왔습니다. 한글 이름이 전부 같은 접두어로 뭉쳐서 성질이 다른 날들이 한 시리즈로 묶였습니다. 엉뚱한 과거 이력을 근거로 경고가 나갈 수 있는 상태였습니다. 같은 날 겹친 두 사건은 한쪽이 흡수되어 아예 소실됐고, 그 사건은 학습이 영구적으로 불가능해졌습니다.

알람과 사건의 연결이 두 곳에 저장되어 실제로 어긋나 있던 것도 있었습니다. 중복 격상 1건과 캐시 유실 6건이 이미 발생한 상태였습니다. 외래키를 가진 쪽을 정본으로 삼고 다른 쪽을 캐시로 강등해 어긋나면 자동 복구되게 바꾸고, 운영 데이터 7건을 정정했습니다.

보관 정리 잡이 격상된 알람까지 삭제 대상에 넣고 있던 것도 이때 걸렸습니다. 외래키 제약 때문에 몇 달 뒤 정리 잡 전체가 실패할 예정이었고, 발현 전에 막았습니다. 발현 시점이 미래라는 것은 중요하지 않다는 뜻이 아니라 지금 고치는 것이 가장 싸다는 뜻입니다. 조용한 성공 보고가 어떤 결함 체인에서 나오는지는 전에 한 번 해부해봤습니다.


키를 바꾸기 전에 관측을 먼저 심었습니다

구조 수정에서 가장 조심한 부분은 키의 역할 분리였습니다. 화면과 스키마를 결정하는 키와, 프롬프트 분화를 결정하는 키가 한 값에 얽혀 있었습니다. 분리는 명확한 개선이지만, 바꾼 뒤에 무엇이 실제로 선택되고 있는지 볼 수단이 없다면 개선인지 확인할 방법도 없습니다.

그래서 순서를 정했습니다. 먼저 "어떤 키가 선택됐는지"를 남기는 로그를 심고, 그 로그가 실제 트래픽에서 값을 뱉는지 확인한 다음에 키를 교체했습니다. 관측을 나중에 붙이면 폴백이 조용히 받아냈는지, 의도한 분기가 탔는지 구분되지 않습니다. 관측이 먼저고 변경이 나중입니다.

다운로드 (1).png

이 순서를 고집하는 이유는 이중화나 폴백처럼 평소에 침묵하는 장치가 존재만으로는 아무 보장이 되지 않기 때문입니다. 존재는 코드에서 확인되지만 동작은 로그에서만 확인됩니다.


검증 주기가 1년인 기능은 검증할 수 없습니다

여기까지가 첫 번째 축이고, 두 번째 축은 더 근본적인 문제였습니다. 이 기능의 원래 대상은 명절이었습니다. 학습 루프는 이번 명절에 난 장애가 다음 명절 경고의 근거가 되는 구조입니다. 즉 한 바퀴가 도는 데 1년이 걸립니다.

오늘 쓴 코드가 맞는지 1년 뒤에 알게 된다면, 그것은 검증이 아니라 기다림입니다. 기준일을 임시로 조작해 흉내를 낼 수는 있지만, 그 방식은 운영 데이터를 사람이 손으로 건드리게 만듭니다. 검증 수단이 없는 기능은 결국 검증되지 않은 채로 운영에 남습니다.

그래서 검증 대상 시나리오를 바꿨습니다. 명절과 같은 성질, 즉 특정 시기에 통신 부하가 몰리는 성질을 가지면서 주기가 짧은 정부 이벤트를 등록했습니다. 훈련 일정과 세금 납기 같은 것들입니다. 기준일을 조작하지 않고 오늘 기준으로 그대로 실행했습니다.

그러자 "D-22, 과거 장애 1건, 점검 대상 시스템" 형태의 경고가 실제로 발행되고, 이어서 AI 점검표 생성까지 동작하는 것을 눈으로 확인했습니다. 만든 것과 도는 것 사이의 간극이 처음으로 닫혔습니다.

기준

원래 시나리오(명절)

대리 시나리오(짧은 주기 이벤트)

학습 한 바퀴

1년

수 주

오늘 검증 가능

불가

가능

기준일 조작 필요

필요

불필요

성질

특정 시기 통신 부하 집중

동일

함께 리허설 수단도 만들었습니다. 기준일을 지정해 재현하거나 강제로 재발사하는 실행 진입점입니다. 이전에는 리허설을 하려면 이벤트의 리드 일수를 임시로 조작해야 했습니다. 이 진입점을 만드는 과정에서 라우터 커밋 누락 버그도 나왔습니다. 200을 반환하는데 저장은 안 되던 유형이고, 회귀 테스트를 붙였습니다.

다운로드 (2).png


남긴 3건과 이 점검의 한계

결함 14건 중 11건을 수정하고 테스트 191건이 통과했습니다. 남긴 3건 중 가장 큰 공백은 알림 실제 발송이 아직 없어 경고가 화면에만 뜬다는 점입니다. 루프가 도는 것과 사람에게 닿는 것은 다른 문제이고, 후자는 아직 열려 있습니다.

대리 시나리오를 고른 근거도 짚어둘 부분이 있습니다. 이번에는 통신 부하라는 공통 성질을 기준으로 골랐습니다. 성질이 다른 이벤트로 대체하면 검증한 것과 실제로 쓸 것이 어긋나므로, 대리 시나리오 선정은 편법이 아니라 설계 판단에 해당합니다.

이런 전수 점검이 의미를 갖는 시점도 정해져 있습니다. 기능이 어느 정도 완성된 뒤여야 합니다. 만드는 중에 하면 아직 이어놓지 않은 마디를 결함으로 세게 되고, 숫자만 커지고 판단은 흐려집니다.


AI 기능을 운영에 올렸다면 점검할 것

데모와 실운영이 같은 코드 경로를 타는가. 시연을 위해 한 곳만 강제 지정한 값이 남아 있지 않은가

루프의 마디마다 "여기까지 왔다"를 남기고 있는가. 끊긴 지점을 로그로 지목할 수 있는가

검증기의 커버리지를 확인했는가. 미등록 대상이 기본값 통과로 빠져나가고 있지 않은가

키·플래그를 바꾸기 전에 무엇이 선택됐는지 남기는 관측을 먼저 심었는가

이 기능의 학습 한 바퀴는 며칠인가. 그 주기가 검증 가능한 길이가 아니라면 같은 성질의 짧은 주기 대리 시나리오가 있는가

운영 데이터를 손으로 조작하지 않고 리허설할 진입점이 있는가

이번 점검에서 얻은 것은 결함 목록보다 순서였습니다. 관측을 먼저 심고, 데모와 실운영이 한 경로인지 확인하고, 검증 주기가 긴 기능은 검증 가능한 시나리오로 갈아끼운 뒤에 완성 여부를 말하는 순서입니다. 완성 판정을 시연이 아니라 한 바퀴 돈 기록으로 내리면, 14건 같은 목록은 몇 주 뒤가 아니라 그날 나옵니다.

#AI전환기#조용한오작동#관측성#검증가능성#학습루프#데모경로#운영점검