Files
Gnuboard7/docs/extension/extension-documentation.md
T
HeuJung 11175d35d6 feat(core,docs): 확장별 개발자 문서 생성기와 검사 하네스 구축
번들 확장 20개 중 에이전트·확장개발자가 읽을 수 있는 문서를 가진 확장이 0개였다.
`docs/api/**` 는 "엔드포인트가 무엇을 받고 무엇을 돌려주는가" 만 답하고, 확장을 고치려는
쪽이 실제로 묻는 것(왜 이렇게 설계됐는가 / 어디를 확장해야 하는가 / 무엇을 건드리면 안
되는가)은 어디에도 없었다. 그 결과 확장이 발행하는 훅은 확장점인데도 사실상 비공개였다.

이 커밋은 인프라만 담는다. 문서 집필은 확장별로 순차 진행한다.

- `php artisan ext:docgen` — 진입 클래스의 선언형 getter 를 실제로 호출하고 소스를 스캔해
 훅·라우트·권한·메뉴·설정·모델·레이아웃·핸들러·테스트 경로·의존 관계를 실측한다.
 `_bundled` 를 읽는다 (활성 디렉토리에 같은 FQCN 이 로드돼 있으면 eval-rename 으로 우회).
- 자동 생성 블록은 **안쪽만** 교체한다. 사람 서술이 소실될 경로를 만들지 않으려고 파괴적
 재생성 플래그를 두지 않았고, 문서에 없는 블록 키는 주입하지 않고 누락으로 보고한다.
- 필수 문서·섹션·블록 목록은 `ExtensionDocScaffolder::DOCUMENTS` 단일 SSoT 다. 검사
 스크립트는 이를 복제하지 않고 `--check --json` 을 소비하며, PHP 를 못 돌리면 "이상 0건"
 이 아니라 "점검 불가" 로 구분 보고한다.
- 두 audit 룰은 도입 시점 전수가 공허 통과하므로(대상 문서 0건) 픽스처 테스트가 판정식을
 잠근다. 스캐너·mermaid·검사 스크립트 축도 같은 이유로 픽스처를 함께 둔다.

동반 수정: 훅이 넘기는 `file_path` 는 항상 절대경로인데 `FILE_RULES` 다수가 루트로
앵커해 있어 그 규칙들이 조용히 죽어 있었다(추적 파일 440개가 절대경로에서 0건 매치).
`toRepoRelative` 로 정규화하고 세 경로 형태의 판정 일치를 테스트로 고정했다.
문서 인덱스 생성기의 CRLF 문서 제목·TL;DR 추출 누락도 같은 성격이라 함께 고쳤다.
2026-08-31 07:58:44 +09:00

16 KiB

확장 개발자 문서 (Extension Documentation)

