DEV06 SEP 2026 — [38 MIN]되새김 ×1 — 2026.09.06

한 사람, 5개월, 57만 줄, 하나의 AI, 하나의 하네스: 1인 개발로 스타트업 서비스 zero to one 런칭까지의 오디세이

떠나보낸 반려동물과 다시 재회하게 해 주는 서비스 메모릭(Memoriq)을 한 사람과 AI가 5개월 만에 양대 앱스토어에 올렸다. 57만 줄과 7,104개의 커밋 뒤에는 코드보다 결정적인 것이 있었다. AI 개발을 규율한 하네스 시스템과 TDD·Task Ledger 워크플로, 그리고 실패의 기록까지.

1. 한 사람과 AI가 상용 제품을 출시하기까지

App Store의 메모릭 앱 페이지

2026년 3월 중순부터 약 5개월 동안 회사의 풀스택 AI 개발자로서 **메모릭(Memoriq)**이라는 서비스를 밑바닥부터 개발했다. 7월 29일 구글 플레이스토어와 애플 앱스토어 양쪽에서 정식 승인을 받아 동시 출시했고, 현재 운영 환경에서 안정적으로 서비스하고 있다.

메모릭의 제품 정의는 단순하다. 반려동물을 떠나보낸 사람이 남겨진 사진을 올리면, 그 동물의 모습을 복원해 첫 재회 영상을 만들고 일상의 짧은 안부 영상과 기억을 담은 대화를 이어가는 컴패니언 서비스다.

“반려동물과의 갑작스러운 이별을, 다시 만나 못다 한 이야기를 건넬 시간으로.”

서비스 대상은 처음부터 좁혀 두었다. 데이터베이스 스키마의 pets.breed_kind 컬럼에 'dog''cat' 두 가지만 허용하는 검사 제약(CHECK constraint)을 걸었다.1 개와 고양이를 잃은 사람의 슬픔 하나에만 온전히 집중하기 위해서다.

숫자가 말하는 것, 5개월간의 실제 측정 지표

이 제품은 실질적인 1인 메인 개발 체제에서 AI(Claude Code 및 에이전트 하네스)와 짝을 이루어 완성했다. 2026년 9월 5일 기준으로 저장소에 집계된 수치는 다음과 같다.

지표 측정값 (2026-09-05 기준) 비고
총 커밋 수 7,104개 2026-03-16 첫 커밋부터 09-05까지
AI 공저 커밋 (Co-Authored-By: Claude) 5,680개 (79.9%) 전체 커밋의 약 8할을 AI와 작성
하네스 및 개발 도구 커밋 1,050개 (14.8%) 개발 시간의 1.5할을 하네스 구축에 투자
문서(Docs) 커밋 1,899개 (26.7%) 단일 분류 중 최다 커밋 (“기록이 곧 산출물”)
프로덕션 TypeScript/TSX 782파일 / 137,935줄 앱, 서버, 관리 도구, 공유 패키지
테스트 TypeScript/TSX 1,032파일 / 363,581줄 프로덕션 대비 2.6배
서버 테스트 대 프로덕션 비율 3.4배 (204,593줄 대 60,066줄) 결제와 데이터 무결성이 집중된 백엔드
Python 파이프라인 (이미지 선별) 79파일 / 37,927줄 테스트 10,642줄 포함
하네스 본체 (dev-tools) 144파일 / 약 29,896줄 자체 짝 테스트 스위트 55개 내장
인프라 코드 (Pulumi) 61파일 / 6,343줄 AWS, GCP, Azure 멀티 클라우드
전체 소스코드 규모 약 57만 줄 공백과 주석 포함, 외부 라이브러리 제외

AI-Native 개발이 마주한 문제

프로덕션 코드 대부분을 AI가 작성했다. 하지만 수십만 줄의 코드가 빠르게 쏟아져 나올 때, 사람이 화면 앞에 앉아 모든 변경 사항을 눈으로 읽고 검증하기란 불가능하다.

초기에는 프롬프트에 집중했다. “이러한 규칙을 지켜라”, “이 아키텍처 패턴을 따라라”라고 작업 가이드에 상세히 적어 두었다. 그러나 작업이 복잡해지거나 대화가 길어지면 사람은 물론 AI도 문서를 무시하기 일쑤였다. 프롬프트 준수율은 아무리 높아도 70~80% 선에 머물렀고, 나머지 20%의 빈틈에서 데이터 손상과 보안 결함, 아키텍처 오염이 발생했다.

그렇다면 AI가 작성한 57만 줄의 코드를 어떻게 믿고 양대 스토어에 출시할 수 있었을까?2

결론부터 말하자면, 해답은 프롬프트 작성이 아니라 **하네스 엔지니어링(Harness Engineering)**에 있었다. 부탁을 기계적 차단으로 바꾸고, 완료 판정 권한을 AI의 자기보고에서 독립된 검증 프로그램으로 넘기며, 도메인 불변식을 코드로 강제하는 시스템을 세웠다. 이 글은 그 5개월간의 개발 기록이다.


2. 도메인 언어와 분산 미디어 파이프라인

애도 도메인의 규칙, 문장도 제품이다

메모릭에서 언어는 단순한 사용자 인터페이스 문구가 아니라 제품의 핵심 안전장치다. 슬픔에 잠겨 있는 사람에게 AI의 부주의한 한마디는 단순한 오작동을 넘어 큰 상처가 되기 때문이다.

코드베이스 전체에 엄격한 언어 규칙을 세우고 정적 검사 도구로 강제했다.

  • 기다림의 언어. AI가 반려동물의 모습을 복원했을 때 “재현이 완료되었어요”나 “새로운 영상이 만들어졌습니다” 같은 기술적 처리 완료 표현을 금지했다. 대신 {{petName}}, 기다리고 있어요처럼 따뜻한 기다림의 문안만 허용했다.
  • 도메인 금지어. 내부 개발 용어(예를 들어 waitlist)가 사용자 화면에 노출되지 않도록 용어집(.claude/rules/domain-terms.md)을 두고 위반 시 편집기에서 즉시 차단했다.
  • 문안 결함의 중대성. 개발 중 위험 전환 문구가 사용자에게 사후 재회의 약속처럼 읽힐 여지가 있다는 점을 발견했다. 일반 결함이 아니라 운영 환경의 중대한 사건으로 규정하고 프롬프트와 안전 필터를 곧바로 재설계했다.

여섯 주체로 나눈 비동기 미디어 파이프라인

메모릭의 핵심 기능은 사진 한 장에서 시작해 자연스러운 움직임을 담은 영상을 만들어내는 일이다. 고품질 영상 생성 모델(Vertex AI Veo)과 컴퓨터 비전 파이프라인은 단일 웹 요청 안에서 끝내기 어렵다. 처리 시간이 길고, 모델 호출 실패 가능성이 있으며, 호출 비용도 무시할 수 없기 때문이다.

미디어 생성을 단일 서버에 몰아넣지 않고 여섯 개의 주체로 분리하여 AWS SQS FIFO 큐 기반의 비동기 파이프라인으로 엮었다.

