📦 목차

제6장: MRV와 탄소제거 크레딧 — 검증에서 등록부·검증가능 자격증명까지

앞 장들에서 DAC 플랜트는 데이터가 되었다. 시설 레코드, 포집 레코드, 흡착제 레코드, 저장 이벤트가 공통 API로 교환되고, 감사 가능한 운영 프로토콜이 그 데이터를 만들어 낸다. 그러나 이것만으로는 아직 제거 주장(removal claim)이 아니다. CO2 1톤이 기업이 사서 폐기할 수 있는 대상이 되려면 독립된 제3자가 증거를 검토하고, 등록부가 그 양에 일련번호가 붙은 단위를 발행하고, 이후의 소유권 이전과 폐기가 모두 같은 검증 수량으로 거슬러 올라갈 수 있어야 한다. 이 장은 사슬의 마지막 구간을 다룬다.

WIA-ENE-050에서 이 작업은 세 곳에 나뉘어 있다. 검증 프로토콜 DAC-MRV-001(PHASE-3-PROTOCOL §5.1), 검증 보고서와 탄소 크레딧 스키마(PHASE-2-DATA §6–7), 그리고 검증기관·등록부·검증가능 자격증명·블록체인 통합(PHASE-4-INTEGRATION §5–6)이다. 명세는 각 조각을 설명하지만 조각 사이에 빈틈을 남긴다. 불확도가 어떻게 검증 수량으로 바뀌는지, 정확히 무엇에 서명하는지, 크레딧이 여러 곳에 존재하게 되었을 때 어느 원장이 기준인지가 정해져 있지 않다. 이 장은 사슬을 순서대로 따라가며, 명세를 넘어서는 권고는 책의 권고임을 밝힌다.

두 가지 전제를 먼저 밝혀 둔다. 첫째, 명세의 예시는 실존 운영사·검증기관·등록부의 이름에 지어낸 수치를 붙여 놓았다. 이 장의 예시는 가상의 시설 dac-example-reykjanes-01과 가상의 "Example Verification Body"를 쓰며, 모든 수치는 예시임을 표시한다. 둘째, 등록부는 자체 방법론과 규정을 공표하고 개정한다. 이 장은 WIA 데이터가 등록부 업무 흐름에 어떻게 대응하는지를 일반적인 수준에서 설명할 뿐, 특정 등록부의 현행 규정을 옮겨 적지 않는다. 사업자는 해당 규정의 최신판을 직접 확인해야 한다.

6.1 DAC-MRV-001의 주기

MRV는 모니터링·보고·검증(Monitoring, Reporting and Verification)의 약어다. 세 단어는 서로 다른 주체가 수행하는 서로 다른 활동이다. 모니터링은 운영자가 플랜트를 측정하는 일이고, 보고는 운영자가 측정 결과를 공개하거나 제출하는 일이며, 검증은 독립 기관이 보고 내용이 측정으로 뒷받침되는지 확인하는 일이다. DAC-MRV-001은 활동마다 별도의 주기를 둔다.

6.1.1 네 단계의 모니터링

PHASE-3-PROTOCOL §5.1.1은 모니터링을 네 단계로 정의한다. 표 6-1은 명세가 각 단계에 넣은 항목과 그 정보를 담는 WIA 레코드를 정리한 것이다. 마지막 열은 각 단계가 어떻게 데이터가 되는지에 대한 이 책의 해석이다.

표 6-1. DAC-MRV-001 모니터링 단계와 생성 레코드
단계명세상 항목담는 레코드증거가 되는 방식
연속(실시간)CO2 포집률, 에너지 소비, 주변 환경 조건, 설비 상태실시간 포집 레코드(PHASE-2-DATA §3)원시 시계열을 QC 플래그와 함께 변경 없이 보존(제5장)
일간흡착제 상태, 성능 지표, 안전 점검, 백업 확인흡착제 레코드, 점검표 레코드, 일간 요약일간 요약에 서명하거나 해시를 남겨 사후 수정을 탐지 가능하게 한다
월간포집 총량 집계, 에너지 효율 분석, 흡착제 열화 추세, 정비포집·에너지 레코드에서 파생한 집계, 정비 레코드각 집계는 입력 원시 레코드와 계산 알고리즘 버전을 명시한다
연간제3자 검증, 종합 시스템 감사, 규제 준수 검토, 장기 성능 추세검증 보고서(PHASE-2-DATA §6)검증 보고서가 위의 모든 것을 ID와 다이제스트로 참조한다

네 단계는 겹겹이 쌓여 있어서, 각 단계의 신뢰도는 그 아래 단계를 넘어설 수 없다. 실무 원칙은 입력을 가리키지 않는 집계는 존재해서는 안 된다는 것이다. 보존된 원시 데이터로 다시 계산할 수 없는 월간 총량은 검증기관이 근거 없는 주장으로 취급해야 한다. FOUNDATION §5.1이 원시 데이터를 7년, 검증 기록을 영구 보존하도록 요구하는 이유가 여기에 있다(제2장).

명세는 검증 빈도를 "최소 연 1회"로 정한다(FOUNDATION §6.2.2). 더 자주 검증하는 것을 막는 규정은 없다. 제거량을 선도 판매하거나 분기마다 크레딧을 발행하려는 플랜트는 분기 단위 검증 기간을 운영할 수 있다. 검증 보고서가 연도가 아니라 시작일과 종료일을 가진 period를 명시적으로 담기 때문에 데이터 모델은 이미 이를 지원한다.

6.1.2 네 갈래의 보고

PHASE-3-PROTOCOL §5.1.2는 수신자가 서로 다른 네 갈래의 보고를 정의한다. 표 6-2는 이를 제7장에서 공개 데이터에 부여하는 접근 등급과 함께 정리한 것이다.

