항공 소프트웨어에서 "적합하다"는 말은 느슨하게 쓰인다. 공개된 예시와 비슷한 파일을 내보낼 수 있다거나, 시연 화면에 초록색 체크 표시가 뜬다는 이유로 적합하다고 불리는 시스템이 있다. 어느 쪽도 감사자가 말하는 의미의 적합성 선언이 아니다. 적합성 선언이란 이름이 붙은 구현체가, 특정 버전에서, 특정 버전 명세의 이름 붙은 요구사항 집합을 충족한다는 진술이며, 제3자가 공급사의 말을 믿지 않고도 확인할 수 있는 증거가 뒷받침해야 한다. 이 장의 내용은 모두 이 네 가지 이름을 정확하게 만드는 일에 관한 것이다.
WIA-SPACE-016은 MUST, MUST NOT, SHOULD, MAY 같은 요구 키워드에 정해진 의미를 부여하는 IETF의 관례(BCP 14, 곧 RFC 8174로 보완된 RFC 2119)를 따른다. 규범적 절에 있는 이런 문장만이 의무를 만든다. 각 PHASE 문서는 목적을 밝히는 짧은 규범적 서두로 시작하고, 명시적으로 비규범이라고 표시된 부속서 묶음(E~I)으로 끝난다. 부속서는 구현 경험, 도입 로드맵, 증거의 형태, 버전 정책, 상호운용 프로파일을 설명한다.
주의 깊은 독자는 긴장 관계를 발견할 것이다. 부속서 G는 비규범이라고 선언되어 있으면서도, 적합성을 주장하는 구현체는 모든 테스트 벡터를 CI에서 실행해야 하며(MUST) 공급사는 부정적 결과를 보고해야 한다(MUST)고 적고 있다. 합리적인 해석은 이 부속서가 데이터 형식이나 API에 기술적 요구사항을 더하지는 않지만, 선언을 하려는 사람에게 적용되는 증거 규칙을 정한다는 것이다. 이 책은 부속서를 그렇게 다루고, 해당 문구를 §8.7의 편집상 미결 과제로 올린다.
부속서 F는 각 PHASE의 내용을 세 계층으로 나누어, 초기 구현자가 각 부분에 얼마나 무게를 실어도 되는지 알게 한다.
| 계층 | 해당 내용 | 변경 방식 | 실무적 의미 |
|---|---|---|---|
| 안정(Stable) | MUST / MUST NOT으로 작성된 규범적 절 | 시맨틱 버저닝 적용. 호환성을 깨는 변경은 병행 지원 기간을 둔 새 주 버전에서만(§8.5 참조) | 스키마, 데이터베이스 제약, 고객과의 계약에 안심하고 반영할 수 있다 |
| 잠정(Provisional) | 부속서 내용과 논의 중인 항목 | 의견이 모이면 주 버전 변경 없이 규범으로 승격될 수 있다 | 기능 플래그나 어댑터 뒤에 구현하고, 문구가 더 엄격해질 것에 대비한다 |
| 참조(Reference) | 테스트 벡터, 시뮬레이터 동작, 참조 TypeScript SDK | PHASE 문서와 별도로 버전이 매겨져, 명세를 고치지 않고도 오류를 바로잡을 수 있다 | 학습과 간이 시험에 유용하다. 규범 문장과 어긋날 때 기준이 되지 않는다 |
이 책의 독자에게는 세 번째 행이 가장 중요하다. 앞 장들에서 물리적으로 불가능한 값을 돌려주는 피로 수명 도우미, 요청 함수가 모의 데이터를 돌려주는 SDK, 항상 성공을 보고하는 CLI를 지적했다. 참조 계층은 명시적으로 비규범이고 독립적으로 버전이 매겨지므로, 적합한 레코드나 API가 갖추어야 할 모습을 전혀 바꾸지 않고도 이 문제들을 고칠 수 있다.
이 분야에서 "인증"이라는 말은 서로 무관한 두 의미로 쓰이며, 둘을 혼동하는 것은 실제적인 위험이다. 감항성의 의미에서는 FAA, EASA, 한국의 경우 국토교통부와 같은 권한 있는 당국만이 설계, 제작 조직, 정비 후 사용 가능 판정을 승인할 수 있고, 그 승인은 형식증명서, FAA Form 8130-3, EASA Form 1 같은 문서에 기록된다. 표준의 의미에서는 구현체가 WIA-SPACE-016에 적합함을 보일 수 있으며, 이때 ISO/IEC 17065(제품·공정·서비스를 인증하는 기관에 대한 국제 표준으로, 부속서 E가 감사 기록 보존과 관련하여 인용한다)에 따라 운영되는 인증기관의 도움을 받을 수도 있다.
두 가지는 결코 서로를 대신하지 못한다. 적합한 기록 시스템은 감항성이 없는 부품의 레코드도 충실히 저장할 수 있고, WIA 적합성 진술이 어떤 부품을 감항성 있게 만들 수도 없다. 참조 자료의 두 가지 세부 사항이 이 경계를 흐리므로 주의해서 읽어야 한다. SDK 타입에서 Certification.standard의 주석은 예시 값으로 "WIA-SPACE-016"을 든다. 적합성 진술을 기록하는 데는 문제가 없지만, CS-25나 TSO 같은 감항 기준을 뜻하는 자리에 써서는 안 된다. 또 시뮬레이터의 테스트 탭은 버튼을 누르면 "Certificate ID"를 표시하는데, 이는 교육용 도구의 장식 문구일 뿐 무엇을 인증하는 증서가 아니다.
1.0 버전의 공개 자료에는 선언의 범위를 제한하는 세 가지 공백이 있다. 규범적 절이 개별 요구사항에 안정된 식별자를 아직 부여하지 않아, 프로파일 매니페스트나 시험 매트릭스가 가리킬 수 있는 것은 절 번호뿐이다. PHASE 2는 기계 판독용 api/openapi.yaml을 언급하지만 공개 저장소에는 이 파일이 없다. 부속서 G가 설명하는 테스트 벡터 디렉터리도 아직 공개되지 않았다. 구현자는 절 번호와 문장에 연결된 자체 요구사항 식별자를 부여하고 증거와 함께 공개할 수는 있다. 그러나 당분간 선언은 단순히 "WIA-SPACE-016 인증"이 아니라 "첨부한 요구사항 목록에 열거된 PHASE 1 v1.0의 규범 요구사항에 적합"과 같은 형태여야 한다.
테스트 벡터는 작고 자기 완결적인 입력과, 올바른 구현체가 그 입력에 대해 내려야 할 판정을 함께 묶은 것이다. 벡터는 특정 언어, 데이터베이스, 배포 형태에 의존하지 않으므로 가장 이식성이 높은 적합성 증거이다. 두 구현체가 벡터 하나에서 판정이 갈리면, 의견이 아니라 구체적인 상호운용 결함을 찾은 것이다.
부속서 G는 디렉터리 구조를 정한다. PHASE마다 tests/phase-vectors/ 아래에 자기 폴더가 있고, 그 PHASE의 모든 규범 요구사항은 적어도 하나의 긍정 벡터(받아들여야 하는 입력)와 하나의 부정 벡터(거부해야 하는 입력)를 가져야 한다.
| 디렉터리 | PHASE | 대표적인 벡터 내용 |
|---|---|---|
tests/phase-vectors/phase-1-data-format/ | 1 — 데이터 형식 | 기대 판정(유효/무효)이 붙은 단일 JSON 문서(AircraftComponent, Inspection, Certification) |
tests/phase-vectors/phase-2-api-interface/ | 2 — API 인터페이스 | 기록된 HTTP 요청·응답 쌍: 상태 코드, 헤더, 문제 상세 본문, 탐색 문서 |
tests/phase-vectors/phase-3-protocol/ | 3 — 프로토콜 | 중복 전달과 순서 뒤바뀜을 포함한 행위자 간 메시지 순서열과 기대 최종 상태 |
tests/phase-vectors/phase-4-integration/ | 4 — 통합 | 검증에 성공해야 하는 증거 묶음(SBOM, 증명)과 검증에 실패해야 하는 변조 사본 |
명세는 벡터의 파일 형식을 정의하지 않는다. 정의되기 전까지는 스스로 기대 판정을 밝히는 형식이 감사하기 가장 쉽다. 아래 예는 이 장 전체에서 쓰는 제안 형식이며 표준의 문구가 아니다. 요구사항 식별자도 §8.1에서 말한 이유로 임의로 지은 것이다.
{
"vector": "p1-neg-inspection-conditional-without-next-date",
"requirement": "P1-INSP-004",
"target": "Inspection",
"expect": "invalid",
"why": "조건부 판정에는 다음 검사 시점이 있어야 한다",
"instance": {
"id": "insp-000912",
"inspectionDate": "2026-03-18",
"inspector": "자격 정비사, 스탬프 4471",
"method": "eddy-current",
"result": "conditional",
"findings": "14번 체결공에 1.2 mm 선형 지시, 수리 교범 한계 이내"
}
}
이 벡터가 시험하는 규칙, 곧 conditional 판정에는 nextInspectionDate가 있어야 한다는 규칙은 참조 타입에 없다는 점에 유의해야 한다. 참조 타입에서 이 필드는 단순히 선택 사항이다. 구현자가 합리적으로 추가할 만한 요구사항이며, 추가했다면 그 사실을 선언해야 한다.
긍정 벡터는 쓰기 쉽다. 부정 벡터는 더 많은 고민이 필요하며, 항공 데이터 시스템이 실제로 무너지는 곳도 바로 여기이다. 좋은 부정 벡터는 격납고나 데이터 이관 과정에서 누군가 저지를 법한 실수를 담는다.
| 벡터에 담은 실수 | 발생 경위 | 검출 수단 |
|---|---|---|
serialNumber 누락 또는 빈 문자열 | 로트 관리 부품을 일련번호 관리 부품용 인터페이스로 내보냄 | 스키마(required, minLength) |
method: "penetrant" | 침투 탐상은 흔한 NDI 방법이지만 1.0 열거형에 없다(제7장 참조) | 스키마(enum). 추가형 변경의 후보이기도 하다 |
알루미늄 합금에 density: 2.78 | g/cm³ 단위의 데이터시트 값을 kg/m³로 정의된 필드에 그대로 복사 | 타당성 minimum(예: 500)을 두었다면 스키마 |
manufacturingDate: "18/03/2026" | 스프레드시트의 지역 형식 날짜 | 스키마(format: date, 단언 모드) |
expiryDate가 issueDate보다 이름 | 가져오기 과정에서 필드가 뒤바뀜 | 코드(필드 간 규칙) |
부품의 status가 retired가 된 뒤의 날짜로 기록된 검사 | 퇴역 부품의 일련번호를 교체품에 재사용 | 코드(이력 규칙) |
partNumber와 serialNumber는 같고 manufacturer가 다른 두 부품 | 두 MRO 시스템의 레코드 병합 | 코드(집합 규칙). 단일 문서가 아닌 PHASE 3 벡터가 필요하다 |
마지막 세 행은 스키마만으로는 결코 시험 체계가 되지 않는 이유를 보여 준다. 필드끼리 비교하거나, 이력을 보거나, 레코드끼리 대조하는 규칙에는 코드가 필요하고, 그 벡터는 문서를 둘 이상 담아야 한다.
부속서 G는 적합성을 주장하는 구현체가 모든 벡터를 지속적 통합(CI)에서 실행하고 그 합격·불합격 매트릭스를 공개하도록 요구한다. 매트릭스에 가치를 주는 규칙이 두 가지 더 있다. 첫째, 부정적 결과도 긍정적 결과와 같은 충실도로 보고해야 한다. 공급사가 통과한 행만 골라 공개할 수 없다. 둘째, 기록된 정당화 없이 건너뛴 벡터는 실패로 간주된다. 두 규칙은 자체 평가의 가장 흔한 빈틈, 곧 불편한 시험을 조용히 더 이상 돌리지 않는 일을 막는다.
| 요구사항 | 벡터 | 기대 | 관측 | 판정 |
|---|---|---|---|---|
| P1-ID-001 | p1-pos-minimal-component | valid | valid | 합격 |
| P1-ID-001 | p1-neg-missing-serial | invalid | invalid | 합격 |
| P1-MAT-003 | p1-neg-density-gcm3 | invalid | valid | 불합격 |
| P1-INSP-004 | p1-neg-inspection-conditional-without-next-date | invalid | — | 건너뜀, 정당화 있음: "프로파일 매니페스트에 partial로 선언한 규칙" |
| P2-ERR-002 | p2-neg-429-without-retry-after | invalid | — | 불합격(정당화 없이 건너뜀) |
불합격 행이라도 프로파일 매니페스트(§8.4)의 partial 또는 excluded 항목과 짝을 이룬다면 여전히 정직한 진술이다. 감사자가 받아들일 수 없는 것은 매트릭스와 매니페스트가 서로 어긋나는 경우이다.
시험 결과를 선언으로 바꾸는 것은 증거의 공개이다. 부속서 G는 준수 패키지를 내용이 정해진 서명된 tar 묶음으로 정의한다. 제7장은 CycloneDX와 in-toto로 물리적 부품과 그 제조 증거를 기술했다. 이 절의 패키지는 다르다. 그런 레코드를 저장하고 교환하는 소프트웨어 구현체를 기술한다.
| 항목 | 형식 | 표준 내 근거 | 검토자가 쓰는 용도 |
|---|---|---|---|
| 소프트웨어 자재명세서(SBOM) | CycloneDX 1.5 또는 SPDX 2.3 | 부속서 G | 검증기와 암호 코드를 포함해 어떤 라이브러리가 결과를 만들었는지 파악 |
| API 기술서 | OpenAPI 문서(PHASE 1 참조 목록에 따르면 3.1) | 부속서 G. 부속서 E는 운영 범위를 여기에 선언하도록 권고 | 선언된 인터페이스를 PHASE 2 작업 그룹과 대조 |
| 테스트 벡터 매트릭스 | 벡터당 한 행의 기계 판독용 표 | 부속서 G | 범위, 실패, 정당화된 건너뜀 확인 |
| 프로파일 매니페스트 | SBOM과 같은 키로 서명한 JSON | 부속서 I | 구현체가 애초에 어떤 요구사항을 주장하는지 파악 |
| 버전 변경 이력 대응표 | PHASE 버전에서 빌드, 컨테이너 다이제스트, SDK 버전으로의 대응 | 부속서 H(SHOULD) | 선언을 배포·재시험 가능한 바이너리에 묶음 |
| 서명된 매니페스트 | 모든 파일과 다이제스트의 목록, Sigstore 서명(DSSE 봉투, Rekor 항목) | 부속서 G | 묶음 안의 어떤 파일도 변조·치환되지 않았음을 증명 |
부속서 G가 Sigstore를 택한 것은 소비자가 "사설 CA를 신뢰하지 않고도" 출처를 검증할 수 있게 하기 위해서이다. 실제로는 세 요소가 함께 작동한다. 서명은 DSSE(Dead Simple Signing Envelope)로 감싸이는데, DSSE는 서명된 바이트를 선언된 페이로드 유형에 묶어 한 종류의 문서에 대한 서명을 다른 종류의 서명으로 재사용하지 못하게 한다. 서명 인증서는 수명이 짧고, 보통 공급사 저장소의 CI 작업 같은 신원에 묶인다. 그리고 서명 행위는 공개된 추가 전용 투명성 로그인 Rekor에 기록되므로 몰래 서명을 만들 수 없다. 검증에는 패키지와 기대하는 서명자 신원만 있으면 된다. 공급사가 운영하는 키 서버를 신뢰하거나 수십 년 동안 살려 둘 필요가 없다.
다음 파이프라인은 벡터에서 서명된 패키지까지의 전 과정을 보여 준다. 어떤 CI 시스템에도 넣을 수 있도록 일반 셸 스크립트로 작성했다. 도구 호출(CycloneDX의 npm 생성기, Sigstore cosign CLI)은 실제 명령이지만, 파일 이름, 요구사항 목록, 버전 번호는 예시이다.
#!/usr/bin/env bash
set -euo pipefail
VERSION="3.4.1" # 구현체 버전(예시)
OUT="evidence-${VERSION}"
mkdir -p "$OUT"
# 1. 모든 벡터 실행. 실행기는 매트릭스를 쓰고, 실패나 정당화 없는 건너뜀이
# 하나라도 있으면 0이 아닌 값으로 끝난다(8.6절의 실행기 참조).
# 실패한 매트릭스도 패키지에 넣는다. 부정적 결과는 숨기지 않고 공개한다.
VEC_STATUS=0
node tools/run-vectors.cjs tests/phase-vectors > "$OUT/vector-matrix.json" || VEC_STATUS=$?
# 2. CycloneDX 1.5 형식의 소프트웨어 자재명세서
npx @cyclonedx/cyclonedx-npm --spec-version 1.5 \
--output-file "$OUT/sbom.cdx.json"
# 3. 인터페이스 기술서, 프로파일 매니페스트, 변경 이력 대응표
cp api/openapi.yaml "$OUT/"
cp conformance/profile.json "$OUT/"
cp conformance/version-map.json "$OUT/"
# 4. 다이제스트 매니페스트를 만든 뒤 키 없는 Sigstore 서명(Rekor에 기록)
( cd "$OUT" && sha256sum -- * > MANIFEST.sha256 )
cosign sign-blob --yes --bundle "$OUT/MANIFEST.sigstore.json" \
"$OUT/MANIFEST.sha256"
tar -czf "${OUT}.tar.gz" "$OUT"
exit "$VEC_STATUS" # CI 작업은 여전히 실패를 보고한다
소비자 쪽 검증은 이 과정을 거꾸로 밟는다. 묶음을 풀고, 기대하는 인증서 신원과 OIDC 발급자를 고정한 채 번들에 대해 cosign verify-blob을 실행한 다음, MANIFEST.sha256의 모든 다이제스트를 다시 계산한다. 신원 고정은 선택 사항이 아니다. 예상하지 못한 신원의 유효한 서명은 누군가 무언가에 서명했다는 사실 말고는 아무것도 증명하지 못한다.
부속서 G는 구현체가 자체 소스 코드를 바꾸지 않았더라도 분기마다 증거 패키지를 다시 공개하도록 요구한다. 이유는 환경 표류이다. 새 컴파일러, 검증기 라이브러리, 기반 이미지는 공급사 코드가 한 줄도 바뀌지 않아도 동작을 바꿀 수 있다. 예를 들어 JSON Schema 라이브러리는 format 키워드를 단언으로 처리하느냐에서 서로 다르고, 업그레이드 한 번으로 그 동작이 뒤집힐 수 있다. 두 분기 패키지를 비교하면 무엇이 달라졌는지 정확히 드러난다.
부속서 E는 7년 보존 기간이면 대부분의 관할권에서 ISO/IEC 17065 감사 요구를 충족한다고 하면서, 규제 당국이 더 긴 보존을 요구하는 경우에는 배포 정책이 보존 기간을 늘려야 한다고 곧바로 덧붙인다. 항공에서는 레코드 자체에 관한 한 이 예외가 오히려 일반적인 경우이다. 미국과 유럽의 운항 규정은 수명 한계 부품의 현재 상태, 감항성 개선지시 이행 상태 같은 일부 기록을 항공기나 부품이 사용되는 동안 보존하고, 소유가 바뀔 때 함께 넘기도록 요구한다. 7년은 적합성 증거의 하한으로 보고, 부품 레코드와 그것이 어떻게 검증되었는지에 대한 증거는 부품의 수명 동안 보존해야 한다.
| # | 점검 항목 | 증거 |
|---|---|---|
| 1 | 주장하는 모든 요구사항에 식별자와 PHASE 절·버전 인용이 있다 | 패키지 내 요구사항 목록 |
| 2 | 모든 요구사항에 긍정 벡터와 부정 벡터가 각각 하나 이상 있다 | 벡터 매트릭스의 범위 열 |
| 3 | 선언 대상 빌드에서 모든 벡터가 CI로 실행되었고, 건너뛴 벡터에는 서면 정당화가 있다 | 벡터 매트릭스, CI 실행 참조 |
| 4 | 실패와 부분 지원을 빠뜨리지 않고 선언했다 | 프로파일 매니페스트 항목과 매트릭스 행의 일치 |
| 5 | SBOM이 매트릭스를 만든 바로 그 빌드를 기술한다 | SBOM과 버전 변경 이력 대응표 |
| 6 | 지원하는 PHASE 2 작업 그룹을 선언했다 | 프로파일 매니페스트와 OpenAPI 문서의 일치 |
| 7 | 패키지가 서명·기록되었고 서명자 신원이 공개되어 있다 | Sigstore 번들, Rekor 항목 |
| 8 | 다음 분기 재점검 일정과 보존 정책을 명시했다 | 배포 정책 문서 |
| 9 | 감항 승인을 암시하는 문구가 없다 | 공개 선언문 검토 |
배포의 범위는 저마다 다르다. 수리 공장은 검사 기록만 남길 수도 있고, 운영자에게는 추적성 이력 전체가 필요하다. 부속서 I의 상호운용 프로파일은 각자가 완전 적합을 가장하지 않고 자기 범위를 정직하게 밝힐 수 있게 한다.
모든 구현체는 JSON 프로파일 매니페스트를 공개하며, 여기에는 PHASE의 요구사항 식별자가 세 가지 상태 가운데 하나와 함께 나열된다. supported, 이유를 붙인 partial, 정당화를 붙인 excluded이다. 매니페스트는 SBOM과 같은 Sigstore 신원으로 서명되므로, 소비자는 범위 진술과 증거가 같은 곳에서 나왔음을 확신할 수 있다. PHASE 2는 구현체가 다섯 작업 그룹 가운데 무엇을 지원하는지도 프로파일에 선언하도록 한다. 아래 예는 둘을 함께 담았다.
{
"profiles": [
"WIA-P1-DATA-FORMAT-Edge-only/2",
"WIA-P2-API-INTERFACE-Edge-only/1"
],
"implementation": {
"name": "example-repair-station-records",
"version": "3.4.1",
"containerDigest": "sha256:0f4c…(생략)"
},
"phaseVersions": {
"phase-1-data-format": "1.0.0",
"phase-2-api-interface": "1.0.0"
},
"operationGroups": {
"componentCatalog": "supported",
"maintenanceEvent": "supported",
"traceability": "partial",
"certificationPackage": "excluded",
"serviceBulletin": "supported"
},
"requirements": [
{ "id": "P1-ID-001", "status": "supported" },
{ "id": "P1-MAT-003", "status": "partial",
"reason": "밀도는 kg/m3만 허용하며, 기존 g/cm3 값은 변환하지 않고 거부한다" },
{ "id": "P2-CERT-001", "status": "excluded",
"justification": "수리 공장은 당국에 인증 패키지를 제출하지 않는다" }
],
"operationGroupNotes": {
"traceability": "2019년 이후 이력만 제공하며, 그 이전 기록은 종이로 보관한다"
}
}
추적성의 partial 항목은 운영자가 출생 이력을 이 시스템에 맡기기 전에 알아야 할 바로 그 정보이다. certificationPackage의 제외는 약점이 아니다. 수리 공장은 형식증명 증거를 제출하는 주체가 아니기 때문이다.
여러 구현체가 협력할 때, 예를 들어 운영자 하나와 계약 MRO 세 곳이 정비 이벤트를 주고받을 때, 부속서 I는 연합이 구성원 프로파일의 합집합과 교집합을 요약한 집계 매니페스트를 공개하도록 요구한다. 교집합은 디렉터리 서비스에 모든 구성원이 할 수 있는 일을 알려 주므로, 어느 공장을 거쳤을지 모르는 부품의 추적성 조회처럼 어디서나 성공해야 하는 요청은 최소 공통분모를 기준으로 라우팅된다. 구성원 하나가 추적성을 부분적으로만 지원한다면, 조회가 불완전한 이력을 조용히 돌려주기 전에 그 사실이 드러난다.
흔한 배포 형태에는 WIA 작업반이 관리하는 공개 레지스트리에서 이름이 붙는다. 부속서 I는 "Edge-only"와 "Federated-with-replay"를 예로 든다. 레지스트리 항목은 변경할 수 없다. 다른 형태가 필요하면 기존 항목을 고치지 않고 새 이름으로 추가하므로, 몇 년 전에 서명된 매니페스트도 원래 뜻을 그대로 유지한다. 프로파일 이름은 PHASE 문서와 같은 SemVer 의미의 주 버전을 가진다. WIA-P1-DATA-FORMAT-Edge-only/2는 해당 이름 프로파일의 두 번째 주 버전에 적합하다는 주장이지, 버전 없는 프로파일의 두 번째 배포라는 뜻이 아니다.
배포가 범위를 넓히더라도 이전 매니페스트는 §8.5의 폐기 기간 동안 유효하고 서명된 상태로 남는다. 덕분에 감사자는 나중에도 시스템이 어떤 레코드를 받아들인 날 무엇을 주장했는지 재구성할 수 있다.
부품 레코드는 소프트웨어보다 오래 산다. 착륙장치 완충 스트럿 하나가 오버홀을 거치며 사용되는 기간은 대부분의 기록 시스템이 운영되는 기간보다 길 수 있다. 따라서 버전 정책은 구현자와 레코드 소유자를 모두 고려해야 한다.
부속서 H는 Semantic Versioning 2.0.0을 채택한다. 주 버전은 규범 요구사항의 하위 호환되지 않는 변경을, 부 버전은 기존 구현을 깨뜨리지 않는 규범 요구사항의 추가를, 수 버전은 편집상의 변경만을 나타낸다. 아래 표는 WIA-SPACE-016에 일어날 법한 변경에 이 규칙을 적용한 것이다.
| 변경 | 버전 증가 | 근거 |
|---|---|---|
| 동작을 바꾸지 않는 오탈자 수정이나 문구 명확화 | 수(patch) | 편집상 변경. 폐기 기간 없이 서명된 정오표로 발행 |
softwarePartNumber나 사이클 계수 같은 선택 필드 추가 | 부(minor) | 추가형 변경. 부속서 E는 새 선택 필드가 호환성을 깨지 않는다고 명시 |
Inspection.method에 penetrant 추가 | 소비자가 모르는 값을 허용한다면 부(minor) | 새 생산자가 기존 검증기가 거부하는 값을 내보낸다. 아래 논의 참조 |
result가 conditional일 때 nextInspectionDate를 필수로 변경 | 주(major) | 기존의 유효한 레코드가 무효가 된다 |
density 단위를 kg/m³에서 g/cm³로 변경 | 주(major) | 같은 필드, 다른 의미. 가장 위험한 종류의 변경 |
| 필드 이름 변경 또는 삭제(오류 페이로드 포함) | 주(major, 아래 주석 참조) | 그 필드를 읽는 모든 소비자에게 하위 호환되지 않음 |
마지막 행에는 부속서 사이의 불일치가 숨어 있다. 부속서 E는 필드의 이름 변경이나 삭제가 "오류 페이로드에서라도 부 버전 증가를 일으켜야 한다(MUST)"고 하지만, 부속서 H는 하위 호환되지 않는 변경을 주 버전으로 정의한다. SemVer에서 이름 변경은 호환되지 않는 변경이므로 이 책은 부속서 H를 따른다. 구현자를 위한 실무 규칙은 더 단순하다. 제자리에서 이름을 바꾸지 말고, 새 필드를 추가하고, 옛 필드를 폐기 예정으로 표시한 뒤, 주 버전에서만 삭제한다.
열거형 값 추가가 호환되는지는 스키마만이 아니라 소비자의 동작에 달려 있다. 소비자가 모르는 값을 거부한다면 새 값은 그들을 깨뜨리고, 모르는 값을 보존하고 표시하도록 되어 있다면 깨뜨리지 않는다. "알 수 없는 열거형 값은 보존해야 하며(MUST) 레코드 전체를 거부하는 원인이 되어서는 안 된다(MUST NOT)" 같은 규칙을 향후 버전에 두면, NDI 방법과 재료 열거형을 부 버전만으로 넓혀 갈 수 있다.
새 주 버전이 발행되면 부속서 H는 이전 주 버전을 최소 180일 동안 병행 유지하도록 요구한다. 이 기간에는 두 버전 모두 WIA 레지스트리에서 안정(Stable)으로 표시되며, 어느 쪽을 인용해도 "WIA 적합"이라 할 수 있다. 그 뒤 옛 버전은 12개월의 일몰 기간에 들어가며, 레지스트리는 이를 폐기 예정(Deprecated)으로 표시하고 대체 요구사항을 가리키는 이관 안내와 변경 이유를 함께 싣는다. 한 버전이 동시에 안정이면서 폐기 예정일 수는 없으므로, 부속서가 명시적으로 말하지는 않지만 두 기간은 차례로 이어져야 한다.
v2.0.0 발행
|
v1.x Stable | Stable (병행 기간, 최소 180일) | Deprecated (일몰, 12개월) | 종료
v2.x | Stable --------------------------------------------------------------------------->
|
0일 180일 이상 180일 이상 + 12개월
같은 문서의 부속서 F는 "WIA가 공개한 모든 참조 구현에서 이전 주 버전과 최소 90일 겹침"을 말한다. 두 수치는 각각 명세(부속서 H)와 참조 구현(부속서 F)이라는 서로 다른 대상을 위한 것이었을 수 있으나, 쓰인 그대로는 충돌한다. 이 책은 버전 관리를 전담하는 부속서인 H의 180일을 기준으로 삼으며, 구현자는 두 경우 모두 더 긴 기간을 전제로 계획할 것을 권한다.
수 버전 정오표는 동작을 바꾸지 않으므로 폐기 기간이 필요 없다. 정오표는 공개 등록부에 기록되고, 각 항목에는 작업반 의장이 서명한다. 부속서 H는 또 구현체가 지원하는 각 PHASE 버전을 그것을 충족하는 특정 빌드, 컨테이너 다이제스트, SDK 버전에 대응시킨 표를 공개하도록 권고한다. 분기 패키지와 함께 이 표가 있으면, 감사자는 어느 날짜에 대해서든 어떤 코드가 어떤 버전의 규칙 아래 레코드를 받아들였는지 말할 수 있다.
레코드 자체에 대해서는 모든 레코드에 스키마 버전을 함께 저장해야 한다. $schema URI나 metadata.wiaSchemaVersion 값이 그 예이다. 그래야 20년 뒤 그 레코드를 읽는 사람이 어떤 규칙이 적용되었는지 추측하지 않아도 된다.
표준에는 직접 다뤄 볼 수 있는 도구가 둘 있다. 셸 스크립트 cli/aircraft-component.sh와 wiastandards.com의 브라우저 시뮬레이터이다. 둘 다 전체 그림을 익히는 데는 도움이 되지만, 현재 형태로는 어느 쪽도 적합성 도구가 아니다.
| 명령 | 도움말이 약속하는 것 | 스크립트가 실제로 하는 것 |
|---|---|---|
validate <file> | 데이터를 표준에 대해 검증 | 파일이 존재하는지만 확인하고 "Validation passed"를 출력한다. 내용은 읽지 않는다 |
generate <type> | 샘플 구현 생성 | 만들 JSON 파일 이름만 출력한다. 파일은 생성되지 않는다 |
check | 시스템 준수 점검 | 조건 없이 "System check passed"를 출력한다 |
info | 표준 정보 표시 | 이름과 버전 문자열을 출력한다 |
명령줄 형태를 미리 정해 두는 골격으로서는 해가 없다. 그러나 CI 파이프라인에 넣으면 해롭다. 빈 파일도 "통과"하기 때문이다.
| 탭 | 하는 일 | 주의점 | 활용 |
|---|---|---|---|
| 데이터 형식 | ID, 유형, 상태로 JSON 객체를 만든다 | 출력이 AircraftComponent와 맞지 않는다. 무게와 치수가 단위 붙은 문자열이고, 인증 필드가 타입과 다르다 | 연습 문제: 스키마를 통과하는 레코드로 변환하기 |
| 알고리즘 | σ = F/A, 강도 대 응력의 비, "피로 수명"을 계산한다 | estimateFatigueLife와 같은 피로 공식을 써서, 응력이 강도와 같을 때 108 사이클을 예측한다 | 응력 산술과 단위 연습. 수명 추정에는 절대 쓰지 않는다 |
| 프로토콜 | 성공 코드가 붙은 API 호출 네 건의 로그를 출력한다 | 고정된 문구이며 요청은 보내지 않는다. 경로도 SDK와 다르다 | 요청 순서의 의도된 모양 파악 |
| 통합 | MES, QMS, ERP, 인증 데이터베이스의 연결 결과를 출력한다 | 고정된 문구이며 아무것도 연결되지 않는다 | 제7장에서 다룬 시스템의 점검 목록 |
| 테스트 | "All Tests Passed"와 2차원 코드처럼 보이는 무늬를 표시한다 | 시험은 실행되지 않는다. 무늬는 무작위 사각형이고 인증서 ID는 고정 문구이다 | 적합성 결과가 결코 이렇게 보여서는 안 된다는 본보기 |
입력도, 매트릭스도, 버전도, 서명도 없는 결과 화면은 부속서 G 증거의 정반대이며, 바로 그 점에서 좋은 교육 소재가 된다. 감사자가 믿을 수 있으려면 이 화면에 무엇이 나타나야 하는지 학생들에게 적어 보게 하면, 그 목록은 표 8-5와 거의 같아진다.
실제 검증기는 두 층으로 이루어진다. 첫째 층은 제4장에서 만든 것과 같은 객체별 JSON Schema 2020-12 문서이며, format 키워드를 단언하는 검증기로 실행한다. 둘째 층은 스키마로 표현할 수 없는 모든 것을 다루는 작은 코드 규칙 집합이다. 참조 SDK의 패키지 정의는 이미 zod를 의존성으로 올려 두었지만 현재 소스는 이를 가져다 쓰지 않는다. 어느 길이든 가능하나, 표준이 명시하는 것은 JSON Schema이고 언어 중립적이라는 점이 여러 구현체에서 돌아가야 하는 벡터에는 중요하다.
아래 실행기는 스키마를 불러오고, 모든 벡터 파일을 실행하고, 필드 간 규칙을 적용하고, §8.3에서 서명하는 매트릭스를 쓴다. format: date가 실제로 검사되도록 Ajv의 2020-12 빌드와 ajv-formats 플러그인을 사용한다. 규칙 함수는 표 8-3에서 다룬 종류의 예시이다.
const fs = require("fs");
const path = require("path");
const Ajv2020 = require("ajv/dist/2020");
const addFormats = require("ajv-formats");
const ajv = new Ajv2020({ allErrors: true, strict: true });
addFormats(ajv); // format: date 단언
const schemas = {
AircraftComponent: ajv.compile(require("../schemas/aircraft-component.json")),
Inspection: ajv.compile(require("../schemas/inspection.json")),
Certification: ajv.compile(require("../schemas/certification.json"))
};
// 스키마로 표현할 수 없는 규칙(필드 간 규칙, 이력 규칙)
const rules = {
Certification: (c) =>
c.expiryDate && c.expiryDate < c.issueDate ? ["expiryDate before issueDate"] : [],
Inspection: (i) => {
const errs = [];
if (i.result === "conditional" && !i.nextInspectionDate)
errs.push("conditional result without nextInspectionDate");
if (i.nextInspectionDate && i.nextInspectionDate <= i.inspectionDate)
errs.push("nextInspectionDate not after inspectionDate");
return errs;
}
};
function walk(dir) {
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) =>
e.isDirectory() ? walk(path.join(dir, e.name))
: e.name.endsWith(".json") ? [path.join(dir, e.name)] : []);
}
const rows = [];
for (const file of walk(process.argv[2])) {
const v = JSON.parse(fs.readFileSync(file, "utf8"));
if (v.skip) {
rows.push({ vector: v.vector, requirement: v.requirement, expect: v.expect,
verdict: v.skip.reason ? "skipped-justified" : "fail" });
continue;
}
const validate = schemas[v.target];
const schemaOk = validate(v.instance);
const ruleErrs = schemaOk && rules[v.target] ? rules[v.target](v.instance) : [];
const observed = schemaOk && ruleErrs.length === 0 ? "valid" : "invalid";
rows.push({ vector: v.vector, requirement: v.requirement, expect: v.expect, observed,
verdict: observed === v.expect ? "pass" : "fail",
detail: schemaOk ? ruleErrs : validate.errors });
}
process.stdout.write(JSON.stringify(rows, null, 2) + "\n");
process.exit(rows.some((r) => r.verdict === "fail") ? 1 : 0);
날짜를 문자열로 비교해도 되는 것은 YYYY-MM-DD 형태의 ISO 8601 달력 날짜가 사전순으로 정렬하면 시간순이 되고, 스키마가 그 밖의 형태를 이미 거부했기 때문이다. 스키마 층이 먼저 실행된다는 전제는 의도된 것이며, 실행기가 스키마를 통과한 문서에만 규칙을 적용하는 이유도 여기에 있다.
이렇게 되면 CLI의 validate 명령도 정직한 래퍼가 될 수 있다. 파일 하나를 긍정 벡터로 감싸 실행하고, 실행기의 종료 코드를 돌려주면 된다.
1.0 버전은 부품을 대체로 정적인 레코드로 모델링한다. 여러 흐름이 레코드를 더 풍부하게, 더 자주 갱신되게, 증거에 더 단단히 묶이게 만드는 방향으로 밀고 있다. 어느 것도 현재 모델을 버릴 것을 요구하지는 않지만, 각각은 모델이 자라야 할 자리를 드러낸다.
구조 건전성 모니터링은 스트레인 게이지, 음향 방출 센서, 초음파 유도파 변환기, 균열 검출 센서 같은 센서를 구조물에 상시 부착하여 분해하지 않고도 상태를 평가하는 방식이다. 규제 당국이 인정하는 경우 모니터링은 정기 검사를 보완하거나 대체할 수 있다. 기록 표준 입장에서는 두 가지 질문이 생긴다. 첫째, 지금의 Inspection에는 검사자와 다섯 값 목록 중 하나인 방법이 있을 뿐인데, 모니터링에 의한 점검에는 센서 시스템, 소프트웨어 버전, 알고리즘이 있고 이들 각각에 추적 가능한 신원이 필요하다. 둘째, 센서 데이터는 연속적이고 방대하므로 부품 레코드 안에 저장해서는 안 된다. 레코드에서 외부 데이터 세트를 참조하되, 레코드가 어떤 데이터에 근거했는지 여전히 증명할 수 있도록 다이제스트를 함께 두는 방식이 유력하다.
일련번호가 붙은 부품 하나의 실제 하중과 손상 상태를 추적하는 모델, 곧 디지털 트윈은 이런 데이터의 자연스러운 소비자이다. WIA 레코드는 트윈의 신원과 승인의 뼈대가 된다. 계산은 트윈이 하고, 그 계산이 어느 부품과 어떤 승인 자료에 속하는지는 레코드가 말한다.
적층 제조 부품의 특성은 공정에 크게 좌우된다. 원료 로트, 장비, 빌드 배치, 공정 변수, 열처리, 후가공이 모두 영향을 준다. FAA와 EASA는 모두 이런 부품의 인정 방법에 관한 지침과 연구 자료를 발간했다. 기록의 관점에서 "재료"는 "공정"과 떼어 놓을 수 없게 되며, 향후 버전에는 MaterialSpecification과 나란히 빌드 식별자와 빌드 기록에 대한 링크가 필요할 가능성이 크다.
이미 운용 중인 두 재료 계열이 1.0 열거형에서 제대로 표현되지 않는다. 2050, 2198 같은 알루미늄-리튬 합금은 전통적인 2xxx·7xxx 계열보다 밀도가 낮고 강성이 좋아 동체와 날개 구조에 쓰인다. 열가소성 복합재는 제2장에서 다룬 열경화성 계열과 달리 다시 녹일 수 있어 유도·저항·초음파 방식으로 용접할 수 있는데, 이는 새로운 특수공정, 용접선에 대한 새로운 검사 요구, 새로운 공정 기록을 낳는다. 지금은 Al-Li 부품을 기존 알루미늄 값 아래에 두고 실제 합금을 grade에 적는 수밖에 없어 계열을 잘못 나타내게 되고, 열가소성 부품은 포괄 값 composite로만 기록할 수 있다. §8.5의 규칙에 따라 정확한 열거형 값을 추가하면 기단 전체에 걸친 조회를 믿을 수 있게 된다.
부품의 출처를 "블록체인에 올리자"는 제안은 주기적으로 나온다. 표준이 이미 쓰고 있는 방식, 곧 공개 투명성 로그에 서명을 기록하는 방식은 중요한 성질, 누구도 이력을 몰래 고쳐 쓸 수 없다는 성질을 토큰도, 합의 네트워크도, 데이터의 공동 보관도 없이 제공한다. 공개되는 것은 서명과 다이제스트뿐이다. 이 방식을 소프트웨어 증거에서 사용 가능 판정 문서와 정비 이벤트로 넓히면 비인가 의심 부품을 위장하기가 훨씬 어려워질 것이다.
이 책에서 확인한 문제들은 구체적인 과제 목록이 된다. "편집상"으로 표시한 항목은 정오표로 고칠 수 있고, 나머지는 §8.5에 따른 릴리스가 필요하다.
| 과제 | 위치 | 제안 방향 |
|---|---|---|
| 개별 규범 요구사항에 안정된 식별자가 없음 | PHASE 1–4 | 벡터, 매트릭스, 프로파일 매니페스트가 참조할 수 있도록 ID 부여 |
| 언급된 OpenAPI 문서와 테스트 벡터 디렉터리가 공개되지 않음 | PHASE 2, 부속서 G | 참조 계층 자료로 공개 |
| 90일 대 180일 겹침 기간 | 부속서 F 대 부속서 H | 하나의 수치로 맞추고 적용 대상을 명시 |
| 이름 변경을 부 버전으로 분류 | 부속서 E 대 부속서 H | SemVer에 맞게 주 버전으로 분류 |
| 비규범 부속서 안에서 MUST를 사용 | 부속서 G | 이 부속서가 적합성 선언을 규율한다고 명시(편집상) |
| 존재하지 않는 부속서 A·D 참조, "OAuth 2.1 (RFC 9700)", "KCAA" | 부속서 E·F, PHASE 2 | 편집상: RFC 9700은 OAuth 2.0 보안 모범 사례(BCP)이며, 한국의 당국은 국토교통부이다 |
| 침투 탐상 방법이 없음, 소프트웨어 부품번호·시간·사이클 필드가 없음 | 참조 타입 | 선택 필드와 열거형 값 추가(부 버전), 모르는 값에 대한 규칙과 함께 |
density는 kg/m³로 정의되었으나 명세 표는 g/cm³ 사용 | 타입 대 PHASE 1 표 | kg/m³를 유지하고 스키마에 단위를 명시하며 타당성 한계 추가 |
사용 가능 판정서와 형식증명은 만료되지 않는데 Certification.expiryDate가 필수 | 참조 타입 | 만료 없는 승인을 위한 별도 선택 필드를 추가(제4장)하거나 증서 종류를 구분. 필수 필드를 선택으로 바꾸면 소비자가 깨짐 |
estimateFatigueLife가 물리적으로 불가능한 결과를 냄 | SDK, 시뮬레이터 | 삭제하거나 교육용 모델임을 명시한 것으로 교체 |
SDK makeRequest가 모의 데이터를 반환하고 CLI는 항상 통과 | SDK, CLI | 실제로 구현하거나, 도구 출력 자체에 골격임을 표시 |
이 가운데 어느 것도 표준의 기본 발상을 훼손하지 않는다. 이 장에서 다룬 장치들, 곧 테스트 벡터, 정직한 프로파일, 서명된 증거, 절제된 버전 관리야말로 이 문제들을 공개적으로 고쳐 나가는 도구이다.