📦 목차

제7장: 구현 아키텍처와 현장 배치

앞의 장들은 WIA-ENE-057을 한 층씩 나누어 설명했다. PHASE 1의 레코드 유형, PHASE 2의 REST 자원, PHASE 3의 스트리밍·메시징 규칙, PHASE 4의 외부 연계가 그것이다. 그러나 실제로 돌아가는 모니터링 플랫폼은 이 모두를 한꺼번에 떠안아야 하며, 그 하드웨어는 데이터센터에 있을 수도 있고, 전력이 불안정한 지방 사무소나 황폐해진 방목지 가장자리의 태양광 함체 안에 있을 수도 있다. 이 장은 명세를 아키텍처로 옮긴다. 구성 요소가 어떻게 맞물리는지, 어떤 배치 프로파일이 어떤 조직에 맞는지, 현장 회선이 느린 위성 모뎀일 때 무엇이 달라지는지, 같은 측정값이 두 번 도착하면 데이터 파이프라인이 어떻게 행동해야 하는지, 그리고 감사인이 요구할 증거를 잃지 않으면서 시스템을 해마다 운영하는 방법을 다룬다. 마지막으로 예시 지역에서 한 계절이 흘러가는 과정을 따라가 보고, 토지 황폐화 데이터를 가장 자주 망가뜨리는 실수를 점검표로 정리한다.

이 장 전체에 두 가지 전제가 적용된다. 첫째, WIA-ENE-057은 데이터 형태와 인터페이스, 증거를 표준화할 뿐 데이터베이스나 클라우드 사업자, 프로그래밍 언어를 지정하지 않는다. 따라서 아래에 나오는 제품명은 해당 구성 요소 부류의 예일 뿐 요구 사항이 아니다. 둘째, 명세의 JSON 예제에서 가져온 모든 수치 — NDVI 값, 강수 총량, 식재 수, 위험 점수 — 는 가상의 SAHEL-REGION-001 지역을 위한 예시값이며 실제 어느 장소의 측정값도 아니다.

7.1 참조 아키텍처

WIA-ENE-057 배치는 여섯 개 층으로 설명할 수 있다. 데이터는 엣지에서 들어와 수집 계층에서 받아들여지고 정규화되며, 형태에 맞는 저장소에 적재된 뒤 처리 계층에서 지표로 변환되고, 서비스 계층과 연계 어댑터를 통해 밖으로 나간다. 그림 7-1은 각 층과, 층 사이의 경계를 규정하는 명세 절을 보여 준다.

┌─────────────────────────────────────────────────────────────────────────┐
│ 연계 어댑터            LDN 보고서 생성기 · 지구관측 다운로더 · GIS 피드   │  PHASE 4
│                        현장 앱 동기화 · 탄소 등록부 내보내기             │
├─────────────────────────────────────────────────────────────────────────┤
│ 서비스                 REST /api/v1/...  ·  WSS 스트림 · OGC WMS/WFS/WCS │  PHASE 2 / 3
│                        웹훅(X-WIA-Signature) · 디스커버리 문서           │
├─────────────────────────────────────────────────────────────────────────┤
│ 처리                   지수 추출 · 구역 통계 · 추세                      │
│                        경보 규칙 · 위험 점수 · 보고서 조립               │
├──────────────────────┬──────────────────────────┬───────────────────────┤
│ 저장                 │ 시계열 저장소             │ 객체 저장소           │
│ 공간 DB(PostGIS 류): │ 센서 측정값,              │ GeoTIFF / NetCDF      │
│ 위치, 폴리곤,        │ 장치 텔레메트리           │ 래스터, 사진,         │
│ 레코드               │                           │ 증거 패키지           │
├──────────────────────┴──────────────────────────┴───────────────────────┤
│ 수집                   MQTT 브로커 · HTTPS 레코드 API · 대량 업로드      │  PHASE 3 §3, §7
│                        위성 멀티파트 수집 · 래스터 파이프라인            │
├─────────────────────────────────────────────────────────────────────────┤
│ 엣지                   토양·기상 센서 · 게이트웨이 · ODK 설문            │
│                        모바일 SDK(오프라인) · 실험실 결과 수기 입력      │
└─────────────────────────────────────────────────────────────────────────┘
그림 7-1. WIA-ENE-057 플랫폼의 계층형 참조 아키텍처. 화살표는 생략했다. 데이터는 위로, 명령과 설정은 아래로 흐른다.

엣지

엣지 계층에는 관측값을 만들어 내는 모든 것이 속한다. 현장에 묻은 토양 수분·온도 탐침, 우량계와 기상 관측소, 여러 탐침을 모으는 게이트웨이, 그리고 사람이다. 현장 직원과 지역 주민 모니터 요원은 ODK 방식의 설문 양식과, 오프라인에서 작동하다가 연결이 회복되면 동기화하는 모바일 클라이언트를 통해 데이터를 보탠다(PHASE 4 §10). 토양 화학 분석 결과는 대개 시료 채취 후 며칠에서 몇 주 뒤에 나오며 사람이 직접 입력한다. 엣지 계층의 책임은 소박하지만 중요하다. 모든 관측값에 UTC 시각과 locationId를 붙이고, 수집 계층이 수신을 확인할 때까지 보관하며, 자신의 상태(배터리, 신호, 펌웨어)를 보고하는 것이다.