표 6-2. DAC-MRV-001 보고 갈래
갈래주기명세상 내용수신자와 접근 등급
내부일간운영 대시보드, 사고 기록, 생산 요약운영 인력, 비공개(private)
공개월간CO2 포집 총량(t), 에너지(MWh), 가동률(%), "탄소 집약도"(MWh/t CO2), WIA-ENE-050 준수 API로 제공누구나, 공개(public)
규제연간배출·제거 종합 보고, 환경영향 평가, 안전사고, 규정 준수 인증관할 당국, 파트너(partner) 또는 비공개
검증연간제3자 감사 보고서, 크레딧 발행 요청, 방법론 문서검증기관과 등록부, 파트너

공개 보고의 항목 이름 하나는 공개 전에 바로잡아야 한다. 명세는 CO2 1톤당 MWh를 "탄소 집약도(carbon intensity)"라고 부르지만, 이 양은 시설·포집 레코드에서 쓰는 용어인 비에너지(톤당 에너지, specific energy)다. 탄소 집약도는 보통 산출물 단위당 배출량, 예를 들어 제거한 1톤당 배출한 tCO2e를 뜻한다. 두 수치 모두 유용하고 공개 보고서에 함께 실을 수 있지만 같은 이름을 써서는 안 된다. 이 책은 specificEnergy(MWh/t)를 공개하고, 이와 별도로 총포집량 대비 전과정 배출량의 비율을 공개하기를 권한다. 구매자가 실제로 묻는 것은 후자다.

공개 월간 수치는 또한 검증 전의 총량이다. 공개 대시보드는 이 수치에 "포집량, 미검증"이라고 표시하고, "검증된 순제거량"은 검증 기간이 종료된 구간에만 보여 주어야 한다.

6.2 검증 단계와 검증 패키지

PHASE-3-PROTOCOL §5.1.3은 검증을 다섯 단계로 기술한다. 문서 검토, 현장 점검, 데이터 분석, 검증 의견서, 인증 발급이다. 이 구성은 온실가스 선언의 타당성 평가와 검증에 관한 국제표준인 ISO 14064-3의 일반적인 검증 구조를 따르며, 검증을 수행하는 기관은 보통 환경정보의 타당성 평가·검증 기관에 대한 요구사항을 정한 ISO 14065에 따라 인정을 받는다. WIA 표준은 두 문서 어느 것도 대체하지 않는다. WIA 표준의 몫은 증거가 예측 가능하고 기계로 점검할 수 있는 형태로 도착하게 만드는 데 있다.

6.2.1 MRV 순서도

그림 6-1은 플랜트에서 폐기된 크레딧에 이르는 전체 순서를 보여 준다. 화살표 하나하나가 메시지 또는 레코드이고, 각 열은 서로 다른 조직이다.

 OPERATOR PLANT      MRV PLATFORM          VERIFIER             REGISTRY           BUYER
      |                   |                    |                    |                 |
      |-- capture recs -->|                    |                    |                 |
      |-- storage evts -->| status: pending-verification            |                 |
      |                   |                    |                    |                 |
      |    (period closes)|                    |                    |                 |
      |                   |-- verification --->|                    |                 |
      |                   |   package + digest | 1 document review  |                 |
      |<------------------------ site visit ---| 2 on-site          |                 |
      |                   |<-- data queries ---| 3 data analysis    |                 |
      |                   |<-- report + VC ----| 4 statement        |                 |
      |                   |   (verify-...)     |                    |                 |
      |                   |-- issuance request (report ID) -------->| 5 issuance      |
      |                   |<-- serial range, status: issued --------|                 |
      |                   |                    |                    |-- transfer ---->|
      |                   |                    |                    |<-- retire ------|
      |                   |<-- status: retired, beneficiary --------|                 |
      |                   |  (update credit record, VC status, token)                 |
그림 6-1. 포집 레코드에서 폐기된 크레딧까지의 MRV 순서. 운영 플랜트(OPERATOR PLANT)·MRV 플랫폼·검증기관(VERIFIER)·등록부(REGISTRY)·구매자(BUYER) 사이의 흐름이다. 크레딧 상태의 기준 원장은 등록부이며 MRV 플랫폼은 이를 반영(미러링)한다.

그림에는 두 가지 설계 선택이 들어 있다. 검증기관은 운영 데이터베이스에 직접 접속하는 대신 다이제스트가 붙은 패키지를 받는다. 그래야 나중에 무엇을 검증했는지를 정확히 특정할 수 있다. 또한 상태 정보는 등록부에서 MRV 플랫폼으로 흐르며, 그 반대가 아니다. 이유는 6.4절에서 설명한다.

6.2.2 각 단계의 입력과 출력

표 6-3. 데이터 교환으로 본 검증 단계
단계(명세)검증기관이 살피는 것WIA 입력출력 레코드
1. 문서 검토시설 설계, 운영 절차, QA/QC 절차, 과거 성능 데이터, 교정 기록시설 레코드, 프로토콜 문서, 교정·정비 레코드observation 또는 non-conformance 범주의 발견 사항
2. 현장 점검설비 순회, 계측기 교정 확인, 운영자 면담, 운영 절차 입회점검표 레코드, 교정 성적서와 연결된 계측기 일련번호발견 사항, 검증기관이 보관하는 사진과 기록
3. 데이터 분석계산 방법, 출처 간 교차 확인, 이상치 통계, 불확도포집·에너지·저장 레코드, QC 플래그, 집계 코드 버전재계산한 총량, 불확도 평가(6.3절)
4. 검증 의견서검증된 제거량, 신뢰수준, 불확도 범위, 적합성, 개선 권고1–3단계의 결과conclusion을 담은 검증 보고서(verify-…)
5. 인증 발급자격증명 발급, 등록부 통보, 공개 보고서 게시검증 보고서검증가능 자격증명(6.5절), 등록부 발행 요청(6.4절)

데이터 표준의 효과가 가장 크게 드러나는 곳은 셋째 단계다. 레코드가 PHASE-2-DATA를 따르면 검증기관은 운영자가 쓰는 정합성 점검(제2장 2.7절)과 QC 규칙(제5장 5.7절)을 그대로 돌려 기간 총량을 독립적으로 재계산할 수 있다.

