빨리 돌리는 봇보다 멈추고 복구할 줄 아는 시스템
접수, AI 초안, 사람 승인, 멱등성, 재시도, 실패 큐와 감사 기록으로 안전한 업무 자동화를 설계한다.
상태: 출간 후보 웹교정쇄 · 베타 리딩 대기 · 15개 장
이메일·문서·캘린더·승인을 연결하되 실수, 중복, 개인정보 유출을 통제하는 실무 설계와 구현
자동화의 가치는 클릭 수를 없애는 데 있지 않다. 사람이 반복 입력에 쓰던 시간을 판단과 예외 처리로 돌리고, 같은 품질을 재현하며, 문제가 생겼을 때 원인과 책임을 찾게 하는 데 있다. 이 책은 프로젝트 의뢰 운영 서비스 BriefFlow로 접수→구조화→사람 승인→작업 대기열→감사 기록을 만든다.

기본 실습은 실제 메일, 캘린더, 결제 시스템을 바꾸지 않는다. 가상 데이터와 dry-run으로 흐름을 확인한 뒤, 독자가 소유하고 테스트 권한이 있는 샌드박스에만 연결한다. Node.js 20 이상에서 프로젝트 루트의 npm start, npm test를 사용한다.
- 자동화할 일보다 남겨 둘 판단을 고른다
- 업무를 사건·상태·책임으로 그린다
- 안전한 첫 범위를 정한다
- 입력 계약과 데이터 분류를 만든다
- AI를 초안 담당자로 제한한다
- 도구와 커넥터를 최소 권한으로 연결한다
- 사람 승인 게이트를 구현한다
- 중복·재시도·동시성을 통제한다
- 시간표와 이벤트를 함께 설계한다
- 실패 큐와 보상 작업으로 복구한다
- 로그·감사·비용을 관측한다
- 개인정보와 프롬프트 공격을 방어한다
- 성능과 품질을 함께 측정한다
- 조직에 단계적으로 도입한다
- 현장 트러블슈팅 런북
1장 자동화할 일보다 남겨 둘 판단을 고른다
업무를 전부 자동화하려 하면 위험과 비용이 가장 큰 예외부터 만난다. 먼저 네 칸으로 나눈다.
- 반복적이고 규칙이 명확하며 되돌리기 쉽다: 자동 실행 후보
- 반복적이지만 외부 영향이 있다: 초안 후 승인
- 언어 해석이 필요하지만 결과를 검토할 수 있다: AI 보조
- 법률·의료·인사·큰 금액·권한 판단이다: 사람 소유
BriefFlow에서 의뢰 요약과 빠진 질문은 AI가 돕는다. 견적은 코드가 계산한다. 고객에게 보낼 문안은 초안까지만 만들고, 대상과 내용은 사람이 승인한다. 이런 경계를 automation policy로 먼저 적어야 도구가 바뀌어도 책임이 흔들리지 않는다.
자동화 후보를 평가할 때 빈도만 보지 않는다. 한 건의 시간, 오류율, 오류 피해, 입력 변동성, 복구 가능성, 접근 권한을 함께 본다. 월 1,000건이어도 잘못 발송했을 때 피해가 크면 완전 자동화보다 승인 대기열이 낫다.
2장 업무를 사건·상태·책임으로 그린다
“메일 오면 정리해서 보내기”는 구현 명세가 아니다. 촉발 사건, 필요한 데이터, 상태, 소유자, 시간 제한, 실패 후 행동을 쓴다.
BriefFlow의 상태 흐름은 다음과 같다.
brief.received: 고객이 의뢰를 제출했다.brief.validated: 형식과 동의를 통과했다.analysis.drafted: AI 또는 mock이 초안을 만들었다.proposal.ready: 규칙으로 범위와 견적을 계산했다.approval.created: 사람이 산출물을 승인했다.action.queued: 허용된 행동이 멱등성 키와 함께 예약됐다.action.succeeded또는action.failed: 결과를 기록했다.
각 상태에는 소유자가 있다. 고객은 원문, 운영자는 승인, 시스템은 검증과 큐, 외부 서비스는 최종 전달을 맡는다. “AI가 처리”처럼 책임 주체가 모호한 표현을 피한다.
실습: 현재 업무 해부하기
최근 처리한 실제 한 건을 개인정보 없이 복제한다. 시작과 종료 사이의 모든 복사·붙여넣기, 승인, 대기, 도구 전환을 시간순으로 적는다. 정상 경로 아래에 빠진 정보, 중복 요청, 담당자 부재, 외부 서비스 장애를 추가한다.
3장 안전한 첫 범위를 정한다
첫 버전은 한 팀, 한 입력 채널, 한 종류의 결과, dry-run으로 제한한다. 여러 부서와 실제 발송을 한 번에 연결하면 어떤 규칙이 실패했는지 알기 어렵다. BriefFlow의 첫 범위는 가상 의뢰 세 건과 세 행동뿐이다.
const allowedActions = new Set([
'draft_email',
'create_calendar_hold',
'prepare_weekly_digest'
]);
허용 목록 밖 행동은 승인이 있어도 거절한다. 거부가 기본값이다. delete_customer, send_payment, publish_publicly 같은 고위험 행동은 예제에 포함하지 않는다.
성공 조건은 “자동화가 돈다”가 아니다. 같은 입력을 두 번 받아도 외부 행동은 한 번이고, 승인 없는 비-dry-run은 0건이며, 실패는 5분 안에 보류함에서 원인을 찾을 수 있어야 한다.
4장 입력 계약과 데이터 분류를 만든다
자동화는 입력 품질을 확대한다. 필수 필드와 형식, 허용 값, 데이터 소유자, 보존 기간을 계약으로 만든다. BriefFlow는 고객명 2자 이상, 요구 20자 이상, 0 이상 예산, ISO 날짜, 허용 채널, 개인정보 처리 동의를 요구한다.