수집

PHASE 3이 기술하는 트래픽은 네 개의 진입점으로 모두 받을 수 있다. MQTT 브로커는 wia-ene-057/{locationId}/{sensorType}/{metric} 형식의 토픽으로 센서 메시지를 받는다. HTTPS 레코드 API는 /api/v1/monitoring/vegetation으로 게시되는 vegetationData 문서처럼 완결된 PHASE 1 레코드를 받는다. 대량 업로드 엔드포인트(/api/v1/bulk/upload/initiate, …/chunk, …/complete)는 밀린 데이터와 과거 자료 이관을 처리한다. 위성 수집 엔드포인트(/api/v1/ingest/satellite)는 GeoTIFF 또는 NetCDF 파일을, 산출물·취득일·운량·좌표참조계를 적은 JSON 메타데이터 파트와 함께 받는다. 그 뒤에는 PHASE 4 §4.2가 MODIS를 예로 기술한 래스터 파이프라인이 있다. 내려받기, NDVI/EVI 층 추출, 재투영, 관심 지역 잘라내기, 구역 통계 계산, 저장, 추세 분석 호출의 순서다.

네 진입점은 모두 하나의 검증 단계로 모여, 무엇이든 기록하기 전에 PHASE 1 §7.2 규칙을 적용해야 한다. NDVI는 −1에서 1 사이, 토양 수분은 0~100 %이되 60 %를 넘으면 검토 표시, 강수량은 음수 불가, 좌표는 유효 범위 이내라는 규칙이다. 검증을 한곳에 두면 게이트웨이, 스프레드시트 이관, 모바일 양식이 "유효한 레코드"의 정의를 두고 서로 어긋날 일이 없다.

저장

데이터에는 성격이 전혀 다른 세 가지 형태가 있고, 이를 한 저장소에 몰아넣으면 적어도 하나는 손해를 본다. 센서 측정값은 폭이 좁고 거의 추가만 일어나는 시계열이므로 시계열 데이터베이스나 (locationId, deviceId, timestamp)로 색인한 분할 관계형 테이블이 알맞다. 위치, 사업 폴리곤, 필지, 그리고 PHASE 1 레코드 자체는 기하와 관계를 가지므로, PostGIS 부류의 공간 데이터베이스를 쓰면 "이 복원 구역 안에 들어가는 레코드는 무엇인가"라는 질문에 데이터를 데스크톱 GIS로 내보내지 않고 답할 수 있다. 래스터, 현장 사진, 서명된 증거 패키지는 큰 바이너리 객체이므로 객체 저장소에 두고, 공간 데이터베이스에는 메타데이터와 체크섬, 영역 정보만 둔다. 래스터 배치 형식으로는 Cloud-Optimized GeoTIFF가 무난한 기본값이다. OGC WCS 서비스와 분석 작업이 파일 전체를 내려받지 않고 필요한 창(window)만 읽을 수 있기 때문이다.

처리

처리 작업은 원자료를 PHASE 1 레코드와 PHASE 2 응답이 노출하는 필드로 바꾼다. 위치·날짜별 지수 값, trendAnalysis 블록, 침식 추정치, 종합 monitoringReport의 4요인 위험 점수, LDN 보고서의 집계표가 그 결과물이다. 이 작업들은 이벤트로 구동된다. 새 레코드가 data.created를 내보내면(PHASE 3 §6.1) 구독자가 그에 의존하는 값을 다시 계산한다. 이 순환은 7.4절에서 자세히 다룬다.

서비스와 연계

서비스 계층은 PHASE 2 REST 자원, WebSocket 스트림, OGC 서비스, 발신 웹훅을 제공하고 /.well-known/wia/desertification-prevention에 디스커버리 문서를 게시한다. 연계 어댑터는 PHASE 4의 연결기들, 즉 지구관측 다운로더, LDN 보고서 생성기, GIS 피처 서비스, 현장 앱 동기화, 탄소 등록부 내보내기다. 각 어댑터는 자체 자격 증명과 자체 상태 정보를 가진 별도 배포 단위로 다루어야 한다. 그래야 상류 자료 저장소 하나가 고장 나도 REST API까지 멈추지 않는다. PHASE 4 §12.2의 integrationHealth 문서는 이 상태를 게시하는 형태로 쓸 만하다.

7.2 배치 프로파일

참조 CLI(cli/desertification-prevention.sh profiles)는 네 가지 적합성 프로파일을 나열한다. 이 프로파일은 누가 런타임을 공유하는지, 증거가 어떤 경로로 밖으로 나가는지를 기술하며, 어느 것을 고르느냐에 따라 격리·감사·연결성 설계가 달라진다.