6.2.3 검증 패키지

PHASE-4-INTEGRATION §6.1.1은 자동 내보내기 절차를 개략적으로 제시한다. 한 기간의 시설 정보, 포집 데이터, 에너지 데이터, 저장 이벤트, 교정 기록, 정비 기록을 모으고, 버전과 표준 식별자로 감싼 뒤 체크섬을 붙여 검증기관 포털로 전송한다. 발상은 타당하지만 예제 코드에는 그대로 옮겨서는 안 되는 문제가 세 가지 있다.

그렇게 구성한 패키지 매니페스트는 다음과 같다. 다이제스트는 줄여 쓴 자리표시자다.

{
  "packageId": "vpkg-example-reykjanes-01-2025",
  "standard": "WIA-ENE-050",
  "standardSlug": "direct-air-capture",
  "schemaVersion": "1.0.0",
  "facilityId": "dac-example-reykjanes-01",
  "period": { "startDate": "2025-01-01", "endDate": "2025-12-31" },
  "createdAt": "2026-01-15T09:00:00Z",
  "files": [
    { "path": "facility.json",            "records": 1,      "sha256": "9f2c…" },
    { "path": "capture/2025-*.jsonl",     "records": 525600, "sha256": "41ab…" },
    { "path": "storage-events.jsonl",     "records": 48,     "sha256": "c07e…" },
    { "path": "calibration.jsonl",        "records": 12,     "sha256": "77d1…" },
    { "path": "maintenance.jsonl",        "records": 96,     "sha256": "e5a0…" },
    { "path": "aggregates/monthly.json",  "records": 12,     "sha256": "0b3f…",
      "derivedFrom": ["capture/2025-*.jsonl"], "algorithm": "monthly-agg@2.3.1" }
  ],
  "canonicalization": "RFC 8785",
  "manifestDigest": "sha256:5d8e…"
}

레코드 수 525,600은 365일 동안 1분에 하나씩 기록한 경우로, 예시를 위한 값일 뿐이다. derivedFrom과 algorithm 필드는 6.1절의 "입력 없는 집계 금지" 원칙을 명시적으로 드러낸다. 검증기관이 패키지를 접수하면 그 manifestDigest가 검증 보고서(확장 필드)에 들어가고, 이후 검증가능 자격증명에도 들어간다. 그때부터는 누구든 어떤 자격증명이 정확히 이 증거를 가리키는지 확인할 수 있다.

6.2.4 정확도 목표

FOUNDATION §4.4는 검증기관이 대조할 목표를 정한다. CO2 포집은 불확도 ±5 %, 에너지 소비는 ±3 %, 저장 검증은 95 % 초과 신뢰도, 시간 해상도는 1시간 이하다. 프로토콜 본문도 검증 의견서에 검증 수량, 신뢰수준("예: 95 %"), 불확도 범위("±X %")를 담도록 하여 같은 생각을 되풀이한다.

유용한 목표이지만 쓰인 그대로는 세 가지 면에서 모호하다. ±5 %가 표준불확도인지 확장불확도인지 밝히지 않고, 백분율의 기준이 총포집량인지 순제거량인지도 밝히지 않으며, 어떤 기간이 목표를 넘으면 어떻게 되는지도 정하지 않는다. 6.3절은 이 목표를 세 질문에 모두 답하는 계산으로 바꾼다.

6.3 불확도와 보수성

제1장의 순제거량 공식에 들어가는 항은 모두 어느 정도의 불확도를 안고 측정되거나 추정된다. 검증 수량은 그 불확도를 반영해야 하고, 조정의 방향은 보수적이어야 한다. 의심스러우면 더 많이가 아니라 더 적게 인정한다. 이 절은 두 가지를 모두 충족하는 방어 가능한 방법 하나를 보여 준다. 방법은 측정불확도 표현 지침(GUM, ISO/IEC Guide 98-3으로 발행)을 따른다. 등록부나 방법론이 별도 절차를 규정한다면 그 절차가 우선한다.

6.3.1 순제거량으로의 불확도 전파

순제거량은 여러 항의 합과 차다.

N = S − E − M − L
uc(N) = √( u(S)² + u(E)² + u(M)² + u(L)² )   (서로 독립인 항)
S = 저장된 CO2(인수인계 계량기) · E = 에너지 관련 배출 · M = 해당 기간에 배분한 내재(전과정) 배출 · L = 손실과 누출

서로 독립인 항이라면 톤 단위 표준불확도를 제곱합의 제곱근으로 합성한다. 틀리기 쉬운 점이 두 가지 있다. 첫째, 이 식은 백분율이 아니라 톤 단위의 절대 불확도를 더한다. 작은 항의 ±10 %는 큰 저장량의 ±2 %보다 훨씬 적게 기여한다. 둘째, 입력을 공유하는 항은 독립이 아니다. 에너지 배출과 손실 추정이 같은 계량기에서 나왔다면 두 오차는 함께 움직이므로 공분산 항을 넣어 합성하거나, 보수적으로 선형 합산해야 한다.

계량기 사양은 보통 표준불확도가 아니라 한계값("±2 % 정확도")으로 표시된다. GUM에 따르면 다른 정보가 없는 한계값은 직사각형 분포로 보고, 반폭을 √3으로 나눈 값을 표준불확도로 쓴다.

6.3.2 계산 예시(예시 수치)

가상의 플랜트가 다음 수치로 연간 검증 기간을 마감했다고 하자. 모든 값은 예시를 위해 지어낸 것이며 실제 시설의 전형적인 값이 아니다.

표 6-4. 한 검증 기간의 불확도 예산(예시)
항값(t CO2)불확도 근거표준불확도(t)
S — 저장량, 인수인계 계량기1,000.0계량기 한계 ±2 %, 직사각형 분포: 20 / √311.5
E — 에너지 배출(2,000 MWh × 0.04 t/MWh)80.0계량기와 배출계수를 합쳐 10 %로 추정8.0
M — 기간 배분 내재 배출40.0전과정평가 추정, 25 %10.0
L — 계량기와 저장지 사이 손실5.0공학적 추정, 50 %2.5
N — 순제거량875.0제곱합의 제곱근17.4

