0
Mycelium으로 복잡성 관리하기: AI 코딩 에이전트 시대의 소프트웨어 아키텍처
backend
요약
기사 전체 정리
Mycelium으로 복잡성 관리하기: AI 코딩 에이전트 시대의 소프트웨어 아키텍처
문제: 인간도, LLM도 같은 벽에 부딪힘
- 소프트웨어 아키텍처는 본질적으로 인간의 인지적 한계를 보완하기 위한 장치임
- LLM도 똑같은 문제를 겪음 - 함수 하나는 완벽하게 짜지만, 수천 개의 움직이는 부품이 있는 프로젝트에선 금방 맥을 못 추게 됨
- 이걸 "context rot"이라 부르는데, 결국 코딩 아키텍처 실패와 같은 문제임
- 공유 가변 상태(shared mutable state)에 LLM을 풀어놓으면 작동하는 솔루션이 나와도 그건 거의 우연에 가까움
Herbert Simon의 시계공 우화
- 첫 번째 시계공: 시계 전체를 한 번에 조립함. 중간에 방해받으면 처음부터 다시 해야 함
- 두 번째 시계공: 작고 안정적인 모듈을 먼저 만들어서 조립함. 방해받아도 소량의 작업만 잃음
- 핵심 교훈 - 계층적 하위 조립체(hierarchical subassemblies)가 복잡성 관리의 열쇠임
- 각 하위 시스템은 독립적으로 진화 가능하고, 한 곳의 오작동이 전체를 무너뜨리지 않음
함수형 프로그래밍의 장점과 한계
- 순수 함수 + 불변 데이터로 전역 상태 문제를 효과적으로 길들일 수 있음
- 프로그램이 데이터 파이프라인처럼 작동 - 입력 데이터가 순수 함수 체인을 거쳐 출력으로 변환됨
- 불변 데이터는 숨겨진 상태나 부작용이 없어서 신뢰할 수 있는 계약이 됨
- 그런데 문제가 있음: 데이터 변환 로직과 라우팅 로직이 여전히 뒤섞이게 됨
- 인증 핸들러 안에 "다음에 어떤 함수를 호출할지" 결정하는 코드가 섞여 들어가면서 커플링 발생
상태 머신으로 제어 역전하기
- 이벤트 버스는 라우팅 로직을 흩뿌려서 전체 흐름 추적이 불가능함
- 의존성 주입은 암묵적 콜백으로 흐름을 숨김
- 상태 머신은 라우팅을 선언적으로 만들어 한 곳에서 볼 수 있게 함
- "무엇을 할지"와 "어떻게 할지"를 강제로 분리시킴
Mycelium 프레임워크
- Clojure 기반 프레임워크로, 프로그램을 워크플로우의 재귀적 생태계로 취급함
- Maestro 위에 구축됨 - 워크플로우를 그래프로 구조화하고, 노드는 상태 계산, 엣지는 전환을 표현
- Cell: 완전히 격리된 미니 애플리케이션. IO 리소스와 입력 상태 맵만 접근 가능
- 각 Cell은 엄격한 라이프사이클을 따름: 상태 맵 수신 → 데이터 로드 → 로직 실행 → 새 상태 출력
- Malli 스키마: Cell의 입출력 계약을 공식적으로 정의함. 입력으로 뭘 받고 출력으로 뭘 낼 수 있는지 정확히 명시
{:id :auth/validate-session
:schema {:input [:map [:user-id :string] [:auth-token :string]]
:output {:authorized [:map [:session-valid :boolean] [:user-id :string]]
:unauthorized [:map [:session-valid :boolean] [:error-type :keyword]]}}}- 라우팅은 별도의
:edges와:dispatches키로 완전히 분리됨 - 워크플로우 전체 결정 트리를 EDN 스펙만 보고 파악 가능
AI 에이전트와의 시너지
- Conductor(전략 에이전트)가 EDN으로 워크플로우를 설계하고, 소규모 전문 에이전트들이 개별 Cell을 구현
- 에이전트가 Cell 작업 시 Malli 스키마가 경계를 명확히 정해줌 - 맵에 어떤 키가 있고, 타입이 뭔지 다 알려줌
- 에이전트가 계약을 위반하면 시스템이 즉시 거부하고 피드백을 제공하는 자기 교정 루프가 작동
- 인간이 지루해하는 보일러플레이트와 ceremony를 LLM은 오히려 좋아함 - 명시적이고 모호하지 않은 지도 역할을 하기 때문
테스트가 놀라울 정도로 쉬워짐
- 각 Cell이 순수 데이터 변환이라 mock 없이 테스트 가능
- "User Not Found" 시나리오 테스트? DB를 mock할 필요 없이
:user키가 없는 상태 맵을 넣어주면 끝 :mycelium/trace로 모든 전환 이력이 기록되어, 실패 시 블랙박스 기록처럼 전체 히스토리를 확인 가능- 리소스(DB 등)는 외부에서 주입되므로 테스트에서 Postgres를 mock으로 교체하는 게 워크플로우에 전혀 영향 없음
재귀적 확장: 무한 스케일
- Cell 네트워크 전체가 다시 하나의 Cell이 될 수 있음 - 자연스러운 재귀 구조
- 로그인 처리 네트워크 → 결제 프로세스의 컴포넌트 → 전체 온라인 상점의 컴포넌트로 계층화 가능
- 스케일링이 코드베이스 내 커플링 증가가 아니라 그래프 위에 컴포넌트를 배치하는 문제가 됨
- 그래프가 너무 복잡해지면 독립적인 서브그래프로 다시 분할 가능 - 컨텍스트가 감당 불가능하게 커질 필요가 없음
댓글
댓글
댓글을 불러오는 중...