프로파일CLI 설명어울리는 조직핵심 설계 과제
single-tenant조직당 런타임 하나자체 모니터링 플랫폼을 운영하는 국가 산림·토지 기관단일 런타임의 패치를 유지하고 증거를 일정대로 게시하는 것
multi-tenant공유 런타임, 헤더 기반 격리여러 NGO나 사업 개발자를 호스팅하는 서비스 제공자한 테넌트가 다른 테넌트의 레코드를 읽거나 덮어쓸 수 없음을 입증하는 것
federated런타임들의 P2P 메시지역 내 기관·NGO 컨소시엄으로, 각자 자기 데이터를 보유식별자와 프로파일을 합의하고 집계 매니페스트를 게시하는 것
air-gapped일괄 증거 패키지, 실시간 연결 없음원격 현장 관측소나 실시간 연결이 없는 민감한 국가 시스템증거 사슬을 끊지 않고 서명된 패키지를 대역 외로 옮기는 것

single-tenant는 따져 보기 가장 쉬운 프로파일이다. 한 조직이 런타임과 서명 키, 데이터를 모두 소유하고, 감사인은 시스템 전체를 들여다볼 수 있다. 모니터링 데이터를 국가 기반 시설 안에 두어야 하는 정부 기관에 맞는다.

multi-tenant 배치는 런타임을 공유하고 요청 헤더로 테넌트를 격리한다. 명세는 헤더 이름을 정하지 않았고, 헤더는 그 자체로는 주장에 불과하다. 실무에서는 테넌트를 인증된 토큰에서 도출하거나 토큰과 대조해야 한다. 테넌트 헤더가 토큰의 조직 클레임과 다르면 조용히 다른 곳으로 보낼 것이 아니라 거부해야 한다. 테넌트 범위 제한은 엔드포인트마다 넣지 말고 데이터 접근 계층(예: 테넌트 키 기반 행 수준 보안)에 두어야 하며, 테넌트 A가 테넌트 B의 locationId를 읽으려는 식의 부정 격리 시험은 모두 8장의 시험 벡터 집합에 포함되어야 한다.

federated 배치는 구성원마다 자기 레코드에 대한 권한을 유지하는 컨소시엄에 맞는다. 각 PHASE 문서의 Annex I는 연합체가 구성원 프로파일의 합집합과 교집합을 요약한 집계 매니페스트를 게시하도록 요구하며, 호출자는 이를 보고 요청을 최소 공통 프로파일로 보낼 수 있다. 어려운 부분은 기술이 아니다. 구성원들은 locationId 발급 방식(구성원 접두어를 붙이면 충돌을 피할 수 있다), 구성원별로 제공하는 레코드 유형, 중복된 복원 대상지를 조정하는 방법을 합의해야 한다.

air-gapped 배치에는 실시간 연결이 없다. 레코드, 래스터, 증거를 서명된 일괄 패키지로 내보내 인편, 이동식 매체, 간헐적 회선으로 옮긴 뒤 다른 곳에서 가져온다. 패키지는 도착지에서 출발지에 접속하지 않고도 검증할 수 있어야 하므로 자체 매니페스트, 체크섬, 서명을 담고 있어야 하며, 가져오는 쪽은 서명이나 체크섬이 맞지 않는 패키지를 거부해야 한다. 회선이 간헐적인 현장 관측소는 흔히 연결형과 에어갭형의 중간에 놓인다. 다음 절의 저장 후 전달 기법을 쓰면 이런 관측소도 대부분의 시간에는 연결된 엣지처럼, 회선이 끊겼을 때는 에어갭 현장처럼 동작할 수 있다.

어떤 프로파일이든, 각 PHASE 문서의 Annex E는 구현체가 운영 범위를 OpenAPI 문서에 선언하도록 권고한다. 그래야 감사인이 알맞은 기대 수준으로 배치를 평가할 수 있다. 나중에 프로파일을 바꾸는 일, 예컨대 single-tenant에서 federated로 옮기는 일은 의도된 사건으로 다루어야 한다. Annex I가 기술하듯 새 프로파일 매니페스트를 게시하고, 이전 매니페스트는 폐기 유예 기간 동안 유효하고 서명된 상태로 유지한다.

7.3 현장 연결성의 현실

사막화 모니터링은 기반 시설이 가장 빈약한 곳에서 이루어진다. 늘 켜져 있는 광대역 회선을 전제로 한 설계는 정작 가장 중요한 곳에서 데이터를 잃는다. 다음 네 가지 기법이 그 차이를 만든다.

모든 구간에서의 저장 후 전달

모든 장치와 게이트웨이는 영속적인 로컬 큐를 두고, 다음 구간이 수신을 확인한 뒤에만 항목을 지운다. 게이트웨이에서는 보통 소형 내장 데이터베이스나 플래시 저장소의 추가 전용 파일이 이 역할을 하고, 모바일 클라이언트에서는 SDK의 오프라인 저장소가 일정 간격으로 동기화한다(PHASE 4 예제는 syncInterval: 3600000, 즉 1시간을 쓴다). 큐에는 상한을 두고, 가득 차면 가치가 가장 낮은 데이터부터 — 측정값보다 일상 텔레메트리를 먼저 — 버리되 버렸다는 사실을 기록해야 한다. 기록 없는 공백은 기록된 공백보다 나쁘다. 분석자가 그 공백을 0을 읽은 센서와 구별할 수 없기 때문이다.

MQTT QoS의 의도적 선택