합성표준불확도는 √(11.5² + 8.0² + 10.0² + 2.5²) ≈ 17.4 t이다. 정규분포에서 약 95 % 포함 확률에 해당하는 포함인자 k = 2를 쓰면 확장불확도는 약 34.8 t, 곧 순제거량의 4.0 %다. FOUNDATION §4.4의 목표에 비추어 보면 포집 측정 자체(한계 ±2 %)와 순제거 결과(확장불확도 ±4.0 %) 모두 ±5 % 안에 든다.

목표는 순제거량을 기준으로 정해야 한다. 차감 항에도 각자의 불확도가 있는데 순제거량은 총량보다 작으므로, 에너지 배출이나 내재 배출이 큰 플랜트는 모든 계량기가 사양을 충족하더라도 순제거량 기준으로는 목표를 넘을 수 있다.

6.3.3 보수적 차감

875 t은 최선 추정값이다. 그러나 이 값은 대략 절반의 경우 실제보다 크다. 보수적 관행은 신뢰 하한을 인정하는 것이다. 단측 95 % 하한(정규분포에서 k ≈ 1.645)을 쓰면 다음과 같다.

verifiedAmount = N − 1.645 × uc(N) = 875.0 − 1.645 × 17.4 ≈ 846.4 → 846 t(내림)

약 29 t의 차감은 불확도의 대가이며, 운영자에게 불확도를 줄일 직접적인 동기를 준다. 계량을 개선하거나, 전과정평가 문서를 보강하거나, 손실 항을 추정 대신 측정하면 차감이 줄어든다. ±5 % 목표가 의미를 갖는 것은 이 연결 덕분이다. 목표를 넘은 기간은 거부되는 것이 아니라 더 큰 차감을 받는다. 불확도가 너무 커서 증거가 어떤 양(+)의 수치도 확신 있게 뒷받침하지 못하면 결론은 not-verified가 된다.

6.3.4 검증 보고서에 불확도 기록하기

검증 보고서 스키마에는 "측정 불확도 백분율"로 설명된 숫자 필드 uncertainty 하나만 있다. 이 숫자만으로는 해석할 수 없다. 이 책은 이 필드에 항상 순제거량 기준 상대 확장불확도를 넣고, 근거를 밝히는 확장 필드를 덧붙이기를 권한다. 위 계산 예시의 보고서는 다음과 같다.

{
  "reportId": "verify-example-reykjanes-01-2025",
  "facilityId": "dac-example-reykjanes-01",
  "period": { "startDate": "2025-01-01", "endDate": "2025-12-31" },
  "verifiedAmount": 846,
  "uncertainty": 4.0,
  "verifier": {
    "organization": "Example Verification Body",
    "accreditation": "ISO 14065 accreditation (reference number of the accreditation body)",
    "auditor": "lead-auditor-07"
  },
  "standards": ["ISO 14064-2", "ISO 14064-3"],
  "methodology": "Registry methodology name and version, as applied",
  "findings": [
    { "category": "observation", "severity": "minor",
      "description": "Loss term L estimated from engineering data; direct measurement recommended." }
  ],
  "conclusion": "verified",
  "verificationDate": "2026-02-20",
  "nextVerificationDue": "2027-02-20",
  "x-uncertaintyBasis": {
    "bestEstimate": 875.0,
    "combinedStandardUncertainty": 17.4,
    "expandedUncertainty": { "k": 2, "value": 34.8, "relativeTo": "net", "percent": 4.0 },
    "conservativeBound": { "oneSidedConfidence": 0.95, "value": 846.4 },
    "method": "GUM (ISO/IEC Guide 98-3), independent terms"
  },
  "x-packageDigest": "sha256:5d8e…"
}

standards에 ISO 27916이 아니라 ISO 14064-2와 ISO 14064-3을 넣었다는 점에 주의해야 한다. 명세의 예시는 ISO 27916을 검증 표준으로 인용하지만, ISO 27916은 원유회수증진(EOR)과 연계된 CO2 저장을 다루는 표준이다. DAC 제거량을 검증하는 일반 표준이 아니므로, 해당 저장이 실제로 EOR 연계일 때에만 적어야 한다. methodology 값에는 등록부가 공표한 방법론 이름과 버전을 그대로 적는다.

6.4 등록부 업무 흐름

검증된 수량은 아직 거래 가능한 단위가 아니다. 단위를 만드는 것은 등록부다. 등록부는 계정을 관리하고, 승인된 증거에 대해 일련번호가 붙은 단위를 발행하며, 계정 간 이전과 폐기를 기록하는 조직이다. FOUNDATION §6.2.1은 네 개의 크레딧 프로그램을 든다. Puro.earth, Verra의 Verified Carbon Standard, Gold Standard, American Carbon Registry다. 탄소 크레딧 스키마의 registry 열거형은 이 넷과 Other를 허용한다.

6.4.1 Puro.earth를 예로 본 생애주기

PHASE-4-INTEGRATION §5.1은 Puro.earth를 예제로 쓴다. Puro.earth는 제거되어 저장된 CO2 1톤을 나타내는 CO2 제거 인증서(CORC)를 발행한다. 명세의 등록·발행 호출은 api.puro.earth/v1 형태의 기본 URL을 전제로 쓰여 있는데, 이 URL과 페이로드는 예시다. 공표된 등록부 API가 아니며, 실제 절차에는 등록부 자체의 문서, 심사, 계정 체계가 개입한다. 옮겨 쓸 수 있는 것은 단계의 순서이며, 이는 프로그램마다 대체로 비슷하다.

