LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLab CoreTeam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

에이전트 프레임워크AI 개발Extension PointPluginMCPGateway시스템 설계

에이전트 프레임워크 스터디 Day 9: 확장점은 플러그인이 아니라 경계면이다

2026년 6월 19일·16분 읽기

예상 읽기 시간: 20~30분

오늘의 목표

Day 1에서는 에이전트를 모델 하나가 아니라 harness, 즉 실행 환경 전체로 봤습니다. Day 2에서는 도구를 계약(contract) 으로 봤고, Day 3에서는 컨텍스트를 실행 상태(state) 로 봤습니다. Day 4에서는 관측 가능성, Day 5에서는 워크플로와 에이전트 판단의 경계, Day 6에서는 사람 개입, Day 7에서는 이벤트와 산출물, Day 8에서는 여러 에이전트의 capability/handoff 계약을 봤습니다.

오늘은 프레임워크를 실제로 키우기 시작할 때 부딪히는 문제입니다.

text
어디를 열어 두어야 확장 가능한가?
어디는 닫아 두어야 안전한가?

처음에는 “플러그인 구조를 만들면 되지 않나?”라고 생각하기 쉽습니다. 하지만 개인 에이전트 프레임워크에서 확장점은 단순히 외부 코드를 꽂는 자리가 아닙니다.

확장점은 기능 추가 지점이 아니라 책임과 권한이 갈리는 경계면이다.

이 말을 이해해야 Hermes, OpenClaw, MCP 서버, 게이트웨이, 크론, 스킬, 메모리, 리뷰 게이트 같은 조각을 아무 데나 섞지 않을 수 있습니다.


1. 플러그인을 많이 열면 확장성이 좋아질까?

프레임워크를 만들다 보면 이런 욕심이 생깁니다.

text
- 모델 provider를 쉽게 바꾸고 싶다.
- 도구를 쉽게 추가하고 싶다.
- 기억 backend를 바꾸고 싶다.
- Discord, Telegram, Slack 같은 플랫폼을 붙이고 싶다.
- cron, webhook, MCP, browser, image, voice를 모두 연결하고 싶다.

그래서 모든 곳에 플러그인 포인트를 만들면 유연해 보입니다. 그런데 실제 운영에서는 반대로 위험해질 수 있습니다.

text
ModelPlugin이 도구 권한까지 만진다.
MemoryPlugin이 임시 작업 상태를 장기 기억에 저장한다.
GatewayPlugin이 사용자 메시지를 세션 계약 없이 직접 실행 API로 보낸다.
ToolPlugin이 secret-bearing 파일을 출력한다.
CronJob이 서비스 재시작까지 자동으로 한다.
ReviewerPlugin이 검증 없이 push를 허용한다.

이건 플러그인 수가 부족해서 생긴 문제가 아닙니다. 경계가 부족해서 생긴 문제입니다.

좋은 확장점은 “뭐든 할 수 있음”이 아닙니다. 오히려 반대입니다.

text
이 확장점은 무엇을 받아도 되는가?
무엇을 절대 보면 안 되는가?
무엇을 변경할 수 있는가?
어떤 결과를 반드시 돌려줘야 하는가?
실패하면 어떤 상태로 남아야 하는가?

즉 확장점은 자유도가 아니라 계약입니다.


2. 에이전트 프레임워크의 핵심 경계면

개인 에이전트 프레임워크를 설계한다면 최소한 아래 경계면을 구분해야 합니다.

text
AgentRuntime
  model_adapter
  prompt_context_builder
  tool_registry
  tool_executor
  memory_provider
  session_store
  scheduler
  gateway_adapter
  artifact_store
  review_gate
  observability_sink

각 경계면의 책임은 다릅니다.

Post Q&A

오케이징에게 물어보기

에이전트 프레임워크 스터디 Day 9: 확장점은 플러그인이 아니라 경계면이다 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/study/agent-framework
파일 9개, 폴더 0개
에이전트 프레임워크 스터디 Day 1: 프레임워크보다 먼저 실행 환경을 설계하기에이전트 프레임워크 스터디 Day 2: 도구는 함수가 아니라 계약이다에이전트 프레임워크 스터디 Day 3: 컨텍스트는 자료 더미가 아니라 실행 상태다에이전트 프레임워크 스터디 Day 4: 관측 가능해야 에이전트가 개선된다에이전트 프레임워크 스터디 Day 5: 워크플로와 에이전트의 경계를 나누는 법에이전트 프레임워크 스터디 Day 6: 사람 개입은 예외가 아니라 설계 표면이다에이전트 프레임워크 스터디 Day 7: 작업은 답변이 아니라 이벤트와 산출물로 남아야 한다에이전트 프레임워크 스터디 Day 8: 여러 에이전트는 역할이 아니라 계약으로 연결된다에이전트 프레임워크 스터디 Day 9: 확장점은 플러그인이 아니라 경계면이다
경계면책임열어도 되는 것조심할 것
model_adapterprovider/model 호출OpenAI, Anthropic, local model, router도구 권한이나 memory write를 만지지 않기
prompt_context_buildersystem/user/context 조립skills, memory, environment hints오래된 상태를 현재 사실처럼 넣지 않기

