처리 및 모니터링
입력 파일이 업로드된 후, 플랫폼은 처리된 출력물(표준화된 포인트 클라우드, 메시 HLOD, 포토스피어 등)을 생성할 수 있습니다. 이 가이드에서는 처리 옵션을 나열하고, 처리를 시작하고, 모든 출력물이 종료 상태에 도달할 때까지 진행 상황을 폴링하는 방법을 설명합니다.
완료된 업로드 세션이 하나 이상 있는 번들을 보유하고 있으며 read:bundle과 write:bundle 스코프를 가지고 있다고 가정합니다.
작동 방식
섹션 제목: “작동 방식”처리는 실행 후 잊기(fire-and-forget) 방식입니다. API는 처리 작업을 대기열에 넣고 즉시 반환합니다.
sequenceDiagram
participant Client as Client
participant RCAPI as RCAPI
Client->>RCAPI: GET processing/options
RCAPI-->>Client: options + metadata
Client->>RCAPI: POST processing
RCAPI-->>Client: accepted or typed error
loop until all terminal
Client->>RCAPI: GET bundle/processings
RCAPI-->>Client: processing records (status, progress)
end
엔드포인트
섹션 제목: “엔드포인트”| 메서드 | 경로 | 스코프 | 목적 |
|---|---|---|---|
GET | /v1/bundles/{bundleId}/processing/options | read:bundle | 가능한 출력 유형과 메타데이터 나열 |
POST | /v1/bundles/{bundleId}/processing | write:bundle | 요청한 출력물에 대한 처리 시작 |
GET | /v1/bundles/{bundleId}/processings | read:bundle | 진행 상황을 폴링하기 위해 번들의 처리 레코드 나열 |
GET | /v1/site/{siteId}/bundles/{bundleId} | read:hierarchy | 처리된 출력 컴포넌트(서명된 링크)와 함께 번들 가져오기 |
1단계: 처리 옵션 나열
섹션 제목: “1단계: 처리 옵션 나열”시작하기 전에 번들이 생성할 수 있는 출력물과 각각의 비용을 확인하세요.
GET {api_url}/v1/bundles/{bundleId}/processing/options?page=1&limit=20&sortBy=type&sortDir=ASCAuthorization: Bearer {access_token}쿼리 매개변수
섹션 제목: “쿼리 매개변수”| 매개변수 | 유형 | 설명 |
|---|---|---|
page | number | 1부터 시작하는 페이지 인덱스(기본값 1) |
limit | number | 페이지 크기(기본값 20, 최대 20) |
sortBy | string | type 또는 cost(기본값 type) |
sortDir | string | ASC 또는 DESC(기본값 ASC) |
응답은 페이지 단위 목록({ items, total })입니다. 각 옵션은 하나의 출력 유형을 설명합니다.
| 필드 | 의미 |
|---|---|
type | 처리를 시작할 때 전달할 출력 컴포넌트 유형(예: StandardizedPointCloud, OgcPointCloudHlod) |
hasProcessing | 이 출력에 대한 처리가 이미 시작된 경우 true |
cost | 유료 출력의 바이트 단위 처리 비용. 무료 등급 출력의 경우 0 |
예:
{ "items": [ { "type": "StandardizedPointCloud", "hasProcessing": false, "cost": 734003200 }, { "type": "OgcPointCloudHlod", "hasProcessing": false, "cost": 734003200 } ], "total": 2}재처리할 의도가 아니라면 hasProcessing이 true인 출력물은 건너뛰세요.
2단계: 처리 시작
섹션 제목: “2단계: 처리 시작”파이프라인이 생성하기를 원하는 출력 컴포넌트 유형 목록과, 총 처리 요금을 확인하는 cost 필드를 전달합니다. 서버는 requestedComponents의 모든 유형에 대해 1단계의 cost 값을 합산하고, 확인 값이 일치하지 않으면 요청을 거부합니다.
비용이 수락되면 처리가 비동기적으로 시작됩니다.
POST {api_url}/v1/bundles/{bundleId}/processingAuthorization: Bearer {access_token}Content-Type: application/json
{ "requestedComponents": [ "StandardizedPointCloud", "OgcPointCloudHlod" ], "cost": 1468006400}위 예제에서 cost는 1단계 옵션 목록의 734003200 + 734003200입니다.
성공 응답
섹션 제목: “성공 응답”204 No Content: 처리가 수락되어 대기열에 추가되었습니다.
오류 응답
섹션 제목: “오류 응답”처리를 시작할 수 없을 때, API는 유형이 지정된 error와 함께 409 Conflict를 반환합니다.
error 값 | 의미 |
|---|---|
ProcessingCostMismatch | 제출된 cost가 요청한 출력물에 대해 계산된 총계와 일치하지 않음 |
InsufficientProcessingCapacity | 조직 처리 한도를 초과함 |
NoInputDataFoundForProcessing | 번들에 마무리된 입력 파일이 없음 |
FailedToLaunchProcessing | 예상치 못한 이유로 파이프라인 제출 실패 |
예:
{ "error": "ProcessingCostMismatch"}업로드된 배치의 구조적 검증(필수 유형, 확장자, 종속성)은 시작 후 처리 파이프라인에서 시행됩니다. 업로드 세션이 완료되었지만 requirements 계약을 위반한 경우, 모든 파일이 성공적으로 업로드되고 시작 호출이 204를 반환했더라도 나중에 처리가 실패할 수 있습니다.
3단계: 진행 상황 폴링
섹션 제목: “3단계: 진행 상황 폴링”성공적으로 시작한 후, 번들의 모든 처리 레코드가 종료 상태에 도달할 때까지 폴링합니다.
GET {api_url}/v1/bundles/{bundleId}/processingsAuthorization: Bearer {access_token}응답은 번들의 모든 처리 레코드가 있는 목록({ items, total })입니다. 쿼리 매개변수는 없습니다. 모든 레코드가 한 번의 호출로 반환됩니다.
각 항목은 하나의 처리 작업을 추적합니다.
| 필드 | 의미 |
|---|---|
id | 처리 레코드의 식별자 |
status | 파이프라인 상태(아래 표 참조) |
progress | 활성 상태일 때의 완료 백분율(0–100) |
createdAt | 작업이 생성된 시점 |
updatedAt | 작업이 마지막으로 상태를 변경한 시점 |
launchedById | 작업을 시작한 사용자의 id 또는 null |
knownErrors | 구조화된 실패. 각각 errorCode와 영향을 받은 errorFilepaths가 있음(파일별이 아닐 때 null) |
처리 상태
섹션 제목: “처리 상태”| 상태 | 종료? | 의미 |
|---|---|---|
Pending | 아니오 | 대기열에 있으며 아직 시작되지 않음 |
Processing | 아니오 | 파이프라인이 실행 중 |
Restarted | 아니오 | 작업이 다시 시작되어 재실행 중 |
Ready | 예 | 출력물이 성공적으로 생성됨 |
Failed | 예 | 구조화되지 않은 파이프라인 실패 |
FailedToLaunch | 예 | 작업이 파이프라인에 진입하지 못함 |
KnownError | 예 | 구조화된 검증 또는 형식 오류 |
모든 레코드가 종료 상태(Ready, Failed, FailedToLaunch, KnownError)가 될 때까지 폴링하세요.
진행 중 응답 예:
{ "items": [ { "id": "3f2b1c4d-8e7a-4b2c-9d1e-0a1b3c4d4e6f", "status": "Processing", "progress": 42, "createdAt": "2026-06-01T14:05:00Z", "updatedAt": "2026-06-01T14:12:00Z", "launchedById": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a2b2c1d", "knownErrors": [] }, { "id": "9c0d1e2f-3a4b-5c6d-7e8f-9a0b2c3d3e4f", "status": "Pending", "progress": 0, "createdAt": "2026-06-01T14:05:00Z", "updatedAt": "2026-06-01T14:05:00Z", "launchedById": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a3b1c1d", "knownErrors": [] } ], "total": 2}알려진 오류가 있는 예:
{ "items": [ { "id": "3f2b1c4d-8e7a-4b2c-9d1e-0a2b3c4d4e5f", "status": "KnownError", "progress": 100, "createdAt": "2026-06-01T14:05:00Z", "updatedAt": "2026-06-01T14:48:00Z", "launchedById": "b1a2c3d4-5e6f-7a8b-1c0d-2e6f3a4b5c6d", "knownErrors": [ { "errorCode": "MissingTrajectory", "errorFilepaths": ["scan.las"] } ] } ], "total": 1}처리 목록에는 레코드별 출력 type이 포함되지 않습니다. 완료된 출력물을 해당 컴포넌트 유형 및 서명된 링크에 다시 매핑하려면 번들의 컴포넌트를 가져오세요(처리된 출력물 가져오기 참조).
폴링 지침
섹션 제목: “폴링 지침”- 간격: 1~5분이 합리적인 기본값입니다. 처리는 입력 크기에 따라 수 분에서 수 시간까지 실행될 수 있습니다.
- 속도 제한: 번들 엔드포인트는 여러 다른 엔드포인트와 속도 제한 버킷을 공유합니다. 1분 미만의 폴링은 피하세요.
처리된 출력물 가져오기
섹션 제목: “처리된 출력물 가져오기”처리 레코드가 Ready에 도달하면, 출력 컴포넌트와 서명된 다운로드 링크를 가져오기 위해 번들을 조회하세요. 번들 세부 정보는 계층 구조 엔드포인트에 있습니다.
GET {api_url}/v1/site/{siteId}/bundles/{bundleId}Authorization: Bearer {access_token}이 엔드포인트에는 read:hierarchy 스코프가 필요하며 components 배열과 함께 번들을 반환합니다.
| 필드 | 의미 |
|---|---|
id | 번들 id |
name | 번들 이름 |
components | 사용 가능한 컴포넌트당 하나의 항목(입력 및 처리된 출력) |
각 컴포넌트에는 다음이 있습니다.
| 필드 | 의미 |
|---|---|
id | 컴포넌트 id |
type | 컴포넌트 유형(예: StandardizedPointCloud, OgcPointCloudHlod) |
version | 컴포넌트 버전 |
signedLink | 컴포넌트를 다운로드하기 위한 서명된 URL. 인증 헤더 불필요 |
roots | 해당되는 경우 컴포넌트 내의 진입점 경로 |
예:
{ "id": "a6f75b3c-f261-4b27-9a2a-9a6cc4178a13", "name": "ZF capture 2026-06-01", "components": [ { "id": "5d6e7f8a-9b0c-1d2e-3f4a-1b3c7d4e8f0a", "type": "StandardizedPointCloud", "version": "1", "signedLink": "https://cdn…/components/…?X-Amz-…" } ]}RealityPlan 프로젝트는 GET /v1/reality-plan/{projectId}/bundles/{bundleId}(스코프 read:twin)를 통해 동일한 번들을 노출합니다.
전체 예제
섹션 제목: “전체 예제”import timeimport requests
API_URL = "https://<your-regional-api-url>"TOKEN = "<access_token>"BUNDLE_ID = "<bundleId>"
headers = {"Authorization": f"Bearer {TOKEN}"}base = f"{API_URL}/v1/bundles/{BUNDLE_ID}"
# 1. Discover outputs.options = requests.get( f"{base}/processing/options", headers=headers, params={"page": 1, "limit": 20},).json()
options_by_type = {item["type"]: item for item in options["items"]}to_process = [ item["type"] for item in options["items"] if not item["hasProcessing"]]total_cost = sum(options_by_type[t]["cost"] for t in to_process)print(f"Launching: {to_process} (cost: {total_cost})")
# 2. Start processing.response = requests.post( f"{base}/processing", headers=headers, json={"requestedComponents": to_process, "cost": total_cost},)
if response.status_code == 409: raise RuntimeError( f"Processing failed to start: {response.json().get('error')}" )response.raise_for_status() # expect 204
# 3. Poll until every job is terminal.TERMINAL = {"Ready", "Failed", "FailedToLaunch", "KnownError"}POLL_SECONDS = 15TIMEOUT_SECONDS = 3600deadline = time.time() + TIMEOUT_SECONDS
while time.time() < deadline: listing = requests.get( f"{base}/processings", headers=headers, ).json()
processings = listing.get("items", []) for p in processings: print(f" {p['id']}: {p['status']} ({p['progress']}%)")
if processings and all(p["status"] in TERMINAL for p in processings): break
time.sleep(POLL_SECONDS)else: raise TimeoutError("Processing did not finish within the timeout")다음 단계
섹션 제목: “다음 단계”업로드된 원시 입력 파일을 검색하려면(보관, 재처리, 감사 목적) 입력 파일 다운로드를 참조하세요.