관리형 Report와 Resource Composition
재사용 가능한 reporting에 App이 공개할 내용과 Core가 Report·Dashboard·Agent query를 하나의 권한 경로로 유지하는 방법을 설명합니다.
관리형 Report와 Resource Composition
App은 자기 Resource 데이터를 재사용 가능하게 만들기 위해 report engine을 새로 만들지 않습니다. App은 설명이 충분하고 actor 권한을 적용한 Resource를 공개합니다. 그러면 Core가 증명할 수 있는 Resource와 relationship만 조합하고, 분석을 Report로 저장한 뒤, 선택한 Report revision을 Dashboard에 배치할 수 있습니다. 이 구조는 업무 규칙과 data policy는 App에 남기고, cross-App planning, revision history, presentation은 Core가 맡게 합니다.
Resource 계약부터 확인하세요. Resource는 active 상태여야 하고, tenant에서 operational이어야 하며, builder에 안전하게 공개되고, 기존 read permission과 row predicate로 보호되어야 합니다. field schema는 어떤 업무 field를 선택·group·aggregate·time bucket할 수 있는지 명시합니다. Core는 database column이나 목록 화면만 보고 이 사실을 추론하지 않습니다.
가장 작은 App contribution 고르기
대부분의 App에는 reporting 전용 코드가 필요 없습니다. Resource descriptor, 번역 label,
resourceListFields(), 표준 authorization을 정확히 유지하면 catalog가 그 선언을 바로
사용합니다.
생성된 Resource module은 UUID 식별자, 검토한 name, timestamp baseline으로 시작합니다.
실제로 존재하고 reporting에 안전한 field만 유지하세요. status enum, reference, 금액,
custom provider는 label·capability·행 권한을 명시적으로 검토한 뒤에만 추가합니다.
App의 일반 Resource query로 표현할 수 없는 custom scope, redaction rule, 안전한
projection이 있을 때만 CompositionQueryContribution을 사용하세요. Authorized provider는
복원된 actor, request, Resource key, 정확한 organization target을 받습니다. 이미 scope를
적용한 query와 App이 승인한 alias·capability를 반환합니다. SQL, browser column name,
다른 App model은 받지 않습니다.
ReportingViewDescriptor는 선택 App metadata로 남습니다. Catalog surface가 App 소유
reporting view를 식별해야 할 때만 AppDescriptorContribution으로 게시하세요. 저장된
Report나 permission grant, 두 번째 query API가 아닙니다. 사용자가 내 Resource로 Report를
만들 수 있다는 이유만으로 추가하지 마세요.
| 필요 | App이 할 일 |
|---|---|
| 일반 Resource를 선택 가능하게 만들기 | 안전한 field capability를 공개하고 기존 read policy를 권한의 기준으로 유지합니다. |
| domain별 scope 또는 redaction read가 필요함 | 해당 Resource에 CompositionQueryContribution을 구현합니다. |
| App 소유 catalog view에 안정된 metadata가 필요함 | 선택적으로 ReportingViewDescriptor를 게시합니다. |
| 재사용 분석, chart, export, schedule, Dashboard 배치가 필요함 | 새 App report contract를 만들지 않습니다. Core가 소유합니다. |
현재 Composition 계약
CompositionSpec은 하나의 현재 schema인 schema_version: 1만 사용합니다. source
alias와 서버가 발급한 relationship key로 제한된 graph를 지정합니다. population은
필수입니다. aggregate는 anchor·union·intersection population을 고르고, row 결과는
row_source도 명시합니다. 모든 relationship은 source, target, 명시적인 match 동작을
가집니다. 이 shape는 typed row·aggregate expression, 중첩 where predicate,
conditional measure, having, comparison, sort, 제한된 limit를 지원합니다.
Payload는 계속 의미적 의도입니다. report는 orders source, 승인된 ordered_at
dimension, count measure를 요청할 수 있지만 orders table, join expression, raw SQL
aggregate는 요청할 수 없습니다. Core는 모든 preview와 실행에서 현재 catalog, 증명된
relationship topology, actor, organization target, field policy, 결과 grain, query budget을
확인합니다. 선언한 type이 정확한 decimal임을 증명할 때 결과는 canonical numeric string으로
반환합니다.
금액 등 정확한 decimal 결과는 Composition query부터 저장된 Report 결과, 표,
Dashboard metric, export까지 canonical numeric string으로 유지합니다. 여기에는
sum, min, max, 비교, 파생 결과가 포함됩니다. JavaScript Number로 변환하지
말고 표시할 때만 문자열을 형식화하며 export에는 wire 값을 그대로 사용하세요.
반올림 없이 그릴 수 없는 chart series는 금액을 바꾸는 대신 해당 제한을 알리고
제외합니다. 일반 integer는 호환성을 위해 JSON number로 유지하지만 JavaScript 안전 범위를
넘는 integer는 같은 canonical numeric string으로 반환합니다. App provider는 원화 금액을
meta.integer_encoding: canonical_numeric_string_when_unsafe로 표시합니다. App provider는 원화 금액을
numeric field로 공개하고 Core가 집계하게 해야 하며, 미리 반올림하거나 별도의 문자열 전용
금액 field를 공개하면 안 됩니다.
정확한 syntax의 기준은 PHP DTO와 대응 frontend validator입니다. 저장 definition은 이
하나의 shape를 사용하며 App은 별도 report wire를 게시하지 않습니다. Catalog에는 허가된 각
Resource의 app_key와 현지화된 app_label이 포함되며 App filter는 organization scope를
넓히지 않습니다.
Report, Dashboard, Agent의 역할은 다릅니다
Report는 immutable revision, sharing, current revision을 가진 재사용 저장
definition입니다. edit 또는 restore는 새 revision을 만듭니다. Dashboard는 layout을
소유하고 특정 reportRef를 고정합니다. Report를 나중에 수정해도 그 배치는 조용히
바뀌지 않습니다. 이전 savedDataWidgetId 배치는 호환을 위해 복사 snapshot으로 남습니다.
Dashboard common filter의 값은 board-local runtime 상태이며 저장된 Report나 revision을 바꾸지 않습니다. Board에는 최대 10개의 definition과 각 pinned Report에 대한 명시적 mapping을 저장합니다. mapping은 source alias, Resource key, field, 호환되는 하나의 operator로 이루어지거나 reason을 가진 exclusion이어야 합니다. Chart 또는 pivot selection도 같은 정확한 mapping을 통해서만 적용되며 label이 같다고 Report를 연결하지 않습니다. Selection을 지우면 사용자의 일반 filter 값이 복원됩니다. 날짜만 있는 값은 date로 유지하고, datetime input은 browser local time이며 Core가 모든 mapped Report에 같은 ISO instant를 보냅니다.
Report 공유는 다른 actor가 definition을 찾아 쓸 수 있게 할 뿐입니다. source Resource 접근 권한을 주지 않습니다. 모든 Report preview, pinned revision query, export, schedule run은 현재 viewer의 report access, organization target, App lifecycle, source permission, row policy를 적용합니다. source를 사용할 수 없으면 redacted data를 노출하는 대신 unavailable 또는 repair 상태가 됩니다.
Core가 Agent에 Report query를 공개할 때 Agent는 사람 화면과 같은 pinned revision과 planner를 사용합니다. 별도 report registry, permission vocabulary, data grant, App이 정의한 reporting tool은 없습니다. App code는 Resource와 authorization을 계속 공개하고, Core가 모든 caller에 같은 actor authority를 복원·평가합니다.
실제 판단 예시
어떤 App이 operations.order Resource를 공개한다고 가정합니다. status, ordered date,
승인된 amount는 composition field로 안전하지만 internal fraud score는 아닙니다. App은
안전한 field capability를 공개하고 기존 row policy를 유지합니다. 사용자는 order count를
월별로 group하고 approved status로 filter하며 허용된 conditional measure를 더한 Report를 만든 뒤 revision을 Dashboard에 고정할 수 있습니다. App은 report table,
Dashboard endpoint, Agent tool을 만들지 않습니다.
나중에 일반 Resource query로 안전하게 표현할 수 없는 regional projection이 필요해지면 App은 authorized composition provider 하나를 추가합니다. Provider도 다른 proven source와 조합하기 전에 App policy를 적용합니다. 그것이 extension point입니다. chart를 만들기 위해 Core planner를 복사하거나 policy를 느슨하게 만들면 안 됩니다.
관련 문서
- Resource 계약 — Resource와 선택 authorized provider를 공개합니다.
- Contribution 계약 — descriptor metadata를 게시합니다.
- Dashboard Widget 추가 — 공용 builder보다 bespoke App widget이 맞을 때 선택합니다.