예제
전자 서명에 기여하기
Workshop Note 이름을 전자 서명 Template 편집기에 나타나는 App 소유 데이터 소스로 게시합니다.
예제 유형 레시피
개요
전자 서명에 기여하기 예제
이 예제는 같은 Workshop Note의 이름을 전자 서명 문서에 자동 입력할 수 있는
App 데이터 필드로 게시합니다. 다른 App의 근로계약이나 자산을 끌어오지 않고,
first-app → resource-generator → contributor → descriptor에서 만든 Workshop
흐름을 그대로 이어갑니다.
이 작업이 끝나면
| 생기는 것 | 확인 위치 |
|---|---|
NoteFieldsV1SignatureDocumentDataSource | Workshop의 정적 서명 데이터 계약 |
| fail-closed Provider skeleton | 실제 Note 값을 재인가·조회할 App 구현 위치 |
노트 이름 자동 입력 필드 | 전자 서명 → 전자 서명 양식 → 양식 만들기 → 서명자 → 필드 추가 |

위 화면은 생성된 descriptor와 locale를 적용한 실제 결과입니다. 필드 추가에서
App: Workshop, 리소스: Workshop 노트, 필드: 노트 이름을 선택할 수 있습니다.
Example NoteFieldsV1 value는 양식 작성 중에만 쓰는 synthetic sample입니다.
어떤 계약을 구현하고 Core가 어떻게 받는가
Generator는 상속용 Core 클래스를 만들지 않고 서로 역할이 다른 두 SDK 계약을 구현합니다.
| 생성 클래스 | 구현하는 SDK 계약 | 역할 |
|---|---|---|
NoteFieldsV1SignatureDocumentDataSource | SignatureDocumentDataSourceContribution | 정적 descriptor와 runtime Provider를 한 소유자로 게시 |
NoteFieldsV1SignatureDocumentDataSourceProvider | SignatureDocumentDataSourceProvider | actor·subject가 포함된 query를 받아 실제 Note 값을 재인가·조회 |
SignatureDocumentDataSourceContribution은 AppDescriptorContribution을 확장하므로
생성 클래스는 appDescriptors()와 signatureDocumentDataSourceProviders()를 모두
제공합니다. Core는 container로 contribution을 만들기 때문에 생성자에 주입된
Provider도 받을 수 있습니다.
NexiaContributionRegistry가 SignatureDocumentDataSourceContribution 발견
├─ AppDescriptorCatalog가 appDescriptors()의 정적 source/field 계약 수집
└─ SignatureDocumentDataSourceRegistry가 Provider 목록 수집
→ appKey + sourceKey + sourceVersion과 기여 소유권이 정확히 같은지 pairing
├─ 양식 작성: CatalogController가 정적 descriptor와 synthetic sample 전달
└─ 요청 준비: RuntimeExecutor가 tenant context와 계약을 검사한 뒤
Provider::resolve(query)를 제한 시간 안에 호출하고 결과를 검증
따라서 필드가 화면에 보이는 것과 실제 Note 값이 채워지는 것은 다른 단계입니다. 정적 descriptor만으로 authoring 선택지는 만들 수 있지만, runtime Provider가 fail-closed 상태라면 실제 요청 준비에서는 값을 내보내지 않습니다.
1. 생성 결과 미리 보기
포함된 스크립트는 기본적으로 dry run입니다.
sh docs/developers/examples/electronic-signature-contribution/commands.sh두 파일의 WOULD CREATE가 보여야 합니다.
packages/workshop/src/Descriptors/NoteFieldsV1SignatureDocumentDataSource.php
packages/workshop/src/Signature/NoteFieldsV1SignatureDocumentDataSourceProvider.php
검토한 뒤에만 실제 파일을 생성합니다.
WRITE=1 sh docs/developers/examples/electronic-signature-contribution/commands.sh이 명령은 subject와 source를 모두 workshop.note로 지정하고, direct-subject
lookup으로 하나의 name 문자열을 제공하는 계약을 만듭니다. Generator는 모델이나
DB column을 추론하지 않습니다.
2. locale 추가
Workshop이 소유한 모든 locale에 같은 key를 추가합니다.
{
"workshop.signature_document_data.fields.name": "노트 이름",
"workshop.signature_document_data.note_fields_v1.label": "Workshop 노트",
"workshop.signature_document_data.note_fields_v1.description": "전자 서명 문서에 연결된 노트 정보를 제공합니다."
}Descriptor의 syntheticSample은 정적 catalog 미리 보기용입니다. 여기에는 실데이터나
민감정보를 넣지 않습니다.
3. Provider 구현
생성된 Provider는 의도적으로 Unavailable을 반환합니다. 파일 생성만으로 실제 Note
값이 노출되어서는 안 됩니다. 다음을 모두 구현한 뒤 활성화합니다.
- Core가 복원한 현재 tenant, Legal Entity, actor 확인
- 요청의
workshop.notesubjectResourceRef재인가 workshop.note.read권한과 레코드별 Policy 확인- 요청 field allowlist와
asOf적용 - bounded lookup과 timeout
- 결과 provenance 및 진단에서 값 제거
Provider는 Note 모델을 App 안에서 조회할 수 있지만, Core 모델이나 다른 App 모델을 import하지 않습니다.
4. 화면과 runtime 차이
필드 추가 dialog는 정적 descriptor와 synthetic sample만 읽으므로 Provider가 아직 fail-closed여도 위 선택지는 보입니다. 실제 값 조회는 전자 서명 요청을 준비할 때 subject와 actor를 복원한 뒤에만 실행됩니다.
양식 작성 시 요청 준비 시
정적 Descriptor catalog Runtime Provider
└─ Workshop 노트 └─ actor·subject 재인가
└─ 노트 이름 · 예제 값 └─ 허용된 Note 이름 반환
5. 갱신과 확인
task artisan -- nexia-runtime:refresh-runtime-caches --if-route-cached --no-ansi
task artisan -- nexia-apps:validate-app-descriptors workshop --no-ansi
task artisan -- nexia-apps:doctor-package-app workshop --no-ansiDoctor에서 paired Signature document data source가 확인되면 위 캡처 경로에서 필드 추가를 열어 Workshop을 선택합니다.
자주 발생하는 오류
| 증상 | 원인 | 해결 | 다시 확인 |
|---|---|---|---|
명령이 WOULD CREATE만 출력하고 파일이 생기지 않음 | Generator와 예제 스크립트의 기본 동작은 dry run임 | 출력을 검토한 뒤 WRITE=1 sh docs/developers/examples/electronic-signature-contribution/commands.sh 실행 | 두 대상 파일이 packages/workshop/src/ 아래에 존재 |
| validator가 locale catalog 누락을 보고함 | 생성된 descriptor의 label·description·field key를 Workshop locale에 추가하지 않음 | 모든 resources/lang/{locale}.json에 세 key 추가 | validator가 locale finding 없이 완료 |
| 필드 추가에는 보이지만 요청 준비에서 값이 나오지 않음 | 생성된 Provider가 의도적으로 Unavailable을 반환하는 fail-closed skeleton임 | actor·subject 재인가, field allowlist, bounded lookup과 provenance를 구현 | 요청 준비에서 허용된 Note 이름과 provenance가 반환됨 |
| Doctor가 paired data source를 찾지 못함 | 정적 descriptor와 Provider의 app key, source key 또는 version이 다름 | 양쪽 identity를 같은 workshop source key와 version으로 맞추고 cache 갱신 | Doctor가 paired Signature document data source를 보고 |
수정 규칙
| 변경 | 처리 |
|---|---|
| 표시 문구 | locale 값만 수정하고 version 유지 |
| optional field 추가 | consumer 호환성을 확인하고 version 검토 |
| type, requiredness, lookup 의미 변경 | 새 source version 게시 후 consumer 이동 |
| source 제거 | 참조 중인 양식·요청을 확인한 뒤 lifecycle 전환 |
포함된 파일
#!/usr/bin/env sh
set -eu
task artisan -- nexia-apps:make-package-signature-data-source workshop NoteFieldsV1 \
--subject-resource-key=workshop.note \
--source-resource-key=workshop.note \
--lookup-mode=direct_subject \
--cardinality=one \
--min-items=1 \
--max-items=1 \
--field-key=name \
--field-type=string \
--field-classification=internal \
--field-formatter=plain
if [ "${WRITE:-0}" != '1' ]; then
printf '%s\n' 'Dry run complete. Rerun with WRITE=1 to create the two fail-closed skeletons.'
exit 0
fi
task artisan -- nexia-apps:make-package-signature-data-source workshop NoteFieldsV1 \
--subject-resource-key=workshop.note \
--source-resource-key=workshop.note \
--lookup-mode=direct_subject \
--cardinality=one \
--min-items=1 \
--max-items=1 \
--field-key=name \
--field-type=string \
--field-classification=internal \
--field-formatter=plain \
--write
printf '%s\n' 'Skeletons created. Add locale labels and implement authorization and provenance before validation.'