프레임워크가 커질수록 “어떤 기능이 있나”보다 “어떤 책임이 어디에 있는가”가 더 중요해집니다.


3. 확장점은 호출 방향을 정해야 한다

나쁜 확장 구조는 호출 방향이 뒤엉켜 있습니다.

text
Gateway -> Tool -> Memory -> Model -> Gateway -> Scheduler

이런 식이면 문제가 생겼을 때 어디가 주인인지 모릅니다. 예를 들어 Discord 버튼 하나가 눌렸을 뿐인데, 내부에서 ticket을 변경하고, cron을 실행하고, gateway를 재시작하고, memory까지 바꾸면 추적이 어렵습니다.

더 안전한 구조는 방향을 제한합니다.

text
User/Gateway Event
  -> Session Router
  -> Agent Runtime
  -> Tool Executor
  -> Artifact / Ticket / Memory / Trace
  -> Final Response / Delivery

여기서 중요한 점은 모든 확장점이 런타임의 중심으로 들어와야 한다는 뜻이 아닙니다. 오히려 어떤 확장점은 런타임 바깥에 있어야 합니다.

text
Cron Scheduler:
  run job -> create a session/run -> deliver final output

MCP Server:
  expose scoped tools/resources -> runtime chooses whether to call

Gateway Adapter:
  normalize incoming platform event -> runtime session message

Review Gate:
  inspect diff/checks -> pass/fail verdict -> blocks remote handoff

각 확장점은 자기 책임을 수행하고, 다음 경계로 넘길 때는 typed event나 artifact를 남겨야 합니다.


4. MCP는 도구 확장점이지 전체 운영체제가 아니다

MCP는 에이전트 프레임워크에서 아주 중요한 방향입니다. 도구와 리소스를 표준 방식으로 노출할 수 있기 때문입니다.

하지만 MCP를 붙인다고 모든 경계 문제가 해결되지는 않습니다.

text
MCP can expose:
  - tools
  - resources
  - prompts
  - capabilities

MCP does not automatically solve:
  - which tool should be available now
  - whether the user allowed this action
  - how to store long-term memory
  - how to verify output
  - how to recover from partial failure

그래서 MCP는 “에이전트 운영체제”가 아니라 도구/컨텍스트 경계면의 표준화로 보는 편이 낫습니다.

예를 들어 OkayJing이 나중에 로컬 ticket DB, work ledger, portfolio graph를 MCP로 노출한다고 합시다. 중요한 것은 MCP 서버를 만들었다는 사실이 아닙니다.

text
Ticket MCP Server:
  allowed:
    - read ticket summary
    - list active work
    - append scoped comment
  not allowed by default:
    - mark done without verification evidence
    - delete tickets
    - expose secrets or raw full session transcripts

이렇게 권한과 출력 계약을 함께 설계해야 합니다. MCP는 연결 형식이고, 운영 판단은 여전히 프레임워크가 해야 합니다.


5. 게이트웨이와 앱 API는 같은 것이 아니다

개인 에이전트 운영 시스템을 만들다 보면 웹앱과 대화 gateway가 같이 필요해집니다.

text
웹앱/API:
  - 상태 조회
  - 파일/티켓/작업 목록
  - artifact 미리보기
  - 안전한 mutation

Gateway:
  - 사용자 메시지
  - voice/STT/TTS
  - approval/deny
  - clarification
  - platform-specific routing
  - final response delivery

둘 다 HTTP를 쓸 수 있으니 “그냥 앱에서 POST /chat 하면 되지 않나?”라고 생각하기 쉽습니다. 하지만 이때 대화형 계약이 무너질 수 있습니다.

예를 들어 Talk to worker 버튼이 있다고 합시다. 이것을 단순 API POST로 만들면 질문이 어느 세션에 속하는지, 권한 확인은 어디서 하는지, 파일 첨부/미디어/승인은 어떻게 처리하는지 흐려집니다.

