본문으로 건너뛰기

EP5. 구조 기반 생성 실습

Arch-Driven Generation


Ep4에서 우리는 Cocrates의 첫 번째 핵심 활동인 Learning 파이프라인을 경험했다. Education → Knowledge Capture → Reflection. AI와 함께 배우는 방법이었다.

오늘은 Cocrates의 두 번째 핵심 활동, 구조 기반 산출물 생성 파이프라인을 직접 실습해보겠다.

Cocrates는 크게 두 가지 일을 한다. 하나는 배움이고, 다른 하나는 만듦이다. Ep4가 배움의 활동이었다면, 오늘은 만듦의 활동이다.

ADR → Spec → Generation → Verification. 이 4단계를 통해 AI와 함께 소프트웨어를 만들어가는 과정을 실제 사례를 통해 생생하게 경험해보자.


💬 "jsondb를 만들어줘" — 하지만 AI는 설계부터 권했다

여러분이 Cocrates에게 이렇게 요청했다고 상상해보라.

"examples/jsondb에 jsondb를 개발해줘."

일반적인 AI라면 어떻게 반응할까? 아마 이럴 것이다. "jsondb? 알겠습니다!" 하고는, 바로 수백 줄의 코드를 쏟아낼 것이다. 검토도 없이, 설계도 없이, 그냥 코드부터 만들어버린다.

하지만 Cocrates는 다르다. Cocrates는 이렇게 응답한다.

"이 jsondb는 어떤 용도로 쓰실 예정인가요? 아키텍처를 먼저 설계하고, 검토와 승인을 받은 뒤에 안전하게 코드를 생성하겠습니다."

여기에 Cocrates의 핵심 가치가 담겨 있다. Ep2에서 선언했던 원칙을 기억하는가? "검토되지 않은 산출물은 생성할 가치가 없다." Cocrates는 이 원칙을 지키는 AI다. 설계 없이, 검토 없이 코드를 만들어내지 않는다.

오늘 만들 것은 jsondb. 아주 간단해 보이는 로컬 JSON 저장소다. 하지만 이 단순한 프로젝트조차도 여러 가지 아키텍처 논쟁이 필요했다.


🏛️ ADR ① — 저장 모델 논쟁: "일반적인 것"의 함정

상황: 데이터를 어떻게 저장할까?

AI의 제안: "MongoDB처럼 Collection-Document 구조가 일반적이고 쉽습니다."

AI가 먼저 제안을 한다. "MongoDB처럼 Collection-Document 구조가 일반적입니다."

여기서 주목할 점은 AI가 "일반적" 이라는 단어를 썼다는 것이다. AI가 가장 많이 학습한 패턴, 수많은 예제에서 봐온 구조를 제안한 것이다.

하지만 생각해보라. *"일반적"*이라는 말은 *"보통은 그렇다"*는 뜻이지, "내 상황에 꼭 맞다" 는 뜻이 아니다.

사용자가 브레이크를 밟았다.

"아니요. 제가 원하는 건 경로를 직접 지정하는 방식이에요. Set("episode/e1.json")이라고 하면 실제로 파일 시스템에 episode/e1.json 파일이 생기는 그런 구조가 필요해요."

이것이 Path-Addressable 방식이다. AI가 처음에 제안한 Collection-Document와는 완전히 다른 접근법이다.

결국 사용자의 요구대로 경로 매핑 구조로 결정되었다.

💡 교훈: 일반적인 것이 항상 내 요구에 맞는 것은 아니다. AI가 가장 흔한 패턴을 제시했을 뿐이다. 진짜 사용 맥락을 아는 사람은 오직 사용자뿐이다.


🏛️ ADR ② — 아키텍처 논쟁: "쉽고 간단한 것"의 함정

상황: jsondb를 어떤 형태로 배포할까?

AI의 제안: "엔진 Library + 앱 구조가 가장 쉽고 간단합니다. import만 하면 바로 쓸 수 있어요."

AI의 두 번째 제안도 그럴듯해 보인다. "라이브러리만 있으면 됩니다. import 해서 바로 쓰면 돼요." 정말 쉽고 간단하다. 당장은 이 방법이 가장 편해 보인다.

하지만 사용자가 질문을 던졌다.

"그런데... 나중에 이 jsondb를 다른 앱에서도 접근해야 하면 어떻게 되지?"