PHASE 3 §3.1은 QoS 수준을 데이터 부류에 대응시킨다. 일상 텔레메트리는 QoS 0(최대 1회), 중요한 측정값은 QoS 1(최소 1회), 긴급 경보는 QoS 2(정확히 1회)다. 손실이 많은 회선에서는 이 선택마다 비용이 따른다. QoS 1은 전달을 보장하지만 재접속 뒤 중복이 생길 수 있으므로 수집 계층이 중복을 제거해야 한다. 어차피 그렇게 해야 한다(7.4절). QoS 2는 발행 한 건마다 네 단계 핸드셰이크를 더하므로, 느린 회선에서는 경보에 고유 식별자를 붙여 QoS 1로 보내고 멱등 수집이 중복을 걸러 내게 하는 편이 대개 더 싸다. 게이트웨이가 잠시 끊겨도 브로커가 메시지를 보관하도록 지속 세션을 쓰고, 게이트웨이의 클라이언트 식별자는 재부팅 후에도 바뀌지 않게 해야 한다. 그렇지 않으면 세션이 사라진다.

청크 체크섬을 쓰는 대량 업로드

회선 장애 뒤 밀린 일주일치 측정값이나 한 계절치 현장 설문처럼 쌓인 자료는 수천 건을 하나씩 게시하기보다 PHASE 3 §7의 대량 업로드 프로토콜로 보내는 편이 낫다. 클라이언트가 레코드 수와 크기를 알리면 uploadId와 청크 크기를 받고, 번호를 붙인 청크를 보낸 뒤 체크섬 목록과 함께 업로드를 완료한다. 명세 예제에는 자리표시 문자열("sha256-chunk1")이 들어 있지만, 실제 클라이언트는 청크마다 SHA-256 다이제스트를 보내고 서버는 청크를 받기 전에 각 다이제스트를 검증해야 한다. 그래야 전송 중 손상된 청크가 조용히 저장되지 않고 재요청된다. 청크마다 번호가 있으므로 중단된 업로드는 처음부터 다시 할 필요 없이 첫 번째 누락 청크부터 재개할 수 있다. 상태 엔드포인트는 "Invalid NDVI value: 1.5" 같은 레코드별 실패를 알려 주는데, 클라이언트는 거부된 레코드를 버리지 말고 정정을 위해 보관해야 한다.

데이터로서의 장치 상태

MQTT 메시지 형식에는 battery, signalStrength, firmware를 담은 metadata 블록이 있다. 이것을 일급 데이터로 다루어야 한다. 서서히 떨어지는 배터리 잔량은 측정이 끊기기 전에 태양광 패널 고장을 알려 준다. 펌웨어 갱신 시점과 겹치는 토양 수분의 급변은 강우보다는 보정 변경일 가능성이 크다. 텔레메트리를 측정값과 함께 저장하고, 모든 품질 검사에서 펌웨어 버전을 하나의 차원으로 삼아야 한다.

시각 동기화

순서, 추세 구간, 경보 시점에 관한 모든 보장은 시계에 달려 있다. PHASE-2 API-INTERFACE §5는 동기화된 시계(NTPv4, stratum-2 이상)를 요구하며, Annex E의 노트는 결정론적 연동이 필요한 곳에만 PTP(IEEE 1588)를 권한다. 토지 모니터링에서 그런 경우는 드물다. 연결된 서버와 게이트웨이는 서로 독립된 시각원 두 곳 이상을 상대로 NTP 클라이언트를 돌려야 한다. 인터넷이 없는 원격 게이트웨이는 GPS 수신기를 시각원으로 쓸 수 있다. 수신기가 UTC가 아닌 GPS 시스템 시각을 내보낸다면 공표된 윤초 차이를 적용해 변환해야 한다. GPS 시각에는 윤초가 반영되지 않기 때문이다. 잠깐씩 깨어나는 배터리 구동 센서는 깨어날 때마다 게이트웨이에서 시각을 받아야 한다. 시각원이 무엇이든 타임스탬프는 끝에 Z를 명시한 UTC로 기록하고, 수집 계층은 터무니없이 먼 미래로 찍힌 측정값을 거부하거나 표시해야 한다. 실시간 시계가 기본 날짜로 초기화된 센서의 전형적인 증상이다.

7.4 데이터 파이프라인 구현

처리 계층은 하나의 순환이다. 레코드가 도착하면 검증·저장되고, 이벤트가 이를 알리며, 의존하는 결과가 다시 계산되고, 규칙이 발동하면 경보가 나간다. 이 순환을 믿을 수 있게 만드는 성질이 세 가지 있다.

멱등 업서트

모든 PHASE 1 레코드는 VEG-20251225-SAHEL-001, SOIL-20251225-SAHEL-001, RAIN-2025-SAHEL-001처럼 식별자를 가지며, 이 식별자는 참고용 표시가 아니라 업서트의 키가 되어야 한다. 같은 레코드를 두 번 받아도 저장소는 한 번 받았을 때와 같은 상태여야 한다. 레코드 식별자가 없는 MQTT 측정값에는 (deviceId, timestamp, metric) 같은 자연 키를 쓴다. 내용까지 같은 두 번째 전달은 수신 확인만 하고 무시한다. 키는 같지만 내용이 다른 두 번째 전달은 정정이므로 새 버전으로 저장하되 이전 버전도 남겨야 한다. 감사인도, LDN 기준선도 특정 시점에 무엇을 알고 있었는지를 볼 수 있어야 하기 때문이다.

