Skip to main content

@MX TAG 시스템

업데이트 2026-08-13 9분 분량 GitHub에서 수정 ↗

에이전트(스스로 일하는 AI 도우미)가 코드를 쓰고 고칠 때 가장 자주 부딪히는 벽은, “이 코드가 왜 이렇게 생겼는지"를 다음 세션이 모른다는 점입니다. 어제 한 시간을 들여 논의한 설계 의도, 조심해야 할 위험 구역, 함부로 바꾸면 안 되는 불변 계약 — 이 모든 맥락은 세션이 끝나면 흔적 없이 사라집니다. 프롬프트로 다시 설명해 줘도 읽히지 않을 수 있고, 문서로 남겨 두어도 코드와 멀어지면 금방 쓰레기가 됩니다.

@MX TAG는 이 맥락을 코드 안에 직접 새겨 넣는 표준 주석 규약입니다. 에이전트가 코드를 읽는 순간 의도와 제약, 위험, 빚이 코드와 함께 눈에 들어오도록, 그래서 세션과 세션 사이에 맥락이 끊기지 않도록 설계된 가교입니다. 프롬프트는 무시될 수 있어도, 코드에 붙은 주석은 코드와 함께 살아남습니다.

@MX TAG를 실제로 스캔하고 추가하고 질의하는 일은 /moai mx 명령어가 맡습니다. 이 페이지는 태그 시스템 자체의 개념과 프로토콜, 라이프사이클을 다룹니다.

왜 코드에 주석으로 다는가

맥락을 어디에 남길 것인지를 두고 흔히 세 가지 선택지를 떠올립니다 — 프롬프트, 외부 문서, 코드 주석. 앞의 두 가지는 모두 코드에서 멀어지면 금방 쓰레기가 된다는 공통된 약점이 있습니다. @MX TAG가 코드 주석을 택한 이유는, 주석만이 코드와 함께 이동하기 때문입니다.

flowchart TD
    A["에이전트 A: 코드에 의도를 담아 작성"] --> B{"맥락을 어디에 남길까?"}
    B -->|프롬프트만| C["다음 세션: 맥락 유실"]
    C --> D["처음부터 다시 설명"]
    B -->|외부 문서| E["코드와 멀어지면 곧 쓰레기"]
    B -->|@MX TAG 주석| F["코드와 함께 맥락 보존"]
    F --> G["다음 세션: 코드를 읽으면 의도·위험·불변이 곧장 보임"]

별도의 JSON 원장(ledger)은 리팩터링 한 번이면 코드와 어긋납니다. 위키 문서는 함수가 이동하는 순간 낡습니다. 반면 인라인 주석은 이름 바꾸기, 이동, 머지를 모두 거치면서도 코드를 따라갑니다. 함수가 죽으면 그 함수에 붙은 태그도 함께 죽습니다 — 고아 레코드가 남지 않습니다. 누군가 함수를 읽을 때 태그는 이미 그 시선 경로 위에 있습니다. 모아 보는 비용도 싸서, grep이나 moai mx query로 필요한 순간에 한 번에 수확할 수 있습니다.

하네스(품질 검증 자동 장치)는 이 특성을 활용합니다. 계획 단계에서 에이전트가 태그 후보를 식별하고, 구현 단계에서 태그를 붙이거나 갱신하며, 동기화 단계에서 빠진 태그를 검증해 채워 넣습니다. SPEC(요구사항 명세서) 문서가 “무엇을 만들지"를 세션 밖에 보존한다면, @MX TAG는 “이 코드가 무슨 의도를 품고 있는지"를 코드 안에 보존합니다. 두 축이 함께 힘을 합쳐 세션 경계를 넘어 맥락이 유지되게 합니다.

다섯 가지 태그 — 다섯 가지 의도

@MX TAG는 한 가지 종류의 주석이 아니라 다섯 가지 서로 다른 의도를 구분합니다. 같은 색의 메모지로 모든 것을 적으면 중요도가 사라지듯, 태그도 용도를 나누어야 다음 에이전트가 “이건 그냥 설명이야"와 “이건 건드리면 위험해"를 한 줄에서 판별할 수 있습니다.

태그의미언제 다는가
@MX:NOTE“이 코드는 이런 의도야”매직 상수, 100줄이 넘는 exported 함수의 godoc 부재, 설명 없는 비즈니스 규칙
@MX:WARN“여기는 위험 구역이야”context.Context 없는 goroutine, 순환 복잡도 15 이상, 전역 상태 변경, if-분기 8개 이상
@MX:ANCHOR“이건 불변 계약이야”fan_in 3 이상 호출자, 공개 API 경계, 외부 시스템 통합 지점
@MX:TODO“아직 덜 됐어”테스트 파일 없는 공개 함수, 미구현 SPEC 요구사항, 처리 없이 반환되는 에러
@MX:DEBT“일부러 단순하게 했고, 한계를 알아”명시된 한계 안에서 정확히 동작하는 단순화, 재방문 트리거가 있는 경우