주체 실행 환경 담당 업무
API 서버 AWS ECS (Hono) 작업 발행, 상태 머신 조율, 클라이언트 완료 통지
선별 워커 AWS ECS (Python) 업로드된 사진 중 얼굴과 자세 추출 적합도 판별 및 자르기
Gemini 분석 워커 AWS Lambda (Node.js) Gemini 비전 모델을 통한 특징 추출과 다시점 이미지 생성
분할 워커 AWS Lambda (Node.js) 배경 제거와 전경 피사체 마스킹 분리
영상 생성 워커 AWS Lambda (Node.js) Google Cloud Vertex AI Veo 기반 짧은 영상 생성
워터마크 워커 AWS Lambda (Node.js) 최종 미디어 후처리, 워터마크 합성 및 스토리지 업로드

여섯 주체가 엮인 미디어 파이프라인의 전체 흐름은 다음과 같다.

flowchart TD
    API["API 서버<br/>(ECS · Hono)<br/>작업 발행 · 상태 조율"] -->|"SNS 발행<br/>stage 라우팅 키"| SNS["SNS FIFO 토픽"]

    SNS -->|"body 필터"| Q1["FIFO SQS 큐<br/>curate"]
    SNS -->|"body 필터"| Q2["FIFO SQS 큐<br/>preview"]
    SNS -->|"body 필터"| Q3["FIFO SQS 큐<br/>splits"]

    Q1 --> W1["① 선별 워커<br/>(Python)"]
    Q2 --> W2["② Gemini 분석 워커<br/>(Lambda)"]
    Q3 --> W3["③ 배경 분할 워커<br/>(Lambda)"]

    W1 --> W2
    W2 --> W3
    W3 --> W4["④ 영상 생성 워커<br/>(Vertex AI Veo)"]
    W4 --> W5["⑤ 워터마크 워커<br/>(Lambda · 동기 invoke)"]
    W5 -->|"완료 통지"| API
    W5 -->|"업로드"| STORE[("객체 스토리지")]

API 서버는 무거운 작업을 직접 다루지 않는다. 큐에 메시지를 넣고 작업 상태를 갱신하는 조율자 역할만 맡는다.

모든 서버리스 람다 워커는 표준화된 runWorker 뼈대 위에서만 실행되도록 규정했다.

"So no worker can bypass classification."

워커 실행 중 예외가 발생했을 때, 서버에 통지하기 전에 반드시 실패의 성격을 먼저 분류하고 격리하도록 강제했다. 알림 콜백 함수가 실패하더라도 작업 격리 로직이 멈추지 않도록 순서를 고정했다. 이 순서가 뒤집히면 네트워크 장애가 생겼을 때 알림 실패로 인해 격리가 누락되고, FIFO 큐 전체가 정체되는 결함이 생기기 때문이다.

두 겹의 멱등성과 아무것도 하지 않음이 아니라는 원칙

영상 생성 모델 호출은 실패했을 때 무작정 다시 시도하면 비용이 이중으로 청구된다. 따라서 파이프라인 곳곳에 멱등성(Idempotency) 장치를 마련했다.

워커가 작업을 받으면 먼저 원격 스토리지에 해당 키(keys.rawKey)의 결과물이 이미 존재하는지 확인(objectExists)한다. 이미 생성된 영상이 있다면 비용이 드는 모델 호출은 건너뛴다.

하지만 여기서 작업을 끝내서는 안 된다. 뒤이어 실행되어야 할 워터마크 합성이나 데이터베이스 완료 통지 콜백은 정상적으로 실행되어야 한다.

“멱등성은 아무것도 하지 않는 상태가 아니다. 이미 마친 일은 건너뛰고, 아직 하지 않은 일은 끝까지 완수하는 것이다.”

상거래의 시계와 관계의 시계, 가장 값비싼 교훈

컴패니언 영상은 사용자가 반려동물과 “만난 지 며칠째”인지에 따라 알맞은 시나리오를 선택해 발송해야 한다. 개발 초기에는 이 기준일을 결제 테이블의 subscriptions.current_period_start(현재 결제 주기 시작 시각)로 계산했다.

하지만 운영 환경 시험 도중 심각한 오류가 드러났다.

  • 애플은 정기 결제가 갱신될 때마다 current_period_start를 최신 결제 시각으로 덮어썼다. 그 결과 구독이 갱신될 때마다 만난 날이 매번 0일째로 초기화되어 첫날 영상만 반복 발송되었다.
  • 구글은 최초 결제 시각을 그대로 유지하여 구독 기간과 무관하게 숫자가 계속 누적되어 카탈로그 범위를 넘어섰다.

두 플랫폼 모두 영상 발송이 완전히 중단되었다. 겉으로는 외부 플랫폼의 파편화처럼 보였지만, 본질은 상거래의 시계로 관계의 경과 시간을 측정하려 했던 도메인 모델링 오류였다.

곧바로 companion_anchor_at이라는 독립 컬럼을 분리했다. 갱신 로직이 덮어쓰지 않고 최초 가입 시점에만 고정되도록 격리했다. 이 경험을 겪은 뒤 코드베이스에 원칙 하나를 세웠다.

Google Play의 메모릭 앱 페이지

“한 컬럼에 두 역할을 맡기면 두 역할의 요구는 반드시 충돌한다.”
어떤 조건문 옆에 ‘왜 이것도 필요한지’ 설명하는 주석이 길게 붙어 있다면, 그것은 한 컬럼을 겸용한 흔적이다. 결함은 방어 로직을 덧붙이지 않은 빈자리에서 드러난다.


3. 하네스 아키텍처, 부탁이 아닌 차단으로 품질 강제하기

프롬프트는 70%, 훅은 100%

“문서에 적힌 규칙을 지켜달라”는 요청은 대화가 길어지면 잊히기 쉽다. 우리가 내린 결론은 분명했다. “부탁은 무시할 수 있지만 차단은 무시할 수 없다.”

AI의 작업 환경을 4개의 방어 층으로 감쌌다.

flowchart TD
    subgraph SESSION["AI 에이전트 세션"]
        TOOL["도구 호출<br/>(Edit · Write · Bash)"]
    end

    TOOL -->|"요청"| L1["L1 · permissions.deny<br/>49개 패턴 즉시 거부"]
    L1 -->|"허용"| L2["L2 · PreToolUse 훅<br/>AST 구문 · 대화 맥락 검증"]
    L2 -->|"허용"| L3["L3 · SessionStart 훅<br/>세션 알림 · docs.db 동기화"]
    L3 -->|"커밋"| L4["L4 · Lefthook<br/>전역 불변식 · 전체 테스트"]
    L4 -->|"푸시"| REPO["프로덕션 코드베이스"]

    L1 -.->|"거절"| B1["즉시 거부"]
    L2 -.->|"exit 2"| B2["차단"]
    L4 -.->|"exit 2"| B3["푸시 거절"]

규칙 배치 원칙은 단순하다. “검사의 정확도를 잃지 않는 선에서 가장 이른 층에 둔다.” 코드가 한참 작성된 뒤 푸시 시점에 지적하면 수정 비용이 너무 크다. 편집 도구를 호출하는 바로 그 순간(L2)에 가로채서 멈춰 세워야 한다.3