이벤트와 재계산

기록에 성공하면 파이프라인은 PHASE 3 §6.1 형식의 이벤트(resourceType, resourceId, locationId를 담은 data.created 또는 data.updated)를 내보낸다. 소비자는 자원 유형별로 구독한다. 새 식생 레코드는 해당 위치의 추세 재계산을 일으키고, 새 토양·강수 자료는 위험 재평가를 일으킬 수 있으며, 평가가 끝나면 assessment.completed가 나간다. 재계산은 위치별로 디바운스해야 한다. 대량 업로드가 한 지역에 측정값 천 건을 몰아서 넣으면 추세는 천 번이 아니라 일괄 처리가 끝난 뒤 한 번 계산되어야 한다. 촉발 이벤트의 correlationId를 거기서 파생된 모든 결과에 이어 붙이면, 경보 하나를 그것을 일으킨 정확한 레코드까지 거슬러 추적할 수 있다.

파생값은 예제 페이로드에서 복사하지 말고 입력값에서 다시 계산해야 한다. 종합 위험 점수가 좋은 시험 사례다. PHASE 1 §6.1 예제의 요인 점수는 35.0, 72.0, 85.0, 90.0이고 가중치는 0.30, 0.25, 0.25, 0.20이다. 가중합은 10.5 + 18.0 + 21.25 + 18.0 = 67.75인데, 예제의 overallScore는 68.5로 적혀 있다. 재계산하는 구현은 예제와 어긋나게 되며, 그것이 옳다. 예제 자체가 내부적으로 맞지 않기 때문이다. 명세는 위험 범주의 수치 경계도 정의하지 않으므로, 플랫폼이 어떤 점수를 "높음"으로 부르는 기준은 자체 정책이며 그렇게 문서화해야 한다.

경보 규칙

경보는 최근 변화를 임곗값과 비교한다. PHASE 2의 웹훅 예제는 수치들이 서로 들어맞기 때문에 좋은 본보기가 된다. NDVI가 90일 동안 0.45에서 0.35로 떨어져 변화량 −0.10이 임곗값 −0.05를 넘었고, 이는 상대적으로 약 22 % 감소다. 아래 평가기는 이 규칙을 구현한다. 품질 관리를 통과한 관측값만 쓰고, 같은 종류끼리만 비교하며(합성 자료와 일 단위 자료를 섞지 않는다), 구간 안에 최소 관측 수를 요구하고, 냉각 기간 동안 반복 경보를 억제한다. 심각도 대응 방식은 규범이 아니라 예시 정책이다.

from dataclasses import dataclass
from datetime import date, timedelta

@dataclass
class Obs:
    day: date
    value: float        # NDVI, 유효 범위 -1..1
    source: str         # 예: "MOD13Q1" (16일 합성)
    quality: str        # "high" | "medium" | "low"

@dataclass
class Rule:
    metric: str = "ndvi"
    window_days: int = 90
    threshold: float = -0.05      # 발동 기준 절대 변화량
    min_obs: int = 3
    cooldown_days: int = 30

def evaluate(obs, rule, today, last_alert_day=None):
    """경보 dict 또는 None 반환. 예시 정책이며 규범이 아니다."""
    start = today - timedelta(days=rule.window_days)
    usable = [o for o in obs
              if start <= o.day <= today
              and o.quality in ("high", "medium")
              and -1.0 <= o.value <= 1.0]
    sources = {o.source for o in usable}
    if len(sources) != 1:
        return None                   # 합성 자료와 일 단위 자료를 섞지 않는다
    if len(usable) < rule.min_obs:
        return None                   # 구간 내 근거 부족
    usable.sort(key=lambda o: o.day)
    first, last = usable[0], usable[-1]
    change = round(last.value - first.value, 4)
    if change > rule.threshold:
        return None
    if last_alert_day and (today - last_alert_day).days < rule.cooldown_days:
        return None                   # 반복 경보 억제
    relative = change / first.value if first.value else None
    severity = "critical" if change < 2 * rule.threshold else "high"
    return {
        "type": "rapid-vegetation-decline",
        "severity": severity,
        "details": {
            "metric": rule.metric,
            "previousValue": first.value,
            "currentValue": last.value,
            "change": change,
            "changePeriod": f"{(last.day - first.day).days} days",
            "threshold": rule.threshold,
        },
        "relativeChange": None if relative is None else round(relative, 3),
    }

예시 계열(2025년 10월 1일 0.45, 11월 1일 0.40, 12월 1일 0.35)을 넣으면 변화량은 −0.10, 상대 변화는 −0.222로 웹훅 메시지와 일치한다. 코드는 일부러 단순하게 썼다. 개발자뿐 아니라 토지 관리자도 규칙을 읽을 수 있어야 하기 때문이다. 운영 환경에서는 두 가지를 보강할 만하다. 첫째, 구간 시작점뿐 아니라 이전 여러 해의 같은 시기와도 비교해야 한다. 사헬에서 10월에서 12월 사이의 NDVI 하락은 대부분 우기가 끝나면서 생기는 정상적인 변화이지 황폐화가 아니기 때문이다. 둘째, 어떤 규칙 버전이 발동했는지 기록해야 임곗값을 조정한 뒤에도 경보를 설명할 수 있다.