표 6-5. 등록부 생애주기와 단계별 WIA 레코드
단계일어나는 일WIA 산출물이후 상태
사업 등록사업자가 시설, 제거 방법, 저장 방식, 선택한 방법론을 기술한다시설 레코드, 저장 계획, 확장 필드에 저장한 등록부 사업 ID등록됨
타당성 평가크레딧 산정 전에 독립 기관이 사업 설계를 방법론에 비추어 점검한다시설 레코드와 함께 보관하는 타당성 평가 결과타당성 확인·적격
모니터링과 검증크레딧 기간마다 검증한다(6.2절)검증 보고서 verify-…검증 수량 확정
발행등록부가 사업자 계정에 일련번호가 붙은 단위를 만든다serialNumber를 가진 탄소 크레딧 레코드, status: issued발행됨
이전단위가 구매자 계정으로 옮겨진다owner 갱신, status: transferred이전됨
폐기보유자가 수혜자를 지정해 단위를 주장에 영구 사용한다status: retired, 폐기 세부사항은 확장 필드폐기됨(최종)
취소오류나 반전(reversal) 등으로 단위를 철회한다사유와 함께 status: cancelled취소됨(최종)

탄소 크레딧 스키마의 status 열거형(issued, transferred, retired, cancelled)은 마지막 네 단계를 다룬다. 그 앞의 사업 상태는 크레딧이 아니라 시설에 속하므로 이 열거형에 없다.

6.4.2 WIA 필드와 등록부 개념의 대응

등록부는 저마다 필드 이름을 쓰고, 그 이름은 시스템과 버전에 따라 바뀐다. 따라서 아래 대응표는 모든 프로그램이 어떤 형태로든 가지고 있는 일반적인 등록부 개념을 기준으로 적었다.

표 6-6. WIA 필드와 대응하는 등록부 개념
WIA 필드등록부 개념구현 메모
facilityId사업 또는 시설 식별자등록부는 자체 사업 ID를 부여한다. 이를 따로 저장하고(예: x-registryProjectId) facilityId를 등록부 키로 재사용하지 않는다
검증 보고서 period모니터링 또는 크레딧 기간같은 시설·같은 저장 흐름에 대해 기간이 겹쳐서는 안 된다
verifiedAmount발행 적격 수량등록부가 자체 차감이나 버퍼 적립을 적용할 수 있으므로 발행량은 더 적을 수 있다
vintage빈티지명세는 빈티지를 제거가 일어난 연도로 정의한다. 프로그램마다 정의가 다르므로 등록부의 정의를 확인한다
serialNumber일련번호 또는 일련번호 블록등록부 값을 그대로 저장하며, 자체 번호를 만들지 않는다
methodology방법론 이름과 버전정확히 복사한다. 버전이 바뀌면 적격성이 달라질 수 있다
verifier.organization, accreditation타당성 평가·검증 기관등록부는 보통 승인 기관 목록을 자체적으로 관리한다
owner계정 보유자반영용일 뿐이며, 등록부 계정이 기준이다
status단위 상태반영용일 뿐이며, 등록부 통지나 주기 조회로 갱신한다
(없음)폐기 수혜자와 목적확장 필드를 추가한다. 예: 수혜자·목적·일자를 담은 x-retirement

6.4.3 일련번호와 "1톤 1단위" 원칙

명세는 일련번호에 대해 일관되지 않다. Puro 예제에서는 1,000 t을 발행하면 톤마다 하나씩 일련번호 목록이 돌아온다. WIA 엔드포인트 POST /credits/issue(PHASE-4 §2.1.2)에서는 1,000 t을 발행하면 serialNumber 하나와 amount: 1000을 가진 크레딧 하나가 돌아온다. 둘 다 쓸 수는 있지만 시스템은 하나를 골라야 한다. 이 책은 크레딧 레코드를 일련번호 블록으로 다루기를 권한다. 발행 한 건당 레코드 하나를 두고, 블록의 첫 번호와 끝 번호를 확장 필드에 담는다. 블록을 쪼개는 이전이나 폐기가 일어나면 쪼개진 부분마다 새 레코드를 만든다. 등록부가 발행을 보고하는 일반적인 방식과 맞고, 100만 톤에 100만 행짜리 표를 만들 필요도 없다.

어떤 표현을 택하든 이중 계산을 막는 불변 조건은 세 가지이며, MRV 플랫폼은 이를 코드 리뷰가 아니라 데이터베이스 제약으로 강제해야 한다.

  1. (등록부, 일련번호) 쌍은 취소된 것까지 포함한 모든 크레딧 레코드에서 유일하다.
  2. 각 검증 보고서에 대해, 그 보고서를 참조하는 크레딧의 amount 합은 보고서의 verifiedAmount를 넘지 않는다.
  3. 저장 이벤트 하나는 최대 한 개의 검증 기간에만 기여한다. 따라서 저장 이벤트는 자신을 소비한 reportId를 담고, 두 번째 보고서는 이를 주장할 수 없다.

셋째 조건은 스키마가 열어 둔 빈틈을 막는다. 저장 이벤트는 pending-verification 상태로 제출되지만(제4장), 데이터 모델 어디에도 나중에 어느 보고서가 그 이벤트를 검증했는지 적는 곳이 없다. 이 역참조가 없으면 같은 주입이 겹치는 두 보고서에서 두 번 계산될 수 있다.

6.4.4 어느 원장이 기준인가

크레딧이 발행되면 그 상태는 여러 곳에 존재한다. 등록부, 운영자의 MRV 플랫폼, 경우에 따라 검증가능 자격증명과 블록체인 토큰이다. 이 가운데 기준 원장은 하나뿐이며, 등록부가 발행한 단위라면 그것은 등록부다. WIA 크레딧 레코드, 자격증명, 토큰은 모두 반영본이다. 이들은 등록부를 따라 갱신되어야 하고, 이를 이용하는 쪽은 신뢰하기 전에 등록부나 등록부에서 파생된 상태 정보를 확인해야 한다.

실무에서 MRV 플랫폼은 등록부의 통지를 구독하고, 통지가 없으면 주기적으로 조회한다. 폐기 통지가 오면 크레딧 레코드를 retired로 바꾸고, 폐기 확장 필드에 수혜자를 기록하고, 자격증명 상태를 갱신하고(6.5절), 대응하는 토큰이 있으면 소각하거나 잠근다(6.6절). 거꾸로 토큰 소각이나 플랫폼 상태 변경으로 등록부에 폐기를 알리는 구조는 1톤을 두 번 주장하게 만드는 바로 그 구조다.