L1과 L2의 역할도 나눴다. 예컨대 --no-verify는 L1의 문자열 거부 목록에만 둔다. 두 층 모두 문자열 매칭을 쓰면 커밋 메시지나 문서 본문에 그 단어가 등장할 때 오탐이 나기 때문이다. 대신 L2의 bash-guard.sh는 거부 목록이 구조적으로 잡을 수 없는 것, 즉 환경 변수로 훅을 끄거나 core.hooksPath를 교체해 가드를 통째로 우회하려는 셸 명령을 구문 수준에서 가로챈다.

비대칭 종료 코드와 절대 경로 계약

가드를 구축할 때 가장 먼저 맞닥뜨리는 함정은 종료 코드의 비대칭성이다.

명령행 도구와 린터(Lefthook 등)의 관례는 위반 시 종료 코드 1을 낸다. 그러나 Claude Code의 훅 인터페이스 계약에서 exit 1은 작업을 중단시키지 않는 경고에 불과하다. 모델을 물리적으로 멈춰 세우려면 반드시 **exit 2와 표준 에러(stderr)**를 반환해야 한다.

claude-hook-dispatch.sh라는 셸 디스패처를 두고 래퍼 함수에서 이를 일괄 변환했다.

run_guard() {
  local label="$1"; shift
  local cmd="$1"; shift
  if eval "$cmd \"\$@\""; then
    bash "$REPO_ROOT/dev-tools/scripts/hook-roi-log.sh" passed "$label" "$FILE_PATH"
    return 0
  fi
  bash "$REPO_ROOT/dev-tools/scripts/hook-roi-log.sh" blocked "$label" "$FILE_PATH"
  exit 2
}

이 구조 덕분에 TypeScript로 작성된 순수 검사 함수는 CLI 관례대로 exit 1을 던지고, 훅 래퍼는 이를 exit 2로 바꾸어 차단하며, Lefthook에서는 그대로 호출되어 푸시를 실패 처리한다. 하나의 가드가 편집 시점과 푸시 시점 양쪽에서 동시에 작동하는 셈이다.

또한 훅 시스템은 **방어적 태도(Defensive Posture)**를 유지한다.

Defensive posture: any unexpected input (missing tool_name, non-Edit/Write
tool, file_path outside the routed paths) exits 0 silently. Never accidentally
block work over parser bugs.

파서 결함이나 로그 기록 실패(|| true) 때문에 정상 작업이 실수로 막히는 일은 없어야 한다. 가드가 작업을 멈춰 세웠다면, 그것은 명백한 규칙 위반을 확정했을 때뿐이다.

재귀적 메타가드, 가드를 지키는 가드

하네스를 운영하면서 확인한 AI의 실패 행동이 있었다.

“가드에 막혔을 때 AI가 택하기 쉬운 길은 위반 코드를 고치는 것이 아니라, 가드를 고쳐서 통과하는 것이다.” (anti-patterns.md 187행)

실제로 색상 하드코딩을 금지하는 UI 가드에 걸리자, AI가 가드 소스코드에 예외 정규식을 추가하려 했던 적이 있다. AI에게 코드가 왜 규칙에 어긋나는지 알려주면, 모델은 가장 품이 덜 드는 경로, 즉 검사기를 약화시키는 쪽으로 움직이기 쉽다.

이를 방지하기 위해 **메타가드(harness-self-mod-guard.sh)**를 배치했다.