경보가 발동하면 파이프라인은 alert.triggered를 내보내고, SUB-20251225-001 같은 구독자에게 X-WIA-Signature 헤더와 함께 전달하며, 실패한 전달은 PHASE 3 §9.1의 지수 백오프로 재시도한다. 계속 실패하는 엔드포인트로의 전달은 서킷 브레이커 뒤에 멈춰 두고 구독자에게 알려야지, 끝없이 재시도해서는 안 된다.

7.5 운영과 관측 가능성

모니터링 플랫폼은 출시 시점이 아니라 여러 해에 걸쳐 평가받는다. 아래의 운영 관행은 Annex E 노트와 PHASE 4 §11–§12에서 가져온 것이다. 다만 명세의 SLA 수치는 설계 목표일 뿐, 현존하는 어떤 서비스의 측정 성능도 아니라는 점을 밝혀 둔다.

오류 예산

Annex E는 구현체가 테넌트별 데이터를 드러내지 않으면서 p95 지연, 오류율, 위반 시간을 담은 월간 오류 예산 요약을 게시하도록 권고한다. PHASE 4 §12.1은 가용성 99.9 %, p95 응답 시간 200 ms 미만, 실시간 데이터 지연 5분 미만, 오류율 0.1 % 미만을 목표로 제시한다. 가용성 99.9 %는 30일 한 달에 43.2분의 중단을 허용한다. 이 계산을 드러내 두면 계획된 점검 시간을 감당할 수 있는지 판단하기 쉽다. 쓸모 있는 월간 요약은 서비스별로 다음 항목을 담는다.

측정 항목목표(명세의 설계 목표)측정 방법
가용성99.9 %엔드포인트 그룹별 합성 탐침 성공 수 ÷ 전체 탐침 수
응답 시간p95 < 200 ms서버 측 지연 히스토그램. 대량·래스터 다운로드는 제외
데이터 지연실시간 데이터 < 5분수집 시각 − 관측 timestamp. 연결형 장치에 한정
오류율< 0.1 %5xx 응답 ÷ 전체 응답. 4xx는 따로 보고
위반 시간보고만 하고 목표 없음위 항목 중 하나라도 목표를 놓친 시간 수

데이터 지연은 연결되어 있어야 하는 장치에 대해서만 측정해야 한다. 이틀간의 장애 뒤 저장 후 전달 큐에서 풀려난 측정값은 설계가 제대로 작동했다는 증거이지 지연 위반이 아니며, 이를 섞으면 지표가 의미를 잃는다.

연계 상태

각 PHASE 4 어댑터는 마지막 동기화 성공 시각, 성공·실패 건수, 데이터 품질 지표를 integrationHealth 예제의 형태로 보고해야 한다. 명세의 예시값(동기화 성공률 100.0, 다운로드 245건, 공유 사업 15건)은 설명용일 뿐이다. 쓸모 있는 관행은 신선도 경보다. 마지막 성공 시각이 해당 산출물의 재방문 주기보다 오래된 지구관측 다운로더는 오류를 보고하지 않더라도 고장 난 것이다.

보관

Annex E는 ISO/IEC 17065 감사 기대를 위한 기준으로 7년 보관 기간을 제시하고, 규제 당국이 더 긴 기간을 요구하면 배치 정책이 이를 연장해야 한다고 적고 있다. 토지 황폐화 업무에는 더 오래 보관할 이유가 하나 더 있다. LDN 평가는 보고 기간을 기준선과 비교하고, 추세 분석에는 긴 계열이 필요하다. 세 부류로 나누어 다루는 것이 좋다. 서명된 증거(매니페스트, 시험 결과, SBOM)와 PHASE 1 레코드는 최소한 감사 기간 동안, 가능하면 사업이 끝날 때까지 보관한다. 파생 산출물(추세, 위험 점수)은 다시 만들 수 있지만 게시하거나 보고한 버전은 남겨 둔다. 원시 센서 텔레메트리는 일 단위 집계를 저장한 뒤 합의한 기간이 지나면 해상도를 낮춰도 된다.

키

PHASE 4 §11.1은 API 키를 90일마다 교체하고, 만료 7일 전에 경고하며, 암호화된 보관소에 저장하고, 역할 기반으로 접근을 통제하도록 정한다. 경고는 자동화해야 한다. 키가 조용히 만료된 현장 게이트웨이는 주말에 보고를 멈추고 큐를 가득 채운다. 매니페스트 서명 키는 배치 정책에 따라 교체하되, 교체한 뒤에도 옛 공개키를 공개 키 이력에 남겨 과거 매니페스트를 계속 검증할 수 있게 한다.

백업: 래스터와 레코드