6.5 W3C 검증가능 자격증명

등록부 항목은 단위가 존재한다는 사실과 누가 보유하는지를 증명한다. 그러나 그것만으로는 구매자가 어디에도 로그인하지 않고 확인할 수 있는, 무엇이 검증되었는지에 대한 휴대 가능한 서명 진술을 얻지 못한다. PHASE-4 §6.2는 이 역할을 CarbonRemovalCertificate 유형의 W3C 검증가능 자격증명(VC)으로 채우고, 시뮬레이터 5번 탭(QR & VC)은 이런 자격증명을 QR 코드로 제시하는 방식을 보여 준다.

6.5.1 자격증명이 담아야 할 주장

VC에는 발급자, 주체(subject), 그리고 주체에 관한 주장들이 있고, 발급자의 서명이 이를 보호한다. 제거 인증서의 자연스러운 발급자는 검증 의견을 내는 당사자인 검증기관이다. 운영자도 자체 자격증명을 발급할 수는 있지만, 스스로 발급한 제거 인증서는 서명 없는 보고서에 비해 보태는 것이 거의 없다.

주장에는 검증기관이 실제로 확인한 내용만 담고, 나중에 바뀔 수 있는 것은 넣지 않아야 한다. 시뮬레이터의 예제 자격증명은 크레딧의 등록부 상태("status": "active"), 기술의 효율, 운영자의 비에너지를 담고 있다. 상태는 크레딧이 폐기되는 순간 바뀌는데, 한 번 서명한 자격증명은 수정할 수 없다. 바뀌는 사실은 등록부나 별도의 상태 메커니즘에 두어야 하며, W3C Bitstring Status List 명세가 자격증명의 폐지·정지 상태를 공개하는 표준 방법을 제공한다. 자격증명 자체에는 시설, 기간, 검증 수량, 저장 방식, 적용 표준, 보고서 식별자, 증거 패키지 다이제스트를 담는다.

위 계산 예시에 대한 JWT 인코딩 제거 인증서의 자격증명 부분을 풀어 보면 다음과 같다.

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://mrv.example.com/contexts/dac-removal/v1"
  ],
  "type": ["VerifiableCredential", "CarbonRemovalCertificate"],
  "issuer": "did:web:verifier.example.com",
  "issuanceDate": "2026-02-20T00:00:00Z",
  "credentialSubject": {
    "id": "https://mrv.example.com/reports/verify-example-reykjanes-01-2025",
    "type": "VerifiedCarbonRemoval",
    "facilityId": "dac-example-reykjanes-01",
    "period": { "startDate": "2025-01-01", "endDate": "2025-12-31" },
    "verifiedAmount": { "value": 846, "unit": "t CO2" },
    "uncertaintyPercent": 4.0,
    "storageMethod": "mineralization",
    "standards": ["ISO 14064-2", "ISO 14064-3"],
    "conclusion": "verified",
    "evidenceDigest": "sha256:5d8e…",
    "registry": { "name": "Puro.earth", "serialBlock": "EXAMPLE-SERIAL-0001 … EXAMPLE-SERIAL-0846" }
  }
}

이 예시는 명세의 예제와 세 가지 점에서 다르다. 첫째, 주체는 포집 이벤트 하나를 가리키는 did:wia:capture:… 식별자가 아니라 해석 가능한 URL로 식별한 검증 보고서다. 검증된 것은 보고서이며, 이벤트마다 DID를 부여하면 보증은 늘지 않고 키 관리 부담만 는다. 둘째, 발급자는 누구나 일반 HTTPS 요청으로 해석할 수 있는 did:web을 쓴다. 명세의 did:wia: 식별자는 did:wia 메서드가 명세되고 검증자가 쓸 수 있는 리졸버가 갖춰진 뒤에야 쓸 수 있으며, 그 전까지는 이 식별자로 발급한 자격증명을 외부인이 확인할 수 없다. 셋째, 사용자 정의 컨텍스트 URL은 발급자가 관리하는 도메인에 둔다. 명세의 wia.org/credentials/dac/v1 컨텍스트는 예시이며, 해석되지 않는 JSON-LD 컨텍스트는 엄격한 검증기에서 처리 실패를 일으킨다.

예시는 명세의 예제와 아래 JWT 라이브러리가 쓰는 VC 데이터 모델 1.1 컨텍스트를 따랐다. W3C는 이후 VC 데이터 모델 2.0을 발표했으며, 여기서는 기본 컨텍스트와 일부 속성 이름이 바뀌었다(예: issuanceDate 대신 validFrom). 명세는 보호 방식도 섞어 쓴다. 시뮬레이터는 내장형 Ed25519Signature2020 증명을 보여 주고, PHASE-4 코드는 JWT를 만든다. 배포 환경은 데이터 모델 버전 하나와 보호 방식 하나를 정하고, 그 선택을 프로파일 매니페스트에 기록해야 한다(제8장).

6.5.2 did-jwt-vc로 발급하고 검증하기

PHASE-4 §6.2는 did-jwt-vc 라이브러리를 쓴다. 발급 예제는 createVerifiableCredential이라는 함수에 중첩 객체를 넘긴다. 이 라이브러리에서 JWT 자격증명을 만드는 문서화된 함수는 createVerifiableCredentialJwt이며, 자격증명을 vc 클레임에 담은 JWT 페이로드를 받는다. 검증 예제의 결함은 더 심각하다. verifyCredential을 const로 가져온 뒤 같은 스코프에 async function verifyCredential을 다시 선언한다. JavaScript는 함수 선언으로 const 바인딩을 재선언하는 것을 허용하지 않으므로 모듈 자체가 로드되지 않는다. 만약 함수를 안쪽 스코프에 선언했다면 가져온 함수를 가리고(shadowing) 스택이 넘칠 때까지 자기 자신을 재귀 호출했을 것이다.