번들 확장이 갖추는 AGENTS.md · README.md · docs/** 의 역할 경계, 골격, 자동 생성 규약, 갱신 의무.

TL;DR (5초 요약)

1. 확장마다 AGENTS.md(개발자·에이전트용) + README.md(사람용) + docs/(상세) 를 갖는다
2. 같은 사실은 한쪽만 SSoT — 소개·기능·요구사항은 README, 설계 의도·확장점·동반 의무는 AGENTS
3. 코드에서 실측되는 표는 `php artisan ext:docgen` 이 @generated 블록 안쪽만 교체한다
4. 블록 밖 전부가 사람 영역 — 생성기는 그 자리를 절대 건드리지 않으며 파괴적 재생성 플래그가 없다
5. 사람만 쓸 수 있는 다섯 자리는 TODO 마커로 남는다 (의도 · 흐름 · 금지패턴 · 사용방법 · 트러블슈팅)

목차


1. 왜 두 문서인가

docs/api/** 는 "엔드포인트가 무엇을 받고 무엇을 돌려주는가" 만 답한다. 확장을 수정하려는 사람이 실제로 필요로 하는 것은 그 앞의 질문이다 — 왜 이렇게 설계됐는가 / 어디를 확장해야 하는가 / 무엇을 건드리면 안 되는가.

그 답이 코드 안에만 있으면 세 가지가 생긴다.

  • 확장을 수정할 때마다 src/ 전체를 훑어 구조를 재발견한다.
  • 확장이 발행하는 훅이 코드 안에만 있어 확장점이 사실상 비공개가 된다.
  • 확장 저장소만 받은 제3자에게는 참고할 문서가 CHANGELOG.md 뿐이다.

독자가 둘이므로 문서도 둘이다. 도입을 검토하고 운영하는 사람은 "무엇을 해결해 주는가" 를 묻고, 확장을 고치는 사람은 "어디를 어떻게 건드리는가" 를 묻는다. 한 문서에 섞으면 양쪽 모두에게 길어진다.

2. 파일 배치

{ext}/
├─ AGENTS.md              에이전트·확장개발자 진입점
├─ README.md              사람(도입검토자·운영자) 진입점 — 한국어
├─ CHANGELOG.md           변경 이력
└─ docs/
   ├─ README.md           문서 통합 목차 + 실측 집계
   ├─ architecture.md     설계 의도 · 계층 지도 · 디렉토리 맵
   ├─ extension-points.md 발행/구독 훅 · 미들웨어 · 채널 · 스케줄     [모듈·플러그인]
   ├─ data-model.md       모델 · 소유 테이블 · 마이그레이션 · Enum    [모듈·플러그인]
   ├─ settings.md         설정 스키마 · 권한 · 메뉴 · 라우트 · 의존   [모듈·플러그인]
   ├─ frontend.md         레이아웃 · 핸들러 · 전역 진입점 · 에셋      [모듈·플러그인]
   ├─ components.md       제공 컴포넌트                              [템플릿]
   ├─ layouts.md          레이아웃 목록 · 라우트 매핑                 [템플릿]
   ├─ handlers.md         템플릿 전용 핸들러 · 부트스트랩             [템플릿]
   └─ api/                API 레퍼런스 (별도 체계)

템플릿은 API·모델·훅 축이 사실상 비므로 골격이 다르다. ext:docgen 이 manifest 유형을 보고 골격을 고르므로 유형을 직접 지정할 필요가 없다.

이 문서들은 {ext}/ 안에 있으므로 릴리즈 페이로드에 실리는 공개 배포물이다. 확장 저장소를 받은 사람이 그대로 읽는다.

3. 역할 경계와 SSoT

같은 사실은 한쪽만 SSoT 로 두고 반대쪽은 링크한다. 다만 두 문서 모두 자기 독자에게는 자족적이어야 한다 — "저쪽을 보라" 만 남기면 어느 쪽도 읽히지 않는다.

README.md (사람) AGENTS.md (에이전트·확장개발자)
확장 소개 · 해결하는 문제 (SSoT) 개발 의도 · 설계 원칙 (SSoT)
핵심 기능 목록 (SSoT) 아키텍처 · 계층 지도 · 디렉토리 맵
동작 방식 다이어그램 (운영자 눈높이) 핵심 흐름 (코드 경로 · 계층 통과 순서)
요구사항 · 의존성 · 연동 확장 (SSoT) 도메인 모델 · 소유 테이블 요약
설치 · 활성화 확장점: 발행 훅 · 구독 훅 · 필터 (SSoT)
관리자 설정 화면 사용법 (템플릿은 이 자리에 제공 컴포넌트 요약) 라우트 · 권한 · 설정 스키마 요약
운영 트러블슈팅 프론트 진입점 · 레이아웃 · 핸들러
라이선스 수정 시 동반 의무 체크리스트

필수 섹션 (유형별)

ext:docgen --check 가 요구하는 헤딩이다. 낱말이 본문 어딘가에 있는 것으로는 충족되지 않고 헤딩이어야 한다.

문서 필수 섹션
AGENTS.md TL;DR (5초 요약) · 1. 이 확장은 무엇인가 · 2. 디렉토리 지도 · 3. 핵심 흐름 · 4. 확장점 · 5. 수정 시 동반 의무 · 6. 금지 패턴 · 7. 테스트 실행 · 8. 문서 목차
README.md 소개 · 주요 기능 · 동작 방식 · 요구 사항 · 설치 · 관리자 설정(템플릿은 제공 컴포넌트) · 사용 방법 · 다른 확장과의 연동 · 문서 · 트러블슈팅 · 변경 이력 · 라이선스
docs/README.md 문서 목차
docs/architecture.md 설계 의도 · 계층 지도 · 디렉토리
docs/extension-points.md (모듈·플러그인) 발행 훅 · 구독 훅 · 훅 리스너 · 레이아웃 확장 · 미들웨어 · 브로드캐스트 채널 · 스케줄 · 알림 정의
docs/data-model.md (모듈·플러그인) 모델 · 소유 테이블 · 마이그레이션 · Enum · Repository
docs/settings.md (모듈·플러그인) 설정 스키마 · 권한 · 메뉴 · 라우트 · 의존 관계
docs/frontend.md (모듈·플러그인) 레이아웃 · 액션 핸들러 · 전역 진입점 · 에셋
docs/components.md (템플릿) 제공 컴포넌트
docs/layouts.md (템플릿) 레이아웃 목록 · 라우트 매핑
docs/handlers.md (템플릿) 템플릿 전용 핸들러 · 부트스트랩

목록의 SSoT 는 생성기이므로, 직접 옮겨 적기보다 ext:docgen --init 이 만든 골격에서 시작하는 편이 어긋나지 않는다.

AGENTS.md 의 5. 수정 시 동반 의무

이 절이 문서의 실효성 핵심이다. 코어 횡단 규정 중 그 확장에 실제로 걸리는 것만 추린다. 전부 나열하면 체크리스트 전체가 형식적으로 읽히고, 정작 걸리는 항목이 묻힌다.

예를 들어 이커머스는 통화 스냅샷과 주문 통화 전 사슬이, 게시판은 비밀글 게이트의 하위 리소스 재적용이, 결제 플러그인은 청구 금액 계약과 샌드박스 실호출이 그 자리에 온다.

ext:docgen --init 이 확장이 실제로 보유한 표면(마이그레이션 · 발행 훅 · 라우트 · 레이아웃 · 빌드 산출물 · 다국어)만 골라 초안을 만든다. 사람은 거기에 그 확장 고유의 항목을 보탠다.

시각 자료

스크린샷을 두지 않고 mermaid 다이어그램과 표로 대체한다. 이미지 파일이 없으므로 릴리즈 용량이 늘지 않고, UI 가 바뀔 때마다 다시 촬영할 의무도 없다. GitHub 이 mermaid 를 네이티브로 렌더한다.

mermaid 문법 오류는 렌더 시점에만 드러나므로, 새 형식을 도입할 때는 실제 렌더를 눈으로 확인한다.

4. 자동 생성 블록 규약

생성기는 마커 안쪽만 쓴다.

<!-- @generated:hooks-published START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
| 훅 이름 | 유형 | 발행 위치 |
| ... |
<!-- @generated:hooks-published END -->

<!-- @intent START -->
이 훅은 ... (사람이 쓰는 영역 — 생성기가 건드리지 않는다)
<!-- @intent END -->

블록 밖 전부가 사람 영역이다. 계약은 세 가지다.

  1. 블록 안쪽 교체가 유일한 쓰기 동작이다. 그래서 --force 같은 파괴적 플래그가 없다. 신규 파일 생성만 --init 으로 분리되어 있고, 그 경로도 기존 파일을 덮어쓰지 않는다.
  2. 문서에 없는 블록 키는 주입하지 않는다. 생성기가 임의 위치에 표를 끼워 넣으면 사람이 잡아 둔 문서 구조가 흔들린다. 대신 누락으로 보고하여 사람이 마커 놓을 자리를 정한다.
  3. 재실행은 멱등이다. 같은 코드 상태에서 두 번 돌리면 한 글자도 바뀌지 않는다.

@intent 블록은 사람 영역임을 눈에 띄게 표시하는 장치일 뿐이며, 생성기는 @generated 마커만 찾는다.

블록 목록

블록 키 내용 출처
badges 버전 · 유형 · 코어 제약 · 라이선스 · 의존 배지 manifest
requirements 코어 버전 · PHP · 의존 확장 · 외부 호스트 manifest · composer.json
install 설치 · 활성화 · 업데이트 커맨드 manifest
integrations · dependencies 정방향/역방향 의존 확장 번들 전수 교차 스캔
docs-index · doc-toc 문서 목차와 작성 상태 파일 존재 여부
stats 훅 · 구독 훅 · 라우트 · 모델 · 테이블 · 마이그레이션 · 레이아웃 · 핸들러 8지표 실측 집계 전 수집기
directory-map 경로별 역할과 수정 시 절차 디렉토리 구조
extension-points-summary 확장점 종류별 개수와 상세 링크 훅·선언형 표면
hooks-published · hooks-subscribed · listeners 발행/구독 훅과 리스너 소스 스캔
layout-extensions · middleware · channels · schedules · notifications 선언형 확장점 진입 클래스 getter
models · tables · migrations · enums · repositories 데이터 모델 소스 파싱
settings-schema · settings-summary · permissions · menus · routes 설정 스키마 · README 용 설정 요약(키·의미·기본값) · 권한 · 메뉴 · 라우트 진입 클래스 getter
layouts · handlers · frontend-entry · assets 프론트 표면 레이아웃/TS 스캔
components · layout-map 템플릿 컴포넌트·라우트 매핑 컴포넌트 스캔 · routes.json
test-commands 테스트 종류별 개수와 실행 명령 테스트 경로 스캔

5. 미채움 마커

생성기가 채울 수 없는 자리 — 코드에서 실측되지 않는 왜 — 에는 다섯 종류의 마커가 남는다.

마커 자리 주 위치
TODO: 의도 설계 의도 · 소개 · 주요 기능 AGENTS 1, README 소개·주요 기능
TODO: 흐름 핵심 흐름 · 동작 방식 다이어그램 AGENTS 3, README 동작 방식
TODO: 금지패턴 금지 패턴 표 AGENTS 6
TODO: 사용방법 운영자 사용 시나리오 README 사용 방법
TODO: 트러블슈팅 증상 → 원인 → 조치 README 트러블슈팅

마커 종류를 다섯으로 고정하는 이유는 잔량을 집계할 수 있게 하기 위해서다. 자유 문구로 남기면 어느 자리가 비었는지 셀 수 없다.

의도 와 흐름 은 두 문서 모두에 나타난다 — 같은 축을 다른 독자에게 서술하는 자리이기 때문이다. 나머지 셋은 한쪽에만 있다.

골격을 만든 직후에는 마커가 반드시 존재하며, 그 상태로 커밋하는 것이 정상 흐름이다. 결함이 아니라 집필 진행 상태다. 다만 잔량이 늘어나는 것은 회귀다.

6. ext:docgen 사용법

php artisan ext:docgen
    {--scope=all : all | module:{id} | plugin:{id} | template:{id}}
    {--init      : 문서가 없는 확장에 골격 파일 생성 (기존 파일은 건너뜀)}
    {--check     : 생성하지 않고 누락·드리프트만 리포트}
    {--json      : 기계 판독 출력}
    {--dry-run   : 대상과 실측 집계만 출력}

전형적인 흐름:

# 1. 대상과 실측 규모 확인
php artisan ext:docgen --scope=module:sirsoft-board --dry-run

# 2. 골격 생성 (없는 문서만)
php artisan ext:docgen --scope=module:sirsoft-board --init

# 3. TODO 마커 자리를 코드 근거로 채운다 (사람)

# 4. 표면을 고친 뒤 자동 생성 블록 갱신
php artisan ext:docgen --scope=module:sirsoft-board

# 5. 문서와 코드가 어긋나지 않는지 확인
php artisan ext:docgen --check

작업 위치는 언제나 _bundled 다. 활성 디렉토리 반영은 update 커맨드로만 한다 (문서만 바뀌었다면 빌드는 불필요하다).

php artisan {module|plugin|template}:update {id} --force

수집 대상은 _bundled 소스다

선언형 표면(라우트 · 권한 · 메뉴 · 훅 리스너 · 설정 스키마 등)은 진입 클래스의 getter 를 실제로 호출해 읽는다. 정규식으로 소스를 긁는 방식과 달리 상속 기본값까지 정확히 반영된다.

같은 확장이 활성 디렉토리에서 이미 부팅되어 있으면 PHP 는 같은 클래스를 다시 정의할 수 없으므로, 진입 클래스명만 바꿔 _bundled 파일을 메모리에 다시 읽는다. 그래서 활성 디렉토리에 반영하기 전이라도 _bundled 의 변경이 문서에 그대로 나타난다.

getter 하나가 실패해도 나머지 수집은 계속되며, 실패 사유가 리포트에 드러난다 — 조용한 누락이 없다.

7. 갱신 의무

확장 표면을 바꾸면 같은 작업 단위에서 문서를 함께 갱신한다.

바꾼 것 해야 할 것
발행 훅 추가 · 이름 변경 ext:docgen 재실행 — 다른 확장이 잡는 계약이 바뀐다
라우트 · 권한 · 메뉴 · 설정 스키마 ext:docgen 재실행
모델 · 마이그레이션 ext:docgen 재실행 + 업그레이드 스텝
레이아웃 · 핸들러 · 에셋 ext:docgen 재실행
설계 방침 · 계층 구조 블록 밖 서술을 직접 갱신 (생성기가 채우지 못한다)
새로 발견한 오용 6. 금지 패턴 에 행 추가

자동 생성 블록이 아무리 정확해도 블록 밖 서술이 낡으면 문서 전체가 의심스러워진다. 생성기 재실행은 갱신의 절반이다.

신규 확장

새 확장을 만들면 php artisan ext:docgen --scope={type}:{id} --init 로 문서 골격을 먼저 만든다. 확장이 문서를 갖고 태어나야 하며, 만들자마자 TODO 마커 자리를 채우는 것이 첫 작업이다.

8. 체크리스트

□ AGENTS.md · README.md · docs/ 필수 문서가 유형에 맞게 있는가?
□ 필수 섹션 헤딩이 모두 있는가?
□ 자동 생성 블록 마커가 모두 있는가?
□ `ext:docgen --check` 가 드리프트 0 인가?
□ TODO 마커 다섯 자리를 코드 근거로 채웠는가? (추측 서술 금지)
□ mermaid 다이어그램이 실제로 렌더되는가? (눈으로 확인)
□ 배지 값이 manifest 와 일치하는가? (생성기가 채우므로 직접 쓰지 않는다)
□ README 와 AGENTS 가 같은 사실을 각자 서술하고 있지는 않은가? (SSoT 한쪽 + 링크)
□ `5. 수정 시 동반 의무` 가 이 확장에 실제로 걸리는 것만 담고 있는가?
□ 버전 상향 시 CHANGELOG 에 기재했는가?

관련 문서