Backend Completeness Principle
"백엔드는 철저하게, 사용자는 간단하게."
Backend is thorough; user-facing surface is simple.
ainote 의 모든 설계 결정은 이 한 줄을 따른다. 본 문서는 이 원칙이 무엇이고, 왜 agent-native 노트 백엔드의 토대가 되는지 설명한다.
1. 두 개의 레이어, 서로 다른 책임
Backend (시스템 관리 레이어)
책임: 상태 관리, 추적, 감사(audit), 검증.
원칙:
- 모든 상태 변화를 기록한다 —
created_at,reviewed_at,reviewed_by,auto_approved같은 메타데이터 전부 - 완전한 비즈니스 로직 — 자동수락 규칙, 상태 머신, 엣지 케이스 처리
- 모든 정보를 API 로 제공 — 상태 조회, 상세 정보, 히스토리, 감사 로그
- 향후 분석 / 감사 / 문제 추적에 필요한 모든 데이터 보존
User-Facing Layer (사용자 인터페이스 + 에이전트)
책임: 사용자 경험, 의사결정 지원.
원칙:
- 최소한의 정보 제공 — 의사결정에 필요한 것만
- 빠른 행동 유도 — 버튼 2-3개 이내
- 낮은 인지 부하 — 사용자가 시스템 내부를 알 필요 없음
2. 구체 예시 — ainote 의 도구들
예 1: handoff_save / handoff_list / handoff_get
사용자(에이전트) 가 보는 것:
handoff_save({project: "logi", topic: "phase4", time: "1555", content: "..."})
# → "✅ handoff saved to handoffs/logi-phase4-1555-2026-05-14.txt"백엔드가 한 일:
- vault 의
file_indices테이블에 path / body / git_sha / updated_at 갱신 - 같은 호출이지만 7일 이전 핸드오프들을 opportunistic purge — 별도 cron 없이 write 트래픽에 cleanup 분산
- VaultIndexer 로 git commit (Render Singapore + 사용자 GitHub repo)
- subscriber 알림 (Phase 2 에서 SSE notification)
사용자는 purge 도, git commit 도, 미래 subscriber 도 모른다. 그런데 에이전트가 handoff_list 가 destructive 임을 알아야 자율 호출을 차단할 수 있다 → 그래서 우리는 destructiveHint: true annotation 으로 advertise.
예 2: vault_sync(action: "push")
사용자가 결정하는 것: conflictResolution: merge | overwrite | abort 한 가지.
백엔드가 처리하는 것:
- 파일 hash 비교 → 충돌 감지
- 충돌 시 conflictResolution 정책 적용 → merge 면 양 버전 보존, overwrite 면 remote 덮어쓰기, abort 면 트랜잭션 롤백
reindex_path!가 빈 content 받으면 indexed file 삭제 가능 (그래서 보수적으로destructiveHint: true)- 디바이스별 sync 상태 업데이트 (multi-device 동기화 무결성)
- Git commit + remote push
예 3: login_and_get_key
사용자가 요청: 이메일 + 비밀번호 → MCP key.
백엔드의 silent side effect: 이 사용자가 아직 MCP key 가 없으면 자동 생성한다 (user.mcp_keys.first || user.mcp_keys.create!).
이게 왜 중요한가? 자율 에이전트가 이 도구를 read-only 로 잘못 인식하면 사용자 동의 없이 비밀 키 생성 행위가 일어난다. 그래서 우리는 readOnlyHint: false 로 명확히 표기 — 에이전트 runtime 이 게이트할 수 있게.
3. 왜 이것이 agent-native 의 토대인가
전통적 API 디자인에서는 backend complexity 와 frontend simplicity 사이의 간극을 사용자 문서 가 메운다. 사용자가 README 를 읽고 side effect 를 학습한다.
에이전트는 README 를 안 읽는다. tools/list 만 본다.
따라서:
- 모든 side effect 를 annotations 로 advertise —
readOnlyHint/destructiveHint/idempotentHint/openWorldHint. README 가 아니라 wire contract 에 박아둔다 - 모든 destructive operation 을 보수적으로 표기 — 의심스러우면 destructive=true. 자율 에이전트가 false negative (destructive 한데 read-only 로 잘못 인식) 보다 false positive (read-only 인데 destructive 로 잘못 인식) 가 안전
- 에이전트가 알아야 할 것만 노출 — 백엔드의 purge cron, git commit, conflict resolution 알고리즘 같은 건 도구 설명에 안 들어간다. 단지 결과 와 side effect class 만
이 분리가 ainote 가 26개 도구를 동시에 Claude · Cursor · Windsurf · OpenAI · LangChain · AutoGen 에 노출할 수 있게 한 토대다. 도구 정의를 단순하게 유지하면서도 백엔드는 production-grade 의 복잡도를 가진다.
4. 출처와 더 깊은 참고
이 원칙은 우리의 다른 프로젝트 CartaG 에서 처음 명문화됐다. CartaG 의 disclosure_requests 시스템 (3자 익명 정보 공개 요청) 에서:
Backend (DB & API)
├─ disclosure_requests 테이블
│ ├─ id, post_id, requester_id
│ ├─ status: pending/approved/rejected/blocked
│ ├─ created_at, reviewed_at, reviewed_by
│ ├─ access_token, token_expires_at
│ ├─ rejection_reason, auto_approved
│ └─ (모든 상태 추적)
Frontend (UI)
└─ DM 메시지 (간단)
├─ Park Min Ho 2024.12.15
├─ "어제 여의도에서..."
├─ [승인] [거절] ← 이것만!
└─ (상태 정보 노출 X)사용자는 "어제 여의도에서..." 한 문장 + [승인] [거절] 버튼만 본다. 백엔드는 access_token / token_expires_at / rejection_reason / auto_approved 같은 메타데이터 전부를 추적한다.
ainote 가 같은 원칙을 에이전트 인터페이스에 적용한 것이다.
5. 안티패턴
이 원칙이 깨지는 상황:
❌ UI / 도구 출력에 복잡한 상태 정보 노출 — "transaction_id: txn_abc123, audit_log_id: log_xyz789 ..." 같은 응답은 사용자도 에이전트도 의사결정에 못 쓴다.
❌ 백엔드에서 "사용자 편의" 라는 이유로 데이터 축약 — handoff_list 응답에서 git_sha 를 빼면, 백엔드의 추적 무결성이 깨진다. 표시 안 하면 됐지 데이터를 버리지 마라.
❌ 프론트엔드/에이전트에서 상태 추적 로직 구현 — 클라이언트가 "이 핸드오프가 며칠 됐으니 곧 purge 되겠다" 같은 계산 하면 안 된다. 백엔드의 진실에 의존하라.
✅ 양쪽이 독립적으로 동작 — 백엔드: 모든 정보를 완전하게 API 로 제공 / 프론트엔드: 필요한 것만 UI 에 표시.
6. Trade-off 의 명시적 선택
복잡도 vs 사용성:
| 측면 | 우리의 선택 |
|---|---|
| 최종 사용자 | ✅ 매우 단순 · ✅ 빠른 액션 · ❌ 상태 추적 정보 없음 (필요 없음) |
| 자율 에이전트 | ✅ tool annotations 로 충분한 메타 · ✅ destructive call 게이트 가능 · ❌ 백엔드 내부 로직 불투명 (그래야 함) |
| 개발자 / 시스템 | ✅ 완전한 복잡도 · ✅ 모든 상태 추적 · ❌ 복잡함 (필요한 복잡함) |
결정 근거: 사용자는 빠른 의사결정이 우선. 에이전트는 신뢰 가능한 hint 가 우선. 시스템은 완전한 추적이 우선. 셋이 충돌하면 분리해서 해결.
7. 적용 체크리스트 — 새 도구 추가 시
새 MCP 도구를 추가할 때 다음을 자문하라:
- 이 도구가 호출되면 백엔드에서 어떤 side effect 가 일어나는가? (DB write, file write, 외부 API 호출, cleanup job, audit log)
- 그 중 사용자/에이전트가 알아야 할 것은? (annotations 로 표현)
- 사용자/에이전트가 결정해야 할 것은? (input schema 파라미터)
- 그 외 모든 것은 백엔드가 알아서 한다 — 문서/도구 응답에 노출하지 마라
- 보수적 표기: 의심스러우면
destructiveHint: true,openWorldHint: true
이 원칙은 ainote 의 모든 도구 / 모든 API / 모든 UI 결정에 적용된다.
새 도구를 추가하기 전 이 페이지를 한 번 더 읽으면 우리 모두에게 도움이 된다.