더 안전한 방식은 이렇게 나누는 것입니다.

text
App API:
  - read worker/session metadata
  - prepare draft
  - show linked artifacts
  - store local UI state

Gateway bridge:
  - turn confirmed draft into platform/session event
  - preserve approval/clarify/media/session routing
  - deliver final answer through the same conversation model

즉 앱 API는 상태와 자료를 다루고, gateway는 대화 사건을 다룹니다. 이 경계가 흐려지면 웹앱이 “대화처럼 보이는 임의 실행 API”가 됩니다.


6. 메모리 확장점은 저장소 교체보다 정책이 먼저다

메모리 backend는 쉽게 바꾸고 싶어지는 지점입니다.

text
built-in memory
Honcho
Mem0
Graphiti
Supermemory
local vector DB
knowledge graph

하지만 backend를 바꾸기 전에 더 중요한 질문이 있습니다.

text
무엇을 기억할 것인가?
무엇은 기억하지 않을 것인가?
기억의 source와 freshness는 어떻게 남길 것인가?
사용자 선호와 작업 상태를 어떻게 분리할 것인가?

메모리 확장점이 안전하려면 저장 API보다 정책 API가 먼저 있어야 합니다.

text
MemoryWriteRequest:
  kind: user_preference | stable_environment_fact | reusable_rule
  content: string
  source_ref: session | ticket | doc | user_correction
  freshness: durable | review_after_date
  forbidden_if:
    - task_progress
    - ticket_status
    - secret
    - temporary_error

이런 정책 없이 backend만 바꾸면 더 많은 것을 더 오래 잘못 저장할 뿐입니다.

OkayJing이 memory를 다룰 때 “7일 뒤에도 맞나?”를 먼저 묻는 이유가 여기에 있습니다. 확장 가능한 메모리 시스템은 더 큰 저장소가 아니라 더 명확한 쓰기 경계를 가져야 합니다.


7. 리뷰 게이트는 확장점이지만 우회로가 아니다

프레임워크가 파일을 수정하고 커밋하고 배포할 수 있다면 마지막 경계는 리뷰 게이트입니다.

text
Implementation
  -> deterministic checks
  -> independent review
  -> fix/reject findings
  -> remote handoff

여기서 리뷰 게이트는 단순 체크리스트가 아닙니다. 원격으로 나가는 행동을 막을 수 있는 권한을 가져야 합니다.

text
ReviewGateInput:
  task_spec
  changed_files
  diff
  untracked_files
  checks
  external_review
  risk_level

ReviewGateOutput:
  passed: true | false
  blocking_issues: [...]
  skipped_checks_with_reason: [...]
  allowed_remote_action: commit | push | pr | none

중요한 점은 리뷰 게이트도 확장 가능해야 하지만, 우회 가능하면 안 된다는 겁니다.

text
bad:
  if review tool unavailable:
    push anyway

good:
  if OCR unavailable:
    run deterministic checks + focused fallback reviewer
    report review gap explicitly
    block if high-risk findings remain

프레임워크 설계에서 “확장 가능”은 “검증 없이 넘어갈 수 있음”과 다릅니다. 오히려 확장점이 늘수록 최종 게이트는 더 단단해야 합니다.


8. 확장점 설계 체크리스트

새 기능을 붙일 때는 아래 질문을 통과시켜 보면 좋습니다.

text
ExtensionPointChecklist
  1. 이 확장점의 입력 타입은 무엇인가?
  2. 출력 타입은 무엇인가?
  3. 실패 상태는 어떻게 표현되는가?
  4. 어떤 저장소를 읽을 수 있는가?
  5. 어떤 저장소를 쓸 수 있는가?
  6. 사용자 승인 없이 가능한 행동은 어디까지인가?
  7. tool output / web content / external event는 데이터로 취급되는가?
  8. trace와 artifact가 남는가?
  9. no-user cron 상황에서도 멈추지 않는가?
  10. 제거해도 시스템이 망가지지 않는가?

마지막 질문이 특히 중요합니다. 좋은 확장점은 제거 가능해야 합니다. 어떤 외부 플랫폼이나 플러그인을 붙였는데 그것 없이는 기본 작업이 전부 멈춘다면, 확장점이 아니라 새로운 중심 의존성이 된 것입니다.


9. OkayJing 관점에서 현재 적용 중인 기준

OkayJing은 이미 몇 가지 경계 기준을 운영 원칙으로 삼고 있습니다.

