feat(core,extensions): 확장 문서 호칭 통일과 레이아웃 편집기 문서 축 신설
확장 문서의 제품 호칭을 「그누보드7」로 통일했다. 가 지적한 두 문구는 개별 오타가 아니라 생성기가 찍는 정형 문구여서, 산출물이 아니라 방출 지점 세 곳을 먼저 고쳤다 — 그러지 않으면 21번째 확장부터 다시 샌다. 정리 범위는 확장 문서와 그 생성기까지이며, 코어 docs 와 언어팩 표시명은 의도적으로 남긴다. 레이아웃 편집기 대응 문서가 없던 문제는 확장마다 docs/editor-spec.md 를 두어 닫았다. 실측은 EditorSpecCollector 가 유지하며, 합본에 런타임과 같은 EditorSpecAssembler 를 써서 문서가 말하는 스펙과 편집기가 읽는 스펙이 갈라질 경로를 두지 않았다. 스펙을 두지 않은 확장에도 문서를 둔다 — 미보유가 정상일 수 있고, 그 정상 여부를 적을 자리가 없으면 다음 사람이 부재를 누락으로 오해하거나 필요한 시점을 놓친다. 초안이 낸 수치 다섯 건이 틀렸고 전부 오류 없이 "사실" 로 실릴 값이었다. 블록 최상위 키를 세어 팔레트가 79 대신 3 이 되던 것, `_` 접두 일괄 배제가 실제 항목을 삼키던 것 등을 정정했다. 게이트를 인위적으로 깨뜨려 점검한 결과 사각 하나가 드러났다 — 채워 넣으라는 표시만 지우고 서술을 쓰지 않으면 검사를 통과해, 빈 문서가 완비로 집계됐다. 미채움을 두 축으로 만들어 닫았고 되돌림으로 검산했다. 편집기 스펙 편집 시 규정이 주입되지 않던 것과 스캐폴딩 안내가 새 문서를 빠뜨리던 것도 함께 고쳤다.
This commit is contained in:
@@ -502,11 +502,18 @@ Icon 은 `<i>` 글리프라 박스 크기가 곧 `font-size` 다. `w-N h-N` 은
|
||||
| 신규 확장을 문서 없이 스캐폴딩 | `php artisan ext:docgen --scope={type}:{id} --init` 으로 골격을 함께 만든다 — 없으면 21번째 확장부터 다시 문서 없이 태어난다 |
|
||||
| 확장 문서를 활성 디렉토리에서 작성 | `_bundled` 에서만 작성하고 update 커맨드로 반영 (문서만이면 빌드 불필요) |
|
||||
| 확장이 훅을 추가할 때 코어 문서를 고침 | 훅 집계는 그 확장의 `docs/extension-points.md` 소유 — 코어에는 총계와 링크만 |
|
||||
| 레이아웃에 `data_source` 를 추가하고 `editor-spec.json` 의 `sampleData` 를 그대로 둠 | 같은 ID 로 프리뷰 샘플 추가 — 없으면 **편집기 캔버스에서만** 그 영역이 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다 |
|
||||
| 컴포넌트를 추가하고 팔레트에만 등록 | 템플릿 스펙은 `componentPalette.entries` · `componentPalette.groups` · `nesting` · `componentCapabilities` **넷 다** — 하나만 빠지면 편집기에서 절반만 동작하고, 어느 단계가 빠졌는지는 증상으로만 구분된다 |
|
||||
| 모듈·플러그인 스펙에 `componentPalette` 선언 | 컴포넌트는 템플릿 소유 — 모듈·플러그인 스펙은 도메인 데이터(`sampleData`·`states`)만 담는다. 같은 자리를 두고 다투면 어느 쪽이 이기는지가 병합 순서에 좌우된다 |
|
||||
| 공용 ID(`settings`·`roles`·`me`)를 확장마다 각자 선언 | 템플릿 스펙 한 곳 — 사본이 갈라져도 오류가 나지 않는다 |
|
||||
| 편집기 스펙을 고치고 update 커맨드 생략 | 서빙은 **활성 디렉토리만** 읽는다(`_bundled` 폴백 없음) — 파일은 고쳤는데 편집기에 직전 내용이 그대로 보인다 |
|
||||
|
||||
이 결함군은 오류를 남기지 않는다. 문서가 코드와 어긋난 채로 계속 읽히는 것이 유일한 증상이며, 훅 이름이 어긋나면 그 확장을 잡으려던 쪽이 **잡히지 않는 훅을 구독**하게 된다(예외도 경고도 없이 리스너가 호출되지 않을 뿐이다).
|
||||
|
||||
mermaid 문법 오류는 GitHub 렌더 시점에만 드러난다. 구조 검사가 잡을 수 있는 것은 선언된 다이어그램 종류·빈 본문·괄호 균형까지이므로, 새 형식은 실제 렌더를 눈으로 확인한다.
|
||||
|
||||
`docs/editor-spec.md` 는 세 유형 공통이다 — 편집기 스펙을 두지 않는 확장에도 문서를 둔다. 미보유가 정상일 수 있고("이 확장은 공용 ID 만 쓴다") 그 정상 여부를 적을 자리가 없으면 다음 사람이 부재를 누락으로 오해하거나 필요한 시점을 놓친다. 그 문서의 "샘플 데이터와 페이지 상태" 절은 그 확장 레이아웃의 `data_source` 중 프리뷰 샘플이 붙지 않는 것을 실측해 나열한다 — 이 결함은 편집기 캔버스에서만 빈 화면으로 나타나므로 그 목록이 유일한 통로다.
|
||||
|
||||
> 상세: [extension-documentation.md](docs/extension/extension-documentation.md)
|
||||
> 정적 검사가 확장 표면 변경 시 문서 미동반과 미채움 마커 잔존을 검출한다. 생성기의 비파괴 계약(블록 밖 손실 0 · 재실행 멱등 · 미존재 키 미주입)과 필수 문서·섹션·블록 목록은 테스트가 잠근다.
|
||||
|
||||
|
||||
@@ -24,11 +24,15 @@
|
||||
- 사이트가 쓰는 외부 라이브러리의 알려진 취약점을 한 번에 점검하는 명령이 추가되었습니다. `php artisan security:audit-dependencies` 로 코어와 설치된 모든 확장을 함께 확인할 수 있고, 개발 대시보드에서도 실행할 수 있습니다. 점검 도구가 원리상 볼 수 없는 동봉 라이브러리는 버전 목록으로 함께 표시해 운영자가 직접 확인할 수 있게 했습니다. (#126 @jiwonpapa 님께서 제보해주셨습니다.)
|
||||
- 확장(모듈·플러그인·템플릿)이 개발자 문서를 갖추기 위한 체계가 마련되었습니다. 확장마다 `AGENTS.md`(확장을 고치는 사람용 — 설계 의도·확장점·수정 시 동반 의무·금지 패턴)와 `README.md`(도입 검토·운영자용 — 기능·설치·사용 방법·트러블슈팅), `docs/` 상세 문서를 두는 형식을 정의했으며, **동봉된 확장 20개 전부에 문서가 채워졌습니다.** 새 확장을 만들면 스캐폴딩 단계에서 이 문서 골격이 함께 생성됩니다.
|
||||
- 확장 문서에서 코드로 확인되는 부분(발행·구독 훅, 라우트, 권한, 메뉴, 설정 항목, 모델과 테이블, 레이아웃, 액션 핸들러, 테스트 실행 경로, 다른 확장과의 의존 관계)을 `php artisan ext:docgen` 이 자동으로 채우고 유지합니다. 사람이 쓴 서술은 손대지 않고 자동 생성 표만 교체하며, `php artisan ext:docgen --check` 로 문서가 코드와 어긋났는지 확인할 수 있습니다. 개발 대시보드에서도 실행할 수 있습니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목이 추가되었습니다. 그 확장이 레이아웃 편집기에 무엇을 선언했는지(추가 가능한 화면 요소, 스타일 조절 항목, 미리보기용 샘플 데이터, 화면 상태)와 화면 요소·데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담으며, 편집기 스펙을 두지 않은 확장에는 그것이 정상인지 아닌지를 적습니다.
|
||||
- 확장 문서 검사가 「설명 자리를 비워 둔 상태」도 미작성으로 셉니다. 종전에는 채워 넣으라는 표시만 지우고 내용을 쓰지 않으면 검사를 통과해, 빈 문서가 완비된 것으로 집계되었습니다.
|
||||
- 확장의 화면에 데이터를 붙였는데 레이아웃 편집기 미리보기에서 그 자리가 비는 경우를 문서가 실측해 알려 줍니다. 이 어긋남은 실제 화면이 정상 동작해 아무 오류도 남지 않으므로 종전에는 편집기를 열어 보기 전까지 드러나지 않았습니다.
|
||||
|
||||
### Changed
|
||||
|
||||
- 템플릿 컴포넌트 정의·다국어·라우트 응답에 조건부 캐시(ETag)가 적용되어, 변경이 없으면 본문 전송 없이 캐시를 재사용합니다. (#122 @glitter-gim 님께서 건의해주셨습니다.)
|
||||
- 레이아웃 편집기를 여는 중 네트워크가 잠시 끊겨도 자동으로 다시 시도합니다. 끝내 실패하면 내부 파일 경로 대신 다음에 무엇을 하면 되는지를 안내합니다.
|
||||
- 확장 문서에서 제품을 가리키는 이름이 「그누보드7」로 통일되었습니다. 종전에는 같은 문서 안에서도 약칭과 정식 명칭이 섞여, 확장만 내려받은 사람에게 별개 제품처럼 보였습니다.
|
||||
- 번들 템플릿의 컴포넌트·핸들러·레이아웃 상세 문서와 확장이 사용하는 활동 로그 항목 목록이 각 확장의 문서로 옮겨졌습니다. 확장이 기능을 늘릴 때 코어 문서를 함께 고쳐야 하던 의존이 사라졌으며, 코어 문서에는 총계와 각 확장 문서로의 링크만 남습니다.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
|
||||
namespace App\Console\Commands\Extension;
|
||||
|
||||
use App\Support\ExtensionDoc\DataModelCollector;
|
||||
use App\Support\ExtensionDoc\DeclarativeSurfaceCollector;
|
||||
use App\Support\ExtensionDoc\DependencyGraphCollector;
|
||||
use App\Support\ExtensionDoc\ExtensionDocContext;
|
||||
use App\Support\ExtensionDoc\ExtensionDocScaffolder;
|
||||
use App\Support\ExtensionDoc\ExtensionInventory;
|
||||
use App\Support\ExtensionDoc\FrontendInventory;
|
||||
use App\Support\ExtensionDoc\HookInventory;
|
||||
use App\Support\ExtensionDoc\TestPathCollector;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use InvalidArgumentException;
|
||||
@@ -49,23 +44,13 @@ class ExtDocgenCommand extends Command
|
||||
* 커맨드를 실행합니다.
|
||||
*
|
||||
* @param ExtensionInventory $inventory 번들 확장 인벤토리
|
||||
* @param DeclarativeSurfaceCollector $surface 선언형 표면 수집기
|
||||
* @param HookInventory $hooks 훅 인벤토리
|
||||
* @param DataModelCollector $data 데이터 모델 수집기
|
||||
* @param FrontendInventory $frontend 프론트 인벤토리
|
||||
* @param TestPathCollector $tests 테스트 경로 수집기
|
||||
* @param DependencyGraphCollector $deps 의존 관계 수집기
|
||||
* @param ExtensionDocContext $context 수집 컨텍스트 조립기
|
||||
* @param ExtensionDocScaffolder $scaffolder 문서 스캐폴더
|
||||
* @return int 종료 코드
|
||||
*/
|
||||
public function handle(
|
||||
ExtensionInventory $inventory,
|
||||
DeclarativeSurfaceCollector $surface,
|
||||
HookInventory $hooks,
|
||||
DataModelCollector $data,
|
||||
FrontendInventory $frontend,
|
||||
TestPathCollector $tests,
|
||||
DependencyGraphCollector $deps,
|
||||
ExtensionDocContext $context,
|
||||
ExtensionDocScaffolder $scaffolder
|
||||
): int {
|
||||
$scope = (string) $this->option('scope');
|
||||
@@ -134,19 +119,7 @@ class ExtDocgenCommand extends Command
|
||||
$results = [];
|
||||
|
||||
foreach ($records as $record) {
|
||||
// 선언형 표면을 먼저 모은다 — 발행 훅의 1차 출처가 그 안의 `getHooks()` 선언이다.
|
||||
$collectedSurface = $surface->collect($record);
|
||||
$declaredHooks = $collectedSurface['values']['getHooks'] ?? [];
|
||||
|
||||
$ctx = [
|
||||
'record' => $record,
|
||||
'surface' => $collectedSurface,
|
||||
'hooks' => $hooks->collect($record, is_array($declaredHooks) ? $declaredHooks : []),
|
||||
'data' => $data->collect($record),
|
||||
'frontend' => $frontend->collect($record),
|
||||
'tests' => $tests->collect($record),
|
||||
'deps' => $deps->collect($record),
|
||||
];
|
||||
$ctx = $context->build($record);
|
||||
|
||||
$results[] = $this->processExtension($ctx, $scaffolder);
|
||||
}
|
||||
@@ -229,6 +202,18 @@ class ExtDocgenCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
// 마커를 지우기만 하고 서술을 안 쓴 자리도 미채움이다. 이 축이 없으면
|
||||
// "TODO 를 삭제한 문서" 가 "채운 문서" 와 같은 모양으로 통과한다.
|
||||
$emptyIntents = ExtensionDocScaffolder::emptyIntentBlocks($content);
|
||||
|
||||
if ($emptyIntents > 0) {
|
||||
$result['unfilled'][] = [
|
||||
'doc' => $doc,
|
||||
'marker' => ExtensionDocScaffolder::EMPTY_INTENT_LABEL,
|
||||
'count' => $emptyIntents,
|
||||
];
|
||||
}
|
||||
|
||||
$docBodies = [];
|
||||
foreach (ExtensionDocScaffolder::blocksFor($doc) as $key) {
|
||||
if (array_key_exists($key, $bodies)) {
|
||||
|
||||
@@ -0,0 +1,566 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support\ExtensionDoc;
|
||||
|
||||
use App\Extension\Helpers\EditorSpecAssembler;
|
||||
|
||||
/**
|
||||
* 확장 편집기 스펙 수집기
|
||||
*
|
||||
* 확장이 `editor-spec.json`(+ 분할 `editor-spec/*.json`)으로 레이아웃 편집기에 선언한
|
||||
* 표면을 실측합니다. 팔레트 항목·스타일 컨트롤·중첩 규칙·샘플 데이터·레시피가 각각 몇
|
||||
* 개이고 어떤 ID 를 갖는지가 산출물입니다.
|
||||
*
|
||||
* 이 축이 문서에 없으면 확장에 새 화면 요소를 추가해도 편집기 팔레트에 나타나지 않는
|
||||
* 상태가 오류도 경고도 없이 남습니다 — 편집기는 선언되지 않은 컴포넌트를 "없는 것" 으로
|
||||
* 다룰 뿐 실패를 보고하지 않기 때문입니다. 스펙을 **갖지 않은** 확장도 그 사실 자체를
|
||||
* 산출물로 돌려줍니다(`present => false`). 미보유는 정상 상태일 수 있고, 문서는 그
|
||||
* 정상 여부를 서술할 자리를 가져야 합니다.
|
||||
*
|
||||
* 합본은 런타임 서빙과 같은 경로(`EditorSpecAssembler`)를 씁니다. 수집기가 별도 병합
|
||||
* 규칙을 갖게 되면 문서가 말하는 스펙과 편집기가 읽는 스펙이 갈라집니다.
|
||||
*/
|
||||
class EditorSpecCollector
|
||||
{
|
||||
/**
|
||||
* 블록별 **항목이 실제로 담긴 자리**.
|
||||
*
|
||||
* 블록 최상위 키를 그대로 세면 안 됩니다 — 블록들은 자기 항목을 `entries` / `groups` /
|
||||
* `byDataSourceId` 같은 하위 자리에 담고, 최상위에는 `comment` 같은 메타 키를 함께
|
||||
* 둡니다. 최상위를 세면 팔레트 79개가 3(comment·groups·entries)으로 집계되는데, 그
|
||||
* 숫자는 오류 없이 문서에 실려 "이 확장은 팔레트 항목이 3개" 라는 사실 주장이 됩니다.
|
||||
*
|
||||
* 값이 빈 배열인 블록은 최상위(메타 키 제외)가 곧 항목입니다.
|
||||
*
|
||||
* 선언 순서가 곧 문서 표의 행 순서입니다.
|
||||
*
|
||||
* @var array<string, array<int, string>> 블록 키 → 항목이 담긴 하위 키 목록
|
||||
*/
|
||||
private const ITEM_PATHS = [
|
||||
'componentPalette' => ['entries', 'groups'],
|
||||
'controls' => [],
|
||||
'componentCapabilities' => [],
|
||||
'nesting' => ['draggable', 'containers'],
|
||||
'sampleData' => ['byDataSourceId', 'byEndpointPattern'],
|
||||
'sampleGlobal' => [],
|
||||
'states' => ['groups'],
|
||||
'stateLabels' => [],
|
||||
'actionRecipes' => [],
|
||||
'conditionRecipes' => ['operators'],
|
||||
'computedRecipes' => [],
|
||||
'errorRecipes' => [],
|
||||
'loadingComponents' => [],
|
||||
'actionChipCandidates' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* 항목이 아니라 설명인 키 — 개수에서 제외한다.
|
||||
*
|
||||
* **접두 규칙으로 넓히지 않는다.** `_` 로 시작하는 키를 일괄 배제하면 실제 항목까지
|
||||
* 삼킨다 — `sampleGlobal._local` 이 그 예이고, 그렇게 빠진 항목은 오류 없이 문서에
|
||||
* "1개 적은 수" 로 실린다. 설명 키는 실측으로 확인된 것만 이름으로 열거한다.
|
||||
*
|
||||
* @var array<int, string> 설명 키 목록
|
||||
*/
|
||||
private const META_KEYS = ['comment', '$comment', '$schema', '_propControlsComment'];
|
||||
|
||||
/**
|
||||
* @var array<int, string>|null 번들 템플릿이 커버하는 샘플 ID (프로세스 단위 메모)
|
||||
*/
|
||||
private static ?array $fallbackSampleIds = null;
|
||||
|
||||
/**
|
||||
* 확장의 편집기 스펙 표면을 수집합니다.
|
||||
*
|
||||
* @param array<string, mixed> $record ExtensionInventory 레코드
|
||||
* @param array<int, string> $layoutRelFiles 이 확장의 레이아웃 파일(확장 루트 기준 상대 경로)
|
||||
* @return array<string, mixed> 편집기 스펙 인벤토리
|
||||
*/
|
||||
public function collect(array $record, array $layoutRelFiles = []): array
|
||||
{
|
||||
$manifestPath = $record['path'].DIRECTORY_SEPARATOR.'editor-spec.json';
|
||||
|
||||
if (! is_file($manifestPath)) {
|
||||
return $this->absent($record, $layoutRelFiles);
|
||||
}
|
||||
|
||||
$spec = EditorSpecAssembler::assemble($manifestPath);
|
||||
|
||||
if ($spec === null) {
|
||||
// manifest 가 있는데 디코드에 실패한 상태다. "스펙 없음" 과 구분해 보고한다 —
|
||||
// 뭉뚱그리면 깨진 JSON 이 "이 확장은 편집기 스펙을 두지 않는다" 로 읽힌다.
|
||||
return $this->absent($record, $layoutRelFiles, malformed: true);
|
||||
}
|
||||
|
||||
$includes = $this->includeMap($manifestPath);
|
||||
|
||||
return [
|
||||
'present' => true,
|
||||
'malformed' => false,
|
||||
'manifest' => $record['relPath'].'/editor-spec.json',
|
||||
'split' => $includes !== [],
|
||||
'includes' => $includes,
|
||||
'version' => $this->stringOrNull($spec['version'] ?? null),
|
||||
'description' => $this->stringOrNull($spec['description'] ?? null),
|
||||
'styleSystem' => $this->stringOrNull($spec['styleSystem'] ?? null),
|
||||
'darkMode' => $this->stringOrNull(($spec['darkMode']['strategy'] ?? null)),
|
||||
'blocks' => $this->blockSummaries($spec, $includes),
|
||||
'paletteGroups' => $this->paletteGroups($spec['componentPalette'] ?? null),
|
||||
'sampleDataIds' => $this->idsAt($spec['sampleData'] ?? null, 'byDataSourceId'),
|
||||
'sampleEndpointPatterns' => $this->idsAt($spec['sampleData'] ?? null, 'byEndpointPattern'),
|
||||
'stateScopes' => $this->idsAt($spec['states'] ?? null, 'groups'),
|
||||
'uncovered' => $this->uncoveredDataSources($record, $layoutRelFiles, $spec),
|
||||
'declaredPaths' => [
|
||||
'sampleData.byDataSourceId' => $this->hasPath($spec['sampleData'] ?? null, 'byDataSourceId'),
|
||||
'sampleData.byEndpointPattern' => $this->hasPath($spec['sampleData'] ?? null, 'byEndpointPattern'),
|
||||
'states.groups' => $this->hasPath($spec['states'] ?? null, 'groups'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 스펙 미보유(또는 손상) 상태의 산출물을 만듭니다.
|
||||
*
|
||||
* @param array<string, mixed> $record 확장 레코드
|
||||
* @param array<int, string> $layoutRelFiles 레이아웃 파일 목록
|
||||
* @param bool $malformed manifest 는 있으나 디코드에 실패했는지 여부
|
||||
* @return array<string, mixed> 인벤토리
|
||||
*/
|
||||
private function absent(array $record, array $layoutRelFiles, bool $malformed = false): array
|
||||
{
|
||||
return [
|
||||
'present' => false,
|
||||
'malformed' => $malformed,
|
||||
'manifest' => null,
|
||||
'split' => false,
|
||||
'includes' => [],
|
||||
'version' => null,
|
||||
'description' => null,
|
||||
'styleSystem' => null,
|
||||
'darkMode' => null,
|
||||
'blocks' => [],
|
||||
'paletteGroups' => [],
|
||||
'sampleDataIds' => [],
|
||||
'sampleEndpointPatterns' => [],
|
||||
'stateScopes' => [],
|
||||
'uncovered' => $this->uncoveredDataSources($record, $layoutRelFiles, []),
|
||||
'declaredPaths' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* manifest 의 `$include` 맵을 원본 그대로 읽습니다.
|
||||
*
|
||||
* 합본 결과에는 `$include` 가 남지 않으므로(assemble 이 벗겨낸다) 분할 여부와 블록
|
||||
* 파일 경로는 manifest 를 다시 읽어야 알 수 있습니다.
|
||||
*
|
||||
* @param string $manifestPath manifest 절대 경로
|
||||
* @return array<string, string> 블록 키 → 상대 경로
|
||||
*/
|
||||
private function includeMap(string $manifestPath): array
|
||||
{
|
||||
$decoded = json_decode((string) file_get_contents($manifestPath), true);
|
||||
|
||||
if (! is_array($decoded) || ! is_array($decoded['$include'] ?? null)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$map = [];
|
||||
|
||||
foreach ($decoded['$include'] as $key => $relative) {
|
||||
if (is_string($key) && is_string($relative) && $relative !== '') {
|
||||
$map[$key] = $relative;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 블록별 요약(항목 수·출처)을 만듭니다.
|
||||
*
|
||||
* 항목이 여러 하위 자리에 나뉜 블록(`nesting`, `sampleData`)은 자리마다 한 행을
|
||||
* 냅니다. 합산하면 "끌 수 있는 컴포넌트 84 + 담을 수 있는 컨테이너 19 = 103" 처럼
|
||||
* 뜻이 없는 수가 되고, 그 수는 표에서 사실처럼 읽힙니다.
|
||||
*
|
||||
* @param array<string, mixed> $spec 합본 spec
|
||||
* @param array<string, string> $includes 블록 키 → 분할 파일 상대 경로
|
||||
* @return array<int, array{key: string, count: int|null, source: string}> 블록 요약
|
||||
*/
|
||||
private function blockSummaries(array $spec, array $includes): array
|
||||
{
|
||||
$summaries = [];
|
||||
|
||||
foreach (self::ITEM_PATHS as $key => $paths) {
|
||||
if (! array_key_exists($key, $spec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source = $includes[$key] ?? 'editor-spec.json (인라인)';
|
||||
$block = $spec[$key];
|
||||
|
||||
if ($paths === []) {
|
||||
$summaries[] = [
|
||||
'key' => $key,
|
||||
'count' => $this->countItems($block),
|
||||
'source' => $source,
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 선언된 하위 자리 중 **실제로 있는 것만** 행으로 낸다. 없는 자리를 0 으로
|
||||
// 내보내면 "선언했는데 비었다" 와 "그 형태를 쓰지 않는다" 가 같은 모양이 된다.
|
||||
foreach ($paths as $path) {
|
||||
if (! is_array($block) || ! array_key_exists($path, $block)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$summaries[] = [
|
||||
'key' => $key.'.'.$path,
|
||||
'count' => $this->countItems($block[$path]),
|
||||
'source' => $source,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $summaries;
|
||||
}
|
||||
|
||||
/**
|
||||
* 값의 항목 수를 셉니다. 맵이면 메타 키를 뺀 나머지, 리스트면 길이입니다.
|
||||
*
|
||||
* @param mixed $value 블록 또는 하위 자리의 값
|
||||
* @return int|null 항목 수 (개수 개념이 없으면 null)
|
||||
*/
|
||||
private function countItems(mixed $value): ?int
|
||||
{
|
||||
if (! is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (array_is_list($value)) {
|
||||
return count($value);
|
||||
}
|
||||
|
||||
return count($this->withoutMeta($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 맵에서 메타 키를 제거합니다.
|
||||
*
|
||||
* @param array<string, mixed> $map 대상 맵
|
||||
* @return array<string, mixed> 메타 키를 뺀 맵
|
||||
*/
|
||||
private function withoutMeta(array $map): array
|
||||
{
|
||||
return array_filter(
|
||||
$map,
|
||||
static fn ($k): bool => ! in_array((string) $k, self::META_KEYS, true),
|
||||
ARRAY_FILTER_USE_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 팔레트 그룹별 컴포넌트 수를 셉니다.
|
||||
*
|
||||
* 팔레트는 `{ groups: [{ label, kind, components[] }], entries: { … } }` 형태입니다.
|
||||
* `groups` 가 편집기 좌측 목록의 묶음이고, 각 묶음이 담는 컴포넌트 이름이 `components`
|
||||
* 입니다.
|
||||
*
|
||||
* @param mixed $palette componentPalette 블록
|
||||
* @return array<int, array{group: string, kind: string, count: int}> 그룹 요약
|
||||
*/
|
||||
private function paletteGroups(mixed $palette): array
|
||||
{
|
||||
if (! is_array($palette) || ! is_array($palette['groups'] ?? null)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
|
||||
foreach ($palette['groups'] as $group) {
|
||||
if (! is_array($group)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$out[] = [
|
||||
'group' => $this->resolveLabel($this->stringOrNull($group['label'] ?? null) ?? '(이름 없음)'),
|
||||
'kind' => $this->stringOrNull($group['kind'] ?? null) ?? '-',
|
||||
'count' => is_array($group['components'] ?? null) ? count($group['components']) : 0,
|
||||
];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 블록의 하위 자리에서 항목 ID 목록을 뽑습니다.
|
||||
*
|
||||
* 맵이면 키가 ID 이고, 리스트면 각 항목의 `id`(없으면 `scope.match`)가 ID 입니다.
|
||||
* 페이지 상태는 리스트 + `scope` 형태라 키가 없습니다.
|
||||
*
|
||||
* @param mixed $block 블록 값
|
||||
* @param string $path 항목이 담긴 하위 키
|
||||
* @return array<int, string> ID 목록
|
||||
*/
|
||||
private function idsAt(mixed $block, string $path): array
|
||||
{
|
||||
if (! is_array($block) || ! is_array($block[$path] ?? null)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = $block[$path];
|
||||
|
||||
if (! array_is_list($items)) {
|
||||
return array_values(array_map(
|
||||
static fn ($k): string => (string) $k,
|
||||
array_keys($this->withoutMeta($items)),
|
||||
));
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (! is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = $this->stringOrNull($item['id'] ?? null)
|
||||
?? $this->stringOrNull($item['scope']['match'] ?? null)
|
||||
?? $this->stringOrNull($item['label'] ?? null);
|
||||
|
||||
if ($id !== null) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이 확장 레이아웃의 `data_source` 중 **프리뷰 샘플이 붙지 않는 것**을 찾습니다.
|
||||
*
|
||||
* 편집기 캔버스는 실제 API 를 부르지 않고 `sampleData` 로 화면을 그립니다. 그래서
|
||||
* 레이아웃에 `data_source` 를 추가하고 샘플을 붙이지 않으면 그 영역만 편집기에서
|
||||
* 빈 화면이 되는데, 실제 화면은 정상 동작하므로 어긋남이 드러나지 않습니다. 오류도
|
||||
* 경고도 남지 않아 문서의 이 목록이 유일한 통로입니다.
|
||||
*
|
||||
* 커버 판정에는 확장 자신의 스펙뿐 아니라 **번들 템플릿의 스펙**도 넣습니다 —
|
||||
* `settings` 처럼 여러 확장이 함께 쓰는 공용 ID 는 템플릿 스펙이 대신 채우도록 설계된
|
||||
* 것이라, 그것까지 미커버로 세면 목록이 잡음으로 가득 차 정작 볼 것이 묻힙니다.
|
||||
*
|
||||
* 번들 템플릿은 출하 기본값입니다. 운영자가 다른 템플릿을 쓰면 커버 집합이 달라질 수
|
||||
* 있으므로, 이 목록은 "반드시 빈다" 가 아니라 "기본 구성에서 빈다" 로 읽습니다.
|
||||
*
|
||||
* @param array<string, mixed> $record 확장 레코드
|
||||
* @param array<int, string> $layoutRelFiles 레이아웃 파일(확장 루트 기준 상대 경로)
|
||||
* @param array<string, mixed> $spec 이 확장의 합본 spec (없으면 빈 배열)
|
||||
* @return array<int, string> 샘플이 없는 data_source ID 목록
|
||||
*/
|
||||
private function uncoveredDataSources(array $record, array $layoutRelFiles, array $spec): array
|
||||
{
|
||||
if ($layoutRelFiles === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$covered = array_flip(array_merge($this->sampleIdsOf($spec), $this->fallbackSampleIds()));
|
||||
$uncovered = [];
|
||||
|
||||
foreach ($layoutRelFiles as $rel) {
|
||||
$abs = $record['path'].DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $rel);
|
||||
$layout = $this->decodeJson($abs);
|
||||
|
||||
if ($layout === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->layoutDataSourceIds($layout) as $id) {
|
||||
if (! isset($covered[$id])) {
|
||||
$uncovered[$id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ids = array_keys($uncovered);
|
||||
sort($ids);
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* spec 의 `sampleData.byDataSourceId` 키 목록을 뽑습니다.
|
||||
*
|
||||
* @param array<string, mixed> $spec 합본 spec
|
||||
* @return array<int, string> ID 목록
|
||||
*/
|
||||
private function sampleIdsOf(array $spec): array
|
||||
{
|
||||
return $this->idsAt($spec['sampleData'] ?? null, 'byDataSourceId');
|
||||
}
|
||||
|
||||
/**
|
||||
* 번들 템플릿 스펙이 채우는 샘플 ID 집합을 돌려줍니다.
|
||||
*
|
||||
* 결과를 프로세스 단위로 기억합니다. 확장 20개를 도는 동안 매번 다시 합본하면 템플릿
|
||||
* 스펙(팔레트·컨트롤·역량을 담아 수만 줄에 이른다)을 스무 번 메모리에 올리게 되고,
|
||||
* 메모리 한도가 낮은 실행 환경(테스트 프로세스)에서는 그대로 OOM 이 됩니다. 남기는
|
||||
* 것은 스펙 전체가 아니라 **ID 문자열 목록**이라 유지 비용도 작습니다.
|
||||
*
|
||||
* @return array<int, string> 번들 템플릿이 커버하는 data_source ID 목록
|
||||
*/
|
||||
private function fallbackSampleIds(): array
|
||||
{
|
||||
if (self::$fallbackSampleIds !== null) {
|
||||
return self::$fallbackSampleIds;
|
||||
}
|
||||
|
||||
$glob = glob(base_path('templates'.DIRECTORY_SEPARATOR.'_bundled'.DIRECTORY_SEPARATOR.'*'.DIRECTORY_SEPARATOR.'editor-spec.json'));
|
||||
$ids = [];
|
||||
|
||||
foreach ($glob === false ? [] : $glob as $manifest) {
|
||||
$assembled = EditorSpecAssembler::assemble($manifest);
|
||||
|
||||
if (is_array($assembled)) {
|
||||
$ids = array_merge($ids, $this->sampleIdsOf($assembled));
|
||||
}
|
||||
|
||||
// 합본 결과를 즉시 놓아준다 — 다음 manifest 를 읽기 전에 회수되게 한다.
|
||||
unset($assembled);
|
||||
}
|
||||
|
||||
return self::$fallbackSampleIds = array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 레이아웃 JSON 트리에서 `data_sources[].id` 를 전부 긁습니다.
|
||||
*
|
||||
* `data_sources` 는 최상위뿐 아니라 컴포넌트 노드에도 붙을 수 있어 트리 전체를 봅니다.
|
||||
*
|
||||
* @param array<string, mixed> $node 레이아웃 노드
|
||||
* @return array<int, string> data_source ID 목록
|
||||
*/
|
||||
private function layoutDataSourceIds(array $node): array
|
||||
{
|
||||
$ids = [];
|
||||
|
||||
if (is_array($node['data_sources'] ?? null)) {
|
||||
foreach ($node['data_sources'] as $ds) {
|
||||
$id = is_array($ds) ? $this->stringOrNull($ds['id'] ?? null) : null;
|
||||
|
||||
if ($id !== null) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($node as $value) {
|
||||
if (is_array($value)) {
|
||||
$ids = array_merge($ids, $this->layoutDataSourceIds($value));
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* `$t:` 접두 다국어 키를 코어 한국어 문자열로 풉니다.
|
||||
*
|
||||
* 편집기 스펙의 라벨은 화면에 그대로 나오지 않고 프론트 i18n 을 거칩니다. 문서에 키
|
||||
* 원문(`$t:layout_editor.palette.group.design`)을 그대로 실으면 읽는 쪽이 그 묶음이
|
||||
* 무엇인지 알 수 없습니다.
|
||||
*
|
||||
* Laravel 의 `__()` 로는 풀리지 않습니다 — 이 키들은 PHP lang 파일이 아니라 프론트
|
||||
* 다국어 JSON(`lang/ko.json` + `$partial` 분할)에 있고, JSON 번역기는 문자열 전체를
|
||||
* 키로 쓰기 때문입니다. 그래서 `$partial` 한 홉을 직접 따라갑니다.
|
||||
*
|
||||
* 풀리지 않으면 **원문을 그대로 둡니다.** 없는 번역을 지어내는 것보다 키가 드러나는
|
||||
* 편이 어디를 고쳐야 하는지 알려 주고, 이 해석이 어긋나도 문서가 틀린 사실을 주장하는
|
||||
* 대신 키만 남습니다.
|
||||
*
|
||||
* @param string $label 라벨 원문
|
||||
* @return string 해석된 라벨 (실패 시 원문)
|
||||
*/
|
||||
private function resolveLabel(string $label): string
|
||||
{
|
||||
if (! str_starts_with($label, '$t:')) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
$root = base_path('lang');
|
||||
$node = $this->langRoot($root);
|
||||
|
||||
if ($node === null) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
foreach (explode('.', substr($label, 3)) as $segment) {
|
||||
if (is_array($node) && isset($node['$partial']) && is_string($node['$partial'])) {
|
||||
$node = $this->decodeJson($root.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $node['$partial']));
|
||||
}
|
||||
|
||||
if (! is_array($node) || ! array_key_exists($segment, $node)) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
$node = $node[$segment];
|
||||
}
|
||||
|
||||
return is_string($node) && $node !== '' ? $node : $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* 코어 프론트 다국어 루트(`lang/ko.json`)를 읽습니다.
|
||||
*
|
||||
* @param string $root `lang` 디렉토리 절대 경로
|
||||
* @return array<string, mixed>|null 디코드 결과
|
||||
*/
|
||||
private function langRoot(string $root): ?array
|
||||
{
|
||||
return $this->decodeJson($root.DIRECTORY_SEPARATOR.'ko.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON 파일을 배열로 읽습니다.
|
||||
*
|
||||
* @param string $path 절대 경로
|
||||
* @return array<string, mixed>|null 디코드 결과 (실패 시 null)
|
||||
*/
|
||||
private function decodeJson(string $path): ?array
|
||||
{
|
||||
if (! is_file($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) file_get_contents($path), true);
|
||||
|
||||
return is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 블록이 그 하위 자리를 **선언했는지** 봅니다.
|
||||
*
|
||||
* 선언하고 비운 것(`0`)과 아예 선언하지 않은 것은 다릅니다 — 전자는 채울 자리가
|
||||
* 있다는 뜻이고 후자는 그 형태를 쓰지 않는다는 뜻입니다. 뭉뚱그려 `0` 으로 적으면
|
||||
* 읽는 쪽이 "채워야 할 자리를 비워 뒀다" 로 오해합니다.
|
||||
*
|
||||
* @param mixed $block 블록 값
|
||||
* @param string $path 하위 키
|
||||
* @return bool 선언 여부
|
||||
*/
|
||||
private function hasPath(mixed $block, string $path): bool
|
||||
{
|
||||
return is_array($block) && array_key_exists($path, $block);
|
||||
}
|
||||
|
||||
/**
|
||||
* 비어 있지 않은 문자열만 통과시킵니다.
|
||||
*
|
||||
* @param mixed $value 후보 값
|
||||
* @return string|null 문자열 또는 null
|
||||
*/
|
||||
private function stringOrNull(mixed $value): ?string
|
||||
{
|
||||
return is_string($value) && $value !== '' ? $value : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support\ExtensionDoc;
|
||||
|
||||
/**
|
||||
* 확장 문서 수집 컨텍스트 조립기
|
||||
*
|
||||
* 문서 블록을 렌더하려면 수집기 여덟 축(선언형 표면·훅·데이터 모델·프론트·테스트·의존
|
||||
* 관계·편집기 스펙)의 산출물을 한 배열로 묶어야 합니다. 그 조립을 커맨드와 계약 테스트가
|
||||
* 각자 하고 있으면, 축이 하나 늘 때 한쪽만 갱신되어 **드리프트가 아닌 드리프트**가 보고
|
||||
* 됩니다 — 테스트가 만든 블록에는 그 축이 없어 파일과 달라지기 때문입니다.
|
||||
*
|
||||
* 그 어긋남은 "생성기를 다시 돌려라" 라는 메시지로 나타나는데, 아무리 돌려도 사라지지
|
||||
* 않습니다. 조립을 이 한 곳이 소유해 그 경로 자체를 없앱니다.
|
||||
*/
|
||||
class ExtensionDocContext
|
||||
{
|
||||
/**
|
||||
* 수집기를 주입받습니다.
|
||||
*
|
||||
* @param DeclarativeSurfaceCollector $surface 선언형 표면 수집기
|
||||
* @param HookInventory $hooks 훅 인벤토리
|
||||
* @param DataModelCollector $data 데이터 모델 수집기
|
||||
* @param FrontendInventory $frontend 프론트 인벤토리
|
||||
* @param TestPathCollector $tests 테스트 경로 수집기
|
||||
* @param DependencyGraphCollector $deps 의존 관계 수집기
|
||||
* @param EditorSpecCollector $editorSpec 편집기 스펙 수집기
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly DeclarativeSurfaceCollector $surface,
|
||||
private readonly HookInventory $hooks,
|
||||
private readonly DataModelCollector $data,
|
||||
private readonly FrontendInventory $frontend,
|
||||
private readonly TestPathCollector $tests,
|
||||
private readonly DependencyGraphCollector $deps,
|
||||
private readonly EditorSpecCollector $editorSpec,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 확장 하나의 수집 컨텍스트를 조립합니다.
|
||||
*
|
||||
* @param array<string, mixed> $record ExtensionInventory 레코드
|
||||
* @return array<string, mixed> 수집 컨텍스트
|
||||
*/
|
||||
public function build(array $record): array
|
||||
{
|
||||
// 선언형 표면을 먼저 모은다 — 발행 훅의 1차 출처가 그 안의 `getHooks()` 선언이다.
|
||||
$surface = $this->surface->collect($record);
|
||||
$declaredHooks = $surface['values']['getHooks'] ?? [];
|
||||
|
||||
// 편집기 스펙 수집기는 레이아웃 목록을 받아 "프리뷰 샘플이 없는 data_source" 를
|
||||
// 판정한다. 레이아웃 경로 규약(모듈·플러그인 `resources/layouts/` ↔ 템플릿
|
||||
// `layouts/`)은 FrontendInventory 가 단독으로 소유하므로, 그 결과를 넘겨 분기가
|
||||
// 두 곳에 생기지 않게 한다.
|
||||
$frontend = $this->frontend->collect($record);
|
||||
$layoutRelFiles = array_map(
|
||||
static fn (array $layout): string => $layout['relFile'],
|
||||
$frontend['layouts'],
|
||||
);
|
||||
|
||||
return [
|
||||
'record' => $record,
|
||||
'surface' => $surface,
|
||||
'hooks' => $this->hooks->collect($record, is_array($declaredHooks) ? $declaredHooks : []),
|
||||
'data' => $this->data->collect($record),
|
||||
'frontend' => $frontend,
|
||||
'tests' => $this->tests->collect($record),
|
||||
'deps' => $this->deps->collect($record),
|
||||
'editorSpec' => $this->editorSpec->collect($record, $layoutRelFiles),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,11 @@ class ExtensionDocScaffolder
|
||||
*/
|
||||
public const GEN_PREFIX = '<!-- @generated:';
|
||||
|
||||
/**
|
||||
* @var string 미채움 축 라벨 — 마커를 지우고 서술을 쓰지 않은 빈 `@intent` 블록
|
||||
*/
|
||||
public const EMPTY_INTENT_LABEL = '(빈 서술)';
|
||||
|
||||
/**
|
||||
* @var string 미채움 마커 — 설계 의도 / 소개 서술
|
||||
*/
|
||||
@@ -166,6 +171,24 @@ class ExtensionDocScaffolder
|
||||
'부트스트랩' => 'frontend-entry',
|
||||
],
|
||||
],
|
||||
// 편집기 스펙은 **세 유형 모두** 가진다. 스펙을 두지 않은 확장에도 문서를 두는 것은
|
||||
// 미보유가 곧 정상일 수 있기 때문이다 — "이 확장은 왜 편집기 스펙이 없어도 되는가 /
|
||||
// 언제 필요해지는가" 를 적을 자리가 없으면, 다음 사람이 그 부재를 누락으로 오해하거나
|
||||
// 반대로 필요한 시점을 놓친다. 미보유 확장의 블록은 그 사실을 명시하고 사람 서술로
|
||||
// 이어진다.
|
||||
'docs/editor-spec.md' => [
|
||||
'types' => ['module', 'plugin', 'template'],
|
||||
// 절 ↔ 블록은 **키로** 묶는다. 순번 결합은 절을 하나 끼우는 순간 그 뒤 블록이
|
||||
// 전부 엉뚱한 헤딩 밑으로 들어가는데, 헤딩 존재·블록 존재를 각각만 보는 게이트는
|
||||
// 그 어긋남을 잡지 못한다. 필수 절 목록은 이 배열의 키에서 파생한다(중복 정의 없음).
|
||||
'blocks' => [
|
||||
'선언 요약' => 'editor-spec-summary',
|
||||
'선언 블록' => 'editor-spec-blocks',
|
||||
'컴포넌트 팔레트' => 'editor-spec-palette',
|
||||
'샘플 데이터와 페이지 상태' => 'editor-spec-samples',
|
||||
'수정 시 동반 의무' => 'editor-spec-obligations',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -347,6 +370,35 @@ class ExtensionDocScaffolder
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 본문이 빈 사람 영역(`@intent`) 블록 수를 셉니다.
|
||||
*
|
||||
* 미채움은 `TODO:` 마커로만 드러나지 않습니다. 스캐폴딩 직후 마커를 **지우기만 하고**
|
||||
* 서술을 쓰지 않으면 마커 검사는 0 을 돌려주고 골격·블록 검사도 전부 통과합니다 —
|
||||
* 결과가 "다 채웠다" 와 구분되지 않아, 비어 있는 문서가 완비로 집계됩니다.
|
||||
*
|
||||
* 그래서 마커 잔량과 빈 본문을 **같은 축**(미채움)으로 함께 봅니다.
|
||||
*
|
||||
* @param string $content 문서 전문
|
||||
* @return int 빈 `@intent` 블록 수
|
||||
*/
|
||||
public static function emptyIntentBlocks(string $content): int
|
||||
{
|
||||
if (preg_match_all('/<!-- @intent START -->(.*?)<!-- @intent END -->/s', $content, $m) === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$empty = 0;
|
||||
|
||||
foreach ($m[1] as $body) {
|
||||
if (trim($body) === '') {
|
||||
$empty++;
|
||||
}
|
||||
}
|
||||
|
||||
return $empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 생성 블록을 마커로 감쌉니다.
|
||||
*
|
||||
@@ -565,6 +617,11 @@ class ExtensionDocScaffolder
|
||||
'assets' => $this->renderAssets($ctx),
|
||||
'components' => $this->renderComponents($ctx),
|
||||
'layout-map' => $this->renderLayoutMap($ctx),
|
||||
'editor-spec-summary' => $this->renderEditorSpecSummary($ctx),
|
||||
'editor-spec-blocks' => $this->renderEditorSpecBlocks($ctx),
|
||||
'editor-spec-palette' => $this->renderEditorSpecPalette($ctx),
|
||||
'editor-spec-samples' => $this->renderEditorSpecSamples($ctx),
|
||||
'editor-spec-obligations' => $this->renderEditorSpecObligations($ctx),
|
||||
default => $this->none('알 수 없는 블록 키: '.$key),
|
||||
};
|
||||
}
|
||||
@@ -593,7 +650,7 @@ class ExtensionDocScaffolder
|
||||
|
||||
$core = $ctx['deps']['coreVersion'] ?? null;
|
||||
if ($core !== null) {
|
||||
$badges[] = $this->badge('G7', $core, '1F883D');
|
||||
$badges[] = $this->badge('그누보드7', $core, '1F883D');
|
||||
}
|
||||
|
||||
$license = $manifest['license'] ?? null;
|
||||
@@ -639,7 +696,7 @@ class ExtensionDocScaffolder
|
||||
private function renderRequirements(array $ctx): string
|
||||
{
|
||||
$rows = [];
|
||||
$rows[] = ['G7 코어', $this->code($ctx['deps']['coreVersion'] ?? '(제약 없음)')];
|
||||
$rows[] = ['그누보드7 코어', $this->code($ctx['deps']['coreVersion'] ?? '(제약 없음)')];
|
||||
$rows[] = ['PHP', $this->code($this->composerPhp($ctx) ?? '^8.2')];
|
||||
|
||||
foreach ($ctx['deps']['requires'] as $dep) {
|
||||
@@ -826,6 +883,7 @@ class ExtensionDocScaffolder
|
||||
'docs/components.md' => '템플릿이 제공하는 컴포넌트',
|
||||
'docs/layouts.md' => '레이아웃 목록과 라우트 매핑',
|
||||
'docs/handlers.md' => '템플릿 전용 핸들러와 부트스트랩',
|
||||
'docs/editor-spec.md' => '레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터',
|
||||
default => '-',
|
||||
};
|
||||
}
|
||||
@@ -2252,7 +2310,7 @@ class ExtensionDocScaffolder
|
||||
$lines = [];
|
||||
$lines[] = '# '.$name;
|
||||
$lines[] = '';
|
||||
$lines[] = "**G7 {$label} · {$record['id']}**";
|
||||
$lines[] = "**그누보드7 {$label} · {$record['id']}**";
|
||||
$lines[] = $this->escape($description);
|
||||
$lines[] = '';
|
||||
$lines[] = self::wrap('badges', $this->renderBlock('badges', $ctx));
|
||||
@@ -2455,10 +2513,284 @@ class ExtensionDocScaffolder
|
||||
'docs/components.md' => '컴포넌트',
|
||||
'docs/layouts.md' => '레이아웃',
|
||||
'docs/handlers.md' => '핸들러',
|
||||
'docs/editor-spec.md' => '레이아웃 편집기 스펙',
|
||||
default => basename($doc, '.md'),
|
||||
};
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 편집기 스펙 블록 렌더러
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 편집기 스펙 보유 여부와 형태를 렌더합니다.
|
||||
*
|
||||
* 미보유는 결함이 아니라 정상일 수 있으므로 "없음" 을 사실로 적고 사람 서술에 넘깁니다.
|
||||
* 다만 manifest 가 있는데 읽지 못한 경우(malformed)는 구분해 적습니다 — 뭉뚱그리면
|
||||
* 깨진 JSON 이 "이 확장은 편집기 스펙을 두지 않는다" 로 굳습니다.
|
||||
*
|
||||
* @param array<string, mixed> $ctx 수집 컨텍스트
|
||||
* @return string 마크다운
|
||||
*/
|
||||
private function renderEditorSpecSummary(array $ctx): string
|
||||
{
|
||||
$spec = $ctx['editorSpec'] ?? null;
|
||||
|
||||
if (! is_array($spec)) {
|
||||
return $this->none('편집기 스펙을 수집하지 못했습니다.');
|
||||
}
|
||||
|
||||
if (($spec['malformed'] ?? false) === true) {
|
||||
return $this->none('`editor-spec.json` 이 존재하지만 JSON 으로 읽지 못했습니다 — 편집기가 이 확장의 선언을 무시하고 있습니다.');
|
||||
}
|
||||
|
||||
if (($spec['present'] ?? false) !== true) {
|
||||
return $this->none('이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다.');
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$rows[] = ['manifest', $this->code((string) $spec['manifest'])];
|
||||
$rows[] = ['형태', $spec['split'] === true
|
||||
? '분할 — manifest + `editor-spec/*.json` '.count($spec['includes']).'개 블록'
|
||||
: '단일 파일 (인라인)'];
|
||||
$rows[] = ['스펙 버전', $this->code((string) ($spec['version'] ?? ''))];
|
||||
$rows[] = ['스타일 시스템', $this->code((string) ($spec['styleSystem'] ?? ''))];
|
||||
$rows[] = ['다크 모드 전략', $this->code((string) ($spec['darkMode'] ?? ''))];
|
||||
|
||||
$table = $this->table(['항목', '값'], $rows);
|
||||
|
||||
$description = $spec['description'] ?? null;
|
||||
|
||||
if (is_string($description) && $description !== '') {
|
||||
return $table."\n\n> ".$this->escape($description);
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 스펙이 선언한 블록별 항목 수를 렌더합니다.
|
||||
*
|
||||
* @param array<string, mixed> $ctx 수집 컨텍스트
|
||||
* @return string 마크다운
|
||||
*/
|
||||
private function renderEditorSpecBlocks(array $ctx): string
|
||||
{
|
||||
$spec = $ctx['editorSpec'] ?? null;
|
||||
|
||||
if (! is_array($spec) || ($spec['present'] ?? false) !== true) {
|
||||
return $this->none('선언된 편집기 스펙 블록이 없습니다.');
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($spec['blocks'] as $block) {
|
||||
$rows[] = [
|
||||
$this->code($block['key']),
|
||||
$this->editorSpecBlockRole($block['key']),
|
||||
$block['count'] === null ? '-' : (string) $block['count'],
|
||||
$this->code($block['source']),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->table(['블록', '역할', '항목 수', '출처'], $rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 편집기 스펙 블록의 역할을 한 줄로 설명합니다.
|
||||
*
|
||||
* 블록 키는 코어가 정한 어휘이므로 확장마다 다시 설명할 필요가 없고, 반대로 설명이
|
||||
* 없으면 확장 문서만 읽는 쪽이 키 이름만으로 의미를 추측하게 됩니다.
|
||||
*
|
||||
* @param string $key 블록 키
|
||||
* @return string 역할 설명
|
||||
*/
|
||||
private function editorSpecBlockRole(string $key): string
|
||||
{
|
||||
return match ($key) {
|
||||
'componentPalette', 'componentPalette.entries' => '편집기 "요소 추가" 팔레트에 나타나는 항목',
|
||||
'componentPalette.groups' => '팔레트 좌측 목록의 묶음',
|
||||
'nesting.draggable' => '캔버스에서 끌어 옮길 수 있는 컴포넌트',
|
||||
'nesting.containers' => '자식을 담을 수 있는 컴포넌트와 그 허용 규칙',
|
||||
'sampleData.byDataSourceId' => '레이아웃 `data_sources` ID 로 붙는 프리뷰 응답',
|
||||
'sampleData.byEndpointPattern' => '엔드포인트 패턴으로 붙는 프리뷰 응답',
|
||||
'states.groups' => '상태 변종을 적용할 범위(라우트·베이스 레이아웃)',
|
||||
'conditionRecipes.operators' => '조건 표현식에 쓸 수 있는 연산자',
|
||||
'controls' => '재사용 스타일 컨트롤 정의',
|
||||
'componentCapabilities' => '컴포넌트별 편집 역량(어떤 속성을 편집기가 다루는가)',
|
||||
'nesting' => '어떤 컴포넌트 안에 무엇을 넣을 수 있는가',
|
||||
'sampleData' => '캔버스 프리뷰용 샘플 응답',
|
||||
'sampleGlobal' => '`_global.*` 프리뷰 baseline 시드',
|
||||
'states' => '페이지 상태 변종(빈 목록·오류 등)',
|
||||
'stateLabels' => '상태값 친화 명칭 카탈로그',
|
||||
'actionRecipes' => '친화 명칭 → 액션 JSON 레시피',
|
||||
'conditionRecipes' => '친화 조건 → `if` 표현식 레시피',
|
||||
'computedRecipes' => '계산값 레시피',
|
||||
'errorRecipes' => '오류 처리 레시피',
|
||||
'loadingComponents' => '로딩 표시 컴포넌트 후보',
|
||||
'actionChipCandidates' => '동작 데이터 칩 컨텍스트 후보',
|
||||
default => '-',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ID 목록을 표 한 칸에 담을 수 있게 줄입니다.
|
||||
*
|
||||
* 70개를 한 줄로 늘어놓으면 표가 읽히지 않고, 그렇다고 개수만 남기면 어떤 ID 인지
|
||||
* 확인할 길이 사라집니다. 앞쪽을 보이고 나머지는 수로 줄입니다.
|
||||
*
|
||||
* @param array<int, string> $ids ID 목록
|
||||
* @param int $limit 나열할 최대 개수
|
||||
* @return string 마크다운 셀 내용
|
||||
*/
|
||||
private function idListCell(array $ids, int $limit = 12): string
|
||||
{
|
||||
if ($ids === []) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
$shown = array_slice($ids, 0, $limit);
|
||||
$cell = implode(' · ', array_map(fn (string $id): string => $this->code($id), $shown));
|
||||
|
||||
$rest = count($ids) - count($shown);
|
||||
|
||||
return $rest > 0 ? $cell." … 외 {$rest}개" : $cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* 팔레트 그룹별 항목 수를 렌더합니다.
|
||||
*
|
||||
* @param array<string, mixed> $ctx 수집 컨텍스트
|
||||
* @return string 마크다운
|
||||
*/
|
||||
private function renderEditorSpecPalette(array $ctx): string
|
||||
{
|
||||
$spec = $ctx['editorSpec'] ?? null;
|
||||
|
||||
if (! is_array($spec) || ($spec['present'] ?? false) !== true) {
|
||||
return $this->none('이 확장은 편집기 팔레트에 항목을 추가하지 않습니다.');
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($spec['paletteGroups'] as $group) {
|
||||
$rows[] = [
|
||||
$this->escape($group['group']),
|
||||
$this->code($group['kind']),
|
||||
(string) $group['count'],
|
||||
];
|
||||
}
|
||||
|
||||
if ($rows === []) {
|
||||
return $this->none('이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다.');
|
||||
}
|
||||
|
||||
return $this->table(['그룹', '종류', '컴포넌트 수'], $rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 샘플 데이터 ID 와 페이지 상태 변종을 렌더합니다.
|
||||
*
|
||||
* 이 두 축은 편집기 캔버스가 실제 API 없이 화면을 그릴 때 쓰는 값입니다. 레이아웃의
|
||||
* `data_sources` ID 와 어긋나면 편집기 프리뷰만 빈 화면이 되는데, 실제 화면은 정상이라
|
||||
* 어긋남이 드러나지 않습니다.
|
||||
*
|
||||
* @param array<string, mixed> $ctx 수집 컨텍스트
|
||||
* @return string 마크다운
|
||||
*/
|
||||
private function renderEditorSpecSamples(array $ctx): string
|
||||
{
|
||||
$spec = $ctx['editorSpec'] ?? null;
|
||||
|
||||
if (! is_array($spec)) {
|
||||
return $this->none('편집기 스펙을 수집하지 못했습니다.');
|
||||
}
|
||||
|
||||
// 스펙이 없어도 **미커버 목록은 낸다.** 스펙이 없는 확장이야말로 프리뷰가 비는
|
||||
// 자리를 가질 가능성이 크고, 여기서 조기 반환하면 정작 필요한 확장에서 그 목록이
|
||||
// 통째로 사라진다 — 그런데 결과는 "빈 자리가 없다" 와 구분되지 않는다.
|
||||
$sections = [];
|
||||
|
||||
if (($spec['present'] ?? false) === true) {
|
||||
// 세 자리를 한 행으로 합치지 않는다 — `data_sources` ID 로 붙는 샘플과 엔드포인트
|
||||
// 패턴으로 붙는 샘플은 어긋났을 때 고칠 자리가 다르고, 페이지 상태는 ID 가 아니라
|
||||
// 적용 범위(라우트/베이스 레이아웃)로 식별된다.
|
||||
$rows = [];
|
||||
|
||||
foreach ([
|
||||
['sampleData.byDataSourceId', $spec['sampleDataIds']],
|
||||
['sampleData.byEndpointPattern', $spec['sampleEndpointPatterns']],
|
||||
['states.groups', $spec['stateScopes']],
|
||||
] as [$label, $ids]) {
|
||||
$declared = ($spec['declaredPaths'][$label] ?? false) === true;
|
||||
|
||||
$rows[] = [
|
||||
$this->code($label),
|
||||
$this->editorSpecBlockRole($label),
|
||||
$declared ? (string) count($ids) : '미선언',
|
||||
$declared ? $this->idListCell($ids) : '-',
|
||||
];
|
||||
}
|
||||
|
||||
$sections[] = $this->table(['자리', '역할', '개수', 'ID'], $rows);
|
||||
} else {
|
||||
$sections[] = $this->none('이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다.');
|
||||
}
|
||||
|
||||
$uncovered = $spec['uncovered'] ?? [];
|
||||
|
||||
if ($uncovered === []) {
|
||||
$sections[] = $this->none('이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버).');
|
||||
} else {
|
||||
$sections[] = '**프리뷰 샘플이 없는 `data_source` '.count($uncovered).'개** — 편집기 캔버스에서 이 자리만 빈 화면이 됩니다. 실제 화면은 정상이라 오류도 경고도 남지 않습니다.'
|
||||
."\n\n".$this->idListCell($uncovered, 20);
|
||||
}
|
||||
|
||||
return implode("\n\n", $sections);
|
||||
}
|
||||
|
||||
/**
|
||||
* 편집기 스펙을 함께 고쳐야 하는 변경과 그 절차를 렌더합니다.
|
||||
*
|
||||
* 이 표가 없으면 확장에 화면 요소를 추가해도 편집기 팔레트에 나타나지 않는 상태가
|
||||
* 오류도 경고도 없이 남습니다 — 편집기는 선언되지 않은 컴포넌트를 "없는 것" 으로
|
||||
* 다룰 뿐 실패를 보고하지 않습니다.
|
||||
*
|
||||
* @param array<string, mixed> $ctx 수집 컨텍스트
|
||||
* @return string 마크다운
|
||||
*/
|
||||
private function renderEditorSpecObligations(array $ctx): string
|
||||
{
|
||||
$record = $ctx['record'];
|
||||
$spec = $ctx['editorSpec'] ?? null;
|
||||
$hasSpec = is_array($spec) && ($spec['present'] ?? false) === true;
|
||||
|
||||
$update = "php artisan {$record['type']}:update {$record['id']} --force";
|
||||
|
||||
$rows = [
|
||||
['컴포넌트를 새로 만들었다', '`componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정'],
|
||||
['레이아웃에 `data_sources` 를 추가했다', '`sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면)'],
|
||||
['`_global.*` 을 새로 읽는다', '`sampleGlobal` 에 baseline 값 추가'],
|
||||
['빈 목록·오류 같은 화면 변종을 추가했다', '`states` 에 변종 추가 · `stateLabels` 에 친화 명칭'],
|
||||
['새 액션·조건 패턴을 도입했다', '`actionRecipes` / `conditionRecipes` 에 친화 명칭 등록'],
|
||||
];
|
||||
|
||||
$table = $this->table(['이런 변경을 했다면', '편집기 스펙에서 함께 할 일'], $rows);
|
||||
|
||||
if (! $hasSpec) {
|
||||
// 스펙이 없는 확장에도 이 표를 남긴다. 지금은 해당 없음이지만, 위 사건 중 하나가
|
||||
// 일어나는 순간 스펙을 **신설**해야 한다는 것이 이 문서가 전할 내용이다.
|
||||
return $this->none('이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다.')
|
||||
."\n\n".$table;
|
||||
}
|
||||
|
||||
// `_bundled` 편집분은 update 커맨드로 활성 디렉토리에 반영된 뒤에만 편집기에 보인다
|
||||
// (`EditorSpecAssembler` 는 활성 디렉토리만 합본한다 — `_bundled` 폴백이 없다).
|
||||
return $table."\n\n"
|
||||
.'편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:'
|
||||
."\n\n```bash\n".$update."\n```";
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 마크다운 유틸
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
3. 분할 형식: manifest(editor-spec.json) + `$include` 맵 → editor-spec/{block}.json. 서버가 합본해 단일 spec 으로 서빙
|
||||
4. 서빙은 활성 디렉토리만 기준 (_bundled 폴백 없음). _bundled 작업분은 {type}:update 로 활성 반영 후 런타임 노출
|
||||
5. 친화 라벨은 $t: 다국어 키, comment 는 작성자 메모(엔진 무시), 알려진 필드 외 자유 필드는 보존만
|
||||
6. 확장별 선언 실측·동반 의무는 그 확장의 docs/editor-spec.md 가 소유 (ext:docgen 이 유지)
|
||||
```
|
||||
|
||||
## 개요
|
||||
@@ -60,6 +61,71 @@ editor-spec.json 이 커지면(코어 admin 템플릿은 단일 파일 18,000줄
|
||||
|
||||
런타임 서빙은 **활성 디렉토리(`templates/{id}/...`)만** 읽는다. `_bundled` 폴백은 없다. `_bundled` 에서 작업한 분할본은 `{type}:update {id} --force` 로 활성 디렉토리에 반영된 뒤에만 편집기에 나타난다. JSON 만 바뀐 경우 빌드는 불필요하고 update 만 실행한다.
|
||||
|
||||
## 확장 개발자의 작업 순서
|
||||
|
||||
아래 규칙들은 블록 하나하나의 **문법**을 말한다. 실제로 확장을 고칠 때 필요한 것은 그
|
||||
앞의 판단이다 — 무엇을 손대야 하고, 무엇을 빠뜨리면 어떤 증상이 나는가.
|
||||
|
||||
### 어느 확장의 스펙에 넣는가
|
||||
|
||||
컴포넌트를 만드는 것은 템플릿의 일이고, 모듈·플러그인은 템플릿이 제공하는 컴포넌트를
|
||||
쓰기만 한다. 그래서 두 부류가 담는 것이 다르다.
|
||||
|
||||
| 선언할 것 | 자리 |
|
||||
|---|---|
|
||||
| `componentPalette` · `controls` · `componentCapabilities` · `nesting` | **템플릿** 스펙 |
|
||||
| `sampleData` · `sampleGlobal` · `states` · `*Recipes` | 그 데이터를 소유한 **모듈·플러그인** 스펙 |
|
||||
| 여러 확장이 함께 쓰는 공용 ID(`settings` · `roles` · `me` 등) | **템플릿** 스펙 |
|
||||
|
||||
공용 ID 를 확장마다 각자 선언하면 같은 ID 의 샘플이 여러 곳에 생기고, 그것들이 갈라져도
|
||||
오류가 나지 않는다 — 어느 것이 쓰이는지가 병합 순서에 좌우된다.
|
||||
|
||||
모듈·플러그인이 `componentPalette` 를 선언하면 템플릿 선언과 같은 자리를 두고 다투게
|
||||
되므로 두지 않는다. 팔레트에 얹고 싶은 것이 있다면 그것은 활성 템플릿의 스펙으로 간다.
|
||||
|
||||
### 무엇을 빠뜨렸는지는 증상으로 가른다
|
||||
|
||||
편집기는 선언되지 않은 것을 **없는 것**으로 다룰 뿐 실패를 보고하지 않는다. 그래서
|
||||
빠뜨린 자리를 알려 주는 것은 오류 메시지가 아니라 증상이다.
|
||||
|
||||
| 증상 | 빠뜨린 자리 |
|
||||
|---|---|
|
||||
| 컴포넌트가 팔레트 목록에 아예 없다 | `componentPalette.entries` |
|
||||
| 팔레트에 등록했는데 목록에 안 보인다 | `componentPalette.groups` 의 어느 묶음에도 없다 |
|
||||
| 팔레트에서 끌어 놓을 수 없다 | `nesting.draggable` |
|
||||
| 놓을 자리가 없다(컨테이너가 거부한다) | `nesting.containers` |
|
||||
| 놓았는데 속성 패널이 비어 있다 | `componentCapabilities` |
|
||||
| 속성 패널에 특정 항목만 없다 | `controls` |
|
||||
| 캔버스의 한 영역만 빈 화면이다 | `sampleData.byDataSourceId`(또는 `byEndpointPattern`) |
|
||||
| 값이 `undefined` 라 영역 전체가 사라진다 | `sampleGlobal` |
|
||||
| 특정 상태(빈 목록·오류·모달 열림)를 볼 수 없다 | `states` |
|
||||
|
||||
캔버스는 실제 API 를 부르지 않고 `sampleData` 로 그린다. 그래서 레이아웃에
|
||||
`data_source` 를 추가하고 샘플을 붙이지 않으면 **편집기에서만** 그 자리가 비고 실제
|
||||
화면은 정상 동작한다 — 오류도 경고도 서버 로그도 남지 않는다.
|
||||
|
||||
캔버스는 또한 정적 시뮬레이션이라 클릭·응답으로 만들어지는 상태를 스스로 만들지 못한다.
|
||||
모바일 드로어(햄버거 클릭), 쿠키 동의 배너(동의 전 방문자), 본인인증 창(428 응답)처럼
|
||||
**어떤 사건 뒤에만 나타나는 화면**은 `states` 로 그 상태를 주입해 두지 않으면 편집
|
||||
자체가 불가능하다.
|
||||
|
||||
### 반영 절차
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없다. 다만 서빙은 **활성 디렉토리만** 읽고
|
||||
`_bundled` 폴백이 없으므로, `_bundled` 에서 고친 뒤 update 커맨드를 돌리지 않으면
|
||||
편집기에는 직전 내용이 그대로 보인다. 파일은 고쳤는데 화면이 안 바뀌었다면 거의 이 경우다.
|
||||
|
||||
```bash
|
||||
php artisan {module|plugin|template}:update {id} --force
|
||||
```
|
||||
|
||||
### 확장별 실측은 그 확장이 소유한다
|
||||
|
||||
어느 확장이 무엇을 얼마나 선언했는지, 그 확장에서 프리뷰 샘플이 붙지 않는 `data_source`
|
||||
가 무엇인지는 **그 확장의 `docs/editor-spec.md`** 가 답한다. `php artisan ext:docgen` 이
|
||||
그 실측 부분을 유지하므로 스펙을 고친 뒤 재실행한다. 코어 문서(이 문서)에는 블록 문법과
|
||||
공통 판단 기준만 둔다.
|
||||
|
||||
## 블록별 작성 규칙
|
||||
|
||||
### componentPalette — 요소 추가 팔레트
|
||||
@@ -240,4 +306,6 @@ export function registerSirsoftAdminBasicEditorWidgets(): void {
|
||||
- 타입 SSoT: `resources/js/core/template-engine/layout-editor/spec/specTypes.ts`
|
||||
- 로더(fetch + 병합): `resources/js/core/template-engine/layout-editor/spec/editorSpecLoader.ts`
|
||||
- 서버 합본 헬퍼: `app/Extension/Helpers/EditorSpecAssembler.php`
|
||||
- 확장별 실측 문서 생성: `php artisan ext:docgen` — 각 확장 `docs/editor-spec.md`
|
||||
- 확장 문서 규정: [extension-documentation.md](extension-documentation.md)
|
||||
- 서빙: `app/Http/Controllers/Api/Public/PublicTemplateController.php`, `app/Http/Controllers/Api/Admin/AdminTemplateAssetController.php`, `app/Services/ModuleService.php`, `app/Services/PluginService.php`
|
||||
|
||||
@@ -54,11 +54,14 @@
|
||||
├─ 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
|
||||
@@ -93,6 +96,7 @@
|
||||
| `docs/components.md` (템플릿) | `제공 컴포넌트` |
|
||||
| `docs/layouts.md` (템플릿) | `레이아웃 목록` · `라우트 매핑` |
|
||||
| `docs/handlers.md` (템플릿) | `템플릿 전용 핸들러` · `부트스트랩` |
|
||||
| `docs/editor-spec.md` | `선언 요약` · `선언 블록` · `컴포넌트 팔레트` · `샘플 데이터와 페이지 상태` · `수정 시 동반 의무` |
|
||||
|
||||
목록의 SSoT 는 생성기이므로, 직접 옮겨 적기보다 `ext:docgen --init` 이 만든 골격에서 시작하는 편이 어긋나지 않는다.
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ Listener · Layout · Test · 다국어(백엔드 PHP + 프론트 JSON)가 각 1
|
||||
`Memo` 모델. 저장 직후 `gnuboard7-hello_module.memo.created` 액션 훅을 발행하고,
|
||||
`LogMemoCreatedListener` 가 그것을 받아 로그를 남깁니다.
|
||||
|
||||
이 한 흐름에 G7 모듈의 규약이 전부 들어 있습니다:
|
||||
이 한 흐름에 그누보드7 모듈의 규약이 전부 들어 있습니다:
|
||||
|
||||
- 검증은 Service 가 아니라 **FormRequest** 에 둔다
|
||||
- Service 는 구체 Repository 가 아니라 **인터페이스**를 주입받는다
|
||||
@@ -129,6 +129,7 @@ Listener · Layout · Test · 다국어(백엔드 PHP + 프론트 JSON)가 각 1
|
||||
- [ ] 규약(FormRequest 검증 · Repository 인터페이스 주입 · 훅으로 부가작업 분리)이 흐트러지지 않았는지 확인 — 이 코드는 본보기로 읽힌다
|
||||
- [ ] `docs/extension/sample-extensions.md` 의 계층 표와 어긋나지 않는지 확인 (파일을 추가·삭제했다면 그 표도 갱신)
|
||||
- [ ] 발행 훅 이름을 바꾸면 `gnuboard7-hello_plugin` 의 구독이 조용히 끊긴다
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어 `memoData` · `memos` 가 편집기 캔버스에서 빈 화면으로 보인다. `data_source` 를 더 늘리면 그 자리도 같은 상태가 된다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -176,6 +177,7 @@ php vendor/bin/phpunit modules/_bundled/gnuboard7-hello_module/tests --filter='<
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [0.1.1] - 2026-08-10
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Hello 모듈
|
||||
|
||||
**G7 모듈 · gnuboard7-hello_module**
|
||||
**그누보드7 모듈 · gnuboard7-hello_module**
|
||||
학습용 최소 샘플 모듈 (Memo CRUD)
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-0.1.2-0066FF?style=flat-square" alt="version 0.1.2">
|
||||
<img src="https://img.shields.io/badge/type-%EB%AA%A8%EB%93%88-555555?style=flat-square" alt="type 모듈">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.0-1F883D?style=flat-square" alt="G7 >=7.0.0">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.0-1F883D?style=flat-square" alt="그누보드7 >=7.0.0">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -70,7 +70,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.0` |
|
||||
| 그누보드7 코어 | `>=7.0.0` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -151,6 +151,7 @@ php artisan module:activate gnuboard7-hello_module
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -43,7 +43,7 @@ Listeners/LogMemoCreatedListener memo.created 구독 — 부가 작업은
|
||||
resources/layouts/ admin 2 + user 1
|
||||
```
|
||||
|
||||
이 지도가 곧 **G7 모듈의 규약**입니다 — 검증은 FormRequest, 데이터 접근은 Repository
|
||||
이 지도가 곧 **그누보드7 모듈의 규약**입니다 — 검증은 FormRequest, 데이터 접근은 Repository
|
||||
인터페이스, 부가 작업은 훅 리스너, 응답 형태는 Resource. 샘플이 잘못된 본을 보이면 그것을 따라
|
||||
한 모듈이 전부 같은 형태가 되므로, 이 네 경계는 편의를 위해서도 흐트러뜨리지 않습니다.
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# Hello 모듈 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
학습용 샘플 모듈이라 편집기 스펙을 일부러 두지 않았습니다. 이 모듈의 목적은 모듈의
|
||||
최소 구조(라우트 → 컨트롤러 → 서비스 → 저장소)를 보여 주는 것이고, 편집기 스펙은 그
|
||||
구조와 무관한 별개 축입니다.
|
||||
|
||||
다만 아래 "샘플 데이터와 페이지 상태" 절이 보여 주듯, 이 모듈에는 프리뷰가 비는 자리가
|
||||
실제로 있습니다. 스펙이 없어도 되는 상태와 스펙이 필요한데 없는 상태는 다릅니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없으므로 표가 비어 있습니다. 이것은 "편집기가 이 모듈을 다루지 않는다"
|
||||
가 아니라 "이 모듈이 편집기에 아무것도 알려 주지 않는다" 는 뜻입니다 — 편집기는 여전히
|
||||
이 모듈의 레이아웃을 열 수 있고, 다만 데이터가 붙지 않은 채로 엽니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
**프리뷰 샘플이 없는 `data_source` 2개** — 편집기 캔버스에서 이 자리만 빈 화면이 됩니다. 실제 화면은 정상이라 오류도 경고도 남지 않습니다.
|
||||
|
||||
`memoData` · `memos`
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`memoData` · `memos` 두 ID 가 미커버입니다. 메모 목록과 폼이 편집기 캔버스에서 빈
|
||||
채로 보인다는 뜻입니다.
|
||||
|
||||
샘플 모듈이므로 이 상태를 그대로 두는 것도 선택입니다 — 다만 그것은 "편집기 스펙이
|
||||
없으면 어떤 화면이 되는가" 를 보여 주는 교보재로서 의도적으로 남긴 것이지, 문제가
|
||||
없다는 뜻이 아닙니다. 스펙을 하나 만들어 보는 것이 이 모듈로 할 수 있는 좋은 연습입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
편집기 스펙을 신설해야 하는 시점은 하나입니다 — **이 확장이 소유한 레이아웃에
|
||||
이 확장만 쓰는 `data_source` 가 생겼을 때**. 그 순간부터 편집기 캔버스의 그 영역은
|
||||
빈 화면이 되고, 실제 화면은 정상 동작하므로 오류도 경고도 남지 않습니다.
|
||||
|
||||
신설 절차는 확장 루트에 `editor-spec.json` 을 만들고 `sampleData.byDataSourceId` 에
|
||||
그 ID 를 넣는 것으로 시작합니다. 팔레트·컨트롤은 템플릿의 일이므로 넣지 않습니다.
|
||||
파일을 만든 뒤 update 커맨드로 활성 디렉토리에 반영해야 편집기가 읽습니다 —
|
||||
`_bundled` 폴백이 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -136,6 +136,7 @@ CRUD 흐름 자체를 **자기 도메인으로 대체**하는 가장 무거운
|
||||
- [ ] 비밀글이 관여하는 새 조회 경로(댓글·첨부·검색 결과 등)를 추가할 때 `SecretContentGate` 재적용 (KVE-2026-1914 — 부모에서 한 번 판정하고 끝나지 않는다)
|
||||
- [ ] `boards`/`board_posts`/`board_comments` 의 count 컬럼(`posts_count`/`comments_count`/`replies_count`/`attachments_count`)은 훅 리스너(`*CountSyncListener`)가 갱신 — Service 에서 직접 증감 금지
|
||||
- [ ] 게시판 삭제 시 `getDynamicPermissionIdentifiers()`/`getDynamicRoleIdentifiers()`/`getDynamicMenuSlugs()` 가 최신 상태를 반영하도록 동일 트랜잭션에서 정리 (module.php `uninstall()` 의 `chunkById` 패턴 참고 — OFFSET 순회로 삭제 금지)
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan module:update sirsoft-board --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -186,6 +187,7 @@ npx playwright test modules/_bundled/sirsoft-board/tests/Playwright/specs/<대
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.1.0] - 2026-08-24
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 게시판
|
||||
|
||||
**G7 모듈 · sirsoft-board**
|
||||
**그누보드7 모듈 · sirsoft-board**
|
||||
게시판 관리를 위한 모듈
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.1.1-0066FF?style=flat-square" alt="version 1.1.1">
|
||||
<img src="https://img.shields.io/badge/type-%EB%AA%A8%EB%93%88-555555?style=flat-square" alt="type 모듈">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -69,7 +69,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -147,6 +147,7 @@ _별도의 관리자 설정 항목이 없습니다._
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# 게시판 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `modules/_bundled/sirsoft-board/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> 레이아웃 편집기 스펙 — 게시판 모듈 도메인 sampleData/sampleGlobal/states. 실제 admin 레이아웃 data_source ID 전수 스캔 기반 도메인 ID(posts/post/reports/report_detail/reporters_list/boards/boards_list/board_types/form_data/form_meta/settings) + 사용자 게시판 페이지 byEndpointPattern. 공용 인프라(roles/availableChannels/identityProviders/boardIdentity*/boardNotificationDefinitions)는 admin 템플릿 스펙(roles/availableChannels/identityProviders)·코어 프리셋 폴백이 커버.
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
단일 파일로 둔 것은 분량 때문입니다. 게시판 스펙은 도메인 데이터 4블록뿐이라 분할할
|
||||
이유가 없습니다 — 분할은 템플릿 스펙처럼 한 파일이 만 줄 단위로 커질 때의 장치입니다.
|
||||
|
||||
`스타일 시스템`·`다크 모드 전략` 이 비어 있는 것도 의도입니다. 그 둘은 화면을 **그리는**
|
||||
쪽의 결정이라 템플릿 스펙이 소유합니다. 게시판이 여기에 값을 넣으면 어떤 템플릿을 깔든
|
||||
게시판이 스타일 체계를 강제하는 셈이 됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 23 | `editor-spec.json (인라인)` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 4 | `editor-spec.json (인라인)` |
|
||||
| `sampleGlobal` | `_global.*` 프리뷰 baseline 시드 | 1 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 8 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
이 네 블록은 "편집기가 게시판 화면을 실제 API 없이 그리려면 무엇이 필요한가" 에서
|
||||
그대로 나옵니다. `byDataSourceId` 23종은 admin 레이아웃의 `data_source` ID 를 전수
|
||||
스캔해 맞춘 것이고, `byEndpointPattern` 4종은 사용자 게시판 페이지처럼 ID 가 아니라
|
||||
호출 주소로 붙는 자리를 덮습니다.
|
||||
|
||||
여기에 없는 것이 무엇인지가 더 중요합니다 — `roles`·`availableChannels`·
|
||||
`identityProviders` 같은 공용 인프라 ID 는 게시판이 쓰지만 게시판이 선언하지 않습니다.
|
||||
그것들은 admin 템플릿 스펙과 코어 프리셋이 채웁니다. 여기에 같이 넣으면 같은 ID 의
|
||||
샘플이 두 곳에 생기고, 둘이 갈라져도 아무 오류가 나지 않습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 23 | `posts` · `post` · `reports` · `report_detail` · `reporters_list` · `boards` · `boards_list` · `board_types` · `form_data` · `form_meta` · `settings` · `availableChannels` … 외 11개 |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 4 | `/api/modules/sirsoft-board/boards/*/posts*` · `/api/modules/sirsoft-board/boards/popular*` · `/api/modules/sirsoft-board/me/*` · `/api/modules/sirsoft-board/users/*/posts*` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 8 | `/board/:slug/:id` · `/board/:slug` · `/boards` · `/board/:slug/write` · `*/admin/boards/:slug/edit` · `*/admin/board/:slug/:id/edit` · `*/admin/boards/settings` · `*/admin/board/:slug/post/:id` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`states.groups` 8종은 게시판 화면 중 **상태에 따라 다르게 보이는 것**만 골랐습니다.
|
||||
비밀글 잠금(`/board/:slug/:id`), 목록의 빈 상태(`/board/:slug`), 작성 폼 등입니다. 상태
|
||||
변종이 없는 화면은 기본 샘플 하나로 충분하므로 등록하지 않습니다.
|
||||
|
||||
게시판 레이아웃에 `data_source` 를 새로 붙일 때는 그 ID 가 공용 인프라인지 게시판
|
||||
도메인인지 먼저 가릅니다. 도메인이면 이 스펙의 `byDataSourceId` 에, 공용이면 템플릿
|
||||
스펙에 갑니다. 잘못 판단해도 편집기 화면만 비므로 실행 중에는 드러나지 않습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan module:update sirsoft-board --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
게시판은 관리자 화면과 사용자 화면을 모두 갖습니다. 관리자 쪽 `data_source` 는
|
||||
`byDataSourceId` 로 붙지만 사용자 게시판 페이지는 템플릿이 렌더하므로 ID 가 아니라
|
||||
`byEndpointPattern` 으로 붙습니다 — 사용자 화면을 건드렸는데 관리자 쪽 자리만 고치면
|
||||
그 화면은 편집기에서 계속 빈 채로 남습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -172,6 +172,7 @@ CRUD 를 바꾸고 싶으면 이 4종 중 하나를 잡으면 되고, 이 모듈
|
||||
- [ ] 목록 응답에 하위 컬렉션(옵션·이미지)을 실을 때 화면이 실제로 그리는 것만 — Repository 의 `relations:` 와 Resource 의 `whenLoaded` 를 함께 본다
|
||||
- [ ] 결제수단·PG 관련 선언을 바꾸면 기설치본 `order_settings.json` 을 정정하는 업그레이드 스텝 동반 (자기 접두사만, 멱등)
|
||||
- [ ] 새 관리자 화면을 추가하면 그 화면의 권한(`getPermissions()`)·메뉴(`getAdminMenus()`)·라우트 이름이 서로 가리키는 대상이 일치하는지 확인
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan module:update sirsoft-ecommerce --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -227,6 +228,7 @@ npx playwright test modules/_bundled/sirsoft-ecommerce/tests/Playwright/specs/<
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 이커머스
|
||||
|
||||
**G7 모듈 · sirsoft-ecommerce**
|
||||
**그누보드7 모듈 · sirsoft-ecommerce**
|
||||
그누보드7 이커머스 모듈 - 상품, 주문, 결제 관리
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.2.1-0066FF?style=flat-square" alt="version 1.2.1">
|
||||
<img src="https://img.shields.io/badge/type-%EB%AA%A8%EB%93%88-555555?style=flat-square" alt="type 모듈">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -96,7 +96,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -198,6 +198,7 @@ _별도의 관리자 설정 항목이 없습니다._
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# 이커머스 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `modules/_bundled/sirsoft-ecommerce/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> 레이아웃 편집기 스펙 — 이커머스 모듈 도메인 sampleData/sampleGlobal/states. admin 레이아웃 data_source ID 전수 스캔 기반 도메인 ID 28종(상품·주문·브랜드·쿠폰·배송정책·정산·설정 등) byDataSourceId + 사용자 페이지(템플릿 렌더) byEndpointPattern. 공용 인프라(roles/availableChannels/identityProviders/ecommerceIdentity*/ecommerceNotificationDefinitions)는 admin 템플릿 스펙·코어 프리셋 폴백이 커버.
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
이커머스는 저장소에서 가장 큰 모듈이지만 편집기 스펙은 여전히 단일 파일입니다. 스펙
|
||||
분량을 키우는 것은 팔레트·컨트롤·컴포넌트 역량인데 그 셋은 템플릿이 소유하기 때문입니다.
|
||||
모듈 쪽에 남는 것은 도메인 데이터라 라우트 239개·레이아웃 206개 규모에도 한 파일에
|
||||
들어갑니다.
|
||||
|
||||
이 사실이 곧 설계 원칙입니다 — 확장이 커진다고 편집기 스펙이 따라 커지지 않습니다.
|
||||
커진다면 그 확장이 템플릿의 일을 하고 있다는 신호입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 51 | `editor-spec.json (인라인)` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 7 | `editor-spec.json (인라인)` |
|
||||
| `sampleGlobal` | `_global.*` 프리뷰 baseline 시드 | 12 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 17 | `editor-spec.json (인라인)` |
|
||||
| `actionRecipes` | 친화 명칭 → 액션 JSON 레시피 | 1 | `editor-spec.json (인라인)` |
|
||||
| `actionChipCandidates` | 동작 데이터 칩 컨텍스트 후보 | 1 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`actionRecipes` 와 `actionChipCandidates` 를 각 1건씩 둔 것이 다른 모듈과 다른
|
||||
지점입니다. 이커머스에는 운영자가 편집기에서 직접 조립하기 어려운 동작(장바구니·주문
|
||||
흐름에 얽힌 것)이 있어, 친화 명칭으로 미리 만들어 둔 레시피가 필요합니다.
|
||||
|
||||
나머지 네 블록은 게시판과 같은 원리입니다 — admin 레이아웃 `data_source` ID 51종을
|
||||
전수로 덮고, 사용자 페이지 7종은 호출 주소로 덮습니다. `sampleGlobal` 12종은 통화·로케일
|
||||
같은 값이 `_global` 에 없으면 상품 카드가 통째로 깨지기 때문에 baseline 으로 박아
|
||||
둔 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 51 | `transactions` · `products` · `product` · `categories` · `orders` · `order` · `reviews` · `brands` · `carriers` · `active_carriers` · `coupons` · `coupon` … 외 39개 |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 7 | `/api/modules/sirsoft-ecommerce/products*` · `/api/modules/sirsoft-ecommerce/cart*` · `/api/modules/sirsoft-ecommerce/checkout*` · `/api/modules/sirsoft-ecommerce/wishlist*` · `/api/modules/sirsoft-ecommerce/user/orders*` · `/api/modules/sirsoft-ecommerce/user/addresses*` · `/api/modules/sirsoft-ecommerce/user/inquiries*` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 17 | `/*?/products/:product_code` · `/*?/products` · `/*?/cart` · `/*?/checkout` · `/*?/orders/:id/complete` · `/*?/reorder/:id` · `*/admin/ecommerce/products/:itemCode/edit` · `*/admin/ecommerce/promotion-coupons/:id/edit` · `*/admin/ecommerce/shipping-policies/:id/edit` · `*/admin/ecommerce/settings` · `*/admin/ecommerce/mileage-deposit-settings` · `/*?/guest/orders` … 외 5개 |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`states.groups` 17종은 이커머스에서 상태가 실제로 화면을 가르는 자리입니다 — 품절
|
||||
상품, 빈 장바구니, 비회원 주문 조회, 재주문 등입니다. 상태를 늘리는 기준은 "그 상태에서
|
||||
운영자가 화면을 따로 손봐야 하는가" 입니다. 값만 다르고 구조가 같은 경우는 변종을
|
||||
만들지 않습니다.
|
||||
|
||||
`sampleGlobal` 에 통화 관련 값을 둘 때는 특정 통화를 정답으로 박지 않도록 주의합니다.
|
||||
기본 통화는 설정이 정하므로, 샘플이 특정 통화를 전제하면 편집기 프리뷰만 그 통화로
|
||||
고정되어 다른 통화 상점의 운영자에게 잘못된 화면을 보여 줍니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan module:update sirsoft-ecommerce --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
이커머스는 `sampleGlobal` 이 12종으로 가장 많습니다. 레이아웃이 `_global.*` 을 새로
|
||||
읽기 시작했는데 baseline 을 안 넣으면 그 값이 `undefined` 가 되어, 표현식이 통째로
|
||||
falsy 로 떨어지며 **영역 전체가 사라집니다.** 값 하나가 비는 것보다 알아채기 어렵습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -23,7 +23,7 @@ modules/_bundled/sirsoft-ecommerce/
|
||||
## 데이터 생성 위치 분리 (CRITICAL)
|
||||
|
||||
E2E spec 이 "특정 유저 + 특정 역할 + 특정 도메인 상황" 에서 동작하려면 백엔드 데이터 생성 코드가
|
||||
필요하다. **그 코드는 데이터를 소유한 영역에 위치해야 한다** — 기존 G7 의 Seeder/Factory 분리 원칙과 동일.
|
||||
필요하다. **그 코드는 데이터를 소유한 영역에 위치해야 한다** — 기존 그누보드7 의 Seeder/Factory 분리 원칙과 동일.
|
||||
|
||||
| 데이터 종류 | 위치 |
|
||||
|---|---|
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
- [ ] 코어 검색·SEO·ckeditor5 의 훅 이름이 바뀌면 이 모듈의 구독 5종이 조용히 끊기므로 함께 확인
|
||||
- [ ] 첨부 제한(`attachment.*`)은 `config/settings/defaults.json` 이 SSoT — 서비스에서 리터럴로 재클램프하지 않는다
|
||||
- [ ] 활동 로그 항목을 추가하면 코어 `lang/{ko,en}/activity_log.php` 의 action 라벨·description 과 번들 ja 팩까지 동반
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan module:update sirsoft-page --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -202,6 +203,7 @@ npx playwright test modules/_bundled/sirsoft-page/tests/Playwright/specs/<대상
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.1.0] - 2026-08-24
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 페이지
|
||||
|
||||
**G7 모듈 · sirsoft-page**
|
||||
**그누보드7 모듈 · sirsoft-page**
|
||||
정적 페이지(정보/정책/안내) 관리 모듈
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.1.1-0066FF?style=flat-square" alt="version 1.1.1">
|
||||
<img src="https://img.shields.io/badge/type-%EB%AA%A8%EB%93%88-555555?style=flat-square" alt="type 모듈">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -73,7 +73,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -156,6 +156,7 @@ _별도의 관리자 설정 항목이 없습니다._
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# 페이지 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `modules/_bundled/sirsoft-page/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> 레이아웃 편집기 스펙 — 페이지 모듈 도메인 sampleData/states. 실제 admin 레이아웃 data_source ID 4종 전수(pages/page/pageData/versions) + 사용자 페이지(/p/:slug, 약관/개인정보) byEndpointPattern. sampleGlobal 은 페이지 도메인이 _global keyspace 를 두지 않아 미작성('필요 시' 조건부 — 정당).
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
페이지 모듈의 스펙은 세 블록뿐입니다. 화면이 "목록 · 편집 · 공개 보기" 로 단순하고,
|
||||
운영자가 편집기에서 손대는 대상이 페이지 **내용**이 아니라 그것을 감싸는 레이아웃이기
|
||||
때문입니다.
|
||||
|
||||
`sampleGlobal` 을 두지 않은 것은 누락이 아닙니다 — 페이지 도메인은 `_global` 키를
|
||||
자기 것으로 쓰지 않습니다. 필요 없는 블록을 빈 값으로라도 선언해 두면 다음 사람이 그
|
||||
빈 값을 채워야 할 자리로 오해합니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 6 | `editor-spec.json (인라인)` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 3 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 3 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`byDataSourceId` 6종 중 `termsContent`·`privacyContent` 는 다른 넷과 성격이 다릅니다.
|
||||
약관·개인정보 페이지는 슬러그가 고정된 특수 페이지라 편집기에서 그 자리에 무엇이 들어갈지
|
||||
미리 보여 줘야 합니다. `byEndpointPattern` 3종도 같은 이유로 이 둘을 따로 덮습니다.
|
||||
|
||||
`states.groups` 3종은 공개 페이지와 관리자 편집·상세를 하나씩 맡습니다. 페이지는 상태
|
||||
변종이 적은 도메인이라 이 수가 늘어난다면 화면이 복잡해지고 있다는 신호입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 6 | `pages` · `page` · `pageData` · `versions` · `termsContent` · `privacyContent` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 3 | `/api/modules/sirsoft-page/pages/terms` · `/api/modules/sirsoft-page/pages/privacy` · `/api/modules/sirsoft-page/pages/*` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 3 | `/page/:slug` · `*/admin/pages/:id/edit` · `*/admin/pages/:id` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
페이지 모듈에서 주의할 것은 `/p/:slug` 처럼 **슬러그가 열려 있는 라우트**입니다.
|
||||
편집기는 특정 슬러그 하나를 골라 프리뷰를 그리므로, 그 샘플이 실제 운영 페이지 중
|
||||
가장 단순한 것을 닮아 있으면 복잡한 페이지에서 레이아웃이 깨지는 것을 편집기에서
|
||||
미리 볼 수 없습니다.
|
||||
|
||||
샘플을 고를 때는 가장 짧은 페이지가 아니라 **가장 많은 요소를 가진 페이지**를 기준으로
|
||||
삼습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan module:update sirsoft-page --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
<!-- @intent END -->
|
||||
@@ -125,6 +125,7 @@ Filter 구독에는 `'type' => 'filter'` 선언이 반드시 필요합니다.
|
||||
- [ ] 구독 대상 모듈의 훅 이름이 바뀌면 이 플러그인이 조용히 아무 일도 하지 않게 된다
|
||||
- [ ] `docs/extension/sample-extensions.md` 의 계층 표와 어긋나지 않는지 확인 (파일을 추가·삭제했다면 그 표도 갱신)
|
||||
- [ ] 플러그인은 완전한 페이지 레이아웃을 등록할 수 없다 — 설정 화면과 `layout_extensions` 만
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어도 되는 상태(공용 ID 만 사용)다. 이 확장만 쓰는 `data_source` 를 새로 붙이는 순간 `editor-spec.json` 신설이 필요해진다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -173,5 +174,6 @@ php vendor/bin/phpunit plugins/_bundled/gnuboard7-hello_plugin/tests --filter='<
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [0.1.1] - 2026-08-17
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Hello 플러그인
|
||||
|
||||
**G7 플러그인 · gnuboard7-hello_plugin**
|
||||
**그누보드7 플러그인 · gnuboard7-hello_plugin**
|
||||
학습용 최소 샘플 플러그인 (Hello 모듈 훅 소비)
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-0.1.2-0066FF?style=flat-square" alt="version 0.1.2">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.0-1F883D?style=flat-square" alt="G7 >=7.0.0">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.0-1F883D?style=flat-square" alt="그누보드7 >=7.0.0">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
<img src="https://img.shields.io/badge/requires-gnuboard7--hello__module-BF8700?style=flat-square" alt="requires gnuboard7-hello_module">
|
||||
</p>
|
||||
@@ -72,7 +72,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.0` |
|
||||
| 그누보드7 코어 | `>=7.0.0` |
|
||||
| PHP | `^8.2` |
|
||||
| 의존 모듈 | `gnuboard7-hello_module` `>=0.1.0` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -158,6 +158,7 @@ php artisan plugin:activate gnuboard7-hello_plugin
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
<!-- @generated:doc-toc END -->
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# Hello 플러그인 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
학습용 샘플 플러그인이라 편집기 스펙을 두지 않았고, 지금 상태에서는 **둘 필요도
|
||||
없습니다.** 이 플러그인이 소유한 화면은 설정 화면 하나이고 그 화면이 읽는 `settings` 는
|
||||
여러 확장이 공유하는 공용 ID 라 admin 템플릿 스펙이 이미 채웁니다.
|
||||
|
||||
이것이 "스펙 없음" 의 정상 형태입니다 — 아래 미커버 목록이 비어 있다는 사실이 그
|
||||
근거입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없습니다. 공용 ID 만 쓰는 확장은 자기 스펙을 갖지 않는 것이 규율에
|
||||
맞습니다 — 같은 ID 의 샘플을 확장마다 두면 어느 것이 쓰이는지가 합본 순서에 좌우되고,
|
||||
둘이 갈라져도 오류가 나지 않습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
미커버가 없습니다. 이 플러그인의 레이아웃이 쓰는 `data_source` 는 전부 번들 템플릿
|
||||
스펙이 채우므로, 편집기에서 설정 화면을 열면 값이 채워진 상태로 보입니다.
|
||||
|
||||
스펙을 갖지 않은 확장이 이 상태여야 정상입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
편집기 스펙을 신설해야 하는 시점은 하나입니다 — **이 확장이 소유한 레이아웃에
|
||||
이 확장만 쓰는 `data_source` 가 생겼을 때**. 그 순간부터 편집기 캔버스의 그 영역은
|
||||
빈 화면이 되고, 실제 화면은 정상 동작하므로 오류도 경고도 남지 않습니다.
|
||||
|
||||
신설 절차는 확장 루트에 `editor-spec.json` 을 만들고 `sampleData.byDataSourceId` 에
|
||||
그 ID 를 넣는 것으로 시작합니다. 팔레트·컨트롤은 템플릿의 일이므로 넣지 않습니다.
|
||||
파일을 만든 뒤 update 커맨드로 활성 디렉토리에 반영해야 편집기가 읽습니다 —
|
||||
`_bundled` 폴백이 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -148,6 +148,7 @@
|
||||
- [ ] 정리 커맨드의 판정 로직을 고쳤다면 fail-open 가드(`hasPotentiallyMissingSources()`)가 여전히 앞에 있는지 확인 — 이 가드가 빠지면 이미지가 조용히 지워진다
|
||||
- [ ] 편집기 폴백 경로를 고쳤다면 저장 계약(`{name}_mode`)과 재시도 시 내용 승계가 유지되는지 확인
|
||||
- [ ] 프론트엔드를 고쳤다면 Playwright 위지윅 spec 을 함께 갱신·실행한다 (단위 테스트만으로는 편집기 장착 회귀가 드러나지 않는다)
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어 `ckeditor5Uploads` 가 편집기 캔버스에서 빈 화면으로 보인다. `data_source` 를 더 늘리면 그 자리도 같은 상태가 된다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -202,6 +203,7 @@ npx playwright test plugins/_bundled/sirsoft-ckeditor5/tests/Playwright/specs/<
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
- CKEditor 5 본체·스타일·번역 파일을 플러그인에 함께 담았습니다. 이제 외부 CDN 에 연결하지 않고 사이트 자신의 서버에서 불러오므로, 폐쇄망이나 외부 접속이 제한된 환경에서도 에디터가 동작합니다.
|
||||
- 에디터를 불러오지 못한 경우 안내와 함께 임시 입력창으로 자동 전환됩니다. 작성한 내용은 그대로 저장되고, 이미 저장된 글을 수정할 때는 기존 본문이 임시 입력창에 그대로 실립니다. [다시 시도] 로 편집기를 되살리면 입력해 둔 내용이 그대로 이어집니다.
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# CKEditor 5 WYSIWYG 에디터
|
||||
|
||||
**G7 플러그인 · sirsoft-ckeditor5**
|
||||
**그누보드7 플러그인 · sirsoft-ckeditor5**
|
||||
CKEditor 5를 이용한 WYSIWYG 에디터 플러그인입니다. 플러그인 설치만으로 기존 HtmlEditor가 교체됩니다.
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.3-0066FF?style=flat-square" alt="version 1.0.3">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -84,7 +84,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -178,6 +178,7 @@ php artisan plugin:update sirsoft-ckeditor5 --force
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# CKEditor 5 WYSIWYG 에디터 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위지윅 에디터 플러그인이지만 편집기 스펙은 두지 않았습니다. 이 플러그인이 다루는 것은
|
||||
**본문 작성기**이고, 레이아웃 편집기가 다루는 것은 그 작성기를 **배치하는 화면**이라
|
||||
서로 다른 층이기 때문입니다.
|
||||
|
||||
에디터 자체의 팔레트(툴바 구성)는 이 플러그인의 설정 화면에서 정하지, 레이아웃 편집기
|
||||
스펙과는 무관합니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없습니다. 다만 이 플러그인은 설정 화면 외에 **업로드 관리 화면**을 하나
|
||||
더 갖고 있고, 그 화면은 자기 도메인 데이터를 읽습니다 — 아래 미커버 목록에 그 결과가
|
||||
드러납니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
**프리뷰 샘플이 없는 `data_source` 1개** — 편집기 캔버스에서 이 자리만 빈 화면이 됩니다. 실제 화면은 정상이라 오류도 경고도 남지 않습니다.
|
||||
|
||||
`ckeditor5Uploads`
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`ckeditor5Uploads` 가 미커버입니다. 업로드 관리 화면의 목록 영역이 편집기 캔버스에서
|
||||
빈 채로 보입니다.
|
||||
|
||||
설정 화면 쪽 `settings` 는 공용 ID 라 템플릿 스펙이 채우므로 문제가 없습니다. 즉 이
|
||||
플러그인은 **화면 둘 중 하나만** 편집기에서 온전히 보이는 상태입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
편집기 스펙을 신설해야 하는 시점은 하나입니다 — **이 확장이 소유한 레이아웃에
|
||||
이 확장만 쓰는 `data_source` 가 생겼을 때**. 그 순간부터 편집기 캔버스의 그 영역은
|
||||
빈 화면이 되고, 실제 화면은 정상 동작하므로 오류도 경고도 남지 않습니다.
|
||||
|
||||
신설 절차는 확장 루트에 `editor-spec.json` 을 만들고 `sampleData.byDataSourceId` 에
|
||||
그 ID 를 넣는 것으로 시작합니다. 팔레트·컨트롤은 템플릿의 일이므로 넣지 않습니다.
|
||||
파일을 만든 뒤 update 커맨드로 활성 디렉토리에 반영해야 편집기가 읽습니다 —
|
||||
`_bundled` 폴백이 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -126,6 +126,7 @@ Daum SDK 를 로드 → 컨테이너 `onMount` 에서 `sirsoft-daum_postcode.set
|
||||
- [ ] `dist/` 는 커밋되는 배포 산출물 — TS 를 고쳤으면 `--production` 재빌드 후 커밋 (`sourceMappingURL` 잔존 금지)
|
||||
- [ ] 조각이 붙는 확장점(`address_search_slot`)을 여는 화면이 그 자리를 없애면 오류 없이 사라진다 — 대상 확장 업그레이드 후 노출 확인
|
||||
- [ ] 프론트엔드를 고쳤다면 Playwright spec 을 함께 갱신·실행한다 (단위 테스트만으로는 장착 회귀가 드러나지 않는다)
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어도 되는 상태(공용 ID 만 사용)다. 이 확장만 쓰는 `data_source` 를 새로 붙이는 순간 `editor-spec.json` 신설이 필요해진다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -171,5 +172,6 @@ cd plugins/_bundled/sirsoft-daum_postcode && powershell -Command "npm run test:r
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Daum 우편번호
|
||||
|
||||
**G7 플러그인 · sirsoft-daum_postcode**
|
||||
**그누보드7 플러그인 · sirsoft-daum_postcode**
|
||||
Daum 우편번호 서비스를 통한 주소 검색 기능을 제공하는 플러그인입니다. API 키 없이 무료로 사용할 수 있습니다.
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.3-0066FF?style=flat-square" alt="version 1.0.3">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -72,7 +72,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
| 외부 스크립트 호스트 | `t1.daumcdn.net` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -158,6 +158,7 @@ php artisan plugin:update sirsoft-daum_postcode --force
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
<!-- @generated:doc-toc END -->
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Daum 우편번호 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
주소 검색 플러그인은 자기 화면을 거의 갖지 않습니다. 주소 검색 창은 외부 서비스가
|
||||
띄우는 것이고, 이 플러그인이 소유한 화면은 그 창의 모양·크기를 정하는 설정 하나입니다.
|
||||
그래서 편집기 스펙을 두지 않으며, 지금 상태에서는 둘 필요도 없습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없습니다. 설정 화면이 읽는 `settings` 는 공용 ID 라 admin 템플릿 스펙이
|
||||
채웁니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
미커버가 없습니다. 이 플러그인의 레이아웃이 쓰는 `data_source` 는 전부 번들 템플릿
|
||||
스펙이 채우므로 편집기에서 설정 화면이 온전히 보입니다.
|
||||
|
||||
주소 검색 창 자체는 외부 스크립트가 띄우므로 편집기 캔버스에는 나타나지 않습니다.
|
||||
그것은 스펙으로 해결할 수 있는 종류가 아닙니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
편집기 스펙을 신설해야 하는 시점은 하나입니다 — **이 확장이 소유한 레이아웃에
|
||||
이 확장만 쓰는 `data_source` 가 생겼을 때**. 그 순간부터 편집기 캔버스의 그 영역은
|
||||
빈 화면이 되고, 실제 화면은 정상 동작하므로 오류도 경고도 남지 않습니다.
|
||||
|
||||
신설 절차는 확장 루트에 `editor-spec.json` 을 만들고 `sampleData.byDataSourceId` 에
|
||||
그 ID 를 넣는 것으로 시작합니다. 팔레트·컨트롤은 템플릿의 일이므로 넣지 않습니다.
|
||||
파일을 만든 뒤 update 커맨드로 활성 디렉토리에 반영해야 편집기가 읽습니다 —
|
||||
`_bundled` 폴백이 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -129,6 +129,7 @@ necessary 4종(`XSRF-TOKEN`/세션/`laravel_maintenance`/`gdpr_session`)을 제
|
||||
- [ ] `gdpr_user_consent_histories` 는 append-only — UPDATE/DELETE 로 기존 행을 고치지 않는다 (완전삭제 시 익명화 UPDATE 예외는 `GdprUserDeleteListener` 단일 지점에서만 수행)
|
||||
- [ ] 새 자동 차단 카테고리(기능/분석/마케팅 외)를 추가하면 배너 UI·`blocked_domains` 스키마·차단 스크립트 3곳 동기화
|
||||
- [ ] `CookieConsentMiddleware` 의 strictly-necessary allowlist(4종)를 확장할 때는 ePrivacy Art.5(3) 면제 항목인지 먼저 검토 — 임의로 늘리면 동의 전 차단 원칙이 무력화된다
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-gdpr --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -179,6 +180,7 @@ npx playwright test plugins/_bundled/sirsoft-gdpr/tests/Playwright/specs/<대상
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# GDPR
|
||||
|
||||
**G7 플러그인 · sirsoft-gdpr**
|
||||
**그누보드7 플러그인 · sirsoft-gdpr**
|
||||
GDPR·개인정보보호법 대응 쿠키 동의 배너와 동의 이력 관리를 제공하는 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.4-0066FF?style=flat-square" alt="version 1.0.4">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.6-1F883D?style=flat-square" alt="G7 >=7.0.6">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.6-1F883D?style=flat-square" alt="그누보드7 >=7.0.6">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -69,7 +69,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.6` |
|
||||
| 그누보드7 코어 | `>=7.0.6` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -185,6 +185,7 @@ Google Analytics, Facebook Pixel, Kakao Pixel 등)가 시드되어 있고 운영
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# GDPR (일반 데이터 보호 규정) — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-gdpr/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> GDPR 플러그인 레이아웃 편집기 샘플 데이터 (쿠키 동의 설정 / 동의 이력 / 개인정보 정책 버전).
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
GDPR 플러그인은 관리자 설정·동의 이력 화면과 **사용자 화면에 얹히는 쿠키 배너**를
|
||||
함께 갖습니다. 스펙이 두 블록만으로 끝나는 것은 이 플러그인이 컴포넌트를 만들지 않고
|
||||
템플릿 컴포넌트로 배너를 조립하기 때문입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 9 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 2 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`byDataSourceId` 9종이 설정·정책 버전·동의 이력 세 갈래를 덮습니다. `gdprPublicSettings`
|
||||
와 `gdprSettings` 가 따로 있는 것이 이 스펙의 핵심입니다 — 공개 응답과 관리자 응답은
|
||||
같은 저장값에서 나오지만 **내보내는 항목이 다릅니다.** 샘플을 하나로 합치면 편집기에서
|
||||
사용자 배너를 편집할 때 관리자만 볼 수 있는 항목까지 보이게 됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 9 | `gdprSettings` · `gdprPolicyVersionCurrent` · `gdprPolicyVersionHistory` · `gdprPolicyVersionSnapshot` · `gdprConsentLog` · `gdprPublicSettings` · `gdprMyConsent` · `gdprMeConsents` · `gdprMyConsents` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 2 | `*/admin/plugins/sirsoft-gdpr/consent-log` · `_user_base` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`states.groups` 가 `_user_base` 를 범위로 갖는 것이 이 플러그인의 특징입니다. 쿠키
|
||||
배너는 특정 라우트가 아니라 **모든 사용자 화면의 베이스 레이아웃**에 얹히므로, 편집기가
|
||||
배너를 보여 주려면 베이스 레이아웃에 상태를 주입해야 합니다.
|
||||
|
||||
배너는 동의 전에만 보입니다. 편집기 캔버스는 정적 시뮬레이션이라 "아직 동의하지 않은
|
||||
방문자" 상태를 만들어 주지 않으면 배너가 화면에 나타나지 않아 **편집 자체가 불가능**합니다.
|
||||
`_user_base` 상태 변종이 그 역할입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-gdpr --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
동의 항목을 추가·제거할 때는 `gdprPublicSettings` 와 `gdprSettings` 샘플을 **함께**
|
||||
고칩니다. 한쪽만 고치면 편집기에서 관리자 화면과 사용자 배너가 서로 다른 항목 목록을
|
||||
보여 주는데, 어느 쪽이 맞는지 화면만 봐서는 알 수 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -143,6 +143,7 @@
|
||||
- [ ] 레이아웃 조각 5개는 대상 화면의 슬롯이 사라지면 오류 없이 빠진다 — 템플릿·코어 회원 화면 업그레이드 후 노출 확인
|
||||
- [ ] 약관 페이지 slug 설정은 `sirsoft-page` 모듈의 페이지를 가리킨다 (manifest 의존 `>=1.0.0`)
|
||||
- [ ] 동의 항목을 늘릴 때는 설정만 바꾼다 — 마이그레이션이 필요해졌다면 EAV 구조를 벗어난 설계라는 신호
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-marketing --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -193,6 +194,7 @@ cd plugins/_bundled/sirsoft-marketing && powershell -Command "npm run test:run -
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.3] - 2026-08-22
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 마케팅 동의
|
||||
|
||||
**G7 플러그인 · sirsoft-marketing**
|
||||
**그누보드7 플러그인 · sirsoft-marketing**
|
||||
이메일 구독, 마케팅 동의, 제3자 제공 동의 등을 관리하는 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.4-0066FF?style=flat-square" alt="version 1.0.4">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.0-1F883D?style=flat-square" alt="G7 >=7.0.0">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.0-1F883D?style=flat-square" alt="그누보드7 >=7.0.0">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
<img src="https://img.shields.io/badge/requires-sirsoft--page-BF8700?style=flat-square" alt="requires sirsoft-page">
|
||||
</p>
|
||||
@@ -78,7 +78,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.0` |
|
||||
| 그누보드7 코어 | `>=7.0.0` |
|
||||
| PHP | `^8.2` |
|
||||
| 의존 모듈 | `sirsoft-page` `>=1.0.0` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -174,6 +174,7 @@ php artisan plugin:update sirsoft-marketing --force
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# 마케팅 동의 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-marketing/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> 마케팅 동의 플러그인 레이아웃 편집기 샘플 데이터.
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
마케팅 동의 플러그인의 스펙은 `sampleData` 한 블록, ID 하나가 전부입니다. 이 플러그인이
|
||||
소유한 화면이 설정 화면 하나뿐이고 그 화면이 읽는 도메인 데이터가 `marketing_settings`
|
||||
하나이기 때문입니다.
|
||||
|
||||
스펙이 작다는 것이 곧 부실을 뜻하지는 않습니다. 필요한 만큼만 선언하는 것이 규율이고,
|
||||
쓰지 않는 블록을 빈 값으로 채워 두면 다음 사람이 그것을 채워야 할 자리로 오해합니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`states.groups` 를 두지 않은 것은 이 플러그인의 설정 화면에 **상태 변종이 없기**
|
||||
때문입니다. 값이 있든 없든 같은 폼이 그려지므로, 상태를 나눠도 편집기에서 보이는 화면이
|
||||
달라지지 않습니다.
|
||||
|
||||
동의 항목이 여러 개로 늘거나 항목별로 화면이 갈라지는 날이 오면 그때 `states` 를
|
||||
신설합니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `marketing_settings` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 미선언 | - |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
마케팅 동의는 회원가입 폼·마이페이지 등 **다른 확장이 소유한 화면**에도 얹힙니다.
|
||||
그 자리들은 레이아웃 확장 조각으로 주입되므로 이 스펙이 아니라 그 화면을 소유한 쪽의
|
||||
샘플로 그려집니다 — 여기 `sampleData` 가 하나뿐인 이유입니다.
|
||||
|
||||
이 플러그인이 주입한 조각이 편집기에서 비어 보인다면 고칠 자리는 여기가 아니라
|
||||
그 화면을 소유한 확장의 스펙입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-marketing --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
<!-- @intent END -->
|
||||
@@ -160,6 +160,7 @@ HookManager::addAction(
|
||||
- [ ] 레이아웃 조각 7개는 대상 화면(알림 설정·알림 템플릿 편집·발송 이력)의 자리가 사라지면 오류 없이 빠진다 — 코어·게시판·이커머스 업그레이드 후 노출 확인
|
||||
- [ ] 크리덴셜 설정을 추가한다면 `frontend_schema` 에 `expose: false` + `sensitive: true` 를 함께 선언
|
||||
- [ ] `dist/` 는 커밋되는 배포 산출물 — TS 를 고쳤으면 `--production` 재빌드 후 커밋 (`sourceMappingURL` 잔존 금지)
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어 `bizppurioCategories` · `bizppurioProfiles` · `bizppurio_templates_list` · `report_url` · `templates_readiness` 가 편집기 캔버스에서 빈 화면으로 보인다. `data_source` 를 더 늘리면 그 자리도 같은 상태가 된다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -216,6 +217,7 @@ npx playwright test plugins/_bundled/sirsoft-message_bizppurio/tests/Playwright/
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.0] - 2026-08-24
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 비즈뿌리오 메시지 발송
|
||||
|
||||
**G7 플러그인 · sirsoft-message_bizppurio**
|
||||
**그누보드7 플러그인 · sirsoft-message_bizppurio**
|
||||
비즈뿌리오 연동 SMS/LMS·카카오 알림톡 발송 플러그인입니다. 코어 알림 시스템 채널로 문자·알림톡을 발송하고 발송 결과를 webhook 으로 수신합니다.
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.1-0066FF?style=flat-square" alt="version 1.0.1">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.6-1F883D?style=flat-square" alt="G7 >=7.0.6">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.6-1F883D?style=flat-square" alt="그누보드7 >=7.0.6">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- @intent START -->
|
||||
비즈뿌리오(Bizppurio)를 연동해 **문자(SMS/LMS)와 카카오 알림톡**을 발송하는 플러그인입니다.
|
||||
|
||||
G7 코어 알림 시스템에 문자·알림톡 채널을 추가하므로, 회원가입·주문 완료처럼 코어와 모듈이
|
||||
그누보드7 코어 알림 시스템에 문자·알림톡 채널을 추가하므로, 회원가입·주문 완료처럼 코어와 모듈이
|
||||
이미 발화하는 알림을 문자와 알림톡으로도 자동 발송할 수 있습니다. 알림을 새로 만들 필요 없이
|
||||
**기존 알림의 채널만 켜면** 됩니다.
|
||||
|
||||
@@ -98,7 +98,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.6` |
|
||||
| 그누보드7 코어 | `>=7.0.6` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -259,6 +259,7 @@ https://your-domain.com/api/plugins/sirsoft-message_bizppurio/webhook
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
| PHONE | body | string | 아니오 | max 20 | 수신 전화번호 |
|
||||
| MEDIA | body | string | 아니오 | max 10 | 실제 발송된 매체 유형(대체발송 시 요청 유형과 다를 수 있음) |
|
||||
| RESULT | body | string | 예 | max 10 | 발송 결과 코드 — ResultCodeResolver 가 성공/실패와 사유로 해석 |
|
||||
| REFKEY | body | string | 예 | max 32 | 발송 시 G7 이 부여한 참조 키 — 이 값으로 bizppurio_dispatches 행을 찾는다 |
|
||||
| REFKEY | body | string | 예 | max 32 | 발송 시 그누보드7 이 부여한 참조 키 — 이 값으로 bizppurio_dispatches 행을 찾는다 |
|
||||
| TELRES | body | string | 아니오 | max 10 | 대체발송(문자) 결과 코드 |
|
||||
| KAORES | body | string | 아니오 | max 10 | 알림톡 발송 결과 코드 |
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# 비즈뿌리오 메시지 발송 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
문자·알림톡 채널 플러그인은 라우트 21개에 관리자 화면도 여럿 갖지만 편집기 스펙이
|
||||
없습니다. 이것은 설계가 아니라 **아직 만들지 않은 상태**입니다 — 아래 미커버 목록이 그
|
||||
결과를 보여 줍니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없습니다. 이 플러그인의 관리자 화면은 연동 설정·알림톡 템플릿 관리 등
|
||||
여러 갈래이고 각각이 자기 도메인 데이터를 읽으므로, 공용 ID 만으로는 덮이지 않습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
**프리뷰 샘플이 없는 `data_source` 5개** — 편집기 캔버스에서 이 자리만 빈 화면이 됩니다. 실제 화면은 정상이라 오류도 경고도 남지 않습니다.
|
||||
|
||||
`bizppurioCategories` · `bizppurioProfiles` · `bizppurio_templates_list` · `report_url` · `templates_readiness`
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
미커버가 5종으로 저장소에서 가장 많습니다 — `bizppurioCategories` ·
|
||||
`bizppurioProfiles` · `bizppurio_templates_list` · `report_url` · `templates_readiness`.
|
||||
|
||||
알림톡 템플릿 관리 화면 전체가 편집기 캔버스에서 빈 채로 보인다는 뜻입니다. 이 플러그인의
|
||||
관리자 화면을 편집기로 손보려는 운영자는 지금 목록도 상태도 볼 수 없습니다.
|
||||
|
||||
연동 설정 쪽 `settings` 만 공용 ID 라 템플릿 스펙이 채웁니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
편집기 스펙을 신설해야 하는 시점은 하나입니다 — **이 확장이 소유한 레이아웃에
|
||||
이 확장만 쓰는 `data_source` 가 생겼을 때**. 그 순간부터 편집기 캔버스의 그 영역은
|
||||
빈 화면이 되고, 실제 화면은 정상 동작하므로 오류도 경고도 남지 않습니다.
|
||||
|
||||
신설 절차는 확장 루트에 `editor-spec.json` 을 만들고 `sampleData.byDataSourceId` 에
|
||||
그 ID 를 넣는 것으로 시작합니다. 팔레트·컨트롤은 템플릿의 일이므로 넣지 않습니다.
|
||||
파일을 만든 뒤 update 커맨드로 활성 디렉토리에 반영해야 편집기가 읽습니다 —
|
||||
`_bundled` 폴백이 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -120,6 +120,7 @@ CBT 인증 URL 로 폼 POST → KG 이니시스가 `sid` 를 콜백으로 전달
|
||||
- [ ] 승인/취소 흐름을 고칠 때 `before_*`/`after_*` 훅 순서와 우선순위(`PaymentRefundListener` < `CancelActivityLogListener`)를 유지 — 로그가 실제 처리보다 먼저 실행되면 안 된다
|
||||
- [ ] IP 화이트리스트(`InicisNotifyIpWhitelist`) 대상 라우트를 추가/변경하면 미들웨어 부착 대상(targets)도 함께 갱신
|
||||
- [ ] 새 결제수단·통화를 추가하면 그 결제수단의 콜백 URL을 관리자 설정 안내(README "콜백/통보 URL 등록")에도 반영
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-pay_kginicis --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -168,6 +169,7 @@ cd plugins/_bundled/sirsoft-pay_kginicis && powershell -Command "npm run test:ru
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.1.2] - 2026-08-22
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# KG 이니시스
|
||||
|
||||
**G7 플러그인 · sirsoft-pay_kginicis**
|
||||
**그누보드7 플러그인 · sirsoft-pay_kginicis**
|
||||
KG 이니시스 표준결제를 sirsoft-ecommerce 에 연결하는 결제 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.1.3-0066FF?style=flat-square" alt="version 1.1.3">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
<img src="https://img.shields.io/badge/requires-sirsoft--ecommerce-BF8700?style=flat-square" alt="requires sirsoft-ecommerce">
|
||||
</p>
|
||||
@@ -22,7 +22,7 @@ KG 이니시스 표준결제를 sirsoft-ecommerce 에 연결하는 결제 플러
|
||||
## 소개
|
||||
|
||||
<!-- @intent START -->
|
||||
KG 이니시스 표준결제를 G7 `sirsoft-ecommerce` 모듈에 연결하는 결제 플러그인입니다. PC 결제는
|
||||
KG 이니시스 표준결제를 그누보드7 `sirsoft-ecommerce` 모듈에 연결하는 결제 플러그인입니다. PC 결제는
|
||||
`INIStdPay.js` 표준결제창을, 모바일 결제는 모바일 표준결제창으로 이동한 뒤 서버 승인 API로
|
||||
최종 승인하는 흐름을 씁니다. 일본 엔(JPY) 결제는 별도의 KG 이니시스 CBT(JPPG) 흐름을 씁니다.
|
||||
|
||||
@@ -85,7 +85,7 @@ CBT 결제창으로 진입하며, 설정이 부족하면 한국 표준결제로
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
| 의존 모듈 | `sirsoft-ecommerce` `>=1.1.0` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -250,6 +250,7 @@ IP와 `devcbt.inicis.com` 443 연결 상태를 먼저 확인합니다.
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# KG 이니시스 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-pay_kginicis/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> KG이니시스 결제 플러그인 레이아웃 편집기 샘플 데이터 (가상계좌 입금통보 URL).
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
KG이니시스 결제 플러그인이 소유한 화면은 관리자 설정 하나뿐입니다. 결제 자체는 PG 결제창에서
|
||||
일어나고 그 창은 이 확장이 그리는 화면이 아니므로, 편집기가 다룰 표면이 설정 화면으로
|
||||
좁혀집니다.
|
||||
|
||||
`스타일 시스템`·`다크 모드 전략` 이 비어 있는 것은 정상입니다 — 화면을 그리는 규칙은
|
||||
템플릿이 정하고, 결제 플러그인은 그 위에 자기 설정 항목만 얹습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 1 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 것은 `vbank_info` 하나, 그리고 설정 화면의 상태 변종 하나입니다. 다른 설정 항목
|
||||
(`settings` 등)은 여러 확장이 공유하는 공용 ID 라 admin 템플릿 스펙이 채웁니다 —
|
||||
여기에 다시 선언하면 같은 ID 의 샘플이 두 곳에 생기고, 둘이 갈라져도 오류가 나지
|
||||
않습니다.
|
||||
|
||||
가상계좌 안내(`vbank_info`)를 굳이 넣은 것은 그 영역이 **결제 수단에 따라 나타났다
|
||||
사라지는** 자리라, 샘플이 없으면 편집기에서 그 영역 자체를 볼 수 없기 때문입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `vbank_info` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 1 | `*/admin/plugins/sirsoft-pay_kginicis/settings` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
상태 변종의 범위가 `*/admin/plugins/sirsoft-pay_kginicis/settings` 하나인 것은 이 플러그인이 자기
|
||||
설정 화면만 소유하기 때문입니다. 주문·결제 화면은 `sirsoft-ecommerce` 가 소유하므로
|
||||
그 화면의 프리뷰는 이커머스 스펙이 그립니다.
|
||||
|
||||
결제 흐름을 편집기에서 확인하려던 것이라면 이 문서가 아니라 이커머스 모듈의 편집기 스펙
|
||||
문서를 봅니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-pay_kginicis --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
이 플러그인의 결제 수단이 이커머스 관리자 화면에 어떻게 보이는지는 편집기 스펙이 아니라
|
||||
결제수단 카탈로그 선언(`needs_pg`·`pg_provider`·`pg_locked`)이 정합니다. 배지가 이상하게
|
||||
보인다면 스펙이 아니라 그 선언을 봅니다.
|
||||
<!-- @intent END -->
|
||||
@@ -124,6 +124,7 @@ OS 판별 후 `executeCliWindows()`/`executeCliLinux()` 로 분기 → CLI 인
|
||||
- [ ] 승인/취소 흐름을 고칠 때 `before_*`/`after_*` 훅 순서와 우선순위(`PaymentRefundListener` < `CancelActivityLogListener`)를 유지 — 로그가 실제 처리보다 먼저 실행되면 안 된다
|
||||
- [ ] IP 화이트리스트(`RestrictKcpIp`) 대상 라우트를 추가/변경하면 미들웨어 부착 대상(targets)도 함께 갱신
|
||||
- [ ] 새 결제수단·통화를 추가하면 그 결제수단의 콜백 URL을 관리자 설정 안내(README "콜백 및 통보 URL")에도 반영
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-pay_nhnkcp --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -172,6 +173,7 @@ cd plugins/_bundled/sirsoft-pay_nhnkcp && powershell -Command "npm run test:run
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.3] - 2026-08-22
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# NHN KCP
|
||||
|
||||
**G7 플러그인 · sirsoft-pay_nhnkcp**
|
||||
**그누보드7 플러그인 · sirsoft-pay_nhnkcp**
|
||||
NHN KCP Standard Pay 결제를 sirsoft-ecommerce 에 연결하는 결제 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.4-0066FF?style=flat-square" alt="version 1.0.4">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
<img src="https://img.shields.io/badge/requires-sirsoft--ecommerce-BF8700?style=flat-square" alt="requires sirsoft-ecommerce">
|
||||
</p>
|
||||
@@ -22,7 +22,7 @@ NHN KCP Standard Pay 결제를 sirsoft-ecommerce 에 연결하는 결제 플러
|
||||
## 소개
|
||||
|
||||
<!-- @intent START -->
|
||||
NHN KCP Standard Pay 결제를 G7 `sirsoft-ecommerce` 모듈에 연결하는 결제 플러그인입니다. PC
|
||||
NHN KCP Standard Pay 결제를 그누보드7 `sirsoft-ecommerce` 모듈에 연결하는 결제 플러그인입니다. PC
|
||||
결제는 `payplus_web.jsp` 결제창 + 서버의 KCP CLI 승인 모듈을, 모바일 결제는 SmartPhone Pay
|
||||
SOAP 승인키 발급 + 모바일 결제창을 씁니다.
|
||||
|
||||
@@ -83,7 +83,7 @@ PC 결제 승인은 `NhnKcpApiService`가 OS 를 판별해 `pp_cli`/`pp_cli_x64`
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
| 의존 모듈 | `sirsoft-ecommerce` `>=1.1.0` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -254,6 +254,7 @@ php artisan plugin:update sirsoft-pay_nhnkcp --force
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# NHN KCP — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-pay_nhnkcp/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> NHN KCP 결제 플러그인 레이아웃 편집기 샘플 데이터 (가상계좌 입금통보 URL / 연동 상태 점검).
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
NHN KCP 결제 플러그인이 소유한 화면은 관리자 설정 하나뿐입니다. 결제 자체는 PG 결제창에서
|
||||
일어나고 그 창은 이 확장이 그리는 화면이 아니므로, 편집기가 다룰 표면이 설정 화면으로
|
||||
좁혀집니다.
|
||||
|
||||
`스타일 시스템`·`다크 모드 전략` 이 비어 있는 것은 정상입니다 — 화면을 그리는 규칙은
|
||||
템플릿이 정하고, 결제 플러그인은 그 위에 자기 설정 항목만 얹습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 2 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 1 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 것은 `vbank_info` 와 연동 점검용 `health` 둘, 그리고 설정 화면의 상태 변종 하나입니다. 다른 설정 항목
|
||||
(`settings` 등)은 여러 확장이 공유하는 공용 ID 라 admin 템플릿 스펙이 채웁니다 —
|
||||
여기에 다시 선언하면 같은 ID 의 샘플이 두 곳에 생기고, 둘이 갈라져도 오류가 나지
|
||||
않습니다.
|
||||
|
||||
가상계좌 안내(`vbank_info`)를 굳이 넣은 것은 그 영역이 **결제 수단에 따라 나타났다
|
||||
사라지는** 자리라, 샘플이 없으면 편집기에서 그 영역 자체를 볼 수 없기 때문입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 2 | `vbank_info` · `health` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 1 | `*/admin/plugins/sirsoft-pay_nhnkcp/settings` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
상태 변종의 범위가 `*/admin/plugins/sirsoft-pay_nhnkcp/settings` 하나인 것은 이 플러그인이 자기
|
||||
설정 화면만 소유하기 때문입니다. 주문·결제 화면은 `sirsoft-ecommerce` 가 소유하므로
|
||||
그 화면의 프리뷰는 이커머스 스펙이 그립니다.
|
||||
|
||||
결제 흐름을 편집기에서 확인하려던 것이라면 이 문서가 아니라 이커머스 모듈의 편집기 스펙
|
||||
문서를 봅니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-pay_nhnkcp --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
이 플러그인의 결제 수단이 이커머스 관리자 화면에 어떻게 보이는지는 편집기 스펙이 아니라
|
||||
결제수단 카탈로그 선언(`needs_pg`·`pg_provider`·`pg_locked`)이 정합니다. 배지가 이상하게
|
||||
보인다면 스펙이 아니라 그 선언을 봅니다.
|
||||
<!-- @intent END -->
|
||||
@@ -117,6 +117,7 @@ API 와 동기화하는 역할만 합니다. 등록은 훅 기반입니다
|
||||
- [ ] 인증 실패(1단계)와 승인 실패(2단계)의 사용자 안내 방식(silent redirect vs `?error=`)을 구분 유지 — §1 "의도적으로 하지 않는 것" 참고
|
||||
- [ ] IP 화이트리스트(`VbankNotifyIpWhitelist`) 대상 라우트를 추가/변경하면 미들웨어 부착 대상(targets)도 함께 갱신
|
||||
- [ ] 새 간편결제 수단을 추가하면 그 결제수단의 계약 상태를 관리자 안내에도 반영
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-pay_nicepayments --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -164,6 +165,7 @@ cd plugins/_bundled/sirsoft-pay_nicepayments && powershell -Command "npm run tes
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.2] - 2026-08-19
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# NicePayments
|
||||
|
||||
**G7 플러그인 · sirsoft-pay_nicepayments**
|
||||
**그누보드7 플러그인 · sirsoft-pay_nicepayments**
|
||||
나이스페이먼츠 결제를 sirsoft-ecommerce 에 연결하는 결제 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.3-0066FF?style=flat-square" alt="version 1.0.3">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
<img src="https://img.shields.io/badge/requires-sirsoft--ecommerce-BF8700?style=flat-square" alt="requires sirsoft-ecommerce">
|
||||
</p>
|
||||
@@ -22,7 +22,7 @@
|
||||
## 소개
|
||||
|
||||
<!-- @intent START -->
|
||||
나이스페이먼츠(NicePayments) 표준결제를 G7 `sirsoft-ecommerce` 모듈에 연결하는 결제
|
||||
나이스페이먼츠(NicePayments) 표준결제를 그누보드7 `sirsoft-ecommerce` 모듈에 연결하는 결제
|
||||
플러그인입니다. 결제 승인은 **인증→승인 2단계**로 나뉩니다 — 결제창(`goPay` iframe
|
||||
팝업/모바일 폼)이 먼저 인증 결과를 서버로 보내고, 서버가 그 결과를 받아 별도 승인 API를
|
||||
호출해야 최종 완료됩니다.
|
||||
@@ -76,7 +76,7 @@ URL로 결과를 POST 하면 결제 완료 처리됩니다.
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
| 의존 모듈 | `sirsoft-ecommerce` `>=1.1.0` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -212,6 +212,7 @@ https://your-domain.com/plugins/sirsoft-pay_nicepayments/payment/vbank-notify
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# 나이스페이먼츠 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-pay_nicepayments/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> 나이스페이먼츠 결제 플러그인 레이아웃 편집기 샘플 데이터 (가상계좌 입금통보 URL).
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
나이스페이먼츠 결제 플러그인이 소유한 화면은 관리자 설정 하나뿐입니다. 결제 자체는 PG 결제창에서
|
||||
일어나고 그 창은 이 확장이 그리는 화면이 아니므로, 편집기가 다룰 표면이 설정 화면으로
|
||||
좁혀집니다.
|
||||
|
||||
`스타일 시스템`·`다크 모드 전략` 이 비어 있는 것은 정상입니다 — 화면을 그리는 규칙은
|
||||
템플릿이 정하고, 결제 플러그인은 그 위에 자기 설정 항목만 얹습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 1 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 것은 `vbank_info` 하나, 그리고 설정 화면의 상태 변종 하나입니다. 다른 설정 항목
|
||||
(`settings` 등)은 여러 확장이 공유하는 공용 ID 라 admin 템플릿 스펙이 채웁니다 —
|
||||
여기에 다시 선언하면 같은 ID 의 샘플이 두 곳에 생기고, 둘이 갈라져도 오류가 나지
|
||||
않습니다.
|
||||
|
||||
가상계좌 안내(`vbank_info`)를 굳이 넣은 것은 그 영역이 **결제 수단에 따라 나타났다
|
||||
사라지는** 자리라, 샘플이 없으면 편집기에서 그 영역 자체를 볼 수 없기 때문입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `vbank_info` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 1 | `*/admin/plugins/sirsoft-pay_nicepayments/settings` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
상태 변종의 범위가 `*/admin/plugins/sirsoft-pay_nicepayments/settings` 하나인 것은 이 플러그인이 자기
|
||||
설정 화면만 소유하기 때문입니다. 주문·결제 화면은 `sirsoft-ecommerce` 가 소유하므로
|
||||
그 화면의 프리뷰는 이커머스 스펙이 그립니다.
|
||||
|
||||
결제 흐름을 편집기에서 확인하려던 것이라면 이 문서가 아니라 이커머스 모듈의 편집기 스펙
|
||||
문서를 봅니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-pay_nicepayments --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
이 플러그인의 결제 수단이 이커머스 관리자 화면에 어떻게 보이는지는 편집기 스펙이 아니라
|
||||
결제수단 카탈로그 선언(`needs_pg`·`pg_provider`·`pg_locked`)이 정합니다. 배지가 이상하게
|
||||
보인다면 스펙이 아니라 그 선언을 봅니다.
|
||||
<!-- @intent END -->
|
||||
@@ -126,6 +126,7 @@
|
||||
- [ ] 가상계좌 웹훅의 secret 대조(`webhook_secret_verify`)를 기본값 `true` 이외로 바꾸지 않는다 — 끄면 토스 노티 위조를 막을 수단이 사라진다
|
||||
- [ ] `order_sheet_mode` 관련 로직을 고칠 때 `RegisterPgProviderListener`(enabled_methods)와 `RegisterTossPaymentMethodsListener`(builtin 결제수단 주입) 양쪽을 함께 갱신 — 한쪽만 고치면 설정과 노출 목록이 어긋난다
|
||||
- [ ] `ValidateTossSettingsListener`에 새 범위 검증을 추가하면 `core.plugin_settings.before_save` 의 `sync: true`를 유지
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어도 되는 상태(공용 ID 만 사용)다. 이 확장만 쓰는 `data_source` 를 새로 붙이는 순간 `editor-spec.json` 신설이 필요해진다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -173,6 +174,7 @@ cd plugins/_bundled/sirsoft-tosspayments && powershell -Command "npm run test:ru
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.2] - 2026-08-19
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 토스페이먼츠
|
||||
|
||||
**G7 플러그인 · sirsoft-tosspayments**
|
||||
**그누보드7 플러그인 · sirsoft-tosspayments**
|
||||
토스페이먼츠 결제 게이트웨이 (통합결제창 연동)
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.3-0066FF?style=flat-square" alt="version 1.0.3">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
<img src="https://img.shields.io/badge/requires-sirsoft--ecommerce-BF8700?style=flat-square" alt="requires sirsoft-ecommerce">
|
||||
</p>
|
||||
@@ -22,7 +22,7 @@
|
||||
## 소개
|
||||
|
||||
<!-- @intent START -->
|
||||
토스페이먼츠 통합결제창 결제를 G7 `sirsoft-ecommerce` 모듈에 연결하는 결제 플러그인입니다.
|
||||
토스페이먼츠 통합결제창 결제를 그누보드7 `sirsoft-ecommerce` 모듈에 연결하는 결제 플러그인입니다.
|
||||
승인은 브라우저 리다이렉트 기반입니다 — 결제창(SDK)이 결제를 처리한 뒤 브라우저를 콜백
|
||||
URL로 돌려보내고, 서버가 그 파라미터로 승인 확인 API를 호출합니다.
|
||||
|
||||
@@ -72,7 +72,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
| 의존 모듈 | `sirsoft-ecommerce` `>=1.1.0` |
|
||||
<!-- @generated:requirements END -->
|
||||
@@ -188,6 +188,7 @@ https://your-domain.com/plugins/sirsoft-tosspayments/webhook/deposit
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -86,7 +86,7 @@ Location: /shop/checkout?error=PAY_PROCESS_CANCELED&orderId=20260711-000001
|
||||
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| paymentKey | query | string | 예 | — | 토스가 발급한 결제 키. Confirm API 호출에 사용한다. |
|
||||
| orderId | query | string | 예 | — | 주문번호 (G7 `orders.order_number`). SDK 호출 시 넘긴 값이 그대로 돌아온다. |
|
||||
| orderId | query | string | 예 | — | 주문번호 (그누보드7 `orders.order_number`). SDK 호출 시 넘긴 값이 그대로 돌아온다. |
|
||||
| amount | query | integer | 예 | min 1 | 결제 금액. 주문의 결제요청 금액과 대조하며, 불일치 시 결제를 승인하지 않는다. |
|
||||
|
||||
**요청 예시**
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| orderId | body | string | 예 | max 100 | 주문번호 (G7 `orders.order_number`). 이 값으로 주문·결제 레코드를 조회한다. |
|
||||
| orderId | body | string | 예 | max 100 | 주문번호 (그누보드7 `orders.order_number`). 이 값으로 주문·결제 레코드를 조회한다. |
|
||||
| status | body | string | 예 | `DONE`, `CANCELED` | 입금 결과. `DONE`=입금완료 → 결제완료 처리, `CANCELED`=입금취소 → 결제실패 처리. |
|
||||
| secret | body | string | 아니오 | max 255 | 결제 승인 응답에서 발급받아 `payment_meta.toss_secret` 에 저장해 둔 값. 위조 방지 대조에 사용. |
|
||||
| transactionKey | body | string | 아니오 | max 255 | 토스 거래 키. 결제완료 처리 시 `transaction_id` 로 기록한다 (없으면 기존 값 유지). |
|
||||
@@ -111,7 +111,7 @@ OK
|
||||
| eventType | body | string | 아니오 | max 64 | 토스 이벤트 종류 (예: `PAYMENT_STATUS_CHANGED`). |
|
||||
| createdAt | body | string | 아니오 | max 64 | 토스가 이벤트를 생성한 시각. |
|
||||
| data | body | array | 예 | — | 결제 정보 객체. `data.orderId`(주문번호)와 `data.status`(토스 결제상태)를 읽어 로컬 상태와 대조한다. |
|
||||
| data.orderId | body | string | 예 | max 100 | 주문번호 (G7 `orders.order_number`). 이 값으로 주문·결제 레코드를 조회해 로컬 결제상태를 확인한다. |
|
||||
| data.orderId | body | string | 예 | max 100 | 주문번호 (그누보드7 `orders.order_number`). 이 값으로 주문·결제 레코드를 조회해 로컬 결제상태를 확인한다. |
|
||||
| data.status | body | string | 예 | max 40 | 토스 측 결제상태 (예: `DONE`, `CANCELED`, `WAITING_FOR_DEPOSIT`). 로컬 `payments.payment_status` 와 함께 로그에 기록해 불일치를 추적한다. |
|
||||
| data.paymentKey | body | string | 아니오 | max 255 | 토스 결제 키. 수신만 하며 이 엔드포인트에서 상태 전이에 사용하지 않는다. |
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# 토스페이먼츠 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
토스페이먼츠 결제 플러그인은 다른 결제 3종과 달리 편집기 스펙을 두지 않았습니다.
|
||||
그럼에도 미커버가 없는 것은 이 플러그인의 설정 화면이 공용 ID 만 읽기 때문입니다 —
|
||||
다른 결제 플러그인이 선언한 `vbank_info` 에 해당하는 영역을 이 플러그인은 설정 화면에서
|
||||
같은 방식으로 다루지 않습니다.
|
||||
|
||||
즉 지금은 스펙 없이도 편집기에서 화면이 온전히 보입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없습니다. 형제 결제 플러그인(`sirsoft-pay_kginicis` 등)은 스펙을 갖고
|
||||
있으므로, 이 플러그인에 가상계좌 안내 같은 도메인 영역을 추가할 때는 그쪽 스펙을 선례로
|
||||
봅니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
미커버가 없습니다. 설정 화면이 읽는 `settings` 는 공용 ID 라 admin 템플릿 스펙이
|
||||
채우므로, 편집기에서 설정 화면이 값이 채워진 상태로 보입니다.
|
||||
|
||||
결제 흐름 화면(주문·결제·완료)은 `sirsoft-ecommerce` 가 소유하므로 그 프리뷰는 이커머스
|
||||
스펙이 그립니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
편집기 스펙을 신설해야 하는 시점은 하나입니다 — **이 확장이 소유한 레이아웃에
|
||||
이 확장만 쓰는 `data_source` 가 생겼을 때**. 그 순간부터 편집기 캔버스의 그 영역은
|
||||
빈 화면이 되고, 실제 화면은 정상 동작하므로 오류도 경고도 남지 않습니다.
|
||||
|
||||
신설 절차는 확장 루트에 `editor-spec.json` 을 만들고 `sampleData.byDataSourceId` 에
|
||||
그 ID 를 넣는 것으로 시작합니다. 팔레트·컨트롤은 템플릿의 일이므로 넣지 않습니다.
|
||||
파일을 만든 뒤 update 커맨드로 활성 디렉토리에 반영해야 편집기가 읽습니다 —
|
||||
`_bundled` 폴백이 없습니다.
|
||||
<!-- @intent END -->
|
||||
@@ -134,6 +134,7 @@ Cache 에 stash(`inicis:pending_record:{key}`) → 회원가입 완료 → `core
|
||||
- [ ] PII 컬럼(이름·생년월일·성별·CI/DI 등)을 다루는 코드 변경 시 GDPR 삭제/탈퇴 정리 리스너(`CleanInicisRecordOnUserDelete`/`CleanInicisRecordOnUserWithdraw`)가 여전히 그 컬럼을 정리하는지 확인
|
||||
- [ ] 팝업 기반 인증 흐름(`startAuth`)을 고칠 때 `window.open`을 사용자 클릭 컨텍스트 밖으로 옮기지 않는다 — Chrome popup blocker 회피가 깨진다
|
||||
- [ ] `duplicate_field`/`duplicate_block_enabled` 로직을 고치면 `InicisDuplicateField` Enum 과 `AssertNoDuplicateInicisIdentity`를 함께 갱신
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-verification_kginicis --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -182,6 +183,7 @@ cd plugins/_bundled/sirsoft-verification_kginicis && powershell -Command "npm ru
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.4] - 2026-08-22
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# KG이니시스 본인인증
|
||||
|
||||
**G7 플러그인 · sirsoft-verification_kginicis**
|
||||
KG이니시스 통합인증의 본인확인(reqSvcCd=03)을 G7 코어 IDV 인프라에 Provider 로 등록하는 플러그인
|
||||
**그누보드7 플러그인 · sirsoft-verification_kginicis**
|
||||
KG이니시스 통합인증의 본인확인(reqSvcCd=03)을 그누보드7 코어 IDV 인프라에 Provider 로 등록하는 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.5-0066FF?style=flat-square" alt="version 1.0.5">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.8-1F883D?style=flat-square" alt="G7 >=7.0.8">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.8-1F883D?style=flat-square" alt="그누보드7 >=7.0.8">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -21,7 +21,7 @@ KG이니시스 통합인증의 본인확인(reqSvcCd=03)을 G7 코어 IDV 인프
|
||||
## 소개
|
||||
|
||||
<!-- @intent START -->
|
||||
KG이니시스 본인확인(휴대폰 인증, reqSvcCd=03)을 G7 코어의 본인인증(IDV) 체계에 연결하는
|
||||
KG이니시스 본인확인(휴대폰 인증, reqSvcCd=03)을 그누보드7 코어의 본인인증(IDV) 체계에 연결하는
|
||||
플러그인입니다. 코어가 정의한 표준 인터페이스를 구현해, 회원가입·비밀번호 찾기·민감작업
|
||||
등 코어가 IDV 를 요구하는 모든 지점에서 이메일 인증 대신 이니시스 팝업이 동작하게
|
||||
합니다.
|
||||
@@ -70,7 +70,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.8` |
|
||||
| 그누보드7 코어 | `>=7.0.8` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -158,6 +158,7 @@ provider 를 쓸지 선택합니다.
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# KG이니시스 본인인증 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-verification_kginicis/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> KG이니시스 본인인증 플러그인 레이아웃 편집기 샘플 데이터.
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
KG이니시스 통합인증 Provider 플러그인은 코어 IDV 인프라에 자기 Provider 를 등록하는 것이 본체이고,
|
||||
화면은 관리자 설정과 **사용자 화면에 뜨는 인증 창**입니다. 그래서 스펙이 담는 것도 그
|
||||
두 자리뿐입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 2 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`byDataSourceId` 는 `inicisRecord` 하나입니다 — 인증 결과 레코드를 화면에 보여 주는 자리입니다.
|
||||
인증 정책·목적·메시지는 코어 IDV 가 소유하고 admin 템플릿 스펙이 그 샘플을 채우므로
|
||||
여기서 다시 선언하지 않습니다.
|
||||
|
||||
`states.groups` 가 2종인 것이 이 플러그인의 특징입니다. 인증은 **여러 화면에 걸쳐
|
||||
나타나는 기능**이라 설정 화면 하나로 끝나지 않습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `inicisRecord` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 2 | `*/admin/plugins/sirsoft-verification_kginicis/settings` · `_user_base` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
상태 범위는 `*/admin/plugins/sirsoft-verification_kginicis/settings` 와 `_user_base` 입니다. `_user_base` 가 들어 있는 것이 핵심입니다 — 본인인증
|
||||
요구는 특정 라우트가 아니라 **어느 화면에서든 428 응답으로 발생**할 수 있고, 그때 뜨는
|
||||
인증 창은 베이스 레이아웃 위에 얹힙니다.
|
||||
|
||||
편집기 캔버스는 실제 428 응답을 받지 않으므로, 그 상태를 변종으로 주입해 두지 않으면
|
||||
인증 창이 화면에 나타나지 않아 **편집할 방법이 없습니다.**
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-verification_kginicis --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
인증 창의 모양을 바꿨다면 이 스펙만으로는 끝나지 않습니다. 인증 창을 여는 주체는
|
||||
템플릿 부트스트랩이 등록한 launcher 이므로, launcher 가 여는 화면과 여기 상태 변종이
|
||||
가리키는 화면이 같은지 함께 확인합니다.
|
||||
<!-- @intent END -->
|
||||
@@ -132,6 +132,7 @@ FormRequest 검증과 Service 저장이 서로 다른 파이프라인 단계이
|
||||
- [ ] 데스크톱/모바일 분기(`isMobileEnvironment()`)를 고칠 때 양쪽 복귀 경로(팝업 종료 감지 / `redirectStash` 복원)를 함께 테스트
|
||||
- [ ] `duplicate_field`/`duplicate_block_enabled` 로직을 고치면 `KcpDuplicateField` Enum 과 `AssertNoDuplicateKcpIdentity`를 함께 갱신
|
||||
- [ ] `normalizeLiveSiteCd()`/`addLiveModeRules()`를 고칠 때 두 훅(`filter_save_data`/`update_validation_rules`)의 실행 순서(검증 먼저, 정규화는 저장 시점)를 유지
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 의 동반 의무 표를 따라 `editor-spec.json` 을 함께 갱신 — 샘플이 없는 `data_source` 는 편집기 캔버스에서만 빈 화면이 되고 실제 화면은 정상이라 오류도 경고도 남지 않는다. 반영은 `php artisan plugin:update sirsoft-verification_nhnkcp --force`
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -184,6 +185,7 @@ npx playwright test plugins/_bundled/sirsoft-verification_nhnkcp/tests/Playwrigh
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
### Added
|
||||
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [1.0.1] - 2026-08-19
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# NHN KCP 휴대폰 본인확인
|
||||
|
||||
**G7 플러그인 · sirsoft-verification_nhnkcp**
|
||||
NHN KCP 휴대폰 본인확인(V2 REST)을 G7 코어 IDV 인프라에 Provider 로 등록하는 플러그인
|
||||
**그누보드7 플러그인 · sirsoft-verification_nhnkcp**
|
||||
NHN KCP 휴대폰 본인확인(V2 REST)을 그누보드7 코어 IDV 인프라에 Provider 로 등록하는 플러그인
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.0.2-0066FF?style=flat-square" alt="version 1.0.2">
|
||||
<img src="https://img.shields.io/badge/type-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-555555?style=flat-square" alt="type 플러그인">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.6-1F883D?style=flat-square" alt="G7 >=7.0.6">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.6-1F883D?style=flat-square" alt="그누보드7 >=7.0.6">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -21,7 +21,7 @@ NHN KCP 휴대폰 본인확인(V2 REST)을 G7 코어 IDV 인프라에 Provider
|
||||
## 소개
|
||||
|
||||
<!-- @intent START -->
|
||||
NHN KCP 휴대폰 본인확인을 G7 코어의 본인인증(IDV) 체계에 연결하는 플러그인입니다.
|
||||
NHN KCP 휴대폰 본인확인을 그누보드7 코어의 본인인증(IDV) 체계에 연결하는 플러그인입니다.
|
||||
`sirsoft-verification_kginicis`와 같은 부류(코어 표준 인터페이스 구현)지만 다른 벤더이며,
|
||||
운영자는 둘 중 하나 또는 둘 다 설치해 사용할 수 있습니다.
|
||||
|
||||
@@ -73,7 +73,7 @@ flowchart LR
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.6` |
|
||||
| 그누보드7 코어 | `>=7.0.6` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -161,6 +161,7 @@ php artisan plugin:update sirsoft-verification_nhnkcp --force
|
||||
| [docs/data-model.md](docs/data-model.md) | 모델·소유 테이블·마이그레이션·Enum | ✅ |
|
||||
| [docs/settings.md](docs/settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 | ✅ |
|
||||
| [docs/frontend.md](docs/frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [docs/api/](docs/api/README.md) | API 레퍼런스 (엔드포인트별 파라미터·응답 필드) | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [data-model.md](data-model.md) | 모델·소유 테이블·마이그레이션·Enum |
|
||||
| [settings.md](settings.md) | 설정 스키마·권한·메뉴·라우트·의존 관계 |
|
||||
| [frontend.md](frontend.md) | 레이아웃·액션 핸들러·전역 진입점·에셋 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [api/](api/README.md) | API 레퍼런스 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# NHN KCP 휴대폰 본인확인 — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| manifest | `plugins/_bundled/sirsoft-verification_nhnkcp/editor-spec.json` |
|
||||
| 형태 | 단일 파일 (인라인) |
|
||||
| 스펙 버전 | `1.0.0` |
|
||||
| 스타일 시스템 | - |
|
||||
| 다크 모드 전략 | - |
|
||||
|
||||
> NHN KCP 휴대폰 본인확인 플러그인 레이아웃 편집기 샘플 데이터.
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
NHN KCP 휴대폰 본인확인 Provider 플러그인은 코어 IDV 인프라에 자기 Provider 를 등록하는 것이 본체이고,
|
||||
화면은 관리자 설정과 **사용자 화면에 뜨는 인증 창**입니다. 그래서 스펙이 담는 것도 그
|
||||
두 자리뿐입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 블록 | 역할 | 항목 수 | 출처 |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `editor-spec.json (인라인)` |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 3 | `editor-spec.json (인라인)` |
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
`byDataSourceId` 는 `nhnkcpRecord` 하나입니다 — 인증 결과 레코드를 화면에 보여 주는 자리입니다.
|
||||
인증 정책·목적·메시지는 코어 IDV 가 소유하고 admin 템플릿 스펙이 그 샘플을 채우므로
|
||||
여기서 다시 선언하지 않습니다.
|
||||
|
||||
`states.groups` 가 3종인 것이 이 플러그인의 특징입니다. 인증은 **여러 화면에 걸쳐
|
||||
나타나는 기능**이라 설정 화면 하나로 끝나지 않습니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 `componentPalette` 를 선언하지 않습니다 — 편집기 팔레트에 추가되는 항목이 없습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일입니다. 모듈·플러그인은 레이아웃 JSON 에서 템플릿이
|
||||
제공하는 컴포넌트를 **쓰기만** 하므로, 편집기 팔레트에 새로 얹을 것이 없습니다. 그래서 이
|
||||
확장의 스펙은 `componentPalette`·`controls`·`componentCapabilities`·`nesting` 을 비우고
|
||||
**도메인 데이터**(`sampleData`·`states`)만 담습니다.
|
||||
|
||||
팔레트에 무언가를 추가하고 싶다면 그것은 이 확장이 아니라 활성 템플릿
|
||||
(`sirsoft-admin_basic` / `sirsoft-basic`)의 스펙에 가야 합니다. 여기에 팔레트를 선언하면
|
||||
템플릿 선언과 같은 자리를 두고 다투게 되고, 어느 쪽이 이기는지가 합본 순서에 좌우됩니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 자리 | 역할 | 개수 | ID |
|
||||
|---|---|---|---|
|
||||
| `sampleData.byDataSourceId` | 레이아웃 `data_sources` ID 로 붙는 프리뷰 응답 | 1 | `nhnkcpRecord` |
|
||||
| `sampleData.byEndpointPattern` | 엔드포인트 패턴으로 붙는 프리뷰 응답 | 미선언 | - |
|
||||
| `states.groups` | 상태 변종을 적용할 범위(라우트·베이스 레이아웃) | 3 | `*/mypage/profile` · `*/admin/plugins/sirsoft-verification_nhnkcp/settings` · `_user_base` |
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
상태 범위는 `*/mypage/profile` · `*/admin/plugins/sirsoft-verification_nhnkcp/settings` · `_user_base` 입니다. `_user_base` 가 들어 있는 것이 핵심입니다 — 본인인증
|
||||
요구는 특정 라우트가 아니라 **어느 화면에서든 428 응답으로 발생**할 수 있고, 그때 뜨는
|
||||
인증 창은 베이스 레이아웃 위에 얹힙니다.
|
||||
|
||||
편집기 캔버스는 실제 428 응답을 받지 않으므로, 그 상태를 변종으로 주입해 두지 않으면
|
||||
인증 창이 화면에 나타나지 않아 **편집할 방법이 없습니다.**
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
|
||||
편집기 스펙은 JSON 이므로 빌드가 필요 없습니다. 다만 편집기 서빙은 **활성 디렉토리만** 읽으므로(`_bundled` 폴백 없음) 편집 후 반드시 반영합니다:
|
||||
|
||||
```bash
|
||||
php artisan plugin:update sirsoft-verification_nhnkcp --force
|
||||
```
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
위 표는 "무엇을 함께 고치는가" 만 말합니다. 실제로 놓치는 자리는 **반영 절차**입니다 —
|
||||
편집기가 읽는 것은 활성 디렉토리이고 `_bundled` 폴백이 없으므로, `_bundled` 에서 스펙을
|
||||
고치고 update 커맨드를 돌리지 않으면 편집기에는 **직전 내용이 그대로 보입니다.** 파일은
|
||||
고쳤는데 화면이 안 바뀌었다면 거의 이 경우입니다.
|
||||
|
||||
또 하나는 검증 시점입니다. 편집기 스펙은 스키마 검증을 통과해도 "레이아웃이 실제로 쓰는
|
||||
ID 와 맞는가" 는 확인해 주지 않습니다. 그 어긋남은 편집기 캔버스에서만 빈 화면으로
|
||||
나타나고 실제 화면은 정상이므로, 위 "샘플 데이터와 페이지 상태" 절의 미커버 목록이 유일한
|
||||
통로입니다.
|
||||
|
||||
인증 창의 모양을 바꿨다면 이 스펙만으로는 끝나지 않습니다. 인증 창을 여는 주체는
|
||||
템플릿 부트스트랩이 등록한 launcher 이므로, launcher 가 여는 화면과 여기 상태 변종이
|
||||
가리키는 화면이 같은지 함께 확인합니다.
|
||||
<!-- @intent END -->
|
||||
@@ -116,6 +116,7 @@ SSoT 는 코어 `config/template.php` 의 `required_admin_components` 입니다.
|
||||
- [ ] TSX 를 고쳤다면 `template:build --production` 후 `dist/` 동반 커밋 (`sourceMappingURL` 잔존 금지)
|
||||
- [ ] `docs/extension/sample-extensions.md` 의 계층 표와 어긋나지 않는지 확인
|
||||
- [ ] 레이아웃 렌더링 테스트(`__tests__/layouts/*.test.tsx`)는 이 템플릿 디렉토리에 둔다 — 코어 디렉토리에 두지 않는다
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어도 되는 상태(공용 ID 만 사용)다. 이 확장만 쓰는 `data_source` 를 새로 붙이는 순간 `editor-spec.json` 신설이 필요해진다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -161,5 +162,6 @@ cd templates/_bundled/gnuboard7-hello_admin_template && powershell -Command "npm
|
||||
| [docs/components.md](docs/components.md) | 템플릿이 제공하는 컴포넌트 | ✅ |
|
||||
| [docs/layouts.md](docs/layouts.md) | 레이아웃 목록과 라우트 매핑 | ✅ |
|
||||
| [docs/handlers.md](docs/handlers.md) | 템플릿 전용 핸들러와 부트스트랩 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
- 아이콘(Font Awesome)을 템플릿에 함께 담아 외부 CDN 없이 동작합니다.
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [0.1.0] - 2026-07-01
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Hello Admin Template
|
||||
|
||||
**G7 템플릿 · gnuboard7-hello_admin_template**
|
||||
**그누보드7 템플릿 · gnuboard7-hello_admin_template**
|
||||
그누보드7 학습용 최소 Admin 템플릿 스켈레톤
|
||||
|
||||
<!-- @generated:badges START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-0.1.1-0066FF?style=flat-square" alt="version 0.1.1">
|
||||
<img src="https://img.shields.io/badge/type-%ED%85%9C%ED%94%8C%EB%A6%BF-555555?style=flat-square" alt="type 템플릿">
|
||||
<img src="https://img.shields.io/badge/G7-%3E%3D7.0.10-1F883D?style=flat-square" alt="G7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C7-%3E%3D7.0.10-1F883D?style=flat-square" alt="그누보드7 >=7.0.10">
|
||||
<img src="https://img.shields.io/badge/license-MIT-8250DF?style=flat-square" alt="license MIT">
|
||||
</p>
|
||||
<!-- @generated:badges END -->
|
||||
@@ -72,7 +72,7 @@ flowchart TD
|
||||
<!-- @generated:requirements START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| G7 코어 | `>=7.0.10` |
|
||||
| 그누보드7 코어 | `>=7.0.10` |
|
||||
| PHP | `^8.2` |
|
||||
<!-- @generated:requirements END -->
|
||||
|
||||
@@ -156,6 +156,7 @@ php artisan template:activate gnuboard7-hello_admin_template
|
||||
| [docs/components.md](docs/components.md) | 템플릿이 제공하는 컴포넌트 | ✅ |
|
||||
| [docs/layouts.md](docs/layouts.md) | 레이아웃 목록과 라우트 매핑 | ✅ |
|
||||
| [docs/handlers.md](docs/handlers.md) | 템플릿 전용 핸들러와 부트스트랩 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| [components.md](components.md) | 템플릿이 제공하는 컴포넌트 |
|
||||
| [layouts.md](layouts.md) | 레이아웃 목록과 라우트 매핑 |
|
||||
| [handlers.md](handlers.md) | 템플릿 전용 핸들러와 부트스트랩 |
|
||||
| [editor-spec.md](editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 |
|
||||
| [../AGENTS.md](../AGENTS.md) | 에이전트·확장개발자 진입점 |
|
||||
| [../README.md](../README.md) | 사람(도입검토자·운영자) 진입점 |
|
||||
<!-- @generated:doc-toc END -->
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Hello Admin Template — 레이아웃 편집기 스펙
|
||||
|
||||
> 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 · 진입점: [AGENTS.md](../AGENTS.md)
|
||||
|
||||
## 선언 요약
|
||||
|
||||
<!-- @generated:editor-spec-summary START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙(`editor-spec.json`)을 두지 않습니다. 편집기는 코어 기본 팔레트와 활성 템플릿의 스펙만으로 이 확장의 화면을 다룹니다._
|
||||
<!-- @generated:editor-spec-summary END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
학습용 관리자 템플릿이라 편집기 스펙을 두지 않았습니다. 이 템플릿의 목적은 관리자
|
||||
템플릿의 최소 구조(레이아웃·라우트·베이스 레이아웃)를 보여 주는 것이고, 편집기 스펙은
|
||||
그 위에 얹히는 별개 축입니다.
|
||||
|
||||
실제 관리자 템플릿이 스펙으로 무엇을 선언하는지는 `sirsoft-admin_basic` 의 같은 문서를
|
||||
봅니다 — 팔레트 79 · 컨트롤 303 · 역량 86 이 그 규모입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 선언 블록
|
||||
|
||||
<!-- @generated:editor-spec-blocks START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_선언된 편집기 스펙 블록이 없습니다._
|
||||
<!-- @generated:editor-spec-blocks END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
선언한 블록이 없습니다. 팔레트·컨트롤·역량·중첩을 선언하지 않았으므로 이 템플릿을
|
||||
활성화한 상태에서는 편집기가 다룰 컴포넌트 어휘가 없습니다.
|
||||
|
||||
이것이 "템플릿이 편집기 스펙을 갖는다" 는 규율의 의미입니다 — 스펙은 편집기의 부가
|
||||
기능이 아니라 **편집기가 그 템플릿에서 동작하기 위한 어휘 자체**입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 컴포넌트 팔레트
|
||||
|
||||
<!-- @generated:editor-spec-palette START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 팔레트에 항목을 추가하지 않습니다._
|
||||
<!-- @generated:editor-spec-palette END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
컴포넌트를 만드는 것은 템플릿의 일이므로, 이 확장이 팔레트에 얹을 것은 원래 없습니다.
|
||||
편집기 팔레트는 활성 템플릿의 스펙이 정합니다 — 이 확장에 편집기 스펙이 생기더라도
|
||||
`componentPalette` 는 여전히 비어 있을 것입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 샘플 데이터와 페이지 상태
|
||||
|
||||
<!-- @generated:editor-spec-samples START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 편집기 스펙을 두지 않아 선언된 샘플 데이터·페이지 상태가 없습니다._
|
||||
|
||||
_이 확장 레이아웃의 `data_source` 는 전부 프리뷰 샘플이 붙습니다 (이 확장 또는 번들 템플릿 스펙이 커버)._
|
||||
<!-- @generated:editor-spec-samples END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
미커버가 없습니다. 이 템플릿의 레이아웃에는 `data_source` 자체가 없기 때문입니다 —
|
||||
정적 화면만으로 구성된 학습용 골격이라 붙일 데이터가 없습니다.
|
||||
|
||||
미커버 0 이 곧 "편집기에서 온전히 보인다" 를 뜻하지는 않습니다. 여기서는 그릴 데이터가
|
||||
애초에 없다는 뜻입니다.
|
||||
<!-- @intent END -->
|
||||
|
||||
## 수정 시 동반 의무
|
||||
|
||||
<!-- @generated:editor-spec-obligations START — ext:docgen 이 갱신. 이 블록 안은 직접 수정하지 않는다 -->
|
||||
_이 확장은 아직 편집기 스펙을 두지 않습니다. 아래 변경이 생기면 `editor-spec.json` 을 신설합니다._
|
||||
|
||||
| 이런 변경을 했다면 | 편집기 스펙에서 함께 할 일 |
|
||||
|---|---|
|
||||
| 컴포넌트를 새로 만들었다 | `componentPalette` 에 항목 추가 · `componentCapabilities` 에 편집 역량 선언 · `nesting` 에 담길 자리 규정 |
|
||||
| 레이아웃에 `data_sources` 를 추가했다 | `sampleData` 에 같은 ID 로 프리뷰 응답 추가 (없으면 편집기 캔버스만 빈 화면) |
|
||||
| `_global.*` 을 새로 읽는다 | `sampleGlobal` 에 baseline 값 추가 |
|
||||
| 빈 목록·오류 같은 화면 변종을 추가했다 | `states` 에 변종 추가 · `stateLabels` 에 친화 명칭 |
|
||||
| 새 액션·조건 패턴을 도입했다 | `actionRecipes` / `conditionRecipes` 에 친화 명칭 등록 |
|
||||
<!-- @generated:editor-spec-obligations END -->
|
||||
|
||||
<!-- @intent START -->
|
||||
이 템플릿을 실제 사용 템플릿으로 발전시킨다면 편집기 스펙 신설이 **가장 먼저** 필요한
|
||||
작업 중 하나입니다. 템플릿의 스펙은 모듈·플러그인과 달리 도메인 데이터가 아니라
|
||||
**컴포넌트 어휘**(팔레트·컨트롤·역량·중첩)를 담기 때문입니다.
|
||||
|
||||
신설 순서는 `componentPalette` → `nesting` → `componentCapabilities` → `controls`
|
||||
입니다. 앞의 둘이 없으면 편집기에서 컴포넌트를 놓을 수조차 없고, 뒤의 둘은 놓은 다음에
|
||||
속성을 바꾸기 위한 것입니다. `sirsoft-admin_basic/editor-spec/` 의 13개 블록 파일이
|
||||
완성된 형태의 선례입니다.
|
||||
<!-- @intent END -->
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
**홈 화면이 이 샘플의 핵심**입니다. `data_sources` 로 학습용 모듈의 메모 API
|
||||
(`/api/modules/gnuboard7-hello_module/memos`)를 호출해 목록을 그립니다 — 모듈이 데이터를,
|
||||
템플릿이 화면을 담당하는 G7 의 기본 경계를 가장 짧게 보여주는 예시입니다. 실제 게시판·이커머스
|
||||
템플릿이 화면을 담당하는 그누보드7 의 기본 경계를 가장 짧게 보여주는 예시입니다. 실제 게시판·이커머스
|
||||
모듈도 방문자 화면을 갖지 않고 같은 방식으로 템플릿에 맡깁니다.
|
||||
|
||||
`manifest.hidden = true` 라 관리자 UI 의 템플릿 목록에 나타나지 않습니다. artisan CLI 로는
|
||||
@@ -117,6 +117,7 @@ User 템플릿은 자기가 그리고 싶은 화면에 필요한 API 를 `data_s
|
||||
- [ ] `manifest.hidden = true` 를 유지 (복제본에서만 제거)
|
||||
- [ ] TSX 를 고쳤다면 `template:build --production` 후 `dist/` 동반 커밋 (`sourceMappingURL` 잔존 금지)
|
||||
- [ ] 레이아웃 렌더링 테스트(`__tests__/layouts/*.test.tsx`)는 이 템플릿 디렉토리에 둔다
|
||||
- [ ] 레이아웃·컴포넌트·`data_source` 를 건드렸다면 [`docs/editor-spec.md`](docs/editor-spec.md) 를 확인 — 이 확장은 편집기 스펙이 없어 `memos` 가 편집기 캔버스에서 빈 화면으로 보인다. `data_source` 를 더 늘리면 그 자리도 같은 상태가 된다
|
||||
|
||||
## 6. 금지 패턴
|
||||
|
||||
@@ -163,5 +164,6 @@ cd templates/_bundled/gnuboard7-hello_user_template && powershell -Command "npm
|
||||
| [docs/components.md](docs/components.md) | 템플릿이 제공하는 컴포넌트 | ✅ |
|
||||
| [docs/layouts.md](docs/layouts.md) | 레이아웃 목록과 라우트 매핑 | ✅ |
|
||||
| [docs/handlers.md](docs/handlers.md) | 템플릿 전용 핸들러와 부트스트랩 | ✅ |
|
||||
| [docs/editor-spec.md](docs/editor-spec.md) | 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 | ✅ |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | 변경 이력 | ✅ |
|
||||
<!-- @generated:docs-index END -->
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
- 아이콘(Font Awesome)을 템플릿에 함께 담아 외부 CDN 없이 동작합니다.
|
||||
- 개발자와 AI 에이전트를 위한 문서를 추가했습니다. 확장 폴더의 `AGENTS.md`(설계 의도·확장점·수정 시 확인할 것)와 `README.md`(도입·운영 안내), `docs/`(상세 문서)로 구성됩니다.
|
||||
- 확장 문서에 「레이아웃 편집기 스펙」 항목을 추가했습니다. 이 확장이 레이아웃 편집기에 무엇을 선언했는지와, 화면 요소나 데이터를 추가할 때 편집기 쪽에서 함께 해야 할 일을 담습니다.
|
||||
- 문서의 제품 표기를 「그누보드7」로 통일했습니다.
|
||||
|
||||
## [0.1.0] - 2026-07-01
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user