이 한마디에 상황이 완전히 뒤집혔다. Library + 앱 구조는 앱 내에서만 동작한다. 다른 앱에서 접근해야 한다면? 결국 Client-Server 아키텍처가 필요해진다.

결국 Library + 앱에서 Client-Server로 구조가 변경되었다. AI가 제안한 "쉽고 간단한" 방법이 미래의 확장성을 담지 못했던 것이다.

💡 교훈: 쉽고 간단한 것이 항상 미래의 요구사항을 담을 수 있는 것은 아니다. AI는 가장 구현하기 쉬운 방법을 우선 제시한다. 사용자가 미래 시나리오를 질문해야 더 나은 구조로 발전할 수 있다.


🏛️ ADR ③ — 동시성 모델 논쟁: "품질"의 함정

상황: 여러 요청이 동시에 들어오면 어떻게 처리할까?

AI의 제안: "DB-level RWMutex 하나면 충분합니다. 간단하게 처리할 수 있어요."*

AI의 세 번째 제안도 단순하다. 뮤텍스 하나면 동시성 문제를 해결할 수 있다. 맞다. 기술적으로는 문제없다.

하지만 사용자가 성능을 예측했다.

"파일 하나를 쓰는 동안, 다른 파일을 읽는 요청까지 모두 대기해야 하나요?"

정확한 지적이다. DB-level RWMutex 하나면, 어떤 파일을 쓰든 간에 모든 읽기 요청이 하나의 뮤텍스를 기다려야 한다. 파일 A를 쓰는 동안 파일 B를 읽으려는 요청도 대기해야 한다. 파일이 100개, 1000개가 되면? 성능 병목이 발생한다.

결국 Per-File RWMutex Map으로 변경되었다. 파일마다 개별 뮤텍스를 두어, 서로 다른 파일에 대한 접근은 동시에 처리할 수 있게 되었다.

💡 교훈: AI의 제안이 기술적으로 틀린 것은 아니다. 하지만 품질(성능, 확장성) 충분히 을 고려하지 않았다. 사용자가 성능 병목을 예측하고 질문해야 품질이 개선된다.


🎯 ADR의 교훈 — AI 제안이 부적절한 3가지 이유

세 가지 ADR을 통해 우리는 중요한 교훈을 얻었다.

AI의 제안이 부적절한 이유는 다양하다. 할루시네이션만이 문제가 아니다.

  1. 일반적이다 (Generic): AI는 가장 흔한 패턴을 제안한다. 하지만 "일반적"이 "내 요구에 맞음"을 의미하지는 않는다. (ADR ①: 저장 모델)
  2. 쉽고 간단하다 (Simple): AI는 가장 구현하기 쉬운 방법을 우선 제시한다. 하지만 미래의 요구사항을 담지 못할 수 있다. (ADR ②: 아키텍처)
  3. 품질을 충분히 반영하지 못한다 (Quality-blind): AI의 제안이 기술적으로 틀리지는 않지만, 성능이나 확장성 같은 품질 속성을 고려하지 않을 수 있다. (ADR ③: 동시성 모델)

이 세 가지를 인식하면, AI의 첫 제안을 더 신중하게 검토할 수 있다.


📋 Spec — 결정의 통합

세 가지 ADR이 모두 승인된 후, Cocrates는 모든 결정을 하나의 자체 완결적인 문서로 통합했다. 이것이 Spec이다.

Spec은 하나만 읽으면 '무엇을 만들지' 완전히 이해할 수 있어야 한다. ADR을 열어볼 필요가 없다.

  • 저장 모델: Path-Addressable (key → 파일 경로 직접 매핑)
  • 아키텍처: Client-Server (다중 프로세스 지원)
  • 동시성 모델: Per-File RWMutex Map (파일 수준 동시성)

⚙️ Generation — Spec 기반 코드 생성

Spec을 근거로 Cocrates가 코드를 생성했다.

  • Go Library 7개 파일
  • REST API Server
  • CLI (Command Line Interface)
  • 모든 테스트 통과

Spec만 보고 생성되었기 때문에, 모든 코드는 Spec의 결정과 일치한다. 추가적인 가정이나 추측이 들어가지 않았다.


🔍 Verification — Spec 검증, 그리고 예상치 못한 발견