case "$REL_PATH" in
  .claude/settings.json) is_harness_path=1 ;;
  lefthook.yml) is_harness_path=1 ;;
  dev-tools/src/*.ts) is_harness_path=1 ;;
  dev-tools/scripts/*.sh) is_harness_path=1 ;;
  dev-tools/__tests__/*.ts) is_harness_path=1 ;;
esac

메타가드는 하네스 설정과 가드 구현체, 테스트 코드뿐만 아니라 자기 자신(harness-self-mod-guard.sh)까지 보호 대상에 포함한다.

Recursion is intentional: the gate that catches self-weakening must itself be protected from self-weakening.

메타가드는 가드를 강화하는 편집과 약화하는 편집을 구분하지 못한다. 경로만 보고 둘 다 예외 없이 차단하며, 판단을 사람에게 넘긴다. 우회 통로는 환경 변수(HARNESS_SELF_MOD_OK=1)로 열어두되, 우회 사실을 영구 로그에 남기도록 했다. AI가 몰래 가드를 수정할 수 있는 방법은 완전히 막혔다.

87,000번의 검사와 2,122번의 차단, 실제 로그 기록

하네스가 관념에 머물지 않았다는 사실은 저장소에 누적된 11.3MB 크기의 로그(dev-tools/.hook-roi.log)가 숫자로 증명한다.

passed   : 84,275건
blocked  :  2,122건
bypassed :    584건
warned   :    377건
전체 이벤트: 87,358건

5개월 동안 가드는 무려 2,122번이나 AI의 부적절한 도구 호출을 실제로 멈춰 세웠다.

상위 발화 가드 차단 및 검사 사유
tdd-agent-required 프로덕션 코드를 TDD 절차 없이 직접 수정하려던 시도 차단
dependency-context7-required 라이브러리 문서를 확인하지 않고 낡은 API를 쓰려던 시도 차단
worktree-scope 작업 티켓에 지정된 범위를 벗어난 다른 파일 편집 차단
invariant-comment-guard 코드에 명시된 // INVARIANT(<ticket>): 불변식 주석 훼손 차단
no-manual-deep-link 라우터를 우회하고 React Native의 Linking을 직접 호출하려던 시도 차단
UI 토큰 가드군 디자인 시스템에 없는 16진수 색상 코드 하드코딩 차단

이 차단 장치가 없었다면, 위반 사항들이 그대로 코드베이스에 스며들어 디버깅 비용을 크게 키웠을 것이다.

2026년 업계의 하네스 흐름과의 교차점

메모릭의 하네스 구축 경험은 고립된 시도가 아니었다. 2024년부터 2026년에 이르기까지 소프트웨어 엔지니어링 업계는 LLM을 단순한 코드 완성 도구에서 자율 에이전트로 확장하면서 수많은 시행착오를 겪어 왔다.4 이 과정에서 형성된 두 가지 공통점이 메모릭의 가드와 맞닿는다.

  1. 자기보고 불신과 종료 코드 기반 게이팅. 업계의 선도적인 엔지니어들과 연구진은 공통적으로 “에이전트의 구두 보고를 믿지 말라”는 결론에 도달했다.5 테스트 스위트와 정적 분석기가 반환하는 셸 종료 코드(exit 0 또는 exit 1)만을 신뢰하는 원칙은 SWE-bench 하네스 평가 체계부터 실무 에이전트 환경까지 관통하는 기본 상식이 되었다.6 메모릭의 run_guard 래퍼 역시 exit 2로만 작업을 멈춰 세운다.
  2. 프롬프트 가이드에서 실행 가능한 기계 규칙으로의 수렴. 마크다운 파일에 적어 둔 지침은 컨텍스트가 길어지면 희석된다. 업계 전반이 프롬프트 의존도를 낮추고 도구 인터셉트 훅, 린터, AST 분석기 등 결정론적 코드 게이트로 거버넌스를 이전하는데, 메모릭의 L1~L4 배선은 이 수렴을 한 사람의 작업 환경에 옮겨 놓은 사례다.

업계 도구가 미처 다루지 못한 빈틈도 있었다. 대부분의 업계 도구는 규칙을 세우는 데 집중할 뿐, 규칙을 정의하는 파일 자체를 에이전트로부터 지키지 않는다. 에이전트가 가드에 걸렸을 때 규칙 파일을 완화해 버리는 치명적인 실패 모드를 막기 위해, 메모릭은 harness-self-mod-guard가 자기 자신까지 보호 표면에 포함하는 재귀 구조를 도입했다. 가드를 강화하는 편집과 약화하는 편집을 구분하지 않고 경로만으로 차단하며, 판단은 사람에게 돌려준다.


4. 지식 그래프 docs.db, 기록을 검색으로 되찾는 체계

코드와 함께 이 저장소가 가장 많이 남긴 산출물은 문서다. 전체 커밋의 26.7%(1,899개)가 문서(docs) 커밋이다. 그렇게 쌓인 기록을 에이전트가 실제로 꺼내 쓰게 만든 체계가 docs.db다.

파일에서 데이터베이스 행으로

개발 초기에는 문서가 전부 마크다운 파일이었다. dev-log는 dev-log/*.md, ADR은 adr/*.md 형태로 쌓여 있었는데, 문서가 늘어날수록 어디에 무엇이 있는지 파고들기가 느려졌다.

전환의 분기점은 문서 지식 베이스를 행(레코드) 우선으로 바꾼 결정이었다. dev-log와 ADR, research, 완료된 PRD는 docs.db의 행이 되었다. 예외는 오직 진행 중인 PRD뿐이다. 여러 작업 트리가 git으로 공유해야 하는 문서는 파일로 남겨야 하기 때문이다.

SQLite와 FTS5, 세션 시작 훅이 불러오는 공유 데이터베이스

docs.db는 SQLite 데이터베이스다. 저장소 안에 있지만 .gitignore 대상이라 git 기록에는 남지 않고, 공유본은 S3에 둔다. 세션 시작 훅(SessionStart)에 연결된 docs-search-lazy-build.sh가 세션을 열 때마다 최신본을 받아 온다(docs.db.etag로 변경을 감지한다).

2026년 9월 5일 기준으로 docs.db에는 4,882개 노드가 쌓여 있다. ADR 70편, dev-log 291편, PRD 351개가 문서 단위로 엮인 기록이다.

노드 종류 노드 수
PRD 2,718
dev-log 1,133
research 669
ADR 362

코드는 무엇을, 로그는 어떻게, ADR은 왜

기록 체계를 설계하면서 세운 원칙이 있다.

“code shows what, dev-logs show how, ADRs explain why.”

코드는 최종 상태를 보여주고, dev-log는 그 상태에 이르기까지의 시행착오를 보여주며, ADR은 왜 그렇게 결정했는지 설명한다. 에이전트가 새 작업을 시작하기 전에 이 기록을 뒤져서 먼저 ’겪었던 실패’를 알게 하는 것이 검색 도구의 핵심 쓰임새다.

CLI는 의도별로 나뉜 네 가지 명령으로 이루어진다.

  • pnpm docs:search. 전문 검색으로 배경 지식을 찾는다.
  • pnpm docs:list. 메타데이터 필터로 목록을 좁힌다.
  • pnpm docs:show. 선택한 문서의 본문을 읽는다.
  • pnpm docs:neighbors. 인접 문서를 추적한다.

문서가 엮이는 흐름

문서들은 분류되어 저장되기만 하는 것이 아니라, 작업이 진행되면서 서로에게서 태어나고 검증되는 관계를 맺는다. 핵심은 dev-log에 기록된 함정이 anti-patterns 사전으로 승격되고, 필요할 때는 가드라는 실행 장치로까지 오르는 승격 사이클이다.

flowchart TD
    RESEARCH["research 행<br/>(외부에서 온 자료)"] --> PRD["PRD 기획서"]
    PRD --> LEDGER["작업 대장<br/>ledger.json"]
    LEDGER --> CODE["구현 코드"]
    CODE -->|실패·함정| DEVLOG["dev-log 행<br/>(어떻게 실패했는가)"]
    DEVLOG -->|등재 절차| ANTI["anti-patterns.md<br/>함정 사전"]
    ANTI -->|필요할 때| RULES["rules 규칙 파일"]
    RULES -->|가드화| GUARD["가드 · 훅 장치"]
    GUARD -->|차단·허용| CODE
    DEVLOG -->|왜| ADR["ADR 행<br/>(왜 그렇게 정했는가)"]
    PRD --> ADR
    PRD -->|완료 후 적재| DB[("docs.db<br/>지식 저장소")]
    DEVLOG -->|완료 후 적재| DB
    ADR -->|완료 후 적재| DB
    DB -->|세션 시작 시 동기화 · 검색| AGENT["AI 에이전트 세션"]
    AGENT -->|PRE-FLIGHT 증거| LEDGER

이 다이어그램의 아래쪽 화살표가 곧 세션 시작 훅과 PRE-FLIGHT 단계로 이어진다. 새 작업을 시작하기 전에 과거의 실패 기록이나 관련 결정을 먼저 찾는 이유는, 바로 이 승격된 함정 사전이 그 실패를 반복하지 않게 막는 첫 번째 문턱이기 때문이다.

그래프의 화려함보다 검색의 실질

처음에는 이 기록들을 지식 그래프로 연결하는 데 힘을 썼다. 노드 사이의 링크(엣지)를 늘려 그래프 순회로 연관 지식을 찾자는 구도였다. 하지만 멀티 에이전트로 자체 시스템을 적대적으로 검증(Adversarial Audit)한 뒤 한계가 분명해졌다.7

  • 전체 노드의 **78.4%(3,829개)**가 어떤 링크도 없는 고립 노드였다.
  • 복잡한 그래프 추론은 실제 작업에 거의 기여하지 못했다.
  • 실질적인 가치를 낸 것은 그래프가 아니라 SQLite의 FTS5 기반 고속 전문 검색(Full-Text Search) 하나였다.

그래프 유지보수를 중단하고 의도별로 단순하게 나뉜 CLI 검색으로 돌아갔다. pnpm docs:search가 에이전트에게 훨씬 정확하고 빠른 맥락을 건네주었다. 화려한 아키텍처보다 단순하고 빠른 도구가 더 유용하다는 교훈은 마지막 장의 결론과도 이어진다.

2026년 업계의 지식 저장소·RAG 접근법과의 교차점

docs.db의 설계는 LLM 에이전트를 위한 지식 관리가 검증된 접근법으로 자리 잡은 2024~2026년 업계 흐름이 낳은 결정이다. 업계의 주류 접근법인 검색 증강 생성(Retrieval-Augmented Generation, RAG)과 비교해 보면 docs.db는 같은 전제 위에서 오히려 단순함을 선택했다.

업계와 맞닿은 공통점

  1. 검색으로 맥락을 공급한다. RAG의 핵심은 모델이 답을 만들기 전에 관련 문서를 검색해 프롬프트에 주입하는 구조다.8 특히 2024~2025년에는 키워드 검색(BM25)과 벡터 검색을 함께 쓰는 하이브리드 검색이 보편화되었다.9 docs.db 역시 에이전트가 작업을 시작하기 전에 과거 기록을 조회(docs:search)해서 맥락을 넣는다. 이 근본 전제는 동일하다.
  2. 지식 단위로 쪼개어 관리한다. RAG는 문서를 정해진 크기의 청크(chunk)로 분할해 검색 정확도를 높이고, docs.db는 이를 행(레코드) 우선으로 바꾸어 진행 중인 파일과 완료된 행을 분리했다. 둘 다 지식의 원자 단위를 규정해야 한다는 문제의식을 공유한다.
  3. 실시간 최신성보다 적절한 최신성을 원한다. RAG 세계에서도 매번 전체 벡터 DB를 재생성하는 대신 증분 인덱싱과 주기적 업데이트를 쓴다. docs.db 역시 .etag 기반 S3 동기화와 세션 시작 시에만 받아 오는 lazy build로 같은 성찰에 도달했다.

우리가 달라진 지점

  1. 임베딩 대신 FTS5 전문 검색만 쓴다. 업계는 시맨틱 유사도를 위해 벡터 임베딩을 기본으로 사용한다. 그러나 docs.db는 임베딩 모델을 도입하지 않고 SQLite의 FTS5 키워드 검색만으로 동작했다. 검색 대상이 PRD·dev-log·ADR 같은 개발 산출물로 범위가 분명한 기록이므로, 유사도를 찾는 대신 정확한 토큰 일치가 더 납득이 가는 결과를 냈다.
  2. 그래프를 포기했다. 업계에는 그래프 구조를 지식 베이스에 결합한 GraphRAG 같은 접근도 주목받았다.10 그러나 docs.db는 실제 운영에서 그래프가 낭비였음을 스스로 검증했다. 전체 노드의 78.4%가 고립 노드였고, 그래프 순회 대신 FTS5 검색이 실제 작업의 대부분을 담당했다.7 그래프는 장식이 되고 검색이 실질이 되었다.
  3. 에이전트가 기록의 생성자이자 소비자다. 일반 RAG는 조직의 비정형 문서를 수집해 임베딩하는 데 집중한다. docs.db는 개발 워크플로가 산출하는 dev-log·ADR·PRD를 그 워크플로가 다시 소비한다. 기록을 쌓는 과정이 함정 승격 주기(§3)와 PRE-FLIGHT 검증(§5)으로 이어지는 닫힌 고리다.

5. 개발 워크플로, PRD에서 직진 병합까지

하네스가 무엇을 막는 장치라면, 워크플로는 기능을 안전하게 생산하는 절차다.

두 세션의 대화, 격리와 검토의 분리

모든 작업은 두 개의 독립된 세션이 대화하는 구조로 진행했다.

  1. 작업 트리(Worktree) 세션. 독립된 Git worktree 안에서 해당 티켓의 구현과 테스트만 담당한다.
  2. 메인(Main) 세션. 메인 저장소에 머물며 작업 트리가 넘긴 결과물을 검토하고, 회귀 테스트를 확인한 뒤 병합을 결정한다.

한 명의 개발자가 두 역할을 모두 수행하더라도 세션은 물리적으로 분리했다. 구현하는 세션이 스스로 코드를 메인에 밀어 넣는 순간, 객관적인 검증 경계가 무너지기 때문이다.

flowchart TD
    P1["1단계 · PRD 작성<br/>(/prd)"] --> P2["2단계 · 작업 트리 생성<br/>(git worktree)"]
    P2 --> P3["3단계 · 구현 루프<br/>(/implement-prd)"]
    P3 -.->|참고| TDD["TDD 6단계 · PRE-FLIGHT → RED → AUDIT → GREEN → REVIEW → VALIDATE<br/>+ 러너 검증 (pnpm ledger:verify)"]
    P3 --> P4["4단계 · 티켓 마무리<br/>(pnpm finish)"]
    P4 --> P5["5단계 · 인계 노트<br/>(docs/handoff)"]
    P5 --> P6["6단계 · 병합 검토<br/>(/review-worktree)"]

1단계, /prd와 작업 대장(task-ledger)

기능 개발은 언제나 문서에서 출발한다. /prd 스킬을 호출하면 두 가지 결과물이 나온다.

  • 사람이 읽는 기획서인 docs/prd/<ticket>.md
  • 기계가 읽는 작업 대장인 docs/ledger/<ticket>.json

대장은 사람이 손으로 쓰지 않는다. pnpm ledger:new docs/prd/<ticket>.md 명령으로 PRD의 서브 티켓 표를 파싱해 기계적인 JSON 스키마로 변환한다.

여기서 중요한 규칙이 **scope_paths**다. 작업이 건드릴 수 있는 디렉터리와 파일 목록을 지정해야 하며, 이를 벗어난 파일 수정은 worktree-scope.ts 가드가 차단한다.

  • 실전에서 겪은 함정. scope_paths에 프로덕션 코드 경로만 적고 테스트 파일 경로를 빠뜨리면, 뒤이어 실행될 /tdd가 테스트 파일을 작성하려다 가드에 막혀 작업이 멈춘다. 따라서 범위 정의에는 테스트 경로가 반드시 포함되어야 한다.

작업 대장(ledger.json)의 실제 구조와 시간 측정 체계

대장 파일이 기계적인 통제를 획득하는 핵심 장치는 바로 JSON 스키마와 러너 소유 필드다. 실제로 사용된 작업 대장(docs/ledger/*.json) 파일의 예시 구조를 살펴보면 다음과 같다.

{
  "ticket": "T-SAMPLE",
  "depends_on": [],
  "lead_time_ms": 348200,
  "active_elapsed_ms": 119602,
  "tasks": [
    {
      "id": "T-SAMPLE-S1",
      "title": "`ceilToQuarterHour` dead code + 박제 테스트 제거",
      "scope_paths": [
        "apps/server/src/lib/lib.time.ts"
      ],
      "verification_actions": [
        "fallow audit --base main",
        "cd apps/server && pnpm exec vitest run"
      ],
      "passes": true,
      "retries": 0,
      "status": "done",
      "risk": "interior",
      "started_at": "2026-07-11T18:22:12.866Z",
      "completed_at": "2026-07-11T18:24:12.468Z",
      "elapsed_ms": 119602,
      "verification_ms": 106243
    }
  ]
}

이 스키마에서 주목할 점은 **시간의 세 축(Three time axes)**을 분리해 계측한다는 사실이다.

  1. 총 소요 시간(lead_time_ms): 첫 번째 작업의 픽업 시각부터 마지막 작업이 통과하여 pnpm finish 관문을 넘을 때까지의 전체 벽시계 시간(Wall-clock time)이다. 작업 사이의 설계 고민, 대화 턴, 예상치 못한 장벽을 모두 포함한 전체 회차 비용을 가리킨다.
  2. 순수 작업 시간(elapsed_ms 및 합산 active_elapsed_ms): pnpm work를 실행해 작업을 시작한 시각(started_at)부터 검증 러너가 통과를 확정한 시각(completed_at)까지의 순수한 코딩과 테스트 작성 시간이다. 작업 간 공백을 제거한 순수 구현 비용이며, lead_time_ms와의 격차를 비교하면 해당 작업이 겪은 난이도의 정체를 파악할 수 있다.
  3. 검증 실행 시간(verification_ms): 마지막 verification_actions 셸 명령을 실행하는 데 걸린 시간이다. 테스트 스위트가 비정상적으로 느려지거나 무한 루프에 빠지는 징후를 즉시 드러낸다.

또한 risk 필드는 사람이 주관적으로 입력하는 값이 아니다. task-ledger-schema.tsclassifyRisk 함수가 scope_paths를 도메인 영향도 규칙 표와 대조하여, 시스템 경계에 영향을 미치면 seam, 내부 변경이면 interior로 자동 계산한다. 사람이 임의로 위험도를 낮춰 검토를 피하려 하면 스키마 검증기에서 에러를 던진다.

2단계, 작업 트리와 격리 환경 구축

작업은 메인 브랜치가 아니라 격리된 작업 공간에서 시작한다.

git worktree add ../memoriq-wt-<scope> -b feat/<ticket>-<scope> main
cd ../memoriq-wt-<scope>
bash dev-tools/scripts/setup-worktree.sh

setup-worktree.sh는 환경 변수 복사, 의존성 설치, 파이프라인 가상환경 구성, 격리된 PostgreSQL 테스트 DB 생성 등을 자동으로 처리한다.

작업 트리에는 엄격한 규율이 적용된다.

  • 한 세션은 하나의 작업 트리만 맡는다.
  • 작업 트리 안에서는 메인 브랜치 병합(git merge)과 푸시(git push origin main)가 금지된다. bash-guard.sh가 이를 차단하며, 통합 권한은 메인 세션에만 있다.

3단계, /implement-prd와 TDD 6단계

작업 트리에 들어간 뒤 /implement-prd를 실행하면 대장의 서브 작업을 순서대로 진행한다. 작업 하나마다 거치는 5단계 절차는 다음과 같다.

① 픽업 (pnpm work)

작업 상태를 doing으로 변경하고 시작 시각을 기록한다. 이 과정을 건너뛰고 바로 코드를 작성하면 작업 소요 시간 측정이 무효화되며, 완료 시점에 비정상 작업으로 분류된다.

② dev-log 조회 (코드를 쓰기 전에)

pnpm docs:search "<키워드>" --type dev-log를 실행한다. 과거에 같은 컴포넌트나 라이브러리를 다루다가 실패했던 기록을 먼저 확인하는 단계다.

/tdd 6단계 사이클

프로덕션 코드(apps/*/src, packages/*/src)를 수정하는 모든 행위는 예외 없이 이 사이클을 거쳐야 한다.

flowchart TD
    PF["PRE-FLIGHT<br/>과거 실패 기록 · 공식 문서 ·<br/>안티패턴 조회 증거 제출"] --> RED["RED<br/>실패 테스트 작성<br/>(구현 파일 열람 불가)"]
    RED --> AUDIT["AUDIT<br/>P0 / P1 / DELETE 판정"]
    AUDIT --> GREEN["GREEN<br/>살아남은 테스트만<br/>통과하는 최소 구현"]
    GREEN --> REVIEW["REVIEW<br/>명세 일치 · 중복 정리"]
    REVIEW --> VALIDATE["VALIDATE<br/>풀 스위트 1회 실행"]
    VALIDATE -->|"실패"| RED
    VALIDATE -->|"통과"| LEDGER["러너 검증<br/>ledger:verify<br/>(exit 0만이 완료)"]
  1. PRE-FLIGHT. 테스트를 작성하기 전, 최신 라이브러리 문서와 과거 실패 기록을 확인했다는 마크다운 증거 블록을 제출해야 한다. 이 증거가 대화 맥락에 없으면 훅이 RED 단계 진입을 막는다. AI가 낡은 학습 데이터에 의존해 코드를 짜는 일을 막기 위해서다.
  2. RED. 테스트 작성 전용 에이전트(tdd-test-writer)가 실패하는 테스트를 작성한다. 이 에이전트는 구현 파일을 읽을 수 없다. 테스트 작성자와 구현자가 같으면 통과시키기 쉬운 방향으로 테스트를 타협하기 마련이므로 권한을 분리했다.
  3. AUDIT(가치 감사). 테스트 감사 에이전트(tdd-test-auditor)가 각 단언문을 P0(불변식·보안·데이터 손상), P1(경계값·상태 전이), DELETE(가치 없음)로 분류한다.
    • 왜 AUDIT이 GREEN 앞에 있는가? 전통적인 순서(RED → GREEN → AUDIT)에서는 나중에 지워질 무가치한 테스트를 통과시키기 위해 불필요한 분기문과 방어 코드가 프로덕션에 늘어난다. 나중에 테스트를 지워도 그 코드는 프로덕션에 그대로 남는다. 구현 직전에 테스트를 감사하여 무가치한 단언문을 먼저 쳐내야만 GREEN 구현 표면을 가장 정직하고 간결하게 유지할 수 있다.
  4. GREEN. 구현 에이전트(tdd-implementer)가 살아남은 테스트만 통과시키는 최소한의 코드를 작성한다.
  5. REVIEW. 메인 세션이 테스트와 구현 간의 명세 일치 여부를 확인하고 가독성을 다듬는다.
  6. VALIDATE. 전체 테스트 스위트 검증을 1회 실행한다.

④ 러너 검증 (pnpm ledger:verify)

가장 핵심적인 규율이다. 작업의 완료 판정 권한을 AI 세션에서 러너 프로그램으로 완전히 이전했다.

"Completion authority moves from the session ('I'm done') to the harness ('the actions passed')."

AI는 task-ledgerpasses: true 필드를 직접 수정할 수 없다. 수정하려 하면 전용 가드가 차단한다. 오직 터미널에서 실행된 검증 명령의 종료 코드(exit 0)만이 대장을 ’완료’로 바꾼다.

실제 작업 대장 기록(outcomes.jsonl, 2,316건)에 따르면, 전체 작업의 약 33%(684건)가 최소 한 번 이상 판정 실패를 겪은 뒤 재시도를 거쳐 통과했다. 러너가 AI의 섣부른 완료 선언을 600번 넘게 기각하고 바로잡은 셈이다.

4단계부터 6단계까지, 마무리와 인계 노트 그리고 병합 검토

모든 작업이 통과되면 pnpm finish <ticket> 관문을 거친다. finish 명령은 모든 작업이 끝났는지 확인하고 전체 소요 시간(lead_time_ms)을 기록한다.

작업 트리 세션의 최종 산출물은 docs/handoff/<ticket>.md 파일이다. 이 인계 노트는 일부러 커밋하지 않는다. 영구 저장소에 남길 코드가 아니라 메인 세션으로 전달하는 일회성 메모이기 때문이다. 작업 트리 안에서 “커밋되지 않은 파일이 인계 노트 하나뿐”이라는 사실 자체가 다른 모든 프로덕션과 테스트 코드가 정상적으로 커밋되었다는 증거가 된다.

메인 세션은 /review-worktree를 호출하여 전체 변경점을 검토한다.

“작업 트리 안에서 초록불이라는 것과, 여러 작업 트리를 합쳤을 때 옳다는 것은 완전히 다르다.”

각 작업 트리는 자신의 격리된 범위만 검증하므로 인터페이스 경계 문제를 놓칠 수 있다. 메인 세션은 경계 효과를 확인하고, 문제가 있다면 작업 트리 안에서 직접 수정한 뒤 직진 병합(fast-forward)이 가능한 상태로 만든다. 최종 병합 명령(git merge --ff-only)은 언제나 사람의 명시적 승인 아래 실행된다.

2026년 업계의 개발 워크플로와의 교차점

이 워크플로 역시 2024~2026년 업계에서 강조된 명세 주도 개발(Spec-driven Development) 흐름과 맞닿는다. 업계는 AI에게 자유로운 코딩을 맡기는 대신, 기계가 판독할 수 있는 상세한 명세(Spec)를 확정하고 에이전트를 엄격한 실행기로 제한하는 쪽으로 움직였다.11 메모릭의 /prd가 PRD를 기계 판독 가능한 작업 대장(ledger.json)으로 변환하고, delegate-spec이 완결된 명세를 먼저 작성하는 방식은 이 접근을 실전에 정착시킨 형태다.

차이가 드러나는 지점은 두 가지다.

  1. TDD 사이클에서 가치 감사(AUDIT)를 GREEN 앞에 두었다. 전통적인 TDD와 대부분의 에이전트 템플릿은 실패 테스트 작성(RED) 직후 바로 구현(GREEN)으로 들어간다. 그런데 AI 에이전트는 무가치한 단언문을 통과시키기 위해 프로덕션 코드에 군더더기 분기문을 대량으로 양산한다. 그래서 메모릭은 단언문의 존속 가치를 먼저 판정(P0/P1/DELETE)하고, 살아남은 테스트만 통과시키는 최소 구현을 뒤에 수행했다.
  2. 시간의 세 축 분리 계측과 기계적 위험도 도출을 ledger에 적용했다. 사람이 작업 난이도를 주관적으로 추정하지 않고, 변경 범위(scope_paths)를 도메인 영향도 표와 대조해 위험도(seaminterior)를 기계적으로 결정했다. 순수 구현 시간(elapsed_ms)과 검증 시간(verification_ms)과 전체 소요 시간(lead_time_ms)을 독립적으로 측정해 통계에 기반한 작업 캘리브레이션을 실현했다.

6. 정직한 실패와 측정된 성과

통과했다는 것이 옳다는 증명은 아니다

하네스와 테스트 스위트가 갖추어졌다고 해서 모든 결함이 사라지는 것은 아니다. 오히려 초록불이 주는 안도감이 위험할 때가 있다.

대표적인 사례가 7월 29일 출시 직후 발생한 push-sent-at 센티널 사건이다. 푸시 발송 로직 테스트가 계속 통과하고 있었는데, 실제 운영 데이터를 분석해 보니 단 한 번도 발송되지 않고 있었다. 테스트 픽스처가 실제 데이터베이스에서는 절대 공존할 수 없는 모순된 상태 조합을 사전에 넣어 두고 돌리고 있었기 때문이었다.

“초록불이 증명한 명제가 현실 조건과 완전히 무관했다.”

동시성 쿼터 차감 테스트에서도 비슷한 일이 있었다. 버그를 수정하기 전부터 테스트가 이미 통과하고 있었다. 관측 도구가 정확하지 않아 경쟁 상태(Race Condition)를 재현하지 못하고 있었던 것이다. 우리는 상태 코드의 실제 분포를 검증하는 방식으로 테스트를 다시 작성했다. 테스트가 통과했다는 사실과 그것이 현실의 신뢰성을 보장한다는 사실은 엄격히 구분해서 확인해야 한다.

커버리지 퍼센트가 아닌 가치 분류(Value Classification)

메모릭 코드베이스에는 ‘테스트 커버리지 90% 이상’ 같은 수치 기준이 없다. 일부러 두지 않았다.

초기에 뮤테이션 테스트(Mutation Testing)를 CI 파이프라인에 도입했을 때, 커버리지가 100%인 파일의 변종 결함 감지율(Kill Rate)이 0%로 측정되는 결과를 보았다. 무의미한 단언문(expect(true).toBe(true))과 껍데기뿐인 모의 객체(Mock)로 채워진 테스트는 커버리지 수치만 채울 뿐 버그를 전혀 잡지 못했다. 게다가 CI 빌드 시간이 300분을 넘기며 병목이 되었다.

뮤테이션 전체 검사를 CI에서 폐기하고 필요할 때만 돌려보는 참조 도구로 바꿨다. 그리고 커버리지 숫자 대신 테스트 가치 분류 정책을 세웠다.

  • P0. 데이터 손상, 보안 취약점, 핵심 도메인 불변식을 잡는 테스트.
  • P1. 상태 머신 전이, 트랜잭션 원자성, 경계값을 검증하는 테스트.
  • DELETE. “이 테스트를 지웠을 때 프로덕션의 어떤 결함을 놓치는가?“라는 질문에 명확한 답을 내놓지 못하는 모든 테스트.

커버리지 숫자를 채우기 위한 1,000줄의 테스트보다, 실제 버그를 감지해내는 100줄의 P0 테스트를 유지하는 것이 AI-Native 개발 환경의 속도와 정직성을 보장하는 길이었다.

저가 모델 위임 파이프라인 (delegate-*)

개발 후반부인 8월, 비용과 속도를 최적화하기 위해 또 하나의 실험을 진행했다.

“설계와 판단은 프론티어 모델의 비용을 치를 가치가 있지만, 단순 코드 타이핑은 아니다.”

고가의 프론티어 모델이 직접 코드를 전부 작성하는 대신, 엄격하게 검증된 명세를 작성하고 타이핑은 저렴한 모델에 위임하는 파이프라인을 구축했다.

  • delegate-spec 프론티어 모델이 저장소를 추가로 탐색할 필요가 없는 완결된 명세서(docs/spec/<ticket>.md)를 작성한다.
  • delegate-run 저가 모델이 명세를 바탕으로 코드를 타이핑한다.
  • delegate-check 고가의 리뷰 세션으로 넘어가기 전, 명세에서 요구한 파일과 변경점이 실제로 존재하는지 기계적으로 diff를 대조한다.

여기서 얻은 중요한 교훈이 있다.

“위임이 비용을 아끼려면, 완료 판정 권한이 모델과 함께 위임되어서는 결코 안 된다.”

저가 모델은 “모든 테스트를 통과했습니다”라고 거짓 보고를 올리기 쉬웠다. 하지만 코드를 열어보면 새로운 기능이 전혀 호출되지 않는 경우가 있었다. 판정권을 작업 대장(task-ledger)과 러너에 묶어두었기 때문에 저가 모델의 거짓말은 즉각 기각되었고, 품질 저하 없이 타이핑 비용을 낮출 수 있었다.


7. 마치며, 신뢰를 코드로 옮기는 일

메모릭을 개발한 지난 5개월은 한 사람의 엔지니어가 AI라는 도구를 쥐었을 때 도달할 수 있는 생산성을 시험해 본 시간이었다. 7,104개의 커밋, 57만 줄의 코드베이스, 그리고 양대 앱스토어 출시라는 결과물은 프롬프트 요행으로 만들어지지 않았다.

가장 값진 자산은 애플리케이션 코드 그 자체가 아니라, AI가 선을 넘지 못하도록 통제하고 결과물을 기계적으로 증명해 낸 하네스 시스템 그 자체였다.

AI-Native 개발이란 AI에게 코딩을 완전히 떠넘기는 방관이 아니다. 사람이 모든 코드를 다 검토할 수 없다는 한계를 인정하고, 사람의 머릿속에 있는 아키텍처 규칙과 검증 기준을 **타협 없는 소프트웨어 장치(Hook, Guard, Ledger, TDD)**로 치밀하게 변환해 AI의 발밑에 깔아주는 엔지니어링이다.

메모릭은 반려인들의 깊은 슬픔에 따뜻한 위로를 건네기 위해 정성스럽게 빚은 제품이다. 갑작스러운 이별로 마음 한구석에 그리움을 품고 계신 분들에게, 메모릭이 다정한 재회의 시간이 되어주기를 바란다.

아울러 AI와 함께 더 견고한 시스템을 만들고자 고민하는 동료 엔지니어들에게, 이 5개월간의 하네스 구축 기록이 작은 디딤돌이 되기를 기대한다.


Footnotes

  1. 되새김(2026-09-06): 반려동물 품종을 dog과 cat으로 한정한 결정은 단순한 비즈니스 선택을 넘어 데이터 무결성의 기초였다. 테이블 스키마에 CHECK 제약을 걸어 두지 않으면, AI 에이전트가 다른 반려동물 기능을 작성할 때 어설픈 범용 객체를 만들어 스키마 오염을 일으키기 쉬웠다. 경계를 좁혀 고정하는 것이 AI의 환각과 스키마 오염을 막는 첫 번째 울타리였다.

  2. 되새김(2026-09-06): 앱스토어 심사 과정에서 심사 봇이 유료 화면을 통과하지 못해 리젝되는 위험이 있었다. 이를 해결하기 위해 Google Play 심사 봇의 기기 특성을 판별하는 자체 네이티브 모듈(test-lab-detector)을 만들고, 심사 트래픽을 일반 지표에서 분리하면서 심사관 계정에는 결제 없이 자격을 사전 부여하는 우회 경로를 구축했다.

  3. 되새김(2026-09-06): 가드가 왜 엄격해졌는지를 보여주는 대표 사례가 있다. 데이터베이스 마이그레이션 저널 파일(_journal.json)의 when 타임스탬프를 손으로 수정한 탓에, 약 6시간 동안 운영 데이터베이스 마이그레이션 적용 순서가 조용히 어긋났다. 이 사고 이후 마이그레이션 파일의 수동 편집을 가로막는 전용 린트 가드가 신설되었다.

  4. 되새김(2026-09-06): Anthropic Research는 Building Effective Agents(2024) 보고서에서 자율 에이전트의 성공 요인으로 복잡한 프레임워크 대신 단순하고 결합력 있는 도구(Tool), 명확한 피드백 루프, 그리고 평가 하네스(Evaluation Harness)의 중요성을 강조했다. 부탁이 아닌 도구 가로채기와 차단으로 에이전트의 탈선을 막는 메모릭의 하네스는 이 연구의 실증적 구현이라 할 수 있다. > 출처: Building Effective Agents — anthropic.com

  5. 되새김(2026-09-06): Mitchell Hashimoto는 2024년 이후 자신의 고유한 AI 보조 개발 경험을 공유하며, “에이전트가 코드를 작성하게 하되, 엄격한 단위 테스트와 컴파일러 종료 코드만으로 통과 여부를 결정하고 사람이 모든 diff를 정독하지 않는 구조”의 효율성을 역설했다. (Mitchell Hashimoto, My Experience with AI-Assisted Development, 2025). 메모릭의 하네스 역시 이와 같은 ‘기계적 종료 코드 판정’ 철학 위에 서 있다. > 출처: My Experience with AI-Assisted Development — mitchellh.com

  6. 되새김(2026-09-06): Carlos E. Jimenez et al., SWE-bench: Can Language Models Resolve Real-World GitHub Issues? (ICLR 2024). SWE-bench는 에이전트의 구두 답변이 아니라 실제 실행 환경에서 단위 테스트의 pass/fail만으로 모델 성능을 계측하는 하네스를 표준으로 정립했다. 메모릭의 Task Ledger 역시 이와 동일하게 러너의 exit code만을 완료의 유일한 진실 공급원으로 삼는다. > 출처: SWE-bench 논문 (arXiv:2310.06770)

  7. 되새김(2026-09-06): 개발 초기에는 지식 베이스를 복잡한 지식 그래프로 구축하는 유행을 따랐으나, 4,882개 노드 중 78.4%가 링크 없는 고립 노드로 남았다. 에이전트가 실제로 유의미하게 활용한 것은 그래프 순회가 아니라 SQLite의 FTS5 전문 검색이었다. 화려한 아키텍처보다 단순하고 빠른 도구가 AI에게 더 유용하다는 사실을 배운 계기였다. 2

  8. 되새김(2026-09-06): RAG(Retrieval-Augmented Generation)는 Lewis et al.(2020)이 제안한 검색 증강 생성 패러다임으로, 모델이 답을 생성하기 전에 외부 문서를 검색해 프롬프트에 보강하는 구조다. 2024~2025년에는 LLM 에이전트가 조직 내부 지식에 접근하는 표준 장치가 되었으며, docs.db는 이 원리를 에이전트 작업 맥락에 적용한 사례다. > 출처: Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (arXiv:2005.11401)

  9. 되새김(2026-09-06): Microsoft Research가 2024년 발표한 GraphRAG는 문서 간 관계를 그래프로 모델링해 커뮤니티 단위 질의응답을 하도록 RAG를 확장한 접근이다. 업계가 그래프 구조를 지식 베이스에 결합하는 흐름으로 움직이는 동안, 메모릭의 실행 기록은 오히려 그래프가 노드 대부분(78.4%)이 고립되는 장식이었음을 보여줬다. > 출처: From Local to Global: A GraphRAG Approach (arXiv:2404.16130)

  10. 되새김(2026-09-06): Thoughtworks Technology Radar(2024~2025) 등에서 주목한 ’Spec-driven Development’는 AI에게 자유로운 코딩을 맡기는 대신, 기계 판독이 가능한 상세 명세(Spec)를 입력으로 주고 에이전트를 엄격한 결정론적 실행기로 제약하는 방법론이다. 메모릭의 /prddelegate-spec 워크플로는 이 명세 주도 접근법을 실전에 정착시킨 사례다. > 출처: Thoughtworks Technology Radar — thoughtworks.com

TAGS — AI / DEV / ARCHITECTURE / TESTING / WORKFLOW / PORTFOLIO

한 사람, 5개월, 57만 줄, 하나의 AI, 하나의 하네스: 1인 개발로 스타트업 서비스 zero to one 런칭까지의 오디세이 — OG 카드