안준헌
AI Agent Team 협업 기반 소프트웨어 개발 자동화
AI에게 설계 → GUI 도구로 옮기면 컨텍스트 유실
도구 A→B→C 넘길 때마다 유실
OA 도구 거쳐도, AI 도구끼리 넘겨도 끊김
컨텍스트가 유지되지 않는 작업 환경
병목 ① 산출물 컨텍스트가 AI에게 전달 안 됨
| 컨텍스트 | 어디에 있나 | 왜 AI가 못 읽나 | |
|---|---|---|---|
| DB 설계 의도 · 히스토리 | GUI Modeling tool (DA#) | 그림 형태 — AI가 읽고 쓸 수 없음 | |
| 기획 의도 · 논리 구조 | PPT 기획서 | 레이아웃 형태 — AI가 해석 불가 |
병목 ② 업무 규칙 컨텍스트가 AI에게 전달 안 됨
| 컨텍스트 | 어디에 있나 | 왜 AI가 못 읽나 | |
|---|---|---|---|
| 산출물 스타일 · 패턴 | 담당자 머릿속 | 명문화되지 않음 | |
| 프로젝트 규칙 | 사내 Wiki 산재 | 비구조화, AI 접근 불가 | |
| 역할별 권한 | 구두 합의 | 기록 자체가 없음 |
AI가 읽고 쓸 수 있는 형태로 전환
BEFORE
GUI 모델링 도구
그림 — AI가 읽을 수 없음
암묵지 · 산재 문서
머릿속, Wiki — 비구조화
AFTER
JSON 스키마
코드 — AI가 읽고 쓰는 형태
하네스 구조화
CLAUDE.md · agents · skills · hooks
그림 형태 — AI가 읽고 쓸 수 없음
{
"schema": "fnb_order",
"migrationDir": "developer/migration",
"defaultSince": { "table": "M3_005", "relation": "M8_004" },
"groups": [
{
"name": "미분류",
"description": "자동 생성 — 후처리에서 논리 그룹으로 재분류 필요",
"tables": [ "tb_claim", "tb_claim_target", "tb_fulfillment", "tb_order",
"tb_order_amount", "tb_order_item", "tb_order_product",
"tb_order_product_option", "tb_order_state_log",
"tb_order_state_projection", "tb_order_state_source",
"tb_payment", "tb_payment_unit", "tb_payment_unit_merchant",
"tb_payment_unit_point", "tb_refund", "tb_refund_unit",
"tb_refund_unit_merchant", "tb_refund_unit_point" ]
},
{ "name": "장바구니", "description": "장바구니 관리",
"tables": [ "tb_cart", "tb_cart_product", "tb_cart_product_option" ] }
],
"tables": {
"tb_claim": {
"comment": "클레임", "entityType": "master",
"columns": {
"claim_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "클레임 Key" },
"order_key": { "type": "bigint", "notNull": true, "comment": "주문 Key" },
"claim_ckey": { "type": "integer", "notNull": true, "comment": "클레임 구분 코드 Key",
"codeIds": { "ORDER": { "CLAIM": { "CLAIM_TYPE": {
"CUSTOMER_CANCEL": {}, "STORE_CANCEL": {}, "RETURN": {} } } } } },
"claim_mid": { "type": "varchar(50)", "comment": "입점사 클레임 식별자" },
"last_state_lkey": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 상태 이력 Key" },
"last_success_refund_key": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 성공 환불 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_claim_target": {
"comment": "클레임 대상", "entityType": "master",
"columns": {
"claim_target_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "클레임 대상 Key" },
"claim_key": { "type": "bigint", "notNull": true, "comment": "클레임 Key" },
"target_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "대상 코드 Key",
"codeIds": { "ORDER": { "ENTITY": { "TARGET_TYPE": {
"ORDER": {}, "FULFILLMENT": {}, "ORDER_PRODUCT": {}, "PAYMENT": {},
"PAYMENT_UNIT": {}, "CLAIM": {}, "REFUND": {}, "REFUND_UNIT": {} } } } } },
"target_key": { "type": "bigint", "notNull": true, "comment": "대상 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_fulfillment": {
"comment": "이행",
"description": "픽업주문과 예약주문 공용. 픽업주문은 queue_no/est_wait_min/queue_req_dttm으로 대기열을 관리하고, 예약주문의 방문 정보(인원·일시)는 tb_order_product.prod_qty와 타임슬롯 옵션으로 표현한다.",
"entityType": "master",
"columns": {
"fulf_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "이행 Key" },
"order_key": { "type": "bigint", "notNull": true, "comment": "주문 Key" },
"store_key": { "type": "integer", "notNull": true },
"fulf_verif_id": { "type": "varchar(512)", "notNull": true },
"fulf_mid": { "type": "varchar(50)", "comment": "입점사 이행 식별자" },
"last_state_lkey": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 상태 이력 Key" },
"queue_no": { "type": "integer", "comment": "대기열 번호" },
"est_wait_min": { "type": "integer", "comment": "예상 대기 시간(분)" },
"queue_req_dttm": { "type": "timestamp", "comment": "대기열 요청 시간" }
}, "audit": true, "since": "M3_005"
},
"tb_order": {
"comment": "주문", "entityType": "master",
"columns": {
"order_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "주문 Key" },
"order_mid": { "type": "varchar(50)", "notNull": true, "comment": "주문 입점사 ID" },
"member_key": { "type": "integer", "notNull": true, "comment": "회원 Key" },
"sales_unit_info_snap_lkey": { "type": "bigint", "notNull": true, "comment": "매출 단위 정보 스냅샷 이력 Key" },
"order_type_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "주문 유형 코드 Key",
"codeIds": { "ORDER": { "ENTITY": { "ORDER_TYPE": { "PICKUP": {}, "RESERVATION": {} } } } } },
"order_ch_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "주문 채널 코드 Key",
"codeIds": { "COMMON": { "CHANNEL": { "WEB_PC": {}, "WEB_MOBILE": {}, "APP_IOS": {}, "APP_AOS": {} } } } },
"prod_qty": { "type": "integer", "notNull": true, "comment": "주문 상품 수" },
"last_success_pay_key": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 성공 결제 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_order_amount": {
"comment": "주문 금액", "entityType": "master",
"columns": {
"amt_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "금액 Key" },
"target_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "대상 구분 코드 Key" },
"target_key": { "type": "bigint", "notNull": true, "comment": "대상 Key" },
"amt_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "금액 구분 코드 Key" },
"currency_cd": { "type": "char(3)", "notNull": true, "comment": "통화 코드" },
"amount": { "type": "numeric(18,2)", "notNull": true, "comment": "금액" }
}, "audit": true, "since": "M3_005"
},
"tb_order_state_log": {
"comment": "거래 상태 이력",
"description": "모든 주문 엔티티(주문·이행·결제·클레임·환불)의 상태 변경을 target_ckey 다형성으로 기록하는 감사(audit) 이력. Event Sourcing 원본.",
"entityType": "history",
"columns": {
"log_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "이력 Key" },
"target_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "대상 코드 Key" },
"target_key": { "type": "bigint", "notNull": true, "comment": "대상 Key" },
"state_group_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "상태 그룹 코드 Key" },
"state_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "상태 코드 Key" },
"cause_group_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "변경원인 그룹 코드 Key" },
"cause_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "변경원인 코드 Key" },
"change_reason": { "type": "varchar(500)", "notNull": true, "comment": "변경 이유" },
"operator_key": { "type": "integer", "notNull": true, "comment": "작업자 Key" },
"change_dttm": { "type": "timestamp", "notNull": true, "comment": "변경 일시" }
}, "audit": true, "since": "M3_005"
},
"tb_order_state_projection": {
"comment": "주문 상태 반영",
"description": "주문 자체에는 상태가 없으나, 고객 페이싱(FO)에서는 이행·결제·클레임 상태를 분리해서 보여줄 수 없으므로 이행 단위별 대표 상태가 필요하다.",
"entityType": "master",
"columns": {
"proj_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "반영 Key" },
"target_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "대상 코드 Key" },
"target_key": { "type": "bigint", "notNull": true, "comment": "대상 Key" },
"src_target_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "소스 대상 코드 Key" },
"src_target_total_cnt": { "type": "integer", "notNull": true, "comment": "소스 대상 총 갯수" }
}, "audit": true, "since": "M3_005"
},
"tb_order_state_source": {
"comment": "주문 상태 소스",
"description": "projection의 하위 엔티티별 상태 카운트 캐시. cnt == src_target_total_cnt이면 모든 하위 엔티티가 동일 상태에 도달.",
"entityType": "master",
"columns": {
"src_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "소스 Key" },
"proj_key": { "type": "bigint", "notNull": true, "comment": "반영 Key" },
"state_group_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "상태 그룹 코드 Key" },
"state_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "상태 코드 Key" },
"cnt": { "type": "integer", "notNull": true, "comment": "갯수" }
}, "audit": true, "since": "M3_005"
},
"tb_payment": {
"comment": "결제", "entityType": "master",
"columns": {
"pay_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "결제 Key" },
"order_key": { "type": "bigint", "notNull": true, "comment": "주문 Key" },
"last_state_lkey": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 상태 이력 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_payment_unit": {
"comment": "결제 단위", "entityType": "master",
"columns": {
"pay_unit_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "결제 단위 Key" },
"pay_key": { "type": "bigint", "notNull": true, "comment": "결제 Key" },
"method_ckey": { "type": "integer", "notNull": true, "default": "0", "comment": "결제수단 코드 Key" },
"last_state_lkey": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 상태 이력 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_refund": {
"comment": "환불", "entityType": "master",
"columns": {
"refund_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "환불 Key" },
"claim_key": { "type": "bigint", "notNull": true, "comment": "클레임 Key" },
"pay_key": { "type": "bigint", "notNull": true, "comment": "결제 Key" },
"last_state_lkey": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 상태 이력 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_refund_unit": {
"comment": "환불 단위", "entityType": "master",
"columns": {
"refund_unit_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "환불 단위 Key" },
"refund_key": { "type": "bigint", "notNull": true, "comment": "환불 Key" },
"pay_unit_key": { "type": "bigint", "notNull": true, "comment": "결제 단위 Key" },
"last_state_lkey": { "type": "bigint", "notNull": true, "default": "0", "comment": "최종 상태 이력 Key" }
}, "audit": true, "since": "M3_005"
},
"tb_cart": {
"comment": "장바구니", "entityType": "master",
"columns": {
"cart_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "장바구니 Key" },
"member_key": { "type": "integer", "notNull": true, "comment": "회원 Key" },
"sales_unit_key": { "type": "integer", "notNull": true, "comment": "매출 단위 Key" }
}, "audit": true, "since": "M3_018"
},
"tb_cart_product": {
"comment": "장바구니 상품", "entityType": "master",
"columns": {
"cart_prod_key": { "type": "bigint", "pk": true, "identity": true, "notNull": true, "comment": "장바구니 상품 Key" },
"cart_key": { "type": "bigint", "notNull": true, "comment": "장바구니 Key" },
"store_prod_key": { "type": "bigint", "notNull": true, "comment": "가게 상품 Key" },
"options_hash": { "type": "varchar(64)", "notNull": true, "comment": "옵션 해시" },
"prod_qty": { "type": "integer", "notNull": true, "comment": "상품 수량" }
}, "audit": true, "since": "M3_018"
}
},
"relations": [
{ "name": "tb_claim_fk_01", "from": { "table": "tb_claim", "column": "last_success_refund_key" }, "to": { "table": "tb_refund", "column": "refund_key" }, "type": "1:1" },
{ "name": "tb_claim_fk_02", "from": { "table": "tb_claim", "column": "order_key" }, "to": { "table": "tb_order", "column": "order_key" }, "type": "N:1" },
{ "name": "tb_fulfillment_fk_01", "from": { "table": "tb_fulfillment", "column": "order_key" }, "to": { "table": "tb_order", "column": "order_key" }, "type": "N:1" },
{ "name": "tb_order_fk_01", "from": { "table": "tb_order", "column": "member_key" }, "to": { "table": "tb_member", "column": "member_key", "schema": "fnb_member" }, "type": "N:1" },
{ "name": "tb_order_product_fk_01", "from": { "table": "tb_order_product", "column": "order_key" }, "to": { "table": "tb_order", "column": "order_key" }, "type": "N:1" },
{ "name": "tb_payment_fk_02", "from": { "table": "tb_payment", "column": "order_key" }, "to": { "table": "tb_order", "column": "order_key" }, "type": "N:1" },
{ "name": "tb_payment_unit_fk_01", "from": { "table": "tb_payment_unit", "column": "pay_key" }, "to": { "table": "tb_payment", "column": "pay_key" }, "type": "N:1" },
{ "name": "tb_refund_fk_01", "from": { "table": "tb_refund", "column": "pay_key" }, "to": { "table": "tb_payment", "column": "pay_key" }, "type": "N:1" },
{ "name": "tb_refund_fk_02", "from": { "table": "tb_refund", "column": "claim_key" }, "to": { "table": "tb_claim", "column": "claim_key" }, "type": "N:1" },
{ "name": "tb_cart_fk01", "from": { "table": "tb_cart", "column": "member_key" }, "to": { "table": "tb_member", "column": "member_key", "schema": "fnb_member" }, "type": "N:1" },
{ "name": "tb_cart_product_fk01", "from": { "table": "tb_cart_product", "column": "cart_key" }, "to": { "table": "tb_cart", "column": "cart_key" }, "type": "N:1" }
],
"logicalRelations": [
{ "name": "상태 이력 대상 참조 (주문)", "table": "tb_order_state_log", "discriminator": "target_ckey", "key": "target_key", "target": "tb_order" },
{ "name": "상태 이력 대상 참조 (이행)", "table": "tb_order_state_log", "discriminator": "target_ckey", "key": "target_key", "target": "tb_fulfillment" },
{ "name": "상태 이력 대상 참조 (결제)", "table": "tb_order_state_log", "discriminator": "target_ckey", "key": "target_key", "target": "tb_payment" },
{ "name": "상태 반영 대상 참조", "table": "tb_order_state_projection", "discriminator": "target_ckey", "key": "target_key", "target": "tb_order" },
{ "name": "상태 소스 → 반영", "table": "tb_order_state_source", "key": "proj_key", "target": "tb_order_state_projection" }
]
}
AI가 직접 읽고 쓰는 형태
— generate.mjs
사람이 리뷰할 땐 JSON에서 자동 생성된 ERD로 — 외부 도구 불필요
도구 체인
JSON 하나로 DB 산출물 전체를 파생
JSON
Single Source of Truth
DDL
CREATE TABLE 스크립트
ERD 시각화
SVG 렌더링 · ELK.js
7
도메인 ERD
9
단계 마이그레이션
Git
버전 관리 용이
Diff
공유 · 리뷰 원활
시간 단축 효과
기존 (GUI 툴) vs 전환 후 (JSON + AI)
하네스
마구(馬具) — AI의 힘을 원하는 방향으로 쓰게 하는 장치
.claude/
핵심 지침 — 프로젝트 규칙서. 모든 에이전트가 읽는 기본 문서
환경 설정 — 모델·권한·MCP 서버·hooks(이벤트 트리거) 구성
역할 부여 — AI 에이전트별 책임·권한·도구 정의
업무 도구 — 에이전트가 사용하는 전문 스킬 정의
커스텀 디렉토리 — 프로젝트별 가드레일 스크립트 모음
주문 기능을 만들려면?
따로 열어도, 합쳐도 문제 — 어떻게 해야 하나?
AI-Legible Workspace
CLAUDE.md가 어떤 레포를 언제 참조할지 지시
필요한 하위 레포만 선택적으로 조합
자기만의 CLAUDE.md + 하네스를 플러그인으로 설치 가능
공통 규칙 및 개별 규칙을 한 곳에서 관리, 각 레포에 동기화
기획자도 코드 에디터 + Git 작업 환경에서 AI와 협업
송금아 이사님 · 이종석 과장님 · 박소미 과장님
러닝커브 1주일 내 해소 · 개발자와의 산출물 공유 원활
하네스 워크플로우
하네스 완성도에 따라 워크플로우가 어떻게 달라지는지 비교합니다
하네스 완성도 20% → 80%로 보강되는 과정을 보겠습니다
하네스 없이 AI에게 프롬프트만
규칙 없음 · 가드레일 없음 · 일관성 없음
리뷰에서 하나둘 고치다 보면 결국 수작업 — AI를 쓰는 이득이 0에 수렴
CLAUDE.md만 있을 때 하네스 시작
프로젝트 규칙서만 존재 · 에이전트/스킬/훅 없음
CLAUDE.md 규칙은 적용되지만, 분석·검증·교정은 모두 사람 몫
워크플로우 하네스 ~20%
W13 · 에이전트 4명 · ~30건 체크 · 단순 pass/fail
풀 워크플로우 — Track A 하네스 ~80%
W14 · 에이전트 6명 · 113건 체크 · 6차원 ICL 자동 교정
* ICL = Iterative Correction Loop — 품질 미달 시 Fix Directive를 생성하여 구현→검증을 반복하는 자동 교정 루프
병렬 워크플로우 실행
이슈가 시간차로 동시 진행 — 사용자는 Spec 승인과 에스컬레이션만 개입
W14 기준 일 평균 ~20건 병렬 처리 · 한 줄 지시 → 전체 자동
규칙이 레포를 넘지 못한다
agents · skills · hooks — 선언한 레포 안에서만 동작
BEFORE
AFTER
지시서가 길면 AI가 잊는다
매뉴얼 200페이지를 한꺼번에 읽으면 앞부분을 까먹는 것과 같음
CLAUDE.md에는 핵심 원칙만
상세 절차 → agents · skills로 위임
같은 규칙이 여러 파일에 반복되면
한 곳에 정의하고 참조
글로 쓴 규칙을 프로그램으로 강제
CLI 명령어 · JS 스크립트 등
Git은 의미 충돌을 못 잡는다
하네스 파일도 git에 올라감 — 라인 충돌 ≠ 의미 충돌
GIT MERGE
HARNESS-KEEPER
코드 리뷰 소요 시간
Bitbucket PR 생성 → 머지 시각 기준 측정
BEFORE
90분 (최대)
AFTER
5분 (최대)
AI Reviewer가 113건 체크리스트를 자동 수행
사람은 BLOCKER 요약만 확인 → 승인/반려
도입 효과
BE git log 기준 · 인·월당 기능 수 · order + catalog + member 합산
총 이슈 (3개 도메인)
증가율
측정 기간
1건 = git 커밋의 고유 Jira 이슈 번호 · order(114) + catalog(287) + member(83) · 진행 중
이전 프로젝트와의 비교
주문 도메인, 도메인 API 기준
| 엔드포인트 | 90개 |
| 집중 개발 | ~6개월 |
| 개발자 | 3명 |
| 엔드포인트/인·월 | ~5개 |
| 엔드포인트 | 43개 (진행 중) |
| 집중 개발 | ~2개월 |
| 개발자 | 1명 |
| 엔드포인트/인·월 | ~20개 |
AI가 읽을 수 있는 구조를 먼저 만들었기 때문입니다.
인천공항은 진행 중이라 최종 수치는 달라질 수 있음
다음 단계
직무 경계 재설정
AI 워크플로우 기준으로 역할 분담을 다시 그림
메타 하네스
하네스 자체를 AI가 생성·보강하는 구조
참고 출처
기술 근거
| W3C | Data on the Web Best Practices |
| OASIS | DITA v1.2 Specification |
| NTIA | AI Accountability Policy Report (2024) |
| Microsoft | RAG in Azure AI Search |
컨퍼런스
| DevMento | Agentic Workflow 2026 — AI 에이전트 주도 개발 실전 컨퍼런스 |
안준헌 · 이커머스사업그룹 · 2026.04.25
감사합니다