Files
Gnuboard7/docs/extension/extension-documentation.md
T
HeuJung 3a0aba85bc feat(core,extensions): 확장 문서 호칭 통일과 레이아웃 편집기 문서 축 신설
확장 문서의 제품 호칭을 「그누보드7」로 통일했다. 가 지적한 두 문구는
개별 오타가 아니라 생성기가 찍는 정형 문구여서, 산출물이 아니라 방출 지점
세 곳을 먼저 고쳤다 — 그러지 않으면 21번째 확장부터 다시 샌다. 정리 범위는
확장 문서와 그 생성기까지이며, 코어 docs 와 언어팩 표시명은 의도적으로 남긴다.

레이아웃 편집기 대응 문서가 없던 문제는 확장마다 docs/editor-spec.md 를
두어 닫았다. 실측은 EditorSpecCollector 가 유지하며, 합본에 런타임과 같은
EditorSpecAssembler 를 써서 문서가 말하는 스펙과 편집기가 읽는 스펙이
갈라질 경로를 두지 않았다. 스펙을 두지 않은 확장에도 문서를 둔다 — 미보유가
정상일 수 있고, 그 정상 여부를 적을 자리가 없으면 다음 사람이 부재를 누락으로
오해하거나 필요한 시점을 놓친다.

초안이 낸 수치 다섯 건이 틀렸고 전부 오류 없이 "사실" 로 실릴 값이었다.
블록 최상위 키를 세어 팔레트가 79 대신 3 이 되던 것, `_` 접두 일괄 배제가
실제 항목을 삼키던 것 등을 정정했다.

게이트를 인위적으로 깨뜨려 점검한 결과 사각 하나가 드러났다 — 채워 넣으라는
표시만 지우고 서술을 쓰지 않으면 검사를 통과해, 빈 문서가 완비로 집계됐다.
미채움을 두 축으로 만들어 닫았고 되돌림으로 검산했다. 편집기 스펙 편집 시
규정이 주입되지 않던 것과 스캐폴딩 안내가 새 문서를 빠뜨리던 것도 함께 고쳤다.
2026-09-01 12:02:55 +09:00

17 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         템플릿 전용 핸들러 · 부트스트랩             [템플릿]
   ├─ editor-spec.md      레이아웃 편집기 선언 · 프리뷰 샘플 커버리지
   └─ api/                API 레퍼런스 (별도 체계)

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

docs/editor-spec.md 만은 세 유형 공통이다. 편집기 스펙(editor-spec.json)을 두지 않는 확장에도 문서를 두는 것은, 미보유가 정상일 수 있고 그 정상 여부를 적을 자리가 필요하기 때문이다 — "이 확장은 왜 스펙이 없어도 되는가 / 언제 필요해지는가" 가 어디에도 없으면 다음 사람이 그 부재를 누락으로 오해하거나 반대로 필요한 시점을 놓친다.

이 문서들은 {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 (템플릿) 템플릿 전용 핸들러 · 부트스트랩
docs/editor-spec.md 선언 요약 · 선언 블록 · 컴포넌트 팔레트 · 샘플 데이터와 페이지 상태 · 수정 시 동반 의무

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

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

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

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

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

시각 자료

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

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

README 첫 화면

확장명은 히어로 이미지 배지가 아니라 평범한 H1 제목으로 적는다. 확장은 서로 대등하게 병렬로 존재하는 구성요소이고, 각자가 코어와 같은 히어로 브랜딩을 달면 그 확장 하나가 독립 프로젝트인 것처럼 보인다. @generated:badges 블록의 버전·유형·코어 제약·라이선스 배지는 manifest 에서 오는 정보 표시이므로 그대로 둔다.

# 페이지

**G7 모듈 · sirsoft-page**
정적 페이지(정보/정책/안내) 관리 모듈

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 에 기재했는가?

관련 문서