레코드와 래스터는 보호 방식이 달라야 한다. PHASE 1 레코드, 현장 설문, 사진, 실험실 결과는 대체할 수 없는 경우가 많다. 2025년에 채취한 토양 시료는 다시 채취할 수 없다. 따라서 정기적이고, 복원 시험을 거친, 외부 보관 백업이 필요하다. 대부분의 위성 래스터는 제공자의 자료 저장소에서 다시 내려받을 수 있다. 단, 어떤 산출물과 컬렉션 버전, 타일, 날짜를 썼는지 정확히 기록해 두었을 때의 이야기다. 그 출처 정보와 자체 생성한 파생 래스터는 백업하고, 원본 장면은 다시 받아도 된다고 받아들인다. 제공자가 자료를 새 컬렉션 버전으로 재처리하기도 하므로, 다시 내려받은 장면이 처음 쓴 것과 조금 다를 수 있다는 점도 알아 두어야 한다. 백업이 제대로 작동한다는 유일하게 믿을 만한 증거는, 적어도 1년에 한 번 백업만으로 한 위치의 전체 이력을 재구성해 보는 복원 시험이다.

7.6 사례 따라가기: SAHEL-REGION-001의 한 계절

아래에서는 예시 지역의 한 계절을 아키텍처를 따라 추적한다. 식별자와 수치는 모두 명세 예제에서 가져온 예시값이며, 일부 날짜는 단계의 순서를 보이기 위해 이 책이 정한 것이다. 이 지역은 위도 14.5, 경도 −4.0에 면적 10,000 ha로 등록된다.

시점(예시)단계인터페이스레코드 또는 이벤트
계절 시작 전좌표·면적·기준선과 함께 위치 등록POST /api/v1/locationsSAHEL-REGION-001, 상태 active
매일토양 탐침이 수분·온도를 발행하고 게이트웨이가 전달MQTT wia-ene-057/SAHEL-REGION-001/soil/moistureSENSOR-SOIL-001의 측정값(예: 0–10 cm에서 12.5 %)
16일마다새 MOD13Q1 합성 영상을 내려받아 잘라내고 요약래스터 파이프라인, POST /api/v1/ingest/satelliteVEG-20251225-SAHEL-001 같은 vegetationData
우기(6~9월) 종료 시우량계 자료로 계절 강수 요약 작성레코드 API연간 레코드 RAIN-2025-SAHEL-001에 반영
2025년 10월 1일 ~ 12월 1일NDVI 계열 0.45 → 0.40 → 0.35추세 재계산…/vegetation/SAHEL-REGION-001/trend?period=90d
2025년 12월 25일경보 규칙 발동(90일간 −0.10, 임곗값 −0.05)X-WIA-Signature가 붙은 웹훅ALERT-20251225-001 → SUB-20251225-001
2025년 12월 25일녹색 띠 사업의 복원 진척 갱신PUT /api/v1/restoration/plan/{planId}/progress사업 REST-2025-SAHEL-001
2025년 4분기 말분기 종합 보고서 조립보고서 작업REPORT-2025-Q4-SAHEL-001
연말 이후보고 기간에 대한 연간 LDN 형식 보고서 생성POST /api/v1/reports/unccd-ldn2025-01-01 ~ 2025-12-31 보고서

등록. 위치 레코드는 다른 모든 레코드가 참조할 정체성을 고정한다. PHASE 2 예제의 기준선 블록에는 desertificationRisk: 68.5가 들어 있는데, 대응하는 종합 예제를 재계산하면 67.75가 나오므로 신중한 구현이라면 자체 계산한 기준선을 저장하고 그 출처를 적어 둔다.

일일 토양 자료. 게이트웨이는 탐침 측정값을 QoS 1로 발행한다. 8월에 이틀간 회선이 끊기면 게이트웨이가 측정값을 큐에 쌓아 두었다가 회선이 돌아오면 늦게 보내고, 이 측정값은 (deviceId, timestamp, metric) 기준으로 중복 제거되며 데이터 지연 지표에서는 제외된다. 60 %를 넘는 측정값은 PHASE 1 §7.2에 따라 검토 대상으로 표시된다. 사질토에서 그런 값은 실제 토양 수분보다는 탐침 고장이나 센서 주변 침수를 뜻하는 경우가 많다.

식생 합성 영상. 16일마다 지구관측 어댑터가 새 MOD13Q1 타일을 받아 NDVI와 EVI를 추출하고, 재투영한 뒤 지역 폴리곤으로 잘라내어, 구역 통계를 출처·해상도·운량 메타데이터와 함께 vegetationData 레코드로 기록한다. 이 레코드의 timestamp는 내려받은 시각이 아니라 해당 합성 영상이 대표하는 기간이어야 한다.

강수. 우기 동안 우량계 측정값이 쌓인다. 연간 예제 RAIN-2025-SAHEL-001은 총 325.4 mm, 그중 6~9월 285.2 mm를 보고하는데, 이는 예시값이다. 이 예제의 SPI −1.8은 일반적인 McKee 분류로 "심한 건조(severely dry)" 등급에 해당하며, 예제가 붙인 "moderate-drought" 라벨과 맞지 않는다. 따라서 범주는 라벨을 복사하지 말고 지수에서 도출해야 한다.