다음 스케치는 두 문제를 모두 고친 것이다. 예시이므로 설치한 라이브러리 버전과 대조해야 하며, 가능하면 개인키는 애플리케이션 메모리가 아니라 키 관리 시스템에 두어야 한다.

// Illustrative sketch — verify against the installed library versions.
import { createVerifiableCredentialJwt, verifyCredential } from 'did-jwt-vc';
import { EdDSASigner } from 'did-jwt';
import { Resolver } from 'did-resolver';
import { getResolver as getWebResolver } from 'web-did-resolver';

const issuer = {
  did: 'did:web:verifier.example.com',
  signer: EdDSASigner(loadIssuerKey()),   // placeholder: replace with your KMS/HSM key lookup
  alg: 'EdDSA',
};

export async function issueRemovalCertificate(report, packageDigest) {
  const payload = {
    sub: `https://mrv.example.com/reports/${report.reportId}`,
    nbf: Math.floor(Date.parse(report.verificationDate) / 1000),
    vc: {
      '@context': ['https://www.w3.org/2018/credentials/v1',
                   'https://mrv.example.com/contexts/dac-removal/v1'],
      type: ['VerifiableCredential', 'CarbonRemovalCertificate'],
      credentialSubject: {
        type: 'VerifiedCarbonRemoval',
        facilityId: report.facilityId,
        period: report.period,
        verifiedAmount: { value: report.verifiedAmount, unit: 't CO2' },
        conclusion: report.conclusion,
        evidenceDigest: packageDigest,
      },
    },
  };
  return createVerifiableCredentialJwt(payload, issuer);
}

const resolver = new Resolver({ ...getWebResolver() });

// Named differently from the import, so nothing is redeclared or shadowed.
export async function checkRemovalCertificate(vcJwt) {
  try {
    const result = await verifyCredential(vcJwt, resolver); // throws if invalid
    return { valid: true, subject: result.verifiableCredential.credentialSubject };
  } catch (err) {
    return { valid: false, reason: String(err.message || err) };
  }
}

서명이 유효하다는 것은 지정된 발급자가 이 주장들에 서명했다는 사실만 증명한다. 완전한 확인에는 세 단계가 더 필요하며, 시뮬레이터의 QR 검증 스케치도 이를 암시한다. 검증자는 발급자 DID가 신뢰하는 인정 검증기관의 것인지, 자격증명이 폐지되지 않았는지, 그리고 자격증명이 특정 크레딧에 관한 주장을 뒷받침하는 데 쓰인다면 등록부에서 그 일련번호들이 해당 주장자 앞으로 폐기되어 있는지를 확인해야 한다.

6.5.3 QR 제시

시뮬레이터 5번 탭은 인증서 ID, 시설, 수량, 날짜, 검증 URL, 자격증명 URL을 담은 JSON 객체를 QR 코드로 인코딩한다. 두 가지를 다듬으면 더 견고해진다. 첫째, QR 코드에는 JSON 문서 대신 HTTPS URL 하나를 넣는다. URL은 심볼을 작게 유지해 작게 인쇄해도 읽기 쉽고, 발급자가 재인쇄 없이 링크 대상을 폐지하거나 갱신할 수 있게 한다. 주장 내용은 누구나 위조할 수 있는 QR 페이로드가 아니라 링크 뒤의 서명된 자격증명에서 읽어야 한다. 둘째, 심볼은 밝은 바탕에 어두운 모듈로, 대비를 높게 유지한다. 시뮬레이터 예제는 짙은 남색 바탕에 빨간 모듈을 지정하는데, 이 배색에서는 더 밝은 색이 "어두운" 모듈이 되어 심볼이 반전된다. 반전되었거나 대비가 낮은 QR 코드는 많은 스캐너에서 인식에 실패한다.

6.6 온체인 앵커링과 토큰

FOUNDATION §5.3.2와 PHASE-4 §5.2는 공개 또는 허가형 네트워크와의 블록체인 통합을 제안한다. 두 문서는 성격이 전혀 다른 두 용도를 섞어 쓰는데, 위험이 크게 다르므로 구분해야 한다.

6.6.1 앵커링과 토큰화

해시 앵커링은 데이터나 거래 가능한 단위가 아니라 데이터의 다이제스트를 게시한다. 예를 들어 운영자는 매일의 서명된 포집 요약으로 머클 트리를 만들고 그 루트를 공개 원장에 기록한다. 나중에 요약과 머클 증명을 가진 사람은 누구나 그 요약이 앵커링 시점에 변경 없이 존재했음을 보일 수 있다. FOUNDATION §5.3.2가 설명하는 것이 바로 이것이다. 검증된 포집 이벤트의 해시와 데이터 무결성을 위한 머클 증명이다. 앵커링은 자산을 만들지 않는다. 앵커가 사라지면 시점 증거만 사라질 뿐 누구의 크레딧도 없어지지 않는다.

토큰화는 명세의 ERC-721 계약처럼 크레딧을 나타내는 이전 가능한 온체인 객체를 만든다. 그러면 토큰은 등록부 옆에 존재하는 두 번째 소유권 원장이 된다. 이는 등록부가 그 사실을 알 때에만 안전하다. 예를 들어 등록부가 해당 일련번호를 전용 계정에 보관하거나 온체인에 표현되었다고 표시하는 경우다. 등록부가 모른다면 같은 1톤이 등록부에서 한 번, 체인에서 한 번 폐기될 수 있다. MRV 사슬 전체가 막으려는 이중 계산이 바로 이것이다.

대부분의 DAC 운영자에게 정직한 질문은 체인이 투명성 로그 이상으로 무엇을 보태느냐는 것이다. 인증서 투명성(Certificate Transparency, RFC 9162) 방식을 따르거나 Sigstore Rekor 인스턴스로 운영하는, 서명된 트리 헤드를 갖춘 추가 전용 로그는 가스 비용이나 지갑 관리 없이 해시 앵커링과 같은 무결성 증거를 준다. 공개 체인 앵커링은 수신자가 특정 로그 운영자로부터의 독립성을 원할 때 도움이 되고, 토큰화는 등록부가 지원에 동의한 실제 온체인 시장이 있을 때에만 의미가 있다.