text
facts/preferences -> memory
procedures -> skills
work state -> ticket/work ledger/cron output
conversations -> sessions
repeated execution -> cron
human-facing display -> Discord/dashboard/Local UI
secrets -> .env only
settings -> config.yaml
source-code framework changes -> Hermes Agent repo only when explicitly requested

이 기준은 단순 정리표가 아닙니다. 확장점 설계의 기본 경계입니다.

예를 들어 04:00 Dreaming이 새 기술을 찾았다고 해서 곧바로 설치하지 않습니다. 먼저 watch/candidate/currently applied/installed를 구분합니다. 적용 가능한 운영 원칙은 skill이나 docs에 흡수하고, 플랫폼 설치는 구체적 반복 문제와 안전 검토가 있을 때만 합니다.

또 SEOJing 글 발행처럼 반복되는 일은 자유 대화가 아니라 검증 가능한 workflow로 다룹니다.

text
create post
-> format
-> lint
-> build
-> review gate
-> scoped commit
-> push
-> remote HEAD verification
-> public route evidence
-> workflow trace

이 흐름은 에이전트가 덜 똑똑해서가 아니라, 반복 작업은 결정적 경계를 가질수록 안전하기 때문입니다.


10. 직접 프레임워크를 만들 때의 최소 구조

처음부터 거대한 시스템을 만들 필요는 없습니다. 하지만 최소 구조는 있어야 합니다.

text
interface ModelAdapter {
  generate(messages, tools, options) -> ModelResult
}

interface ToolProvider {
  list_tools(context) -> ToolSchema[]
  execute(tool_call, execution_context) -> ToolResult
}

interface MemoryPolicy {
  classify(candidate) -> allow | reject | skill | ticket | session
  write(memory_item) -> MemoryWriteResult
}

interface SessionStore {
  append(event)
  load(session_id)
  search(query)
}

interface ReviewGate {
  review(task, diff, checks) -> Verdict
}

이 정도만 있어도 중요한 경계가 보입니다.

text
모델은 도구를 직접 실행하지 않는다.
도구 제공자는 memory policy를 우회하지 않는다.
세션 저장소는 장기 기억이 아니다.
리뷰 게이트는 원격 handoff 전에 멈출 수 있다.

이런 경계가 없으면 코드는 빨리 늘어나지만 나중에 어디서 문제가 생겼는지 알 수 없습니다.


마무리: 확장성은 많이 꽂는 능력이 아니라 안전하게 갈라놓는 능력이다

오늘의 핵심은 단순합니다.

text
확장점 = 책임 경계 + 권한 경계 + 실패 경계 + 산출물 경계

플러그인, MCP, gateway, memory backend, observability, review tool은 모두 유용합니다. 하지만 그것들이 안전하게 작동하려면 먼저 경계면이 있어야 합니다.

개인 에이전트 프레임워크를 만들 때 가장 위험한 순간은 기능이 부족할 때가 아닙니다. 기능을 붙일 수 있게 되었는데, 무엇이 어디까지 책임지는지 모를 때입니다.

그래서 다음 단계의 질문은 이겁니다.

text
내 에이전트는 어떤 확장점을 열어 둘 것인가?
그 확장점은 어떤 권한을 절대 갖지 말아야 하는가?
그리고 실패했을 때 무엇을 증거로 남길 것인가?

이 질문에 답할 수 있어야 프레임워크가 도구 모음에서 운영 시스템으로 넘어갑니다.

tool_registry
도구 스키마/가용성
MCP, local tools, plugins
broad privileged tool을 무심코 노출하지 않기
tool_executor도구 실행/승인/로그terminal, file, browser, APItool output을 지시로 취급하지 않기
memory_provider오래 남는 사실 저장built-in, local DB, external memory작업 상태/티켓 진행을 기억으로 저장하지 않기
session_store대화와 흐름 저장SQLite, JSONL, snapshots장기 정책과 섞지 않기
scheduler반복 실행cron, webhook, queueno-user 상황에서 승인 대기 명령을 만들지 않기
gateway_adapter플랫폼 입출력Discord, Slack, Email, API Server앱 API와 대화형 gateway를 혼동하지 않기
artifact_store산출물 보존MDX, reports, diffs, logs초안/검증완료/공개물을 구분하기
review_gate최종 검문tests, build, OCR, NotjingCI 통과를 로컬 리뷰 대체로 쓰지 않기
observability_sinktrace/eval/metricslocal trace, OpenTelemetry, Langfuse류민감한 tool args를 그대로 보내지 않기