경보. 12월까지 90일간의 NDVI 추세가 7.4절의 규칙을 발동시킨다. 웹훅은 구독자에게 전달되고, 구독자의 엔드포인트는 조치하기 전에 HMAC 서명을 검증한다. 앞서 말했듯 10~12월의 하락은 우기 종료와 겹치므로, 누군가 황폐화가 가속되었다고 결론 내리기 전에 경보 처리기가 이전 해들의 같은 시기와 비교한 결과를 함께 보여 주어야 한다.

복원 진척. 복원 팀은 계획 대비 진척을 기록한다. PHASE 2 예제는 PHASE 1의 사업 식별자(REST-2025-SAHEL-001)와 다른 계획 식별자(REST-PLAN-20251225-001)를 쓴다. 구현체는 두 문자열이 같다고 가정하지 말고 계획과 사업 사이의 연결을 명시적으로 저장해야 한다.

분기·연간 보고. 분기가 끝나면 보고서 작업이 저장된 레코드로 REPORT-2025-Q4-SAHEL-001을 조립하면서 위험 점수를 다시 계산하고 해당 기간에 발생한 경보를 나열한다. 한 해가 마감되면 LDN 보고서 엔드포인트가 보고 기간에 대한 연간 패키지를 만든다. 6장에서 설명했듯 이 패키지는 UNCCD 자체 절차를 통한 국가 보고를 뒷받침할 뿐 대신하지 않는다.

7.7 흔한 구현 함정

토지 황폐화 플랫폼의 데이터 품질 실패는 대부분 특이한 것이 아니다. 같은 몇 가지가 되풀이되며, 저마다 값싼 점검 방법이 있다. 아래 점검표는 코드 리뷰와 8장의 부정 시험 벡터를 만드는 좋은 출발점이다.

함정무엇이 잘못되는가점검 방법
축 순서GeoJSON은 경도, 위도 순서이고, PHASE 1의 coordinates 객체는 latitude와 longitude를 이름으로 명시하며, EPSG:4326을 쓰는 일부 OGC 요청은 위도를 먼저 기대한다. 둘을 바꾸면 SAHEL-REGION-001(14.5, −4.0)이 수천 km 떨어진 적도 남쪽 중앙아프리카의 한 지점으로 옮겨진다.모든 기록 경로에서 기준점이 같은 자리로 왕복하는지 시험한다. ±90을 벗어난 위도는 거부한다.
단위 불일치어떤 레코드는 토양 양분을 "ppm"으로, 다른 레코드는 퍼센트로 적는다. 바이오매스는 ha당 톤인데 탄소는 CO₂ 톤이다.모든 정량 필드에 명시적 unit을 요구하고, 수집 시 양마다 하나의 정규 단위로 변환한다.
퍼센트와 비율명세는 둘을 섞어 쓴다. confidence: 0.92는 비율이고 cloudCover: 5와 survivalRate: 82.5는 퍼센트다. 비율을 퍼센트로 저장하면 신뢰도 92 %가 0.92 %가 된다.스키마에 필드별 표기 규칙을 명시하고 그에 맞게(0~1 또는 0~100) 범위를 검증한다.
NDVI 범위예제는 NDVI에 scale: "0-1"을 붙였지만 검증 규칙은 −1~1을 허용한다. 음수를 0으로 잘라 내면 수면과 젖은 나지가 가려진다.규칙대로 −1..1을 받아들이고 절대 잘라 내지 않는다.
시간 해상도 혼합16일 MOD13Q1 합성 영상을 일 단위 값이나 단일 장면 값과 비교하면 가짜 급변이 생긴다.모든 레코드에 source와 합성 기간을 싣고, 추세는 한 산출물 안에서만 계산한다.
피복 합계토지 피복 비율의 합이 100 %가 아니거나, 같은 지역에 대해 레코드 유형마다 시가지 비율이 다르다.작은 허용 오차로 합계를 검증하고, 서로 다른 레코드 유형의 비율을 섞지 않는다.
나무 이중 계산같은 식재가 국가 사업, 지역 이니셔티브, 나무 심기 네트워크 등 여러 프로그램에 보고되어 프로그램마다 한 번씩 계산된다.식재마다 안정적인 식별자를 주고 프로그램은 이를 참조하게 한다. 수량을 각 프로그램 레코드에 복사하지 않는다.
시간대와 날짜 전용 필드erosionData.assessmentDate는 날짜 전용("2025-12-25")인데 위험 API의 assessmentDate는 UTC 타임스탬프다. 현지 날짜를 UTC로 바꾸면 전날로 밀릴 수 있다.날짜 전용 필드는 달력 날짜로 유지하고, 타임스탬프는 Z를 붙인 UTC로 저장하며, 둘을 암묵적으로 변환하지 않는다.
파생값 복사예제의 overallScore나 식재 수를 계산하지 않고 그대로 저장한다.파생 필드는 입력값에서 다시 계산하고, 앞의 67.75 점수처럼 손으로 풀어 본 예로 시험한다.

마지막 함정은 기술이 아니라 조직의 문제다. 명세의 예제 수치를 참조 데이터로 취급하는 것이다. 이 책이 위험 점수, SPI 라벨, 탄소 총량, 식재 수 같은 여러 내부 불일치를 짚어 온 것은, 구현자가 JSON 예제에 우연히 적힌 값이 아니라 산술과 정의에 기대어 시험을 만들도록 하기 위해서다.

이 장의 요약