AircraftComponent 레코드의 모든 필드를 살펴보고, 마지막으로 이 레코드에 대한 JSON Schema 2020-12 정의를 직접 작성해 시험한다. 예제로 항전 LRU를 계속 쓰는 까닭은 구조 부품보다 데이터 모델을 더 까다롭게 시험하기 때문이다.
항전(avionics)은 항공기에 탑재되는 전자 계통 전반을 가리킨다. 항법, 통신, 감시, 비행 관리, 표시 장치, 자동비행, 그리고 이들을 묶는 감시·기록 장비가 모두 여기에 속한다. 수송기에서는 대부분이 전자장비실의 랙에 꽂힌 장비 박스 형태로 존재하며, 안테나·센서·조종실 표시기와 배선으로 연결된다. 공학의 질문은 각 계통이 무엇을 하고 얼마나 정확하거나 이중화되어야 하는가이다. 이 책이 다루는 기록의 질문은 어느 물리적 장비가, 어떤 소프트웨어를 싣고, 어디에, 어떤 승인 근거로 장착되었는가이다.
위성항법(GNSS)은 GPS, GLONASS, Galileo, BeiDou 같은 위성군의 신호로 위치를 구한다. 항공용 수신기는 해의 무결성을 끊임없이 점검하며, 많은 경우 정지궤도 위성으로 보정값과 무결성 정보를 방송하는 위성기반 보강시스템(SBAS)을 함께 쓴다. 북미의 WAAS, 유럽의 EGNOS, 일본의 MSAS, 인도의 GAGAN, 한국의 KASS가 그 예다. SBAS 덕분에 모든 활주로에 지상 착륙 보조시설을 두지 않고도 수직 유도가 있는 접근이 가능해진다. PHASE-2 원문에 실린 정확도 수치는 수신기 등급과 위성 배치에 따라 달라지므로 참고치로 보아야 하며, 데이터 형식이 검사하는 요구사항이 아니다.
관성기준장치(IRS)는 가속도와 회전 각속도를 적분하며, 여객기에서는 주로 링레이저 자이로나 광섬유 자이로를 쓴다. 외부 신호가 필요 없어 GNSS 두절이나 전파 교란에도 살아남지만, 위치 오차가 시간에 따라 누적된다. 항법급 장치는 흔히 순수 관성 성능이 "시간당 약 1해리 급"이라고 표현되며, 항공기가 정지한 상태에서 정렬해야 하고 고위도일수록 정렬 시간이 길어진다. 비행관리시스템(FMS)은 두 센서 위에 자리한다. ICAO의 28일 AIRAC 주기로 개정되는 항법 데이터베이스와 기체별 성능 데이터베이스를 보유하고, 수평(LNAV)·수직(VNAV) 유도를 계산한다. FMS는 항전 기록이 "부품번호 하나"로 끝나지 않는 이유를 잘 보여 준다. 같은 하드웨어에 서로 다른 소프트웨어 릴리스가 실릴 수 있고, 데이터베이스는 4주마다 바뀐다.
항공교통 음성 통신은 약 118~137 MHz 대역의 VHF 진폭변조 무선을 쓴다. 유럽의 주파수 혼잡으로 8.33 kHz 채널 간격이 도입되어, 기존 25 kHz 채널 하나가 셋으로 나뉘었다. 19 MHz 폭의 대역에 25 kHz 간격이면 약 760개, 8.33 kHz 간격이면 약 2,280개의 채널이 들어간다. 8.33 kHz 간격에서는 조종사가 선택하는 여섯 자리 "채널 이름"이 실제 반송파 주파수와 항상 같지는 않으므로, "주파수"를 저장하는 레코드는 둘 중 무엇을 뜻하는지 밝혀야 한다. VHF 전파는 사실상 가시선 전파이므로 도달 거리는 고도에 따라 늘어난다.
d [NM] ≈ 1.23 × √h [ft] (표준대기 전파 수평선)
고도 h = 35,000 ft의 항공기, 해수면의 지상국:
√35,000 ≈ 187.1 → d ≈ 1.23 × 187.1 ≈ 230 NM
두 항공기 모두 35,000 ft:
d ≈ 230 + 230 ≈ 460 NM (지형·송신 출력·수신 감도가 허용하는 경우)
PHASE-2 원문이 순항 중 VHF 도달 거리를 수백 해리로 적은 것은 이 기하학 때문이다. 가시선을 넘어서면 항공기는 단측파대(SSB) 음성과 HF 데이터링크를 쓰는 HF 무선을 사용하는데, 전리층 전파에 의존하므로 시간대·계절·태양 활동에 따라 품질이 달라진다. SATCOM은 Inmarsat 같은 정지궤도 시스템과 Iridium 같은 저궤도 시스템으로 음성과 데이터를 제공하며, 후자는 극지방까지 덮는다. 데이터 속도는 세대와 서비스에 따라 크게 다르므로, 레코드에는 속도 수치 대신 단말의 부품번호와 구성을 저장해야 한다. ACARS 메시지와 관제사–조종사 데이터링크 통신(CPDLC)이 이 전송 수단 위에서 운용된다.
TCAS II는 ICAO ACAS II를 구현한 장비로, 주변 항공기의 트랜스폰더를 1030 MHz로 질문하고 1090 MHz로 응답을 받는다. 거리, 거리 변화율, 고도로부터 최근접 시각을 추정한 뒤 교통 권고(TA)를, 필요하면 상대 항공기의 TCAS와 조율된 수직 기동 지시인 회피 권고(RA)를 낸다. 경고 시간은 고정값이 아니다. 판정 문턱값이 고도에 따라 여러 감도 수준으로 커지며, 대략 TA는 20~48초, RA는 15~35초 범위다. PHASE-2 원문의 단일 수치는 이 범위의 한 점으로 읽어야 한다. 충돌회피 로직 버전(현재 7.1)도 기록 시스템이 보여 줄 수 있어야 하는 구성 항목이다.
ADS-B는 발상을 뒤집어, 항공기가 GNSS 기반의 자기 위치·속도·식별 정보를 스스로 방송하며, 가장 흔하게는 1090 MHz 확장 스퀴터(1090ES)를 쓴다. ADS-B Out 장착은 미국과 유럽을 비롯한 여러 관할권에서 의무화되어 있으나 규정·시행일·적용 공역은 서로 다르고, 미국은 일부 공역에서 978 MHz UAT 장비도 인정한다. 부품 레코드는 장비가 무엇을 할 수 있고 무엇으로 승인되었는지를 담고, "이 항공기가 이 공역에서 적합한가"는 운영자의 형상 관리에 맡겨야 한다.
대부분의 항전 기능은 현장교환품(LRU), 즉 정비사가 게이트에서 떼어 내고 바꿔 끼울 수 있는 밀폐 박스로 포장되며, 고장 난 장비는 정비 공장으로 보내진다. ARINC 600 같은 외형 규격이 랙·커넥터·냉각을 표준화하고, ARINC 429와 ARINC 664 Part 7(AFDX) 같은 데이터 버스가 박스들을 잇는다. 장비마다 네 가지 식별 정보가 중요하다.
| 기능 | 대표 장비 | P/N·S/N 외에 필요한 형상 정보 |
|---|---|---|
| 항법 | GNSS 수신기, IRS, FMS 컴퓨터 | 소프트웨어 P/N, 항법 데이터베이스 주기, SBAS 기능, 장착 위치 |
| 통신 | VHF·HF·SATCOM 송수신기, 데이터링크 장치 | 소프트웨어 P/N, 8.33 kHz 기능, 활성화된 데이터링크 응용, 개조 상태 |
| 감시 | TCAS II 컴퓨터, ADS-B Out 기능의 모드 S 트랜스폰더 | 충돌회피 로직 버전, ADS-B 출력 버전. 24비트 항공기 주소는 기체에 속한다 |
| 모든 LRU | — | 승인 근거(예: TSO), DO-160G 범주, 탑재 소프트웨어의 DAL, 출고 증명서 참조 |
트랜스폰더의 24비트 ICAO 주소는 장착 시 기체 등록에 맞추어 설정되며, 박스에 속하지 않는다. 장비와 장착의 이 구분은 이 장 전체에 되풀이된다. WIA 레코드는 장비를 기술한다. 장착은 시간에 따라 변하는 관계이며, 추적성 작업 그룹(제5장)과 정비 이벤트 흐름(제6장)이 기록한다.
PHASE-1 문서는 범위를 한 문장으로 밝힌다. 이 문서는 "항공기 부품 식별, 구조 한계, 재료 인증 기록, 추적성 메타데이터를 위한 정규 데이터 구조"를 정의하며, 이는 JSON Schema 2020-12로 표현되고 해당 PHASE가 유효한 동안 안정적으로 유지된다. 각 구절은 참조 타입의 필드 묶음에 대응한다.
| PHASE-1 관심사 | 답하는 질문 | 참조 타입의 필드 |
|---|---|---|
| 부품 식별 | 어떤 설계이며, 어떤 개별 장비인가? | id, type, partNumber, serialNumber, manufacturer, manufacturingDate |
| 구조 한계 | 크기와 무게는 얼마이며, 어떤 하중을 견딤이 입증되었는가? | dimensions, structuralAnalysis |
| 재료 인증 | 무엇으로, 어떤 물성으로 만들어졌는가? | material |
| 추적성 메타데이터 | 어떤 승인·검사·이력이 붙어 있는가? | status, certification, inspectionHistory, metadata |
부품 데이터는 공급자의 ERP 내보내기, PDF 출고 증명서, MRO 공장의 작업지시 시스템, 항공사의 기술 기록 데이터베이스 등 여러 형태로 조직 사이를 오간다. 각 시스템은 같은 일련번호를 저마다의 어휘로 기술하고, 전달 단계마다 숫자 하나가 뒤바뀌거나 개조 상태가 누락될 여지가 생긴다. 정규 형식은 이런 시스템을 대체하지 않는다. 대신 교환용 표현을 하나로 정해, 받는 쪽이 사람이 보기 전에 기계적으로 검증할 수 있게 한다. PHASE 1이 API(PHASE 2)와 프로토콜(PHASE 3)보다 앞서는 이유가 여기에 있다. 인터페이스의 정밀도는 그것이 나르는 데이터의 정밀도를 넘지 못한다.
JSON Schema 2020-12는 OpenAPI 3.1이 정렬된 초안이며, PHASE 2가 인터페이스를 OpenAPI로 기술하므로 이 점이 중요하다. 스키마 하나로 저장된 레코드, API 요청 본문, 조직 간 메시지를 모두 검증할 수 있다. 이 초안은 재사용 가능한 하위 스키마를 위한 $defs를 도입했고, 형식(format) 검사를 선택적 어휘로 분리했다. 후자는 4.6절에서 중요해진다.
PHASE-1 파일은 짧은 서론 뒤로 공학 참고 자료(동체·날개·꼬리 구조, 알루미늄·티타늄·복합재 물성)로 채워져 있으며, 이는 제2장에서 다루었다. 기계가 읽을 수 있는 스키마는 들어 있지 않다. 레코드의 구체적 형태는 SDK 패키지 @wia/aircraft-component의 TypeScript 참조 타입(api/typescript/src/types.ts)이 정의한다. 이 장은 그 타입을 규범적 형태로 보고 JSON Schema를 도출하며, 타입이 열어 둔 문제에 대한 답은 제안으로 표시한다. 파일의 참고문헌은 확인할 수 없는 하드웨어 부품 식별 표준도 인용하므로, 이 책은 그 표준에 기대지 않는다. 함께 인용된 품질체계 표준 AS9100D와 AS9145는 실재하며 제7장에서 다시 다룬다.
AircraftComponent 해부AircraftComponent는 루트 객체로, 일련번호가 붙은 물리적 부품 하나마다 레코드 하나를 둔다. 아래 표는 참조 타입의 모든 필드와 단위·형식, 필수 여부, 그리고 현실에서 그 값을 공급하는 문서를 정리한 것이다. 실무에서는 마지막 열이 가장 중요하다. 출처가 불분명한 필드는 제각각 채워지기 때문이다.
| 필드 | 타입 | 단위 / 형식 | 필수 | 대표 출처 문서 |
|---|---|---|---|---|
id | string | 불투명 식별자(UUID 권장) | 예 | 레코드를 만든 시스템이 부여 |
type | ComponentType | 열거값 9개 | 예 | 공학적 분류 |
status | ComponentStatus | 열거값 6개 | 예 | 수명주기 상태(아래 참조) |
partNumber | string | 표기 그대로 | 예 | 명판, 도면, 부품 카탈로그 |
serialNumber | string | 표기 그대로 | 예 | 명판, 출고 증명서 |
manufacturer | string | 법인명 | 예 | 생산승인 보유자, CoC |
manufacturingDate | string | ISO 8601 날짜 | 예 | 생산 기록 |
dimensions | Dimensions | mm, kg | 예 | 외형·장착 도면 |
material | MaterialSpecification | MPa, kg/m³ | 예 | 재료 성적서, 도면 주기 |
structuralAnalysis | StructuralAnalysis | kN, MPa, 사이클 | 아니요 | 응력 해석 보고서 |
certification | Certification | ISO 8601 날짜 | 아니요 | 승인 문서 |
inspectionHistory | Inspection[] | 배열, 비어 있어도 됨 | 예 | 검사 보고서, 작업지시서 |
metadata | object | 자유 형식 | 아니요 | 위에서 모델링되지 않은 모든 것 |
id는 시스템 내부에서 레코드를 가리키는 키다. UUID처럼 불투명해야 하며, 부품번호와 일련번호를 조합해 만들어서는 안 된다. 장비가 개조되어 재식별되면 부품번호가 바뀌고, 시스템을 대사(reconcile)하면 레코드가 병합되는데, 업무 데이터를 품은 식별자는 두 경우 모두 깨진다. 장비의 업무상 식별은 제작사 + 부품번호 + 일련번호의 조합이다(4.5절).
type은 fuselage, wing, tail, landing-gear, flight-control, hydraulic-system, fuel-system, avionics, engine-mount 아홉 값 중 하나를 가진다. 이 거친 분류는 검색(ComponentSearchQuery.type, 제5장)을 돕고 어떤 필드가 의미 있는지 알려 주지만, 업계가 쓰는 ATA iSpec 2200 장 번호 체계를 대신하지는 못한다. 그 체계에서 통신은 23장, 항법은 34장, 자동비행은 22장이다. 항전 장비는 ATA 장 번호도 metadata에 기록해 두는 것이 좋다.
status는 design, testing, certified, production, maintenance, retired를 가지며, 이 목록에는 두 가지 수명주기가 섞여 있다. 앞의 셋은 설계를 기술하므로 한 부품번호의 모든 일련번호에 똑같이 적용된다. 뒤의 셋은 개별 장비를 기술한다. 생산됨, 정비 순환 중, 퇴역이다. 일련번호 단위 레코드에서 이 책은 장비 기준 해석을 쓴다. 새로 출고된 장비는 production, 떼어 내 정비 공장으로 보낸 장비는 maintenance, 폐기되었거나 수명이 끝난 장비는 retired이다. 앞의 상태들은 시제품과 시험품, 예컨대 DO-160 시험을 거치고 비행하지 않을 인증 시험용 장비에 쓴다. 상태 전이는 제6장에서 정식화한다. 구현체는 자신이 택한 해석을, 가능하면 프로파일 매니페스트(제8장)에 밝혀야 한다.
아래 예는 가상의 VHF 송수신기를 기술하며, 이름·번호·치수는 모두 예시다. 재료 블록은 단위를 보여 주기 위해 PHASE-1의 2024-T3 값을 빌려 썼을 뿐이다. 실제 섀시 합금은 제작사 도면에서 가져와야 한다. 참조 타입에 담을 자리가 없는 사실은 metadata에 넣었다(4.5절).
AircraftComponent 레코드{
"id": "3f2b8c1e-6a4d-4e0b-9c57-1d2e8a7b5f10",
"type": "avionics",
"status": "production",
"partNumber": "EXV-2100-01",
"serialNumber": "VX21-004517",
"manufacturer": "Example Avionics Co.",
"manufacturingDate": "2024-03-15",
"dimensions": { "length": 320, "width": 60, "height": 194, "weight": 3.2 },
"material": {
"type": "aluminum-2024",
"grade": "2024-T3 (chassis sheet)",
"strength": 470,
"fatigueLimit": 140,
"density": 2780,
"corrosionResistance": "medium"
},
"inspectionHistory": [
{
"id": "insp-2024-000871",
"inspectionDate": "2024-03-18",
"inspector": "QA-017",
"method": "visual",
"result": "pass",
"findings": "수입 검사: 명판, 커넥터 핀, 실(seal), 라벨 적합."
}
],
"metadata": {
"wiaSchemaVersion": "1.0.0",
"manufacturerCode": { "scheme": "CAGE", "value": "EX001" },
"ataChapter": "23",
"hardwareModStatus": "B",
"softwareParts": [ { "role": "radio application", "partNumber": "EXV-SW-2100-0304" } ],
"approvalBasis": [ { "authority": "FAA", "basis": "TSO-C169a" } ],
"environmentalQualification": "RTCA DO-160G, categories per the unit's declaration",
"capabilities": { "channelSpacingKHz": [25, 8.33] },
"releaseCertificate": { "form": "FAA Form 8130-3", "trackingNumber": "EX-000123" },
"usage": { "hoursSinceNew": 0, "cyclesSinceNew": 0, "asOf": "2024-03-18" }
}
}
세 가지는 일부러 뺐다. structuralAnalysis가 없는 것은 LRU의 기계적 인증이 DO-160 진동·충격 시험이기 때문이며, 필드를 채우려고 가해 하중을 지어내면 레코드의 진실성이 떨어진다. certification이 없는 이유는 4.4절에서 설명한다. 장착 위치나 기체 등록부호가 없는 것은 그것이 장착에 속하는 정보이기 때문이다.
DimensionsDimensions는 length, width, height를 밀리미터로, weight를 킬로그램으로 담는다. 여기서 "weight"는 힘이 아니라 질량이며, 뉴턴이나 파운드힘으로 변환하는 시스템은 이 점을 알아야 한다. 세 길이는 취급·포장·보관용 외곽 치수이지 형상 모델이 아니다. 날개 패널이라면 "높이"가 수 미터 폭에 수 밀리미터 두께일 뿐이다. 도면이 무게 공차를 허용한다면 장비의 실측 무게를 기록하라. 항공기 중량·평형 기록이 궁극적으로 기대는 값이 그것이다.
MaterialSpecificationMaterialSpecification은 type(aluminum-2024, aluminum-7075, titanium-ti6al4v, carbon-fiber, fiberglass, aramid, composite), 자유 텍스트 grade, MPa 단위의 strength와 fatigueLimit, kg/m³ 단위의 density, 세 단계의 corrosionResistance를 담는다. 재료 자체는 제2장에서 다루었다. 일관되게 기록하려면 네 가지 규약이 필요하다.
2.78이 있다면 거의 확실히 단위 오류이며, 스키마로 잡아낼 수 있다(4.6절).metadata.fatigueBasis). 그렇지 않으면 읽는 사람이 그 아래에서는 부품이 결코 파손되지 않는 응력으로 오해할 수 있다.composite나 비슷한 합금으로 적는 것은 오해를 낳으며, 정직한 방법은 새 값을 제안하는 것이다(4.5절).StructuralAnalysisStructuralAnalysis는 응력 해석을 요약한다. appliedLoad(kN), calculatedStress(MPa), 무차원 safetyFactor, fatigueLife(사이클), analysisDate, analyst로 이루어진다. 이것은 요약이지 해석 자체가 아니다. 하중 조건, 유한요소 모델, 임계 위치별 여유를 담은 응력 보고서는 설계 조직의 관리 문서로 남으며, 레코드는 metadata에서 문서 번호와 개정판으로 그것을 인용해야 한다.
safetyFactor라는 필드명은 모호하다. 허용 응력 대 가해 응력의 비는 여유계수(reserve factor)이고, 안전 여유(margin of safety)는 그 비에서 1을 뺀 값이다(MS = 허용/가해 − 1, 제1장). CS/FAR 25.303의 안전계수 1.5는 또 다른 것으로, 비교 전에 제한하중을 극한하중으로 바꾸는 데 쓰인다. 이 책은 제1장에 따라 지배 하중 조건의 임계 위치에서 계산한 허용값 대 극한 수준 설계 응력(특수 계수 포함)의 비를 저장하고, 하중 조건은 metadata에 적는다. 이 규약에서는 합격 기준이 1.0이므로, 기본 문턱값이 1.5인 SDK 도우미 ComponentAnalyzer.isSafe는 최솟값 1.0을 명시해 호출해야 한다. 기본값 1.5는 제한하중에서 계산한 응력에만 들어맞는다(제5장). fatigueLife는 피로·손상 허용 해석(제7장)에서 가져와야 하며, SDK의 estimateFatigueLife를 써서는 안 된다. 그 공식은 가해 응력이 극한강도와 같을 때 10⁸ 사이클을 돌려준다. 제5장에서 반면교사로 살펴본다.
CertificationCertification에는 필수 필드 여섯 개가 있다. standard, authority, certificateNumber, issueDate, expiryDate, 그리고 불리언 compliance이다. 참조 타입의 주석은 standard의 예로 "WIA-SPACE-016"을 들지만, PHASE 2의 인증 패키지 그룹은 항공 당국용 증거를 말하므로 이 객체는 범용이다. 레코드는 막연한 "FAA 인증" 대신 "TSO-C169a"와 "FAA"처럼 정확한 값을 써야 한다.
문제는 expiryDate이다. TSO 승인, 형식증명, FAA Form 8130-3, EASA Form 1에는 달력상의 만료일이 없다. 반납·정지·대체되거나 부품 상태가 바뀔 때까지 유효하다. 필수 만료일 필드를 지어낸 먼 미래 날짜로 채우면 레코드를 위조하는 셈이다. 그래서 LRU 예제는 선택 필드인 certification 객체를 빼고 승인 근거를 metadata.approvalBasis에 기록했다. 실제로 만료되는 것, 예컨대 실런트 배치의 저장 수명이나 시험 장비의 교정은 별도 레코드로 모델링하는 편이 낫다.
InspectionInspection은 id, inspectionDate, inspector, method, result와 선택 필드 findings, nextInspectionDate로 이루어진다. 다섯 방법(visual, ultrasonic, radiographic, magnetic-particle, eddy-current)은 비파괴 검사 기법이며 제7장에서 설명한다. 곧바로 두 빈틈이 보인다. 가장 흔한 방법 중 하나인 침투 탐상이 빠져 있다. 정비 공장이 항전 장비를 인수 시험 절차에 따라 실제로 점검하는 방식인 기능·벤치 시험도 없다. 열거값이 늘어날 때까지 이런 시험은 metadata나 정비 이벤트(제6장)에 두고, visual에는 실제로 육안 검사한 내용만 기록해야 한다.
result 값은 pass, fail, conditional이다. "conditional"은 한계 이내이지만 지켜봐야 한다는 뜻이며, findings와 nextInspectionDate 없이는 조치로 이어지지 않는다. 4.6절의 스키마가 이 짝을 강제한다. inspector는 손으로 친 이름이 아니라 품질 시스템이 자격 보유자로 해석할 수 있는 식별자여야 한다. 많은 검사가 비행시간이나 사이클로 일정이 잡히는데, 달력 날짜인 nextInspectionDate로는 이를 표현할 수 없다. 사용량 카운터가 필요한 이유가 하나 더 생긴다.
일련번호는 한 제작사의 한 부품번호 안에서만, 때로는 한 제작사 안에서만 유일하다. 두 공급자가 모두 일련번호 1001을 출고할 수 있다. 따라서 WIA 레코드의 업무 키는 (제작사, partNumber, serialNumber) 세 값이어야 하고, 등록소는 같은 조합의 중복을 거부해야 한다. manufacturer는 자유 텍스트여서 "Example Avionics Co."와 "Example Avionics Company"가 서로 다르게 비교되므로, 코드화된 식별자도 metadata에 저장하라. 항공우주 공급망의 상당 부분에서는 CAGE 코드를 쓴다. 미국에서는 국방군수국(DLA)이 부여하는 다섯 자리 코드이며, 다른 참여국 조직에는 NCAGE 코드가 쓰인다. 예제는 다른 체계도 명시할 수 있도록 이를 {"scheme": "CAGE", "value": "EX001"}로 기록했다.
부품번호와 일련번호는 하이픈과 앞자리 0까지 표기된 그대로 저장해야 한다. 정규화하면 한 데이터베이스 안에서는 대조가 쉬워지지만, 검사원이나 미승인 의심 부품 조사자가 확인하는 실물 명판과의 대조는 불가능해진다. 검색용 정규화 형태가 필요하면 파생값으로 따로 두라.
날짜는 모두 문자열이다. 이 책은 manufacturingDate, inspectionDate, nextInspectionDate, issueDate, expiryDate, analysisDate에 YYYY-MM-DD 형태의 ISO 8601 달력 날짜(RFC 3339의 full-date)를 요구한다. 시각이 필요한 이벤트는 UTC 오프셋을 명시한 완전한 RFC 3339 타임스탬프를 쓴다. 항공기와 정비 공장은 여러 시간대에 걸쳐 있기 때문이다. 단위는 필드 정의로 고정되어 있으며(mm, kg, kN, MPa, kg/m³, 사이클), 단위 접미사가 붙은 문자열로 적지 않는다. 인치나 파운드를 받는 시스템은 경계에서 변환하고, 감사를 위해 원래 값을 metadata에 남길 수 있다.
항전 예제를 다루다 보면 실제 기록 시스템에 필요하지만 참조 타입에 없는 사실들이 드러난다. 아래의 확장 제안은 PHASE 1의 향후 부 버전에 대한 이 책의 제안이며, 현행 표준의 일부가 아니다.
| 빈틈 | 중요한 이유 | 임시 위치 | 확장 제안 |
|---|---|---|---|
| 소프트웨어 부품번호 | LRU의 기능은 탑재된 DO-178C 소프트웨어에 달려 있다 | metadata.softwareParts[] | 선택 필드 softwareParts 배열 |
| 개조 상태 | SB 반영은 P/N 변경 없이 장비를 바꾼다 | metadata.hardwareModStatus | 선택 필드 modStatus |
| 시간과 사이클 | 수명 한계와 반복 검사는 시간·사이클로 센다 | metadata.usage | 기준일이 있는 선택 객체 usage |
| 코드화된 제작사 | 자유 텍스트 이름은 안정적으로 비교되지 않는다 | metadata.manufacturerCode | 선택 필드 manufacturerCode {scheme, value} |
| 만료 없는 승인 | expiryDate는 필수인데 많은 승인에는 만료가 없다 | metadata.approvalBasis[] | Certification 옆의 새 선택 필드 |
| 침투 탐상, 기능 시험 | 흔한 검사를 기록할 수 없다 | findings + metadata | method 값 추가 |
사용량 카운터의 필드명은 풀어 써야 한다. 기록 분야의 약어 TSN, CSN, TSO(신품 이후 시간, 신품 이후 사이클, 오버홀 이후 시간)는 충돌한다. "TSO"는 기술표준품이기도 하다. 모든 카운터에는 유효 기준일이 있어야 한다. 기준일 없는 사이클 수는 기록한 다음 날 이미 낡은 값이 된다.
PHASE 문서들의 부속서 E는 추가 변경(새 선택 필드, 새 오류 코드)은 호환을 깨지 않으며, 필드의 이름 변경이나 삭제는 "부 버전 증가를 반드시 유발해야 한다(MUST)"고 적는다. 부속서 H는 Semantic Versioning 2.0.0을 채택하여, 하위 호환이 깨지는 변경을 주 버전으로 다룬다. 두 부속서는 이름 변경과 삭제에서 어긋난다. 이 책은 버전 정책 전용 문서인 부속서 H를 따라 이를 주 버전 변경으로 취급한다.
이 규칙으로 보면 위 제안 대부분은 부 버전 변경이다. 옛 생산자는 보내지 않고 옛 소비자는 무시할 수 있는 선택 필드를 추가하기 때문이다. 두 가지는 주의가 필요하다. 새 열거값은 형식상 추가 변경이지만 옛 목록으로 엄격히 검증하는 소비자를 깨뜨리므로 사전 공지와 함께 내놓아야 한다. 관대한 소비자라면 모르는 값을 만났을 때 레코드를 거부하지 말고 검토 대상으로 표시해야 한다. 기존 expiryDate를 선택 필드로 바꾸면 그 필드가 항상 있다고 가정한 소비자가 깨지므로, 표에서는 별도 필드를 제안했다. 표준화되기 전까지 이 책이 쓰는 metadata 키는 구현 프로파일로서 프로파일 매니페스트(제8장)에 선언하고, 각 레코드에는 작성 기준이 된 스키마 버전(metadata.wiaSchemaVersion)을 적는다.
SDK 패키지는 zod 라이브러리를 의존성으로 선언하지만 클라이언트 코드는 이를 호출하지 않는다. CLI의 validate와 check 명령도 현재는 입력을 살피지 않고 성공 메시지를 출력한다(제8장). 따라서 배포 환경은 자체 검증기를 갖추어야 한다. 아래 스키마는 참조 타입에 이 장에서 논증한 제약을 더해 도출한 것으로, 표준이 공표한 스키마가 아니라 출발점으로 제시하는 이 책의 구성이다.
AircraftComponent용 JSON Schema 2020-12(발췌, $id는 자리표시자){
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:example:wia-space-016:aircraft-component:1.0.0",
"title": "AircraftComponent",
"type": "object",
"required": ["id", "type", "status", "partNumber", "serialNumber",
"manufacturer", "manufacturingDate", "dimensions",
"material", "inspectionHistory"],
"additionalProperties": false,
"properties": {
"id": { "type": "string", "minLength": 1 },
"type": { "enum": ["fuselage", "wing", "tail", "landing-gear",
"flight-control", "hydraulic-system", "fuel-system",
"avionics", "engine-mount"] },
"status": { "enum": ["design", "testing", "certified",
"production", "maintenance", "retired"] },
"partNumber": { "type": "string", "pattern": "^\\S(.*\\S)?$" },
"serialNumber": { "type": "string", "pattern": "^\\S(.*\\S)?$" },
"manufacturer": { "type": "string", "minLength": 1 },
"manufacturingDate": { "type": "string", "format": "date" },
"dimensions": { "$ref": "#/$defs/Dimensions" },
"material": { "$ref": "#/$defs/MaterialSpecification" },
"structuralAnalysis":{ "$ref": "#/$defs/StructuralAnalysis" },
"certification": { "$ref": "#/$defs/Certification" },
"inspectionHistory": { "type": "array", "items": { "$ref": "#/$defs/Inspection" } },
"metadata": { "type": "object" }
},
"$defs": {
"Dimensions": {
"type": "object",
"required": ["length", "width", "height", "weight"],
"additionalProperties": false,
"properties": {
"length": { "type": "number", "exclusiveMinimum": 0 },
"width": { "type": "number", "exclusiveMinimum": 0 },
"height": { "type": "number", "exclusiveMinimum": 0 },
"weight": { "type": "number", "exclusiveMinimum": 0 }
}
},
"MaterialSpecification": {
"type": "object",
"required": ["type", "grade", "strength", "fatigueLimit",
"density", "corrosionResistance"],
"additionalProperties": false,
"properties": {
"type": { "enum": ["aluminum-2024", "aluminum-7075", "titanium-ti6al4v",
"carbon-fiber", "fiberglass", "aramid", "composite"] },
"grade": { "type": "string", "minLength": 1 },
"strength": { "type": "number", "exclusiveMinimum": 0 },
"fatigueLimit": { "type": "number", "minimum": 0 },
"density": { "type": "number", "minimum": 500, "maximum": 20000,
"$comment": "kg/m3; bounds catch g/cm3 entries" },
"corrosionResistance": { "enum": ["low", "medium", "high"] }
}
},
"Inspection": {
"type": "object",
"required": ["id", "inspectionDate", "inspector", "method", "result"],
"additionalProperties": false,
"properties": {
"id": { "type": "string", "minLength": 1 },
"inspectionDate": { "type": "string", "format": "date" },
"inspector": { "type": "string", "minLength": 1 },
"method": { "enum": ["visual", "ultrasonic", "radiographic",
"magnetic-particle", "eddy-current"] },
"result": { "enum": ["pass", "fail", "conditional"] },
"findings": { "type": "string" },
"nextInspectionDate": { "type": "string", "format": "date" }
},
"if": { "properties": { "result": { "const": "conditional" } } },
"then": { "required": ["findings", "nextInspectionDate"] }
}
}
}
StructuralAnalysis와 Certification도 같은 방식이며 지면 관계로 생략했다. 두 객체의 모든 필드가 필수이고 compliance는 불리언이다. 세 가지 설계 결정은 설명이 필요하다.
metadata. additionalProperties: false는 serialNo 같은 오타 필드를 거부한다. 그렇지 않으면 진짜 serialNumber가 빠진 채 오타 필드가 조용히 저장된다. 확장은 metadata로 간다. 대가는 1.0.0 검증기가 최상위 필드를 추가한 이후 부 버전의 레코드를 거부한다는 점이며, 따라서 수신 시스템은 레코드의 wiaSchemaVersion에 따라 스키마를 골라야 한다.exclusiveMinimum: 0과 넓은 밀도 범위는 공학적 한계가 아니라 부호·단위 오류를 잡는 값싼 덫이다. 범위는 발포재부터 중금속까지 아우르며, g/cm³로 입력된 2.78을 거부하려고 둔 것이다.if/then 블록은 조건부 판정 규칙을 구현하고, 식별자 패턴은 스프레드시트에서 흔히 생기는 앞뒤 공백을 금지하면서도 제작사의 표기 체계는 제약하지 않는다.format의 함정2020-12에서 format은 기본적으로 검증(assertion)이 아니라 주석(annotation)이다. 형식 검증을 켜지 않으면 검증기가 "15/03/2024"를 날짜로 받아들일 수 있다. Ajv 라이브러리에서는 2020-12 빌드를 쓰고 formats 패키지를 등록해야 한다.
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
import schema from "./aircraft-component.schema.json";
const ajv = new Ajv2020({ allErrors: true, strict: true });
addFormats(ajv); // 이것이 없으면 "format": "date"를 검사하지 않는다
const validate = ajv.compile(schema);
export function checkRecord(record: unknown): string[] {
if (validate(record)) return [];
return (validate.errors ?? []).map(
(e) => `${e.instancePath || "/"} ${e.message ?? "invalid"}`
);
}
TypeScript 서비스 내부에서는 zod로 검증하고 같은 정의에서 정적 타입을 얻는 편이 편리하다. 아래 대응 정의는 Inspection을 다루며 같은 조건부 규칙을 담는다. 날짜 정규식은 모양만 확인하므로, 실운용용 정의는 2024-02-30 같은 불가능한 날짜도 거부해야 한다. zod 정의와 JSON Schema를 일치시키는 것은 시험의 몫이다. 제8장이 테스트 벡터에 대해 설명하듯 CI에서 같은 양성·음성 예제로 둘 다 돌려야 한다.
Inspection 대응 정의import { z } from "zod";
const isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD");
export const Inspection = z.object({
id: z.string().min(1),
inspectionDate: isoDate,
inspector: z.string().min(1),
method: z.enum(["visual", "ultrasonic", "radiographic",
"magnetic-particle", "eddy-current"]),
result: z.enum(["pass", "fail", "conditional"]),
findings: z.string().optional(),
nextInspectionDate: isoDate.optional(),
}).strict().superRefine((v, ctx) => {
if (v.result === "conditional" && (!v.findings || !v.nextInspectionDate)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "conditional result needs findings and nextInspectionDate",
});
}
});
export type Inspection = z.infer<typeof Inspection>;
스키마의 가치는 거부함을 입증한 잘못된 입력만큼이다. 아래 각 사례는 4.3절의 유효 레코드를 조금 변형한 것이며 모두 실패해야 한다. "무효"라는 사실만이 아니라 보고된 키워드까지 확인하는 시험이어야 엉뚱한 이유로 실패하는 레코드를 잡아낸다.
| 유효 레코드의 변형 | 실패해야 할 키워드 | 현실의 원인 |
|---|---|---|
serialNumber 삭제 | required | 수입 시 일련번호 미기록 |
serialNumber 대신 serialNo | additionalProperties, required | 시스템 간 필드명 표류 |
"type": "avionic" | enum | 수기 입력 또는 옛 분류 |
"manufacturingDate": "15/03/2024" | format(형식 검증을 켠 경우만) | 지역 형식의 스프레드시트 날짜 |
"density": 2.78 | minimum | kg/m³ 자리에 g/cm³ 입력 |
"weight": -3.2 | exclusiveMinimum | 변환 스크립트의 부호 오류 |
검사 "method": "penetrant" | enum | 1.0에서는 옳은 실패이며, 빠진 열거값을 알리는 신호 |
nextInspectionDate 없는 "result": "conditional" | required(then 내부) | 후속 조치 없는 미결 소견 |
"partNumber": "EXV-2100-01 " | pattern | 내보내기에서 생긴 뒤 공백 |
이 스키마를 통과한 레코드는 형식이 올바를 뿐이다. 그렇다고 내용이 참이 되지도, 기술된 부품이 감항성을 갖추게 되지도 않는다. 스키마는 일련번호 VX21-004517이 주장된 승인하에 실제로 생산되었는지, 출고 증명서가 존재하는지, 적힌 소프트웨어가 박스에 실제로 탑재되어 있는지 알 수 없다. 이 질문들은 API의 추적성·인증 작업(제5장), 공급자·MRO·운영자·당국 사이의 출고·정비 흐름(제6장), 품질체계의 서명된 증거 패키지(제7장)가 답한다. PHASE 1은 모든 당사자가 같은 장비에 대해, 같은 단위로, 같은 필드명으로 말하게 함으로써 그 과정들을 가능하게 한다. 스키마는 그 합의를 강제하는 가장 값싼 자리이며, 새 구현체가 가장 먼저 통과해야 할 시험이다.