Spec을 보고 생성했지만, Spec과 다르게 구현될 수 있다. 생성된 코드는 72개의 항목으로 검증되었다.

  • 71개 항목: 통과 (pass)
  • 1개 항목: 실패 (fail) — CLI Schema URL에 이중 슬래시(//) 문제 발견

그리고 중요한 발견이 있었다. Undocumented ASR 6건.

Undocumented ASR이란 Spec에 명시되지 않았지만 생성 결과에 구현된 구조적 결정을 말한다. 이번 사례에서는 6건이나 발견되었다.

  • URL encoding 처리 방식
  • SetPath 검증 누락
  • 그 외 4건의 암묵적 설계 결정

Undocumented ASR은 실패가 아니다. 오히려 Spec을 더 완벽하게 다듬을 수 있는 기회다. 발견된 6건을 구조 설계로 돌아가 검토하면 된다.


🔄 Living Cycle — 구조는 고정되지 않는다

여기서 많은 사람들이 오해할 수 있는 점이 있다. 바로 이 과정을 Waterfall로 보는 것이다.

Waterfall: 구조 설계 → 구현 → 검증 (끝)

하지만 현실은 다르다. 이것은 순환(Cycle)이다.

ADR → Spec → Generation → Verification
↑ │
└──────── 피드백 반영 ←───────┘

검증에서 문제가 발견되면 구조 설계, 즉 ADR로 돌아가야 한다. 결정을 재검토하고, 필요하면 ADR 자체를 변경해야 한다. 요구사항이 변경되면? 마찬가지다. ADR 변경 → Spec 갱신 → 재생성 → 재검증의 순환을 거친다.

ADR도 살아있는 문서다. 오늘 내린 결정이 내일도 유효하라는 법은 없다.

Spec도 살아있는 문서다. ADR이 바뀌면 Spec도 바뀌어야 한다.

구조가 한 번 정해지면 변하지 않는 것이 아니다. 구조가 개발을 이끌지만, 검증 결과 및 요구사항 변경이 다시 구조를 개선한다.

구조 기반 개발은 Waterfall이 아니다. 구조가 개발을 이끌고, 개발 경험이 구조를 개선한다.


📌 오늘의 핵심

  1. AI의 제안을 검토하라. 할루시네이션만 문제가 아니다. 일반적이어서(Generic), 쉽고 간단해서(Simple), 또는 품질을 반영하지 못해서(Quality-blind) 부적절할 수 있다. 사용자의 검토와 승인이 필수다.
  2. ADR과 Spec은 함께 살아있는 문서다. 검증이나 요구사항 변경 시 구조 설계(ADR)로 돌아가 결정을 재검토한다. 구조는 한 번 정해지면 변하지 않는 것이 아니다.
  3. 검증이 곧 품질이다. Spec 검증으로도 모든 걸 잡을 수 없지만, 검증 없이는 그조차 발견할 수 없다. Undocumented ASR은 실패가 아니라 Spec을 개선할 기회다.
  4. 구조 기반 개발은 Living Cycle이다. 구조가 개발을 이끌고, 개발 경험이 구조를 개선한다. Waterfall이 아니다.

스스로에게 질문하자.

  • Artifact Generation 파이프라인의 4단계를 말할 수 있는가?
  • AI 제안이 부적절한 3가지 이유를 사례와 함께 설명할 수 있는가?
  • Undocumented ASR이 무엇인가?
  • 구조 기반 개발이 Waterfall이 아닌 이유는?

🎬 다음 편 예고

오늘 우리는 구조 기반 생성 파이프라인 — ADR, Spec, Generation, Verification — 이 어떻게 실제 프로젝트에서 작동하는지 경험했다.

다음 편, Ep6에서는 한 걸음 더 나아간다. 오늘은 기존에 있던 스킬을 사용했다면, 다음 편에서는 우리가 직접 새로운 스킬을 Cocrates와 함께 만들어본다. 바로 "스킬을 만드는 스킬"이다.

"보고서 써줘" 대신 "보고서 쓰는 법"을 AI에게 가르치는 법. 설명문이나 보고서를 자동으로 생성해주는 스킬을 직접 설계하고 만들어보는 시간이다.

마지막으로 한 가지 질문을 던지고 이번 편을 마무리하겠다.

"지금까지 AI가 짜준 첫 번째 제안을, 나는 아무 의심 없이 복사·붙여넣기 하진 않았는가?"


이 시리즈는 Cocrates Harness 프레임워크를 소개합니다. Cocrates는 소크라테스식 대화로 사용자가 주도권을 잡고 성장하도록 설계된 에이전트 하네스입니다.