NOTE는 읽는 이에게 ‘왜’를 전달합니다. WARN은 ‘조심’을 경고합니다. ANCHOR는 ‘건드리지 마’라는 불변 계약을 새깁니다. TODO는 ‘아직 끝나지 않았어’라는 미완료 표시입니다. DEBT는 ‘완성됐지만 일부러 단순하게 뒀어’라는, 가장 오해받기 쉬운 표시입니다.

태그 문법

태그는 언어의 주석 문법 위에 얹히는 한 줄 주석이며, 필요한 경우 아래에 하위 라인을 덧붙입니다.

go
// @MX:NOTE: 이 매직 상수는 결제 게이트웨이 스펙의 최소 타임아웃
// @MX:WARN: context.Context 없이 goroutine 시작 — 부모 취소가 전파되지 않음
// @MX:REASON: 부모 컨텍스트 취소가 자식 goroutine에 닿지 않아 누수 위험

태그는 소스 파일에 인라인으로 붙는 주석입니다. 별도의 JSON 원장이나 데이터베이스가 아니며, 에이전트와 사용자는 grep이나 moai mx query로 태그를 모아 봅니다. 에이전트가 생성한 태그에는 [AUTO] 접두어가 붙어 사람이 직접 단 태그와 구분됩니다.

DEBT와 TODO — 가장 흔한 혼동

다섯 태그 가운데 가장 자주 오해받는 것이 @MX:DEBT입니다. 이름만 보면 “빚"이니 “미완성"으로 읽기 쉽지만, 정확히 반대입니다. DEBT가 붙은 코드는 이미 완성돼 정확히 동작합니다. 다만 더 완전한 구현이 가능함에도 불구하고, 명시한 한계 안에서 일부러 단순하게 짰다는 사실을 기록하는 것입니다.

flowchart TD
    subgraph TODO["@MX:TODO — 미완성 작업"]
        T1["RED/ANALYZE 단계에서 생성"] --> T2["GREEN 단계에서 해결·제거"]
        T2 -.|"3회 미해결 시"| T3["WARN으로 승격"]
    end
    subgraph DEBT["@MX:DEBT — 완성된 단순화"]
        D1["의도적 단순화 채택"] --> D2["CEILING 한계 안에서 정확 동작"]
        D2 --> D3["여러 GREEN 단계를 넘어 지속(정상)"]
        D3 --> D4["UPGRADE 트리거 발화 시 더 완전한 구현으로 교체"]
    end

반면 @MX:TODO는 진짜 미완성을 가리킵니다. 코드가 아직 끝나지 않았으며, GREEN(구현 완료) 단계에서 제거되어야 합니다. 세 번의 반복을 넘겨도 남아 있으면 WARN으로 승격되어 “이 미완성은 방치되고 있다"고 경고합니다.

DEBT에게는 이런 승격 규칙이 적용되지 않습니다. DEBT는 여러 GREEN 단계를 넘어 그대로 남아 있어도 정상입니다 — 코드는 이미 완성됐고, 한계 안에서 정확히 돌기 때문입니다. DEBT가 사라지는 순간은 “다른 작업이 끝났을 때"가 아니라, @MX:UPGRADE 트리거가 발화해 단순화를 더 완전한 구현으로 교체할 때입니다. DEBT를 TODO처럼 취급해 자동 승격시키면, 정상적으로 오래 사는 단순화에서 거짓 위험 신호가 계속 튀어 나오게 됩니다.

두 하위 라인 — CEILING과 UPGRADE

DEBT는 두 하위 라인을 동반합니다. 둘이 어떻게 짝을 이루는지를 한 예에서 보면, 인메모리 맵 캐시를 ‘일부러’ 단순하게 둔 경우를 떠올려 보겠습니다.

go
// @MX:DEBT: in-memory map cache, no eviction
// @MX:CEILING: < 10k entries
// @MX:UPGRADE: switch to LRU when entry count exceeds 10k

@MX:CEILING은 “이 단순화가 정확히 동작하는 한계"를 적습니다(예: 엔트리 1만 개 미만). @MX:UPGRADE는 “이 한계에 도달했을 때 더 완전한 구현으로 바꾸는 트리거"를 적습니다. 둘 가운데 부패(rot) 판정의 핵심은 UPGRADE입니다. UPGRADE가 없는 DEBT는 끝날 조건이 없어 조용히 부패합니다. moai mx query --kind DEBT --json은 이런 항목을 "rotRisk": "no-trigger"로 표시합니다. 반대로 CEILING이 없는 것은 품질 메모일 뿐, 부패 판정 기준은 아닙니다.

