Resource Transfer
Resource 내보내기 소스를 공개하고 App 소유 가져오기에 검증된 recipe 또는 pipeline을 선택합니다.
SDK transfer 계약과 공통 host 작업 공간으로 App Resource의 가져오기·내보내기를 연결합니다.
App은 공통 Import·Export 기반을 그대로 사용할 수 있습니다. Core는 파일 형식·업로드·mapping·preview·orchestration·polling·공통 UI를 제공하고 선언된 권한을 적용합니다. App은 schema·권한 선언·scope filtering·export rows·업무 검증·저장·retry 규칙을 소유합니다. Core의 공통 gate가 App의 행 가시성이나 업무 저장 권한을 대신하지 않습니다.
전송 동작 연결
내보내기는 ResourceTransferExportSourceContribution에 TransferSchema, 범위, 필수 권한, App 소유 source 클래스를 선언합니다. 실제 조회는 허가된 레코드만 선택하고 보호 필드를 가려야 합니다. 아래 People Operating Unit 사례가 이 판단을 App 안에 두는 방식입니다.
가져오기는 선언형 행 처리라면 recipe, 별도 분석·미리보기·실행이 필요하면 ResourceImportPipelineContribution을 선택합니다. 화면에 동작을 노출하기 전에 권한과 업로드·스키마 제한을 정하세요. 미리보기에 성공했더라도 실행 시점과 각 업무 변경에서 다시 권한을 검사합니다.
결과 확인
허가된 사용자는 지정 범위만 내보내거나 유효한 파일을 미리 보고 적용할 수 있습니다. 잘못된 입력, 접근 불가 레코드, 중복 실행은 거절하거나 안전하게 재처리해야 합니다. 패키지 검사는 App 테스트하기를 따릅니다.
내보내기·가져오기·백그라운드 실행
Resource Transfer는 검토 가능한 export와 import를 위한 플랫폼 경로입니다. App은 export source를 선언하고, 일반 Resource 생성이 올바른 write boundary가 아닐 때 import recipe 또는 pipeline을 선택합니다. 호스트가 파일 처리와 orchestration을 소유하고, 선언된 source나 pipeline은 App의 인가와 도메인 규칙을 유지합니다.
packages/people/src/Contribution/OperatingUnitWorkerExportContribution.php는
실제 export-only projection을 다음처럼 공개합니다.
use Amuzcorp\Nexia\PeopleCore\Domain\WorkerDirectory\OperatingUnitWorkerDirectoryExportSource;
use Nexia\ResourceTransfer\Contracts\ResourceTransferDefinition;
use Nexia\ResourceTransfer\ResourceTransferExportSourceDefinition;
use Nexia\ResourceTransfer\TransferSchema;
new ResourceTransferExportSourceDefinition(
resourceKey: 'people.operating_unit_worker',
labelKey: 'people.operating_unit_worker.resource_label',
sourceClass: OperatingUnitWorkerDirectoryExportSource::class,
schema: TransferSchema::make()
->exportOnly('worker_number', 'people.workforce_export.columns.worker_number')
->exportOnly('display_name', 'people.workforce_export.columns.display_name'),
scope: ResourceTransferDefinition::SCOPE_OPERATING_UNIT,
permissionKeys: [
'people.operating_unit_worker.read',
'people.workforce_export.execute',
],
);위 코드는 생성자 발췌입니다. Manifest contribution 경로의 ResourceTransferExportSourceContribution::resourceTransferExportSources()에서 반환하고, 나열한 권한은 PermissionContribution으로 공개합니다. sourceClass는 아래 row source 계약을 구현합니다. People의 전체 클래스에서 두 interface 연결을 확인하세요.
전체 구조: 각 단계의 소유자 선택
| 단계 | App이 제공 | 호스트가 제공 |
|---|---|---|
| Export | Schema, 권한, 인가된 lazy row, scope filtering, 감사 근거 | 선언된 권한 gate, format, row limit, file response |
| Generic import | Resource schema와 선언된 create action | Mapping, 검토, 승인된 create 호출 |
| Recipe import | Dataset graph와 provider | Resource action을 통한 transactional prerequisite 실행 |
| Pipeline import | Schema, 권한, 검증, batch lifecycle, 업무 저장과 retry 규칙 | Upload, mapping, preview, 확인, orchestration, polling |
Export contribution
ResourceTransferExportSourceDefinition은 resourceKey, labelKey,
sourceClass, schema, scope, permissionKeys를 지정합니다. formats 기본값은
CSV, XLSX, JSON, text이고 rowLimit 기본값은 10,000입니다. 데이터를 표현하지 못하는
format은 제거하고 streaming 비용에 맞춰 row limit을 정하세요. Read를 재사용하지 말고
전용 export permission을 사용합니다.
Source는 정확히 다음 SDK 계약을 구현합니다.
interface ResourceTransferExportSource
{
public function rows(ResourceTransferExportRequest $request): iterable;
public function evidence(ResourceTransferExportRequest $request): array;
}Row는 yield하거나 lazy collection으로 반환합니다. rows() 안에서 request가 전달한
actor의 record visibility와 해석된 Legal Entity 또는 Operating Unit scope를 적용하고,
선택된 column과 유효 row limit을 지키세요. evidence()에는 실제 query shape, filter,
scope를 기록합니다.
Import recipe 또는 pipeline
| 경로 | 선택할 때 | Commit 소유자 |
|---|---|---|
| Generic Resource Transfer | Resource 하나에 안전한 match key와 선언된 create action이 있음 | Core |
ImportRecipeContribution | 파일 하나가 여러 Resource에 걸치지만 App batch state machine은 없음 | 선언된 Resource action을 통한 Core |
ResourceImportPipelineContribution | App이 batch state, tolerance, 고정 근거, retry를 소유 | App pipeline |
Recipe는 Core model class가 아니라 Resource key와 provider 계약을 선언합니다. Core는 각 action을 인가하고 host transaction·ownership gate 안에서 prerequisite graph를 실행합니다.
Pipeline definition은 container에서 해석하는 ResourceImportPipeline을 더합니다.
receive(), validateRows(), apply(), retry()가 App lifecycle을 보존합니다.
ImportBatchState.applyEligible, 고정 normalized row, rejection, 검토 가능한 fill
proposal을 따르세요. ImportSourceProfile은 이름 있는 vendor workbook 형식을
분리합니다. 선택한 profile은 definition-wide column alias를 대체하며 서로 섞이지
않습니다.
큰 workbook은 ChunkedResourceImportPipeline을 구현하고 반복 가능한
ResourceImportRowSource를 chunks()로 처리합니다. 전체 source를 메모리에 만들지
마세요. 제한된 rowDispositions가 row별 이력을 생략해도 정확한
dispositionCounts는 전체 건수를 유지합니다. 기존 pipeline은 validateRows()를
계속 사용할 수 있습니다.
ImportPlan은 고유한 ChoiceOption 값에서 MultipleChoiceDecision을 요청할 수
있습니다. 호스트는 알 수 없거나 중복된 답을 거부하고 허용된 부분집합을 선언 순서로
반환합니다.
인쇄된 값이 다른 Resource를 식별하면 TransferSchema::reference()를 사용합니다.
호스트가 승인 전에 해석하고 pipeline은 자기 Legal Entity 경계에서 식별자를 다시
검사합니다. 제한된 bulk 해석은 ResourceReferences::resolveMany()를 한 번
호출합니다. Dispatcher는 owner의 batch capability 또는 권한 적용 단건 fallback을
사용합니다. 제한 없는 N+1 loop를 만들지 마세요.
사용자 정의 백그라운드 import capability
AuthorizedImportFileStore::claim()은 clean upload를 actor, Legal Entity,
Resource에 결속합니다. find()는 AuthorizedImportFile metadata 권한을 다시
검사하고, withLocalCopy()는 checksum 검증 임시 경로를 제공한 뒤 callback 종료 시
삭제합니다. Host storage 좌표나 전체 파일 bytes는 노출하지 않습니다.
BackgroundOperationStore는 open(), progress(), complete(), fail(),
failIfPending()으로 actor 소유 임시 ticket을 제공합니다. 두 capability 모두
pipeline 인가, idempotency, transaction을 대체하지 않습니다.
선언과 프런트엔드 gate
App package에서 ImportContributionValidator::recipes(), pipelines(),
portfolio()를 실행합니다. Core는 discovery 중 내재 검증을 반복하고 활성 permission,
Resource 소유권, operational App 상태 같은 host 사실을 더합니다.
.nexia/resource-import-coverage.json은 설치된 모든 App을 분류해야 하며
php artisan nexia-resources:validate-resource-import-coverage가 누락과 runtime drift를
거부합니다.
Mapping이 있는 human import는 resourceKey 하나로 NxDatasetImportWorkspace를
mount합니다. Workspace가 upload, mapping, preview, commit, ticket polling을 소유하지만
선택한 recipe나 pipeline의 write authority를 가져가지는 않습니다. Dataset Import는
Agent capability가 아니며 Agent는 공통 data.* 계약을 계속 사용합니다.
공통 프런트엔드 연결
컴포넌트는 @nexia/sdk/host에서, props 타입은 @nexia/sdk에서 가져옵니다.
| 공개 컴포넌트 | 용도 | App이 전달하는 값 |
|---|---|---|
NxResourceTransferActions | 사용 가능한 동작에서 공통 transfer 화면으로 이동 | 인가된 Resource 응답의 transferMeta와 필수 onImported prop (화면 이동에서는 호출하지 않음) |
NxResourceImportDialog | 일반 Resource import의 업로드·preview·실행·상태 확인 dialog | open, onClose, transfer URL과 완료 callback |
NxDatasetImportWorkspace | Recipe 또는 pipeline의 mapping·preview·commit·polling 작업 공간 | resourceKey; 조직 문맥은 호스트에서 읽음 |
NxDatasetExportWorkspace | 기여한 dataset의 metadata 조회, format·column 선택과 export | resourceKey와 허용된 조직 target 문맥 |
서버 metadata와 실행 gate에 따라 사용 가능 여부가 달라집니다. 컴포넌트를 mount한 것만으로 권한이 생기지 않습니다. Props와 관련 registry는 React 컴포넌트와 훅을 참고하세요.
경계
- Export는 일반 list read가 아니라 보호된 bulk action입니다.
- Row limit은 pagination이 아닌 truncation guardrail이며 도달 사실을 알려야 합니다.
- Request에 scope가 있어도 실제 filtering은
rows()안에서 수행합니다. - 감사 근거에는 일반 event label이 아니라 실제 실행한 query를 기록합니다.
- Pipeline은 host upload, 검토, operational gate를 우회할 권한이 아닙니다.
- 저장 mapping은 같은 Resource, header shape, source profile, schema version에만 재사용합니다. Decision, fill, reference, exclusion은 현재 파일 사실입니다.