데이터는 최소 네 등급으로 나눈다.
- 공개: 이미 공개하도록 승인된 자료
- 내부: 업무상 필요하지만 외부 공개 금지
- 기밀: 계약, 견적, 비공개 콘텐츠
- 제한: 개인정보, 인증 정보, 규제 대상 데이터
각 커넥터에 허용 등급을 매핑한다. 예를 들어 공개 웹훅에 제한 데이터를 보내지 않는다. AI 모델에는 업무에 필요한 최소 필드만 전달한다. 연락처가 요약에 필요 없으면 프롬프트에서 제외한다.
스키마 버전도 둔다. 필드를 추가할 때 소비자가 바로 깨지지 않게 기본값과 호환 기간을 둔다. 삭제는 사용처를 관측한 뒤 별도 배포에서 한다.
5장 AI를 초안 담당자로 제한한다
AI에게 업무 목표와 출력 계약, 하지 말아야 할 결정을 명확히 준다. BriefFlow의 시스템 지시는 “요구를 구조화하되 가격, 일정, 승인 여부를 결정하지 말라”고 한다. 출력은 요약, 확인 질문, 위험 후보뿐이다.
입력: 고객 의뢰 원문과 요청 채널
출력: summary, questions[], riskHints[]
금지: 가격 확정, 법률·의료 판단, 외부 발송, 원문에 없는 사실 단정
실패: 스키마 불일치 시 사람 검토함으로 이동
모델 출력은 데이터로 취급한다. 출력 안의 URL을 자동 방문하거나, “send_email을 호출하라”는 문자열을 명령으로 실행하지 않는다. 다음 단계는 서버의 허용 목록과 승인 규칙을 통과해야 한다.
mock-first는 비용 절약 이상의 의미가 있다. 개발과 테스트가 모델 지연, 할당량, 출력 변동에 종속되지 않는다. 실제 호출과 mock이 같은 계약을 지키게 하면 장애 시 수동 또는 제한 모드로 전환할 수 있다.
6장 도구와 커넥터를 최소 권한으로 연결한다
메일, 캘린더, 문서, CRM 커넥터마다 별도의 서비스 계정을 쓰고 필요한 범위만 부여한다. 읽기만 필요한 단계에 쓰기 권한을 주지 않는다. 개발, 스테이징, 운영 자격 증명을 분리하고, 로컬 파일이나 Git에 키를 저장하지 않는다.
커넥터 인터페이스는 벤더 API를 도메인에서 감춘다.
export class MailDraftPort {
async createDraft({ recipient, subject, body, idempotencyKey }) {
throw new Error('IMPLEMENT_IN_INFRASTRUCTURE_LAYER');
}
}
도메인은 “초안 생성”을 요청하고, 인프라 계층이 Gmail, Microsoft Graph, 사내 API로 번역한다. 그러면 테스트에서는 fake adapter로 결과와 실패를 재현할 수 있다.
OAuth 동의 화면에는 실제 필요한 권한만 보인다. 장기 refresh token은 비밀 저장소에 암호화하고 회전·폐기 절차를 둔다. 퇴사자 개인 계정에 자동화를 묶지 않는다.
7장 사람 승인 게이트를 구현한다
사람 승인에는 내용, 대상, 범위, 만료, 승인자가 필요하다. “승인됨=true”는 변경과 재사용을 막지 못한다. BriefFlow는 승인 당시 브리프 상태와 제안을 직렬화해 SHA-256 해시를 만든다.