하위 라인 전체

태그 본줄 아래에 덧붙는 하위 라인은 일곱 가지입니다. 각 하위 라인이 어떤 역할을 지니는지를 한눈에 정리한 표입니다.

하위 라인용도필수 여부
@MX:REASONWARN·ANCHOR의 위험·불변 사유WARN·ANCHOR에 필수
@MX:CEILINGDEBT의 동작 한계DEBT에 권장
@MX:UPGRADEDEBT의 재방문 트리거DEBT에 권장(없으면 부패)
@MX:SPEC태그가 가리키는 SPEC ID해당 SPEC이 있을 때
@MX:LEGACYSPEC 없는 레거시 코드 표시레거시에 사용
@MX:TEST태그와 짝지어진 테스트TODO 검증에 사용
@MX:PRIORITYWARN의 P1–P5 우선순위WARN 우선순위 정렬에 사용

라이프사이클 — 다섯 태그가 각자 다르게 산다

다섯 태그는 태어나는 조건도, 사라지는 조건도, 오래 살아남는 방식도 각기 다릅니다. 한 표로 정리하면 다음과 같습니다.

태그언제 태어나나언제 사라지나오래 남는 특성
@MX:NOTE컨텍스트가 필요할 때코드가 삭제될 때시그니처 변경 시 재검토
@MX:WARN위험 감지 시위험 구조가 해결될 때구조적 위험이면 지속
@MX:ANCHORfan_in ≥ 3자동 삭제 금지fan_in < 3이면 NOTE로 강등(리포트 동반)
@MX:TODORED/ANALYZE 단계GREEN 단계에서 해결3회 미해결 시 WARN 승격
@MX:DEBT의도적 단순화 채택 시UPGRADE 트리거 발화 시여러 GREEN을 넘어 지속(정상)

ANCHOR의 “자동 삭제 금지"는 의도된 규칙입니다. 불변 계약은 리팩터링 중에도 보존되어야 하며, 더 이상 불변이 아니게 되었을 때는 에이전트가 리포트를 통해 사람에게 알리고 NOTE로 강등합니다. 이렇게 해야 계약이 조용히 사라지는 일이 없습니다. TODO는 GREEN 단계에서 코드가 완성되는 순간 제거되지만, DEBT는 UPGRADE 트리거가 발화하기 전까지는 코드가 완성돼 있어도 그대로 남습니다 — “완성됐느냐"가 아니라 “단순화를 교체할 때가 왔느냐"가 제거 조건이기 때문입니다.

언어별 주석 문법

태그는 언어의 주석 문법 위에 얹힙니다. 16개 지원 언어는 세 가지 주석 접두어 가운데 하나를 씁니다.

언어접두어예시
Go · Java · TS · Rust · C/C++ · Swift · Kotlin · Dart · Zig · Scala//// @MX:NOTE:
Python · Ruby · Elixir## @MX:WARN:
Haskell---- @MX:ANCHOR:

설정 — .moai/config/sections/mx.yaml

태그의 역치와 한도, 제외 패턴은 프로젝트 설정에서 다룹니다.

  • thresholdsfan_in_anchor(ANCHOR를 다는 최소 호출 수, 기본 3), complexity_warn(WARN을 다는 순환 복잡도 역치, 기본 15), branch_warn(WARN을 다는 if-분기 수 역치, 기본 8).
  • limits — 파일당 태그 수 한도. anchor_per_file(기본 3), warn_per_file(기본 5), note_per_file(기본 10), todo_per_file(기본 5). 한도를 넘으면 ANCHOR는 fan_in이 낮은 것부터, WARN은 P1–P5 우선순위만 남기도록 정리됩니다.
  • exclude**/*_generated.go, **/vendor/**, **/mock_*.go 등 태깅에서 뺄 파일 패턴.
  • require_reason_for — REASON을 반드시 달아야 하는 태그 타입 목록(WARN·ANCHOR 권장).

태그 언어 — 코드 주석 언어를 따름

태그 설명과 @MX:REASON.moai/config/sections/language.yamlcode_comments 설정을 따릅니다(기본 en). 한국어 프로젝트라면 code_comments: ko로 설정해 태그 본문을 한국어로 적을 수 있고, 다국어 팀이라면 프로젝트 기준 언어 하나로 통일합니다. 이 설정은 에이전트가 태그를 생성할 때 반드시 읽고 반영해야 합니다 — 설정을 무시하고 에이전트가 임의 언어로 태그를 적으면, 코드 리뷰 시 통일성이 무너집니다.

다음 단계