
오전 10시 30분.
Slack은 조용했습니다.
평소라면 그 시간에 Traffic Daily Report가 와야 했습니다.
전날 교통 서비스의 퍼널 전환율.
주문과 취소 실적.
그리고 자동 해석 코멘트까지.
매일 오던 메시지였습니다.
그런데 오지 않았습니다.
처음엔 지연이라고 생각했습니다.
몇 분쯤 늦을 수 있으니까요.
하지만 곧 이메일함이 눈에 들어왔습니다.
Databricks 작업 실패 알림.
순간 느낌이 왔습니다.
“아, 이건 그냥 늦는 게 아니다.”
Slack이 조용한 데는 이유가 있었습니다.
1. 메시지가 오지 않았다
문제는 단순해 보였습니다.
Slack 알림이 안 왔다.
하지만 장애는 늘 단순한 얼굴로 나타납니다.
겉으로는 Slack 문제처럼 보였습니다.
그런데 정말 Slack 문제일까요?
가능성은 두 가지였습니다.
하나.
Databricks에서 리포트 생성이 실패했다.
둘.
리포트는 만들어졌지만 Slack 발송에 실패했다.
이 둘은 완전히 다른 문제입니다.
첫 번째라면 데이터와 Notebook을 봐야 합니다.
두 번째라면 Slack Token, Channel ID, 발송 함수를 봐야 합니다.
여기서 섣불리 판단하면 안 됐습니다.
저는 Codex를 열었습니다.
그리고 첫 요청을 아주 짧게 던졌습니다.
- 코드는 수정하지 마.
- 원인만 분석해줘.
- Job이 실행됐는지, Notebook이 어디서 멈췄는지, Slack 발송 함수가 호출됐는지만 확인해줘.
핵심은 하나였습니다.
고치지 말고, 먼저 찾아라.
2. 범인은 Slack이 아니었다
Codex가 로그를 따라가기 시작했습니다.
Job은 실행됐습니다.
Notebook도 시작됐습니다.
그런데 중간에 멈췄습니다.
Slack 발송 함수는 호출되지도 않았습니다.
그 말은 곧 하나였습니다.
Slack은 범인이 아니었습니다.
Slack은 아무것도 받지 못했을 뿐이었습니다.
문제는 그 앞에 있었습니다.
Databricks Notebook.
그 안의 category_metrics() 함수.
그리고 그 안의 cancels_pct 계산.
오류 메시지는 명확했습니다.
[DIVIDE_BY_ZERO]
0으로 나눴다는 뜻이었습니다.
전일 취소 건수인 prev_cancels가 0이었습니다.
그런데 오늘 취소 건수와 비교해 증감률을 계산하려고 했습니다.
전일 값이 0인데 증감률을 계산한다?
수학적으로 불가능합니다.
운영상으로는 충분히 발생할 수 있습니다.
전일 취소가 0건인 날은 있을 수 있으니까요.
문제는 코드가 그 상황을 예상하지 않았다는 점이었습니다.
드디어 장애의 위치가 잡혔습니다.
3. 하나를 고치니 또 하나가 나왔다
원인을 찾았습니다.
이제 고치면 됩니다.
하지만 여기서 욕심내면 안 됩니다.
장애 대응 중 가장 위험한 순간은 원인을 찾은 직후입니다.
- “이참에 주변 코드도 정리하자.”
- “비슷한 계산식도 다 손보자.”
- “메시지 포맷도 조금 개선하자.”
그러면 장애 복구가 아닙니다.
새로운 변경 작업이 됩니다.
그래서 수정 범위를 막았습니다.
고칠 것은 하나.
prev_cancels가 null 또는 0이면 cancels_pct를 null로 처리한다.
그 외 계산 방식은 그대로 둔다.
- Slack 발송 로직은 건드리지 않는다.
- 메시지 포맷도 건드리지 않는다.
- Job 설정도 건드리지 않는다.
Codex에게 그렇게 지시했습니다.
수정이 끝났습니다.
하지만 바로 운영 Slack에 보내지 않았습니다.
send_to_slack=false.
dry-run을 돌렸습니다.
운영 데이터로 계산은 하되, Slack에는 보내지 않는 방식이었습니다.
그런데 실패했습니다.
이번엔 구문 오류였습니다.
SyntaxError: '(' was never closed
괄호 하나가 닫히지 않았습니다.
AI가 고친 코드도 검증이 필요했습니다.
다시 지시했습니다.
괄호 오류만 고쳐줘.
다른 로직은 건드리지 마.
다시 dry-run.
이번에는 처음 오류였던 cancels_pct는 지나갔습니다.
그런데 또 멈췄습니다.
이번엔 cancel_amount_pct.
전일 취소금액인 prev_cancel_amount가 0이었습니다.
취소금액 증감률을 계산하다가 다시 0으로 나눈 것입니다.
패턴이 보였습니다.
문제는 단순히 취소 건수 하나가 아니었습니다.
전일 값이 0일 수 있는 증감률 계산에 방어 로직이 부족했던 것입니다.
다시 최소 수정.
prev_cancel_amount가 null 또는 0이면 cancel_amount_pct도 null.
화면에는 기존 방식대로 N/A.
다른 것은 그대로.
다시 dry-run.
이번에는 성공했습니다.
- Job 상태는 Succeeded.
- 메시지 preview 4종 생성 완료.
- Slack 발송 없음.
- 0분모 오류 재발 없음.
이제 운영으로 보낼 준비가 됐습니다.
4. 결 — Slack 메시지는 다시 도착했다
마지막 단계였습니다.
운영 재실행.
이번에는 send_to_slack=true.
하지만 조건을 분명히 걸었습니다.
- 한 번만 실행한다.
- 실패해도 임의로 재시도하지 않는다.
같은 기준일로 여러 번 실행하면 Slack 메시지가 중복 발송될 수 있기 때문입니다.
실행했습니다.
결과는 성공.
- Slack 메인 요약 메시지 1건.
- 퍼널 상세 스레드 1건.
- 주문·취소 상세 스레드 1건.
- 자동 해석 스레드 1건.
모두 발송됐습니다.
오전 10시 30분에 오지 않았던 메시지가 마침내 도착했습니다.
겉으로 보면 작은 장애였습니다.
Slack 메시지 하나가 안 온 일.
하지만 그 안에는 꽤 많은 판단이 들어 있었습니다.
- Slack 문제인지 Databricks 문제인지 구분하는 일.
- AI에게 분석과 수정을 분리해 맡기는 일.
- 수정 범위를 최소화하는 일.
- dry-run으로 검증하는 일.
- 운영 재실행을 한 번으로 제한하는 일.
- 그리고 마지막으로 장애보고서를 남기는 일.
장애는 복구됐습니다.
하지만 여기서 진짜 중요한 질문이 남았습니다.
PM은 이런 상황에서 어디까지 할 수 있어야 할까요?
오늘의 교훈: AI 시대의 PM에게 장애 대응이란 무엇인가
이번 경험을 통해 제가 확인한 것은 단순하지 않았습니다.
“PM도 코드를 고칠 수 있다”는 이야기가 아닙니다.
“AI가 개발자를 대신할 수 있다”는 이야기도 아닙니다.
핵심은 그보다 현실적입니다.
PM은 이제 장애 상황을 더 이상 남의 일처럼 바라보기 어렵다는 것입니다.
PM은 서비스의 목적을 알고 있습니다.
운영 맥락을 알고 있습니다.
어떤 데이터가 누구에게 왜 필요한지도 알고 있습니다.
그렇다면 장애가 났을 때 최소한 1차적으로는 문제를 좁힐 수 있어야 합니다.
PM이 장애 대응에서 해야 할 일
PM이 모든 장애를 직접 해결할 필요는 없습니다.
그건 불가능하고, 바람직하지도 않습니다.
하지만 최소한 다음은 할 수 있어야 합니다.
| PM이 해야 할 일 | 의미 |
| 장애 신호 인지 | 알림 실패, 이메일 오류, 데이터 이상을 빠르게 감지 |
| 문제 위치 구분 | 데이터 문제인지, 코드 문제인지, 발송 문제인지 분리 |
| 영향 범위 판단 | 고객 영향인지 내부 운영 영향인지 구분 |
| AI에게 분석 지시 | 무작정 수정이 아니라 원인 분석부터 요청 |
| 수정 범위 제한 | 고칠 것과 건드리지 않을 것을 명확히 지정 |
| dry-run 검증 | 실제 운영 반영 전 안전하게 확인 |
| 운영 재실행 통제 | 중복 실행, 중복 발송, 임의 재시도 방지 |
| 장애보고서 작성 | 원인, 조치, 검증, 남은 리스크 기록 |
이 정도만 해도 PM의 역할은 크게 달라집니다.
개발자에게 “안 됩니다”라고 전달하는 사람에서,
“여기까지 확인했고, 원인은 이쪽으로 보이며, 영향 범위는 이 정도입니다”라고 말하는 사람으로 바뀝니다.
협업의 질이 달라집니다.
AI 도구를 쓸 때 가장 중요한 원칙
이번 작업에서 가장 중요했던 원칙은 세 가지였습니다.
1. 먼저 분석하고, 나중에 수정한다
AI에게 처음부터 “고쳐줘”라고 하면 위험합니다.
장애 대응의 첫 단계는 수정이 아닙니다.
원인 위치를 찾는 것입니다.
좋은 요청은 이렇습니다.
코드는 수정하지 말고 원인만 분석해줘.
Job 실행 여부, Notebook 실패 위치, Slack 발송 함수 호출 여부를 확인해줘.
이 한 문장이 작업의 안전성을 크게 높입니다.
2. 고칠 것보다 고치지 않을 것을 더 명확히 한다
운영 장애 대응에서는 변경 범위가 곧 리스크입니다.
이번 작업에서도 고치지 않을 것을 분명히 했습니다.
- Slack 발송 로직은 변경하지 않는다.
- 메시지 포맷은 변경하지 않는다.
- Job 스케줄은 변경하지 않는다.
- KPI 정의는 변경하지 않는다.
- 관련 없는 리팩토링은 하지 않는다.
AI는 유능합니다.
그래서 더 위험할 수 있습니다.
많이 바꿀 수 있기 때문입니다.
PM은 AI에게 “무엇을 할지”뿐 아니라 “무엇을 하지 말지”까지 지시해야 합니다.
3. 실제 운영 전에는 반드시 dry-run을 한다
운영 장애에서 가장 피해야 할 것은 성급한 복구입니다.
고쳤다고 바로 운영에 반영하면 안 됩니다.
특히 Slack 알림처럼 여러 사람이 보는 채널은 더 그렇습니다.
dry-run은 운영 리스크를 줄이는 최소 장치입니다.
이번에도 send_to_slack=false로 먼저 검증했습니다.
메시지 preview가 생성되는지 확인했습니다.
Slack에는 보내지 않았습니다.
그 후에야 운영 재실행을 했습니다.
그리고 재실행도 한 번만 했습니다.
장애보고서는 왜 남겨야 하는가
장애를 고쳤다고 끝이 아닙니다.
기록하지 않으면 같은 문제가 다시 발생했을 때 다시 처음부터 추적해야 합니다.
장애보고서는 복잡할 필요가 없습니다.
다만 핵심은 빠지면 안 됩니다.
| 항목 | 내용 |
| 장애 요약 | 언제, 무엇이 실패했는가 |
| 영향 범위 | 어떤 리포트나 업무에 영향이 있었는가 |
| 원인 | 로그로 확인된 직접 원인은 무엇인가 |
| 조치 | 실제로 무엇을 수정했는가 |
| 검증 | dry-run 결과는 어땠는가 |
| 운영 재실행 | 실제 발송 또는 복구가 성공했는가 |
| 남은 리스크 | 중복 발송 등 주의할 점은 무엇인가 |
| 종료 여부 | 추가 조치가 필요한가 |
좋은 장애보고서는 길 필요가 없습니다.
다만 나중에 봤을 때 이 질문에는 답할 수 있어야 합니다.
왜 터졌고, 어떻게 고쳤고, 다시 터지면 어디를 보면 되는가.
PM에게 필요한 것은 코딩 실력보다 운영 문해력이다
이번 일을 겪으며 가장 크게 느낀 점은 이것입니다.
PM에게 필요한 것은 단순한 코딩 실력이 아닙니다.
물론 코드를 알면 도움이 됩니다.
하지만 더 중요한 것은 운영 문해력입니다.
운영 문해력이란 이런 것입니다.
- 시스템이 어떤 흐름으로 움직이는지 아는 것.
- 장애가 어디서 발생했는지 구간을 나누는 것.
- 로그에서 핵심 단서를 읽는 것.
- AI에게 작업 범위를 정확히 지시하는 것.
- 실제 운영 반영 전 검증 조건을 설계하는 것.
- 장애 이후 기록을 남기는 것.
이 능력이 있으면 PM은 개발자를 대체하지 않아도 됩니다.
대신 개발자와 훨씬 더 정확하게 협업할 수 있습니다.
“안 돼요”가 아니라,
“Slack 발송 전 Notebook 계산 단계에서 실패했고, 전일 값 0에 대한 증감률 계산이 원인으로 보입니다”라고 말할 수 있습니다.
이 차이는 큽니다.
PM의 일은 요구사항 문서에서 끝나지 않는다
오늘의 시작은 아주 작았습니다.
Slack 메시지가 오지 않았습니다.
그뿐이었습니다.
하지만 그 작은 이상 신호를 따라가다 보니 Databricks Notebook의 계산 오류가 있었고, 0분모 예외 처리가 빠져 있었고, dry-run 검증이 필요했고, 운영 재실행과 장애보고서까지 이어졌습니다.
이 경험은 저에게 하나의 기준을 남겼습니다.
AI 시대의 PM은 개발자가 될 필요는 없습니다.
하지만 자신이 운영하는 시스템이 어디서 실패했는지 모른 채 기다리기만 하는 사람으로 남아서도 안 됩니다.
- AI 도구를 잘 활용하면 PM도 장애를 구조화할 수 있습니다.
- 원인을 좁힐 수 있습니다.
- 최소 수정 범위를 통제할 수 있습니다.
- 검증 조건을 만들 수 있습니다.
- 그리고 복구 이후 기록까지 남길 수 있습니다.
결국 중요한 것은 AI에게 판단을 넘기는 것이 아닙니다.
사람이 판단 구조를 만들고,
AI에게 실행 가능한 단위로 일을 나누고,
매 단계마다 검증하고 승인하는 것입니다.
오늘 오지 않은 Slack 메시지 하나는 그 사실을 분명히 알려줬습니다.
PM의 일은 기획서에서 끝나지 않습니다.
서비스가 실제로 운영되는 순간, PM의 일은 다시 시작됩니다.
'Productivity Notes > Vibe Coding' 카테고리의 다른 글
| 바이브코딩으로 플랫폼을 만들며 깨달은 것들 (0) | 2026.06.25 |
|---|---|
| 바이브코딩 시즌2: GuideNet Phase 1을 끝내며 배운 하네스 엔지니어링 (0) | 2026.06.24 |
| PM이 AI로 주간 뉴스 클리핑 봇을 직접 만들어본 이야기 (0) | 2026.06.12 |
| 데이터 실적이 맞지 않을 때, PM은 어디서부터 확인해야 할까 (feat. CODEX) (1) | 2026.05.16 |
| PM의 데이터 자동화 실험: Databricks SQL과 Slack으로 데일리 리포트 만들기 (1) | 2026.05.15 |