전자결재 — 결국 "사건 → 판단 → 사람의 승인"이었다
전자결재는 우리가 만든 네 개의 핵심 모듈 가운데 AI 에이전트의 작동 방식에 가장 가까운 시스템이었습니다. 만들고 나서야 깨달았습니다 — 전자결재는 결재 도장을 디지털로 옮긴 게 아니라, “사건이 일어나면 시스템이 흐름을 판단하고, 마지막 결정은 사람이 내린다”는 에이전트의 원형(原型) 그 자체였다는 것을. 양식 11종을 만드는 줄 알았던 작업이 실은 **상태(state)와 전이(transition)**를 다루는 일이었다는, 그 반전의 기록입니다.
1. 먼저, 전자결재가 뭔가요
섹션 제목: “1. 먼저, 전자결재가 뭔가요”회사에서 돈을 쓰거나, 휴가를 가거나, 물건을 사거나, 출장을 가려면 보통 “윗선의 허락”이 필요합니다. 종이 시절에는 결재판에 서류를 끼워 부서장 책상에 올려 두고, 도장을 받아 다음 사람에게 넘겼습니다. 전자결재는 이 과정을 화면 안으로 옮긴 것입니다. 신청서를 쓰고(상신), 정해진 사람들이 순서대로 보고, 승인 도장을 찍거나 반려합니다.
구매요청서, 지출결의서, 출장신청서, 연차신청, 법인카드 사용, 견적서, 공문서, 업무보고서, 회의록… 양식은 11종이 넘습니다. 하지만 회사의 거의 모든 업무는 결국 한 문장으로 수렴합니다 — “누군가 신청하고, 누군가 승인한다.” 이 공통 골격을 보는 순간, 만들어야 할 것이 “11개의 서로 다른 화면”이 아니라 “1개의 흐름 엔진 + 11개의 서식”이라는 게 분명해졌습니다.
| 업무 성격 | 대표 양식(예) | 공통 골격 |
|---|---|---|
| 돈을 쓰는 일 | 지출결의서 · 법인카드 · 구매요청서 | 신청 → 결재선 → 승인/반려 |
| 자리를 비우는 일 | 연차신청 · 출장신청서 | 신청 → 결재선 → 승인/반려 |
| 알리고 보고하는 일 | 공문서 · 업무보고서 · 회의록 · 견적서 | 신청 → 결재선 → 승인/반려 |
표에서 보듯, 양식의 칸은 제각각이어도 흐름은 하나입니다. 그래서 “양식마다 따로 만들기”가 아니라 “흐름은 공유하고 칸만 갈아 끼우기”로 설계의 방향을 잡았습니다. 새 양식을 추가하는 일이 “또 하나의 화면을 짜는 일”이 아니라 “서식 정의 한 벌을 등록하는 일”이 되도록.
2. 왜 외주를 끊고 직접 만들었나
섹션 제목: “2. 왜 외주를 끊고 직접 만들었나”원래는 외주 전자결재(메신저와 묶인 패키지)를 쓰고 있었습니다. 빠르고 편했습니다. 그런데 우리가 지향하는 방향 — 사람이 데이터를 떠먹이는 ERP가 아니라, 에이전트가 사건을 감지해 먼저 일하고 사람은 승인만 하는 ERP — 에서 보면 외주 결재는 치명적인 한계가 있었습니다. 우리 업무 흐름과 깊게 엮을 수 없다는 것이었습니다.
결재는 회사 업무의 길목입니다. 출퇴근 정정도, 구매도, 휴가도 전부 이 길목을 지납니다. 그 길목을 외주의 닫힌 상자 안에 두면, 나중에 에이전트가 그 흐름에 끼어들 자리가 없습니다. 업무의 심장은 직접 쥔다는 원칙에 따라, 전자결재는 직접 만들기로 했습니다.
3. 진짜 어려운 건 양식이 아니라 “상태”였다
섹션 제목: “3. 진짜 어려운 건 양식이 아니라 “상태”였다”처음엔 “양식 11개 만들면 끝”이라고 생각했습니다. 완전히 틀렸습니다. 양식은 입력 칸의 모음일 뿐입니다. 진짜 어려운 건 문서가 살아 움직이는 방식, 즉 상태였습니다.
문서 하나가 작성(DRAFT) → 상신(SUBMITTED) → 결재중(IN_APPROVAL) → 승인(APPROVED) 또는 반려(REJECTED)로 흐릅니다. 결재선이 여러 단계면 “결재중” 상태가 한참 이어집니다. 문제는, 코드 곳곳에서 제멋대로 상태를 바꾸기 시작하면 금세 엉망이 된다는 것입니다. 어떤 화면은 반려된 문서를 다시 승인 가능하게 만들고, 어떤 처리는 완료된 문서를 슬쩍 작성중으로 되돌립니다. 그렇게 무결성이 무너집니다.
4. 처방: 모든 상태 변경은 단 하나의 문(門)을 통과한다
섹션 제목: “4. 처방: 모든 상태 변경은 단 하나의 문(門)을 통과한다”그래서 규칙을 하나로 못 박았습니다. 상태를 바꾸려는 모든 코드는, 쓰기 직전에 단 하나의 “전이 심판관(state machine)“을 거쳐야 한다. 심판관은 단순한 질문 하나를 합니다 — “지금 이 전이(예: 결재중 → 승인됨)가 허용 목록에 있는가?” 없으면 막습니다.
// 개념 예시 — 상태를 쓰기 직전, 한 곳에서 검문assertStateTransition(현재상태, 바꿀상태); // 불법 전이면 여기서 거부// '승인됨' → '취소됨' ✅ 허용// '승인됨' → '작성중' 🚫 거부 (완료 문서 직접 되돌리기 금지)// '반려됨' → 무엇이든 🚫 거부 (종착 상태)문서.status = 바꿀상태; // 통과해야만 여기 도달5. 완료된 문서는 절대 못 고친다 — 왜 그렇게까지
섹션 제목: “5. 완료된 문서는 절대 못 고친다 — 왜 그렇게까지”전자결재는 단순한 메모가 아니라 법적 효력을 갖는 기록입니다. 결재가 끝난 지출결의서의 금액을 나중에 슬쩍 고칠 수 있다면, 그 결재는 법적으로 무의미해집니다. “승인받았다”는 사실 자체를 믿을 수 없게 됩니다.
전자서명과 마감기간
섹션 제목: “전자서명과 마감기간”같은 정신이 두 곳에 더 적용됐습니다.
- 전자서명 — 한번 기록된(RECORDED) 서명은 “삭제”가 아니라 “무효화(REVOKE) 기록 추가”만 됩니다. 서명을 지우는 게 아니라, 무효화했다는 사실을 또 하나의 기록으로 남깁니다. 전자서명법의 정신을 그대로 코드에 옮긴 것입니다.
- 마감기간 — 회계 마감이 끝난 기간의 문서는 재오픈 자체를 거부합니다. 마감된 장부를 함부로 다시 여는 건 회계 감사에서 치명적이기 때문입니다.
| 대상 | 금지한 것 | 이유 |
|---|---|---|
| 승인 완료 문서 본문 | 몰래 수정 | 법적 효력 상실 |
| 반려·회수·취소된 문서 | 다시 살리기 | 종착 상태 — 흐름의 명확성 |
| 전자서명 이력 | 삭제 | 전자서명법 — 무효화 기록만 |
| 마감기간 문서 | 재오픈 | 회계 감사 무결성 |
6. 그리고 출퇴근이 결재와 만났다
섹션 제목: “6. 그리고 출퇴근이 결재와 만났다”가장 인상적이었던 순간은 출퇴근 정정 요청을 전자결재에 통합했을 때였습니다. 사무직 직원이 “어제 퇴근 시각이 잘못 찍혔어요”라고 정정을 신청하면, 같은 트랜잭션 안에서 전자결재 문서가 자동으로 생성되어 결재선을 탑니다. 최종 승인이 떨어지는 순간 근태 데이터에 정정이 반영됩니다.
- 직원이 모바일에서 근태 정정을 신청합니다.
- 시스템이 같은 작업 단위 안에서 “정정 결재 문서”를 자동 생성하고 결재선을 붙입니다.
- 결재자가 승인/반려합니다(사람의 판단).
- 승인 시점에만, 한 번만(중복 방지 가드) 근태가 실제로 고쳐집니다.
핵심은 정정이 “별도의 수정 기능”이 아니라 **“결재 흐름을 탄 사건”**이 됐다는 것입니다. 누가, 언제, 무엇을, 왜 고쳤는지가 결재 기록으로 영구히 남습니다. 단일 백엔드 위에서 모듈이 한 데이터를 공유하기에 가능한 일이었습니다.
7. 다 만들고 나서 본 것 — 에이전트의 원형
섹션 제목: “7. 다 만들고 나서 본 것 — 에이전트의 원형”완성하고 나서야 구조가 눈에 들어왔습니다. 전자결재의 골격은 우리가 지향하는 AI 에이전트와 똑같았습니다.
그래서 AI를 붙일 때도 같은 안전 원칙을 그대로 가져왔습니다 — 에이전트의 자율 행동은 기본적으로 꺼져 있고, 사람의 결재로만 켜집니다. 모든 판단은 근거(Evidence)와 함께 기록으로 남습니다. 결재가 “함부로 안 고친다 / 한 곳에서 강제한다 / 흔적을 남긴다”였듯, 에이전트도 같은 규율 위에 세웠습니다.
이 글은 SL.AIMS를 만들며 겪은 현장 회고 중 하나입니다. 전체 그림은 〈사례연구: SL.AIMS〉에 있습니다.