6.6.2 예제 계약의 보안 검토

명세의 DACCarbonCredit 계약은 짧고 읽기 쉽지만 배포하기에 안전하지 않다. 표 6-7은 그 결함을 정리한 것이다.

표 6-7. PHASE-4 예제 계약의 결함과 수정
결함결과수정
mintCredit가 접근 제어 없는 external 함수다누구나 임의의 시설과 수량으로 크레딧을 발행할 수 있다등록부가 확인한 발행에 묶인 키가 보유하는 발급자 역할로 발행을 제한한다(예: OpenZeppelin AccessControl)
등록부 일련번호의 유일성 점검이 없다같은 등록부 단위를 반복해서 발행할 수 있다사용된 일련번호 해시의 mapping을 두고 중복을 거부한다
retireCredit이 플래그만 세우고 소각하거나 잠그지 않는다"폐기된" 토큰을 여전히 이전·재판매할 수 있다폐기 시 소각하거나, 이전 훅에서 폐기된 토큰의 이전을 막는다
폐기 이벤트가 없다오프체인 시스템이 폐기를 안정적으로 추적할 수 없다토큰, 일련번호 해시, 수혜자 참조를 담은 CreditRetired 이벤트를 낸다
증거를 변경 가능한 URL 문자열로 저장한다발행 뒤에 URL 뒤의 보고서가 바뀔 수 있다보고서나 패키지 다이제스트(bytes32)를 저장하고 URL은 참고용으로만 쓴다
토큰마다 수량을 자유롭게 입력한다등록부 블록과 다른 수량이 발행될 수 있다등록부 단위 하나당 토큰 하나로 하거나, 수량을 일련번호 블록과 대조한다
Web3 예제가 공개 RPC에서 getAccounts()로 서명한다공개 RPC 노드는 계정을 노출하지 않으므로 서명할 계정이 돌아오지 않는다명시적 서명자(지갑 또는 KMS 기반)를 쓰고, 확정과 재구성(reorg)을 처리한다

OpenZeppelin Contracts 5.x와 등록부 단위 하나당 토큰 하나를 전제로 고친 스케치는 다음과 같다. 예시일 뿐 감사를 거치지 않았으며, 독립된 스마트 계약 감사와 등록부의 동의 없이 배포해서는 안 된다.

// SPDX-License-Identifier: MIT
// Illustrative only — not audited.
pragma solidity ^0.8.20;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

contract DACRemovalUnit is ERC721, AccessControl {
    bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");

    struct Unit {
        bytes32 serialHash;    // keccak256 of the registry serial number
        bytes32 evidenceHash;  // digest of the verification report or package
        uint16  vintage;
        bool    retired;
    }

    mapping(uint256 => Unit) public units;
    mapping(bytes32 => bool) public serialUsed;
    uint256 public nextTokenId;

    event UnitMinted(uint256 indexed tokenId, bytes32 serialHash, bytes32 evidenceHash);
    event UnitRetired(uint256 indexed tokenId, bytes32 serialHash, address holder, bytes32 beneficiaryRef);

    constructor(address admin) ERC721("DAC Removal Unit (example)", "DACRU") {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
    }

    function mintUnit(address to, bytes32 serialHash, bytes32 evidenceHash, uint16 vintage)
        external onlyRole(ISSUER_ROLE) returns (uint256 tokenId)
    {
        require(!serialUsed[serialHash], "serial already tokenized");
        serialUsed[serialHash] = true;
        tokenId = nextTokenId++;
        units[tokenId] = Unit(serialHash, evidenceHash, vintage, false);
        _safeMint(to, tokenId);
        emit UnitMinted(tokenId, serialHash, evidenceHash);
    }

    function retireUnit(uint256 tokenId, bytes32 beneficiaryRef) external {
        require(ownerOf(tokenId) == msg.sender, "not holder");
        units[tokenId].retired = true;
        _burn(tokenId);  // a retired unit can no longer move
        emit UnitRetired(tokenId, units[tokenId].serialHash, msg.sender, beneficiaryRef);
    }

    function supportsInterface(bytes4 id)
        public view override(ERC721, AccessControl) returns (bool)
    {
        return super.supportsInterface(id);
    }
}

두 가지 세부 사항은 설명이 필요하다. 수혜자는 이름이 아니라 bytes32 참조로 넘긴다. 공개 체인에 쓴 것은 무엇이든 공개되고 영구히 남으며, 수혜자가 GDPR 같은 개인정보 법령의 적용을 받는 개인일 수 있기 때문이다. 또한 retireUnit에서 소각하면 소각된 토큰에 대해 ownerOf가 되돌려지므로(revert) 두 번째 폐기가 불가능해진다. 소각 뒤에도 units 매핑에 이력이 남아 조회할 수 있다.

고친 계약도 질문을 옮길 뿐이다. ISSUER_ROLE 키가 등록부가 해당 일련번호를 온체인 표현용으로 보관하고 있음을 확인한 뒤에만 토큰을 발행하고, 폐기 이벤트가 누군가 이를 신뢰하기 전에 기준 원장인 등록부에 보고될 때에만 안전하다. 등록부와의 이 연결이 없다면 계약은 잘 짜인 두 번째 원장일 뿐이고 이중 계산 위험은 그대로 남는다.

이로써 사슬이 완성되었다. 측정된 톤, 검증되고 보수적으로 하한을 잡은 수량, 기준 원장이 하나뿐인 등록부 단위, 그리고 휴대 가능한 서명 증거다. 제7장은 크레딧 사슬에서 플랜트를 둘러싼 시스템으로 넘어가, 파이프라인과 저장지, 에너지·전력망 조정, 기업 탄소회계, 공개 데이터를 다룬다.