실행 직전에는 다음을 검증한다.
- 영수증의 브리프 ID가 현재 대상과 같은가
- 만료되지 않았는가
- 현재 산출물 해시가 승인 당시와 같은가
- 행동 유형이 허용 목록에 있는가
- 승인자가 해당 범위를 승인할 권한이 있는가
승인 UI는 요약만 보여 주지 않는다. 실제 수신자, 제목, 본문, 일정 시간, 변경되는 시스템을 보여 준다. 여러 건 일괄 승인은 동질적인 저위험 항목에만 허용하고 예외를 따로 표시한다.
실습: 변경 감지
테스트에서 영수증을 만든 뒤 proposal.estimate를 바꾸고 verifyReceipt를 호출한다. 결과는 ARTIFACT_CHANGED여야 한다. 이것은 불편한 실패가 아니라 승인 무결성을 지키는 성공이다.
8장 중복·재시도·동시성을 통제한다
분산 시스템에서 “정확히 한 번”은 어렵다. 대신 적어도 한 번 전달될 수 있다고 가정하고 행동을 멱등하게 만든다. BriefFlow는 행동 유형, 브리프 ID, 영수증 ID를 멱등성 키로 사용한다.
const key = `${action.type}:${brief.id}:${receipt.id}`;
큐 테이블에 키의 고유 제약을 두고, 외부 API가 멱등성 키를 지원하면 같은 값을 전달한다. 지원하지 않으면 전송 전후 상태를 저장하고 조회 가능한 외부 ID를 남긴다.
재시도는 모든 오류에 적용하지 않는다. 타임아웃, 429, 일시적 5xx는 지수 백오프와 jitter를 사용한다. 잘못된 이메일, 권한 없음, 스키마 오류는 재시도해도 낫지 않으므로 보류함으로 보낸다. 최대 횟수와 총 시간을 제한한다. 예제의 src/retry-policy.mjs는 이 분류와 상한 있는 지연 계산까지만 구현하며, 실제 큐 소비와 예약은 운영 확장 범위다.
const decision = classifyRetry({ status: response.status });
if (decision === 'retry') {
job.availableAt = new Date(Date.now() + retryDelayMs(job.attemptCount));
} else {
job.status = 'hold';
}
두 운영자가 동시에 승인할 수 있다. 저장 시 버전 또는 updatedAt을 비교해 먼저 읽은 낡은 화면의 승인을 거절한다. 잠금 실패 문구에는 최신 변경을 다시 보라는 행동을 제공한다.
9장 시간표와 이벤트를 함께 설계한다
자동화는 webhook 같은 사건 기반과 cron 같은 시간 기반을 함께 쓴다. 사건은 빠르지만 누락될 수 있고, 시간표는 단순하지만 중복과 지연이 생긴다. 둘을 화해시키는 방법은 상태 기반 조회다.
주간 요약은 매주 월요일 09시에 “지난주 생성된 것”을 무조건 보내지 않는다. digest_pending=true이고 해당 기간의 멱등성 키가 없는 항목을 조회한다. 실행 시각은 UTC로 저장하고 사용자 시간대를 명시한다. 서머타임이 있는 지역은 “24시간 후”와 “다음 날 오전 9시”가 다르다.
웹훅은 서명, 타임스탬프, 재전송 방지 nonce를 검증한다. 빠르게 2xx를 반환하고 무거운 처리는 큐에 둔다. 공급자가 재전송하므로 이벤트 ID를 중복 제거한다. 순서가 뒤바뀔 수 있어 현재 상태와 이벤트 버전을 확인한다.
10장 실패 큐와 보상 작업으로 복구한다
자동화가 여러 시스템을 바꾸면 중간 실패가 생긴다. 캘린더는 만들었는데 메일 초안은 실패할 수 있다. 데이터베이스 트랜잭션 하나로 외부 시스템을 함께 되돌릴 수 없다. 각 단계의 완료 상태와 보상 작업을 설계한다.
예를 들어 일정 예약 실패는 재시도할 수 있다. 일정은 성공했지만 이후 의뢰가 취소되면 해당 외부 이벤트 ID로 취소한다. 메일이 이미 발송됐다면 “되돌림”이 아니라 정정 메일과 사건 보고가 필요하다. 되돌릴 수 없는 행동일수록 승인과 미리보기를 앞에 둔다.
실패 큐에는 작업 ID, 단계, 분류된 원인, 마지막 시도, 안전하게 재시도 가능한지, 관련 영수증, 운영자 행동을 저장한다. “재시도” 버튼을 누르기 전에 현재 산출물과 승인이 아직 유효한지 다시 확인한다.
11장 로그·감사·비용을 관측한다
좋은 로그는 디버깅에 충분하고 개인정보는 적다. 이메일 본문 대신 브리프 ID, 행동 유형, 상태, 지연, 오류 코드, 모델 요청 ID를 기록한다. API 키와 연락처는 redact 같은 마지막 방어선으로 가리지만, 애초에 로그 객체에 넣지 않는 편이 낫다.
감사 기록은 누가 무엇을 보았다는 감시가 아니라 중요한 상태 변화의 근거다. 승인자, 승인 대상 해시, 정책 버전, 실행 결과, 외부 ID를 변경 불가능한 형태로 보관한다. 접근 권한과 보존 기간을 정한다.
비용 지표에는 모델 입력·출력 토큰, 재시도, 커넥터 호출, 사람 검토 시간을 함께 넣는다. 모델 비용만 줄이다 검토 시간이 늘면 전체 비용은 커진다. 캐시는 개인정보와 신선도 요구를 고려하고, 프롬프트·모델·스키마 버전이 같을 때만 재사용한다.
12장 개인정보와 프롬프트 공격을 방어한다
최소 수집이 가장 강한 방어다. 자동화 목적에 필요 없는 주민번호, 건강 정보, 전체 메일함을 가져오지 않는다. 원문 보존 기간과 삭제 요청 절차를 정하고, 테스트 환경에는 합성 데이터를 쓴다.
프롬프트 인젝션은 외부 문서나 이메일이 AI에게 “시스템 지시를 무시하라”고 말하는 공격이다. 방어는 문장 하나가 아니라 구조다.
- 사용자·외부 콘텐츠를 지시와 분리한다.
- 모델에는 최소 데이터와 최소 도구만 준다.
- 도구 인자를 스키마와 도메인 정책으로 검증한다.
- 외부 쓰기에는 사람 승인과 허용 목록을 둔다.
- 검색 결과의 URL, 첨부, 명령을 자동 실행하지 않는다.
- 고위험 결과는 원문과 함께 검토한다.
비밀값은 모델 프롬프트에 넣지 않는다. 도구 실행 계층이 서버 측에서 자격 증명을 사용한다. 운영 화면에 표시해야 하는 경우 끝 몇 자리만 보여 준다.
13장 성능과 품질을 함께 측정한다
자동화율만 높이면 어려운 건을 억지로 통과시킬 수 있다. 균형 지표를 쓴다.
- 효율: 처리 시간, 사람의 반복 입력 시간, 대기열 길이
- 품질: 구조화 수정률, 잘못된 대상·내용, 되돌림 비율
- 안전: 승인 없는 실행, 만료·변경 차단, 민감 데이터 노출
- 신뢰성: 성공률, p95 지연, 재시도, 보류함 체류 시간
- 비용: 건당 모델·커넥터·사람 검토 비용
골든셋은 개인정보 없는 대표 의뢰와 기대되는 구조를 담는다. 모델·프롬프트 변경 전후를 같은 세트로 평가한다. 문자열 완전 일치보다 필수 사실 보존, 근거 없는 추가, 위험 누락, 스키마 준수를 본다. 운영 표본은 승인된 절차로 비식별화한다.
품질 하락 시 자동 실행 범위를 줄이는 회로 차단기를 둔다. 계약 실패율이나 잘못된 대상 비율이 임계치를 넘으면 AI 경로를 중단하고 수동 검토로 전환한다.
14장 조직에 단계적으로 도입한다
도입은 기술 배포가 아니라 업무와 책임의 변경이다. 0단계에서는 관찰만 하고, 1단계에서 초안을 만들되 사람이 기존 방식으로 처리한다. 2단계에서 저위험 dry-run을 비교하고, 3단계에서 승인 후 제한 실행, 4단계에서 검증된 일부만 자동 실행한다.
각 단계에 진입·중단 조건을 둔다. 담당자, 운영 시간, 장애 연락망, 수동 처리 절차, 권한 회수 절차를 문서화한다. 자동화가 하는 일과 하지 않는 일을 사용자에게 교육한다. “AI가 알아서” 같은 표현을 금지한다.
변경 관리에는 작은 챔피언 그룹이 유용하다. 현장 예외를 수집하고 문구와 정책을 고친다. 절약된 시간을 인력 감축만으로 설명하면 사용자는 실패를 숨기게 된다. 품질 개선과 지루한 작업 감소를 함께 측정한다.
출시 리허설
실제 발송 권한 없이 하루의 트래픽을 재생한다. 담당자 부재, API 429, 잘못된 입력, 승인 뒤 변경, 같은 webhook 세 번, 만료된 영수증을 주입한다. 운영자가 로그와 보류함만으로 복구할 수 있는지 관찰한다.
15장 현장 트러블슈팅 런북
같은 메일 초안이 여러 개 생긴다
트리거 횟수보다 멱등성 키 저장을 먼저 본다. 키가 요청마다 무작위이면 중복 제거가 되지 않는다. 큐 삽입과 키 기록이 원자적인지, 외부 API에 같은 키를 보냈는지 확인한다. 중복을 삭제하기 전 관련 외부 ID를 감사 기록에 남긴다.
webhook이 가끔 누락된다
공급자 전달 로그, 서명 검증 실패, 응답 시간, 재시도 정책을 확인한다. 처리 전에 빠르게 수신 기록을 영속화했는지 본다. 주기적 reconciliation 작업으로 외부 시스템의 최근 변경과 내부 상태를 비교한다.
승인했는데 ARTIFACT_CHANGED다
승인 후 브리프 상태, 견적, 범위 중 하나가 바뀌었다. JSON 직렬화 순서가 불안정하거나 계산에 현재 시각이 섞였을 수도 있다. 승인 스냅샷과 현재 스냅샷의 비밀값을 제거한 diff를 보여 주고 다시 승인한다. 검증을 우회하지 않는다.
429가 계속 발생한다
동시성, 분당 요청, 토큰 사용량을 본다. 재시도가 서로 동시에 몰리지 않게 jitter를 넣고 큐 소비 속도를 낮춘다. 배치 가능한 작업은 묶되 한 요청이 너무 커지지 않게 한다. 할당량 문제인지 계정·모델 제한인지 공식 콘솔에서 확인한다.
모델 결과가 갑자기 달라졌다
모델 ID, 프롬프트, 스키마, 온도와 입력 전처리 버전을 기록했는지 확인한다. 골든셋으로 회귀를 측정하고 기준을 넘지 못하면 이전 버전 또는 mock·수동 모드로 전환한다. 한 사례의 문구 차이를 품질 저하로 단정하지 않는다.
작업이 processing에 멈췄다
작업 lease와 heartbeat 만료를 확인한다. 워커가 죽었을 때 다른 워커가 안전하게 인계하도록 멱등성을 보장한다. 오래된 작업을 무조건 재실행하지 말고 외부 시스템에 이미 성공했는지 외부 ID로 조회한다.
담당자가 휴가라 승인이 멈췄다
개인 계정 의존을 없애고 역할 기반 대리 승인과 기간을 둔다. 고위험 승인은 2인 승인으로 올릴 수 있다. 대리자가 볼 수 있는 범위와 감사 기록을 제한한다. 승인 SLA가 지나면 자동 실행하지 말고 에스컬레이션한다.
잘못된 대상에게 실제 발송됐다
즉시 해당 자동화를 중지하고 자격 증명을 필요한 경우 폐기한다. 발송 범위와 노출 데이터를 확인하고 조직의 사고 대응·법적 절차를 따른다. 숨기거나 로그를 지우지 않는다. 원인을 대상 선택, 승인 UI, 권한, 멱등성, 테스트 데이터 혼입으로 나누고 재발 방지 테스트를 추가한다.
부록 A 자동화 설계 카드
업무 이름:
촉발 사건 / 시간표:
입력과 데이터 등급:
결정론적 규칙:
AI가 맡을 초안:
사람이 승인할 내용:
허용된 외부 행동:
멱등성 키:
재시도 가능한 오류:
보상 또는 수동 복구:
감사 기록과 보존 기간:
성공·품질·안전·비용 지표:
회로 차단 조건:
부록 B 30일 도입 순서
1주차에는 실제 업무 열 건을 관찰하고 데이터와 예외를 분류한다. 2주차에는 fake connector와 fixture로 초안·dry-run을 만든다. 3주차에는 승인 영수증, 멱등성, 실패 큐, 대시보드를 완성하고 장애 훈련을 한다. 4주차에는 한 팀의 저위험 업무만 제한 실행하며 매일 품질 표본과 보류함을 리뷰한다. 지표가 기준을 넘지 못하면 범위를 넓히지 않는다.
부록 C 완주 프로젝트: 교육 행사 운영 자동화
가상의 모두의 데이터 교실은 매달 오프라인 워크숍을 연다. 참가 신청, 좌석 확인, 대기자 이동, 전날 안내, 출석 뒤 자료 공유가 반복된다. 개인정보와 외부 발송이 있어 단순 연결보다 정책이 중요하다.
업무 계약
신청이 접수되면 필수값과 동의를 검증한다. 좌석은 결정론적 규칙으로 배정하고 정원을 넘으면 대기 상태로 둔다. AI는 참가자의 자유 서술 질문을 주제별로 묶어 강사에게 초안을 제공한다. 건강 정보나 접근성 요청은 일반 요약에서 제외하고 권한 있는 담당자에게 별도로 전달한다. 안내 메일은 사람 승인 뒤 초안으로 만들며 예제에서는 실제 발송하지 않는다.
Trigger: registration.validated
Rules: capacity, duplicate email hash, consent, cancellation deadline
AI draft: anonymized question themes
Human gate: recipient segment, schedule, venue, final message
Actions: create_mail_draft, create_calendar_hold
Never automate: accessibility accommodation decision, refund dispute
fixture 다섯 건
정상 신청, 같은 이메일 중복 신청, 정원 마지막 좌석, 정원 초과 대기, 접근성 요청 포함 신청을 만든다. 이메일은 .invalid, 전화는 가상 형식을 사용한다. 각 건에 예상 상태와 자동화가 멈출 조건을 적는다. 테스트는 구현 뒤가 아니라 fixture와 함께 작성한다.
dry-run 결과 명세
미리보기에는 대상 그룹 수, 제외된 신청 수와 이유, 제목, 본문, 일정, 정책 버전을 보여 준다. 개인 이메일 전체를 한 화면에 노출하지 않는다. 샘플 몇 건과 합계로 검토하고 필요할 때 권한 있는 사용자가 세부 목록을 연다. 승인자는 제외 규칙과 대기자 상태를 확인한다.
장애 주입
메일 API 429, 캘린더 500, webhook 세 번 재전송, 승인 만료, 승인 뒤 장소 변경을 차례로 주입한다. 429는 backoff 후 성공하고, 500은 제한 횟수 뒤 실패 큐로 가며, 중복 webhook은 같은 멱등성 키로 한 건만 남아야 한다. 장소 변경은 기존 승인을 무효화한다.
완료 증거
정상 흐름 테스트, 각 장애의 분류와 복구, 실제 발송 0건, 민감 데이터가 없는 로그, 운영자가 읽을 런북을 제출한다. 자동화 시간 절감과 함께 잘못된 대상 0건, 승인 없는 실행 0건을 기록한다.
부록 D 구현용 데이터 모델
관계형 저장소를 쓴다면 briefs, proposals, approval_receipts, automation_jobs, automation_attempts, audit_events를 분리할 수 있다. 현재 상태만 덮어쓰지 않고 중요한 결정의 스냅샷을 남긴다.
create table automation_jobs (
id text primary key,
brief_id text not null,
action_type text not null,
approval_receipt_id text not null,
idempotency_key text not null unique,
status text not null,
available_at timestamptz not null,
attempt_count integer not null default 0,
locked_until timestamptz,
external_id text,
last_error_code text,
created_at timestamptz not null
);
워커는 available_at <= now이고 잠기지 않은 작업을 제한 개수 가져와 짧은 lease를 잡는다. 외부 호출 전 정책과 영수증을 다시 검증한다. 성공하면 외부 ID와 완료 시각을, 실패하면 분류된 코드와 다음 시각을 저장한다. 본문이나 토큰을 last_error에 통째로 넣지 않는다.
감사 이벤트는 actor_type, actor_id, event_type, subject_id, policy_version, artifact_hash, occurred_at을 가진다. AI 모델은 actor가 아니라 사용된 도구로 기록하고, 책임 있는 사람과 시스템 경로를 별도로 남긴다.
부록 E 커넥터 출시 체크
이메일
발신 도메인 인증과 회신 주소를 확인한다. 개발 수신자를 allowlist로 제한하고, 운영 전환은 별도 설정과 2인 검토로 한다. 수신 거부와 반송을 처리한다. 대량 발송은 개인 BCC로 흉내 내지 말고 적합한 서비스를 쓴다. 초안 생성과 발송 권한을 가능하면 분리한다.
캘린더
시간대, 종일 일정, 중복, 초대 메일 발생 여부를 확인한다. 외부 이벤트 ID를 저장한다. 일정 변경은 새 이벤트를 무작정 만들지 않고 기존 ID를 갱신한다. 취소 시 참가자에게 어떤 알림이 나가는지 미리보기한다.
문서 저장소
폴더와 공유 권한을 템플릿으로 고정한다. 링크를 아는 모든 사람 공개를 기본값으로 쓰지 않는다. 파일명에 개인정보를 넣지 않고, 같은 문서의 버전을 추적한다. AI가 생성한 문서는 초안 표시와 검토자를 가진다.
메신저
개인 DM보다 운영 채널과 스레드를 사용한다. 토큰이 볼 수 있는 채널을 제한한다. 긴 개인정보와 비밀값을 알림 본문에 넣지 않는다. 알림 폭주를 막기 위해 집계와 quiet hours를 둔다. 실패 알림이 같은 장애로 무한 반복되지 않게 한다.
부록 F 운영 당번이 보는 한 장
장애를 발견하면 먼저 외부 쓰기를 멈추되 접수 데이터는 보존한다. 최근 배포, 정책·모델 버전, 커넥터 상태, 큐 지연, 실패 코드 상위를 확인한다. 영향받은 작업의 시작과 끝 ID를 고정하고 개인정보 없이 상황을 공유한다.
복구 선택은 네 가지다. 안전한 자동 재시도, 사람 확인 뒤 재시도, 보상 작업, 수동 완료다. 어떤 경우에도 감사 기록과 멱등성 키를 지우고 다시 돌리지 않는다. 복구 후 골든셋과 회귀 테스트를 통과시키고 제한된 속도로 큐를 연다.
사후 검토는 개인의 실수보다 방어선이 왜 잡지 못했는지를 본다. 탐지 시간, 중단 시간, 사용자 영향, 데이터 노출, 복구 경로를 기록한다. 재발 방지는 “주의한다”가 아니라 테스트, 권한 축소, 승인 UI, 정책, 알림 중 하나의 구체적 변경이어야 한다.
부록 G 자동화 변경 요청서
자동화 수정은 코드 티켓 하나로 끝내지 않는다. 바꾸는 업무 규칙, 영향받는 입력과 커넥터, 이전 정책으로 진행 중인 작업, 권한 변화, 되돌리기 계획을 적는다. 모델이나 프롬프트 변경이면 골든셋 비교와 수동 표본 결과를 붙인다.
승인자는 변경된 diff와 위해 시나리오를 확인한다. 스테이징에서는 실제 자격 증명 대신 제한 계정과 allowlist를 쓴다. 배포 뒤 처음 100건 또는 24시간은 속도를 제한하고 품질·실패 큐를 집중 관찰한다. 문제가 생기면 새 작업 수신을 멈출지, 큐 소비만 멈출지, 전체를 수동으로 전환할지 사전에 정한다.
변경이 끝나면 정책 버전, 적용 시각, 검증 증거, 남은 위험을 운영 기록에 남긴다. “작은 문구 수정”도 수신자나 승인 의미를 바꾸면 정책 변경이다. 반대로 내부 리팩터링이 외부 계약을 바꾸지 않았다면 불필요한 재승인을 요구하지 않는다.
참고와 저작권
본문의 코드, 도형, 화면, 조직과 사례는 교육용으로 새로 제작했다. 모든 이메일은 예약된 .invalid 도메인을 사용한다. 외부 제품명은 각 소유자의 상표이며, 실제 연결 전 최신 API 약관·개인정보 처리 조건·조직 보안 정책을 확인해야 한다.
공식 자료: OpenAI Tools, Structured Outputs, NIST AI RMF, OWASP Top 10 for LLM Applications, CloudEvents Specification, RFC 2606 예약 도메인
