제8장
WIA-BIO-010 v1.0이 구현에 실제로 요구하는 것이 무엇인지, 그 요구를 어떻게 테스트 벡터로 바꾸는지, 참조 SDK에 벡터를 돌리면 어떤 결과가 나오는지, 그리고 그 결과가 컴퓨터 시스템 밸리데이션, 실태조사, 표준의 다음 개정과 어떻게 이어지는지를 다룬다.
앞의 일곱 장은 WIA-BIO-010을 한 층씩 분해해 살폈다. 이 장은 표준화 작업에서는 맨 마지막에, 감사에서는 맨 처음에 나오는 질문을 다룬다. 구현이 표준대로 동작한다는 것을 어떻게 보여 줄 것인가?
솔직하게 말하면 v1.0은 이 질문에 거의 도움을 주지 않는다. 필수 구성요소, 인터페이스, 유효 한계, 오류 코드는 나열하지만 적합성 등급도, 시험 스위트도, 인증 절차도 정의하지 않는다. 이 책은 그 빈자리를 채우려고 등급 이름이나 합격률, 인증 건수를 지어내지 않는다. 대신 더 쓸모 있는 것을 제시한다. 조항까지 추적할 수 있는 요구사항 목록, 기댓값을 손으로 검산할 수 있는 테스트 벡터, 그리고 참조 코드가 어디서 통과하고 어디서 실패하는지에 대한 분명한 기록이다. 감사자나 실태조사관이 보자고 하는 것도 바로 이런 자료다.
엄밀하게 읽으면 WIA-BIO-010 v1.0의 의무 조항은 §8에 모여 있고, §3.4(21 CFR Part 11), §4(통계 방법), §5(이상사례 보고), §7(데이터 모델), §9(안전성 프로토콜)가 이를 뒷받침하는 정의를 제공한다. §8.1은 "WIA-BIO-010을 준수하는 모든 시스템은 다음을 포함해야 한다"는 문장으로 시작해 다섯 구성요소를 열거하고, §8.2~§8.4는 인터페이스, 유효 한계, 오류 코드를 기술한다. 의무를 나타내는 표현은 이 밖에는 쓰이지 않으므로, 나머지는 지침으로 읽는 것이 적절하다.
| 조항 | 요구 내용 | 검증 방법 |
|---|---|---|
| §8.1 (1) | 시험 등록 모듈: 계획서와 메타데이터 관리 | §7.1 필드를 갖는 시험 기록의 생성·변경·조회, 변경 계획서 간 계획서 버전 추적 |
| §8.1 (2) | 검증 기능을 갖춘 EDC 시스템 | §7.2 방문 데이터 입력, §8.3 범위 적용, 감사추적과 함께 쿼리 발행·종결 |
| §8.1 (3) | 안전성 보고: SAE 탐지와 보고 | §5.4 기준에서 중대성 도출, 올바른 0일에서 계산한 신속보고 시한 |
| §8.1 (4) | 통계 엔진: 표본크기, 검정력, 분석 | §4 공식과 부록 A 예제(수정본, 8.2 참조)와 결과 일치 |
| §8.1 (5) | 규제 제출: CDISC 내보내기와 eCTD 생성 | define.xml을 갖춘 SDTM/ADaM 데이터셋의 외부 적합성 검사 통과, 밸리데이션된 도구로 eCTD 패키징 |
| §8.2 | 세 인터페이스: 시험 등록, 환자 데이터 수집, 통계 계산 | §8.2.1~8.2.3의 입출력 형태 또는 그에 대한 문서화된 대응표를 기준으로 한 인터페이스 시험 |
| §8.3 | 활력징후 5종과 검사값 4종의 유효 한계 | 각 한계의 경계값, 바로 안쪽, 바로 바깥쪽에 대한 경계 시험 |
| §8.4 | 오류 코드 B001~B006과 각 코드가 뜻하는 조치 | 정해진 입력으로 각 코드를 유발할 수 있고, 조용히 무시되지 않고 반환됨 |
§8 밖의 두 조항은 의무 표현이 아니더라도 진지한 적합성 주장에는 반드시 끌어들여야 한다. §3.4는 21 CFR Part 11을 명시하며, 감사추적이나 접근 통제 없이 임상 데이터를 수집하는 시스템은 표준의 문구와 상관없이 규제 대상 업무에 쓸 수 없다. §9.1은 21 CFR 50.25의 동의 요소를 나열하고, §8.4는 "피험자 동의서 누락"(B006)을 데이터 수집 거부 사유로 정한다. 제7장에서 두 기능의 구현을 보였고, 여기서는 이를 시험 사례로 바꾼다.
package.json에 Jest 설정은 있지만 패키지에 테스트 파일은 없다. 시뮬레이터는 임상시험 로직이 없는 범용 템플릿이고, CLI는 시연용 출력을 찍는다(제5장). 어느 것도 적합성 시험이 아니다.spec 폴더의 네 파일은 다른 표준 번호를 달고 있고 바이오마커 분석법 기준을 다룬다. WIA-BIO-010의 요구사항으로 인용해서는 안 된다.따라서 v1.0에 대한 적합성 선언은 등급을 주장하지 말고, 다루는 조항을 명시하고 조항마다 증거를 가리켜야 한다. 이 장의 나머지는 그 증거를 만드는 과정이다.
시험을 작성하기 전에 어떤 계약을 시험할지부터 정해야 한다. §8.2 인터페이스와 @wia/bio-010 타입은 비슷하지만 같지 않아서, 한쪽 기준으로 쓴 시험은 정확성과 무관한 이유로 다른 쪽에서 실패한다.
| 항목 | 명세 §8.2 / §7 | SDK(types.ts) | 시험에 주는 영향 |
|---|---|---|---|
| 시험 식별자 | id(§8.2.1), trialId(§7.1) | id | 입력 시 §7.1 JSON을 대응시키고, 필드 이름을 문자 그대로 비교하지 않는다 |
| 일차 평가변수 | §8.2.1은 문자열, §7.1은 객체 | 입력은 문자열, 출력은 Endpoint 객체 | 객체를 시험한다. 시점은 duration에서 도출된다 |
| 표본크기 | §8.2.1은 숫자, §7.1은 객체 | 입력은 숫자, 출력은 SampleSizeInfo | sampleSize.planned를 확인한다 |
| 시험 상태 | §8.2.1은 5개 값 | 6개 값(suspended 추가) | suspended를 확장으로 인정한다 |
| 데이터 수집 응답 | dataId 항상 존재 | 실패 시 dataId 미정의, timestamp 추가 | success가 true일 때만 dataId를 단언한다 |
| 통계 매개변수 | 표준편차·사건 발생률 없음 | 표본크기 계산에 둘 중 하나가 필수 | §8.2.3 입력만으로는 표본크기를 낼 수 없다 |
| 오류 코드 | B001~B006 | B001~B010 | B007~B010은 SDK 확장이다 |
가장 중요한 것은 끝에서 두 번째 행이다. §8.2.3의 StatisticalParameters는 effectSize는 있지만 분산도 사건 발생률도 없으므로, 표본크기를 계산하기에 정보가 부족하다. 명세의 형태를 그대로 따르는 SDK의 독립 함수 calculateStatistics는 그래서 표본크기를 요청받으면 B005를 던지고, 검정력을 요청받으면 휴리스틱으로 빠진다. 시험 스위트는 이것을 구현 결함일 뿐 아니라 명세 결함으로도 기록해야 한다.
테스트 벡터는 구현자가 아닌 사람도 재현할 수 있는 고정 입력과 기대 출력의 쌍이다. 벡터마다 바뀌지 않는 식별자, 정확한 입력, 허용 오차를 붙인 기대 출력, 기대값의 근거가 되는 조항, 그리고 기대값을 얻은 방법(손 계산, 독립 라이브러리, 규제 문헌)을 기록한다. 구현이 돌려준 값을 그대로 옮겨 적은 기대값은 그 구현이 결정적이라는 사실밖에 증명하지 못한다.
아래 벡터는 통계(ST), 유효 한계(VA), 안전성 보고(AE), 피험자 동의(IC)의 네 묶음으로 나뉜다. 통계 기대값은 따로 밝히지 않는 한 명세의 소수 둘째 자리 Z 관례(1.96, 0.84, 1.28)를 따르며, 제2장에서 설명한 대로 모든 표본크기는 두 배로 만들기 전에 군별로 올림한다. 날짜는 역일 기준이다.
| ID | 입력 | 기대 결과 | 근거 |
|---|---|---|---|
| TV-ST-01 | 연속형, α 0.05 양측, 검정력 0.80, σ 1.2, Δ 0.5 | 90.3 → 군당 91, 총 182. 중도탈락률 20% 반영 시 군당 114, 총 228 | §4.1, 부록 A.1(명세는 올림 전 값을 부풀려 113/226으로 적음) |
| TV-ST-02 | ST-01과 같되 σ 1.5 | 141.12 → 군당 142, 총 284. 284 / 0.8 = 총 355 | §4.1 예제 수정본(명세는 141/282/353으로 적음) |
| TV-ST-03 | 이분형, p₁ 0.25, p₂ 0.15, α 0.05 양측, 검정력 0.80 | 249.7 → 군당 250 | §4.1 이분형 공식, 제2장 계산 |
| TV-ST-04 | ST-02와 같되 α 0.01 | Z1−α/2 2.576, 210.0 → 군당 211 | 올바른 임계값을 적용한 §4.1 |
| TV-ST-05 | ST-02와 같되 검정력 0.85 | Z1−β 1.036, 161.6 → 군당 162 | 올바른 임계값을 적용한 §4.1 |
| TV-ST-06 | 검정력, 군당 n 100, σ 1.5, α 0.05 양측, Δ 0.3 / 0.5 / 0.7 / 1.0 | 0.29 / 0.65 / 0.91 / 0.997(±0.005) | Φ(Δ/(σ√(2/n)) − 1.96). §4.2 표를 대체 |
| TV-ST-07 | 검정력, 군당 n 100, 표준화 효과크기 0.5, σ 없음 | 효과크기를 Cohen's d로 읽으면 0.94, 그렇지 않으면 호출을 거부해야 함 | σ = 1로 둔 같은 공식 |
| TV-ST-08 | 부록 A.2 표(30/200 대 50/200) | RR 0.60, SE(log RR) 0.208, 95% CI 0.40~0.90 | §4.4, 부록 A.2 수정본 |
| TV-VA-01 | 수축기 혈압 59 mmHg | 범위 이탈 소견(B007). 정상 범위 플래그와 구별되어야 하며 조용히 수용되어서는 안 됨 | §8.3 하한 60 |
| TV-VA-02 | 수축기 혈압 60 mmHg | B007 소견 없이 수용, 임상 플래그(참고 범위 하한 90 mmHg 미만) | §8.3, VITAL_SIGNS_RANGES |
| TV-VA-03 | 수축기 혈압 250 mmHg | B007 소견 없이 수용, 임상 플래그(참고 범위 초과) | §8.3 상한 250 |
| TV-VA-04 | 수축기 혈압 251 mmHg | 범위 이탈 소견(B007). 정상 범위 플래그와 구별되어야 하며 조용히 수용되어서는 안 됨 | §8.3 |
| TV-VA-05 | 이완기 혈압 151, 체온 43 °C, 호흡수 61 | 값마다 B007 소견 | §8.3 |
| TV-VA-06 | HbA1c 15.1%, 혈당 19 mg/dL, 크레아티닌 15.1 mg/dL, ALT 2001 U/L | 값마다 B007 소견 | §8.3 |
| TV-AE-01 | SAE, 사망, 인과관계 의심, 예상하지 못함. 발생일 2026-03-02, 의뢰자 인지일 2026-03-05 | 신속보고 기한 2026-03-12(0일 + 7) | §5.4, 21 CFR 312.32(c)(2) |
| TV-AE-02 | SAE, 입원, 인과관계 의심, 예상하지 못함. 날짜는 같음 | 신속보고 기한 2026-03-20(0일 + 15) | §5.4, 21 CFR 312.32(c)(1) |
| TV-AE-03 | SAE, 입원, 인과관계 "unlikely", 예상된 사건 | 기록·검토 대상이지만 IND 신속 안전성 보고 대상 아님 | 21 CFR 312.32(c)(1) |
| TV-AE-04 | hospitalization: true이면서 serious: false | 불일치로 거부하거나 serious를 true로 도출 | §5.4 정의 |
| TV-IC-01 | 동의일 없는 등록 | B006, 피험자 기록 미생성 | §8.4, §9.1 |
| TV-IC-02 | 등록되지 않은 피험자의 방문 데이터 | B002, 아무것도 기록하지 않음 | §8.4 |
반올림 관례. Z 관례에 따라 답이 한 명씩 달라지는 경우가 있다. 소수 둘째 자리 임계값을 쓰면 TV-ST-02의 검정력 90% 조건은 (1.96 + 1.28)² × 18 = 188.96으로 군당 189가 되고, 전체 정밀도 값(1.95996, 1.28155)을 쓰면 189.13이 되어 190으로 올림된다. 어느 쪽도 틀리지 않았지만, 벡터에는 어떤 관례를 썼는지 적어야 한다. 계획서에 들어갈 값이라면 결코 더 작아지지 않는 전체 정밀도 값이 더 안전하다.
홀수 총계와 중도탈락. TV-ST-02는 총계를 부풀려 284 / 0.8 = 355를 얻는다. 같은 보정을 군별로 적용하면 142 / 0.8 = 177.5, 올림해서 178, 총 356이 된다. 1:1 무작위배정에서 355명은 똑같이 나눌 수 없으므로 설계 문서에는 보통 군당 178명으로 적는다. 355를 기대하는 시험은 명세의 산술을 확인하는 것이고, 군당 178을 기대하는 시험은 실제로 쓸 수 있는 설계를 확인하는 것이다. 벡터가 어느 질문에 답하는지 정하고 이름을 그에 맞게 붙인다.
유효 한계와 참고 범위. TV-VA-02와 TV-VA-03은 수용하되 플래그를 달아야 하는 값이다. §8.3의 한계는 불가능하거나 믿기 어려운 입력을 가능한 입력과 가르고, VITAL_SIGNS_RANGES의 참고 범위는 정상과 비정상을 가른다. 수축기 혈압 250 mmHg는 입력 오류가 아니라 가능하면서도 위급한 값이다. 시험은 수용과 플래그라는 두 결과를 모두 확인해야 한다. §8.3 한계를 벗어난 값을 그다음에 차단할지, 열린 질의와 함께 저장할지는 문서화된 에디트 체크 정책의 문제다. 제7장은 EDC가 원자료에 적힌 내용을 기록할 수 있어야 하므로 질의와 함께 저장하는 쪽을 권한다. 벡터가 요구하는 것은 그 소견이 B007로 제기되고 정상 범위 플래그와 혼동되지 않는다는 점뿐이다.
안전성 날짜. AE 벡터는 발생일과 의뢰자 인지일을 일부러 사흘 떼어 놓았다. 발생일부터 세는 시스템도 두 날짜가 같은 벡터에서는 통과하기 때문이다. 날짜는 예시이며, 벡터는 의뢰자가 사건을 인지한 날 보고 대상 요건이 확정된다고 가정한다.
벡터는 저장소에 있는 @wia/bio-010 1.0.0의 TypeScript 소스를 수정 없이 컴파일해 실행했다. 표는 문서가 말하는 값이 아니라 코드가 실제로 돌려준 값을 적은 것이다.
| ID | 기대값 | SDK 1.0.0 반환값 | 판정 |
|---|---|---|---|
| TV-ST-01 | 91 / 182 / 228 | 91 / 182 / 228 | 통과 |
| TV-ST-02 | 142 / 284 / 355 | 142 / 284 / 355 | 통과(명세 본문은 실패) |
| TV-ST-03 | 군당 250 | 군당 146 | 실패: 이분형 두 항을 따로 제곱 |
| TV-ST-04 | 군당 211 | 군당 142 | 실패: Z가 1.96으로 고정 |
| TV-ST-05 | 군당 162 | 군당 142 | 실패: 0.90 미만 검정력은 모두 0.84 사용 |
| TV-ST-06 | 0.29 / 0.65 / 0.91 / 0.997 | 0.293 / 0.654 / 0.910 / 0.997 | 통과 |
| TV-ST-07 | 0.94 또는 거부 | 0.70(휴리스틱 0.5 + 0.4·(n/100)·Δ) | 실패 |
| TV-ST-08 | RR과 CI | RR 함수 없음 | 미구현 |
| TV-VA-01 | B007 소견 | success: true, 경고 "out of normal range" | 실패: 90 미만의 다른 값과 같은 경고 |
| TV-VA-02 | 수용, 플래그 | success: true, 경고 | 통과 |
| TV-VA-03 | 수용, 플래그 | success: true, 경고 없음 | 부분 통과: 141~250 구간은 플래그 없음 |
| TV-VA-04 | B007 소견 | success: true, 경고 "critically high" | 실패: 임상 플래그 문구일 뿐 유효성 소견이 아님 |
| TV-VA-05 | B007 소견 | 검사하지 않음, 경고 없음 | 실패 |
| TV-VA-06 | B007 소견 | HbA1c 15.1과 크레아티닌은 검사하지 않음, 혈당과 ALT는 경고만 | 실패 |
| TV-AE-01 | 2026-03-12 | 2026-03-09(발생일 + 7) | 실패: 0일이 틀림 |
| TV-AE-02 | 2026-03-20 | 2026-03-17(발생일 + 15) | 실패: 0일이 틀림 |
| TV-AE-03 | 신속보고 없음 | reportingRequired: true | 실패: 인과관계와 예상성 무시 |
| TV-AE-04 | 거부 또는 도출 | reportingRequired: false | 실패: 플래그 교차 확인 없음 |
| TV-IC-01 | B006 | 등록 기록 반환 | 실패: 동의 확인 없음 |
| TV-IC-02 | B002 | 데이터 수용(SDK는 등록 상태를 보관하지 않음) | 실패: SDK 범위 밖 |
벡터 스무 개 가운데 통과 넷, 부분 통과 하나라는 결과가 SDK의 가치를 판정하는 것은 아니다. 연속형 평가변수의 산술, 표준편차를 준 경우의 검정력 공식, 기술통계(1.96으로 고정된 구간은 제외)는 정확하며, 대부분의 사용자가 가장 먼저 호출할 부분도 이것이다. 실패는 세 곳에 몰려 있다. 코드가 실제로는 받아들이지 못하는 통계 매개변수(0.05가 아닌 α, 0.80이나 0.90이 아닌 검정력, 이분형 평가변수), 믿기 어려운 값과 비정상 값을 구분하지 못하는 유효성 검사, 그리고 틀린 날부터 세면서 보고 여부를 가르는 두 속성을 무시하는 안전성 로직이다.
패키지는 ts-jest를 쓰는 Jest로 설정되어 있다. 아래 스위트는 벡터 일부를 코드로 옮긴 것이다. 현재 코드가 실패하는 시험은 Jest 28부터 제공되는 test.failing으로 작성했다. 이 시험은 단언이 실패하는 동안 통과로 표시되고, 결함이 고쳐지면 빨간색으로 바뀐다. 덕분에 오늘 빌드는 녹색을 유지하면서도 알려진 결함이 모두 실행 가능한 형태로 기록되고, 결함을 고친 사람은 해당 시험을 일반 시험으로 승격해야 한다.
// __tests__/conformance.test.ts
import { ClinicalTrialSDK, ClinicalTrialErrorCode } from '../src';
const sdk = new ClinicalTrialSDK();
const base = { alpha: 0.05, power: 0.8, testType: 'two-tailed' as const };
const noFlags = {
serious: false, death: false, lifeThreatening: false, hospitalization: false,
disability: false, congenitalAnomaly: false, other: false,
};
describe('Statistics (TV-ST)', () => {
test('TV-ST-01 App. A.1: 91 per group, 228 after dropout', () => {
const r = sdk.calculateSampleSize({ ...base, effectSize: 0.5, standardDeviation: 1.2 });
expect(r.sampleSizePerGroup).toBe(91);
expect(r.adjustedSampleSize).toBe(228);
});
test('TV-ST-02 §4.1 corrected: 142 per group', () => {
const r = sdk.calculateSampleSize({ ...base, effectSize: 0.5, standardDeviation: 1.5 });
expect(r.sampleSizePerGroup).toBe(142);
});
test.failing('TV-ST-03 binary 25% vs 15%: 250 per group', () => {
const r = sdk.calculateSampleSize({
...base, effectSize: 0.1, controlRate: 0.25, treatmentRate: 0.15,
});
expect(r.sampleSizePerGroup).toBe(250);
});
test.failing('TV-ST-04 alpha 0.01 uses Z = 2.576: 211 per group', () => {
const r = sdk.calculateSampleSize({
...base, alpha: 0.01, effectSize: 0.5, standardDeviation: 1.5,
});
expect(r.sampleSizePerGroup).toBe(211);
});
test.each([
[0.3, 0.293], [0.5, 0.654], [0.7, 0.910], [1.0, 0.997],
])('TV-ST-06 power at delta %p is %p', (delta, expected) => {
const r = sdk.calculatePower({
sampleSize: 100, alpha: 0.05, effectSize: delta,
standardDeviation: 1.5, testType: 'two-tailed',
});
expect(r.power).toBeCloseTo(expected, 2);
});
});
describe('Validation limits (TV-VA)', () => {
const vitals = (systolicBP: number) =>
sdk.collectPatientData({ trialId: 'T', patientId: 'T-001', visit: 'V1',
vitalSigns: { systolicBP } });
test('TV-VA-02 SBP 60 accepted and flagged', () => {
const r = vitals(60);
expect(r.success).toBe(true);
expect(r.warnings.length).toBeGreaterThan(0);
});
// A §8.3 breach must surface as a B007 finding, in either channel
const hasB007 = (r: { validationErrors: string[]; warnings: string[] }) =>
[...r.validationErrors, ...r.warnings].some((m) => m.includes('B007'));
test.failing('TV-VA-01 SBP 59 raises a B007 finding', () => {
expect(hasB007(vitals(59))).toBe(true);
});
test.failing('TV-VA-04 SBP 251 raises a B007 finding', () => {
expect(hasB007(vitals(251))).toBe(true);
});
});
describe('Safety reporting (TV-AE)', () => {
test.failing('TV-AE-01 fatal SUSAR: day 0 is sponsor awareness', () => {
const r = sdk.recordAdverseEvent({
subjectId: 'T-001', trialId: 'T', term: 'Sudden death',
startDate: '2026-03-02', ongoing: false, severity: 'death',
causality: 'possible', outcome: 'fatal',
seriousness: { ...noFlags, serious: true, death: true },
// awareness date is not part of the v1.0 type; see 8.6
} as any);
expect(r.reportingDeadline?.toISOString().slice(0, 10)).toBe('2026-03-12');
});
});
describe('Consent (TV-IC)', () => {
test.failing('TV-IC-01 enrollment without consent throws B006', () => {
let err: unknown;
try {
sdk.enrollPatient({ trialId: 'T', demographics: { age: 58, sex: 'M' } } as any);
} catch (e) {
err = e;
}
expect(err).toMatchObject({ code: ClinicalTrialErrorCode.MISSING_CONSENT });
});
});
두 번의 타입 캐스팅은 의도한 것이다. AE 시험에 as any가 필요한 이유는 v1.0 타입에 의뢰자 인지일을 담을 자리가 없기 때문이며, 타입에 그 필드가 생기기 전에는 통과할 수 없다. 동의 시험에 캐스팅이 필요한 이유는 타입이 consentDate를 필수로 선언하면서도 런타임에서는 전혀 확인하지 않기 때문이다. 데이터가 JSON으로 들어오는 순간 컴파일 시점의 보호는 사라지므로, 중요한 것은 런타임 확인이다.
고정된 임계값. calculateSampleSize는 α의 임계값을 검정 유형만 보고 1.96 또는 1.645로, 검정력의 임계값을 0.9와 비교해 0.84 또는 1.28로 고른다. 다른 α나 검정력은 아무 표시 없이 0.05와 0.80 또는 0.90인 것처럼 계산된다. 게다가 결과의 power 필드는 요청한 값을 그대로 되돌려 주므로, 검정력 85%를 요청한 호출자는 80%로 계산한 결과에 0.85라는 이름표가 붙은 값을 받는다. 예외를 던지는 것보다 위험한 이유는 그 뒤의 어떤 단계도 이를 알아챌 수 없기 때문이다.
휴리스틱 검정력. 표준편차가 없으면 calculatePower는 0.5 + 0.4 × (n/100) × Δ를 0.99로 제한해 돌려준다. 이것은 검정력 함수가 아니다. 변동성과 관계없이 n = 100, Δ = 0.5인 모든 시험에 0.70을 주고, n에 따라 직선으로 증가한다. 표준편차를 전달할 방법이 없는 명세 형태의 calculateStatistics 래퍼를 거치는 호출은 모두 이 분기로 들어간다.
기한의 기준일. recordAdverseEvent는 사건의 startDate에 7일 또는 15일을 더한다. 발생일은 틀린 0일이며, 신속보고가 필요한지는 인과관계와 예상성에 달려 있는데 이 메서드는 둘 다 무시한다. 제4장에서 수정한 계산을 제시했고, 벡터는 그 계산이 계속 옳게 유지되도록 지킨다.
경고만 하는 유효성 검사. collectPatientData는 범위 검사 결과로 validationErrors를 채우는 일이 없다. 오류를 만드는 것은 숫자가 아닌 측정값뿐이다. §8.3 한계를 벗어난 활력징후도 비정상이지만 유효한 값과 같은 종류의 경고만 만들고, 이완기 혈압, 체온, 호흡수, 크레아티닌은 아예 검사하지 않는다. 코드가 비교 전에 값의 참·거짓부터 따지므로 0이라는 값은 수축기 혈압 검사를 통째로 건너뛴다.
식별자 충돌. 표의 벡터는 아니지만 시험할 가치가 있다. enrollPatient는 시험 ID에 0~999 사이의 난수를 붙여 피험자 식별자를 만든다. 피험자 60명 가운데 적어도 두 명이 같은 식별자를 받을 확률은 약 84%다(i = 0~59에 대해 1 − ∏(1 − i/1000)). 식별자는 난수가 아니라 순번이나 등록 체계에서 받아야 한다.
지금 SDK를 쓰려면 제5장과 제7장의 방식을 적용한다. SDK가 잘못 처리하는 입력을 거부하고, §8.3 한계를 벗어난 값에 B007 소견을 제기하며, 동의와 등록 상태를 확인하고, 인지일에서 안전성 기한을 계산하는 얇은 래퍼를 앞에 둔다. 벡터 스위트는 래퍼를 대상으로 돌린다. 원본이 수정되면 test.failing 항목이 래퍼의 어느 부분을 걷어낼 수 있는지 정확히 보여 준다.
시험 스위트 통과는 필요조건이지 충분조건이 아니다. 규제기관은 임상시험 기록을 생성·수정·저장하는 컴퓨터 시스템이 밸리데이션되어 있기를 기대한다. 즉 시스템이 실제 운영 환경에서 의도한 기능을 일관되게 수행한다는 문서화된 증거가 있어야 한다. Part 11은 이를 직접 요구하고(21 CFR 11.10(a)), ICH E6는 시험에 쓰이는 모든 시스템에 이를 요구하며, 식약처 관할 임상시험에 적용되는 KGCP도 같은 기대를 담고 있다. 업계에서 널리 쓰는 틀은 ISPE의 GAMP 5로, 시스템이 피험자 안전과 데이터 무결성에 주는 위험에 비례해 노력의 크기를 정한다.
전통적으로는 세 단계의 적격성 평가로 나눈다.
실패가 피험자에게 해를 주거나 일차 평가변수를 훼손할 수 있는 곳을 가장 강하게 시험한다. WIA-BIO-010에서 고위험 기능은 분명하다. 안전성 보고 시한, 동의 강제, 일차 평가변수 데이터의 유효 한계, 그리고 등록 목표를 정하는 데 쓴다면 표본크기 엔진이다.
추적성 매트릭스는 각 요구사항을 그것을 검증하는 시험, 그리고 그 시험이 만든 증거와 연결한다. 감사자는 어느 조항에서 출발하든 서명된 결과에 도달할 수 있고, 실패한 시험에서 출발하면 어떤 요구사항이 위험한지 알 수 있다. 몇 행으로 형태를 보인다.
| 요구사항 | 출처 | 위험도 | 시험 | 증거 |
|---|---|---|---|---|
| 수축기 혈압 60~250 mmHg 밖이면 B007 제기 | §8.3 | 중 | TV-VA-01~04 | OQ 실행 기록, 빌드 ID, 검토자 서명 |
| 의뢰자 인지일부터 신속보고 시한 계산 | §5.4, 21 CFR 312.32 | 고 | TV-AE-01~03 | OQ 실행 기록, 모의 시험의 PQ 사례 파일 |
| 문서화된 동의 없이는 데이터 기록 불가 | §8.4(B006), §9.1 | 고 | TV-IC-01, PQ 시나리오 "동의 철회" | OQ 기록, 기관 사용자가 서명한 PQ 스크립트 |
| 연속형 평가변수의 표본크기 | §4.1, 부록 A.1 | 중 | TV-ST-01, 02, 04, 05 | OQ 기록, 독립 계산표 |
| 모든 데이터 변경에 감사추적 | §3.4, 21 CFR 11.10(e) | 고 | Part 11 OQ 세트 | 변경 스크립트와 대조한 감사추적 보고서 발췌 |
매트릭스는 코드와 함께 버전 관리하여, 요구사항이 개정되면 다시 해야 할 시험과 증거가 즉시 드러나게 한다.
다섯 번째 필수 구성요소인 CDISC 내보내기와 eCTD 생성은 SDK로 시험할 수 없다. SDK는 데이터셋을 만들지 않기 때문이다. generateSubmission은 상태가 draft이고 넘겨받은 데이터셋 이름 목록을 담은 기록을 돌려줄 뿐이다. 데이터셋 적합성은 이 표준 밖의 도구로 데이터셋 자체에 대해 확인한다. 보통 다음을 점검한다.
데이터 표준에 대한 어떤 인증서도 실태조사 대비를 대신하지 못한다. 의뢰자 감사나 규제기관의 GCP 실태조사(예: FDA의 BIMO(Bioresearch Monitoring) 프로그램, EU 회원국 조사 기관, 한국의 식약처)는 시험이 어떻게 수행되었고 기록을 믿을 수 있는지를 본다. 데이터 시스템에 대해서는 점검 목록이 꽤 안정적이다.
이 목록의 모든 항목은 JSON 스키마나 SDK의 범위 밖에 있다. 그래서 데이터 표준의 구현만으로는 어떤 시험도 실태조사에 대비된 상태가 되지 않는다. 구현이 할 수 있는 일은 증거를 쉽게 만들어 내도록 돕는 것이다.
v1.0에 적합성 절차가 없으므로, 구현자가 할 수 있는 가장 방어 가능한 진술은 자체 평가다. 요구사항, 각 요구사항의 출처 조항, 보유한 증거, 상태를 적은 표다. 아래 양식은 8.1의 규범적 핵심에 규제 대상 사용에 필요한 Part 11과 동의 요구사항을 더한 것이다. 상태 칸은 구현자가 채운다. "충족", "부분 충족", "미충족"이면 충분하며, "충족"이 아닌 항목에는 설명을 단다.
| # | 요구사항 | 명세 § | 보유할 증거 | 상태 |
|---|---|---|---|---|
| 1 | §7.1 필드와 계획서 버전 관리를 갖춘 시험 등록 | 8.1, 7.1 | 인터페이스 시험, 예시 기록, 변경 이력 | |
| 2 | 등록 기관 식별자는 발급받은 그대로 저장하고 자체 생성하지 않음 | 7.1 | 필드 정의, 자체 생성 NCT 번호를 거부하는 시험 | |
| 3 | 방문, 측정값, 검사, 활력징후의 EDC 수집 | 8.1, 7.2 | OQ 기록, CRF 명세 | |
| 4 | §8.3 유효 한계 이탈 시 B007 제기(문서화된 정책에 따라 차단 또는 질의) | 8.3 | TV-VA-01~06 결과 | |
| 5 | 참고 범위 플래그를 유효 한계와 분리 | 8.3, 9.2 | TV-VA-02, 03 결과, 플래그 명세 | |
| 6 | §5.4 기준에서 중대성을 도출하고 교차 확인 | 5.4 | TV-AE-04 결과 | |
| 7 | 보고 대상 사례에 한해 의뢰자 인지일부터 신속보고 시한 계산 | 5.4 | TV-AE-01~03 결과, 안전성 SOP | |
| 8 | 코딩된 용어마다 MedDRA·CTCAE 버전 기록 | 5.1, 5.2 | 데이터 사전, 코딩 보고서 | |
| 9 | 지원하는 입력에서 표본크기·검정력이 §4와 일치, 나머지는 거부 | 4.1, 4.2 | TV-ST-01~07 결과, 지원 범위 명시 | |
| 10 | 가설검정과 신뢰구간이 정확한 분포를 쓰거나 근사임을 밝힘 | 4.3, 4.4 | TV-ST-08, 소표본 시험 | |
| 11 | 외부 검사를 통과하는 define.xml 포함 SDTM/ADaM 내보내기 | 8.1, 3.1~3.3 | 검증 도구 보고서, define.xml | |
| 12 | B001~B006을 해당 조건에서 반환 | 8.4 | 코드별 시험 1건 이상 | |
| 13 | 문서화된 동의 없이는 데이터 기록 불가 | 8.4, 9.1 | TV-IC-01 결과, 동의 SOP | |
| 14 | 감사추적, 전자서명, 접근 통제 | 3.4 | Part 11 OQ 세트, 접근 권한 검토 | |
| 15 | 의도한 용도에 대한 시스템 밸리데이션 | 3.4 | 밸리데이션 계획, 매트릭스, 보고서 |
자체 평가의 가치는 증거 칸이 결정한다. 8.3에서 설명한 래퍼 없이 SDK에 의존하는 시스템은 4, 6, 7, 9, 13행을 "충족"으로 표시할 수 없다. 벡터 결과가 그렇게 말하고 있으며, 정직한 평가라면 그 사실을 그대로 적어야 한다.
README에는 WIA 인증 웹 주소가 있지만 v1.0은 그 뒤의 절차를 기술하지 않으므로, 이 책은 이를 연락처로만 다룬다. 앞으로 어떤 인증 제도가 생기더라도 그것이 증명할 수 있는 것은 구현이 표준의 형식과 인터페이스를 명세대로 처리한다는 사실까지다. 의뢰자 시스템의 밸리데이션이나 시험 수행의 적정성은 증명할 수 없으며, 이는 여전히 의뢰자의 책임이다.
1.0판은 공개된 첫 초안이다. 아래 방향은 현재 본문에 아직 반영되지 않은 임상시험 규제와 실무의 변화에서 나온 것이며, v1.0의 기능이 아니라 다음 개정을 위한 제안이다.
ICH GCP 가이드라인은 2025년에 E6(R3)로 개정·채택되어, 명세 참고문헌에 인용된 E6(R2)를 대체했다. 개정판은 설계 단계의 품질 확보와 위험 비례적 감독을 한층 강조한다. 품질에 결정적인 요인을 식별하고, 모니터링을 그 요인에 집중하며, 기관 방문과 함께 중앙 집중식·데이터 기반 모니터링을 쓰라는 것이다. 데이터 표준에서 이는 결정적 데이터를 명시적으로 표시하고, 쿼리 발생률, 방문 허용 구간 일탈, SAE 보고 지연처럼 데이터에서 계산하는 핵심 위험 지표를 지원하는 것을 뜻한다. v1.0에는 이 가운데 어느 것도 없다.
2019년의 E9(R1) 부록은 시험이 추정하려는 치료 효과를 정확히 밝히도록 요구하며, 치료 중단이나 구제 약물 같은 중간사건을 어떻게 다루는지도 여기에 포함된다. 명세 §1.4는 여전히 LOCF를 핵심 용어로 올려 두고 있지만, 현재 실무는 결측 데이터를 다루는 기본 방법으로 LOCF를 권하지 않는다. 앞으로의 데이터 모델에는 중간사건과 그 날짜, 그리고 각 사건에 적용한 전략(treatment policy, hypothetical, composite, while-on-treatment, principal stratum)을 담는 필드가 있어야 분석 데이터셋을 계획서가 선언한 추정 대상까지 추적할 수 있다.
재택 방문, 환자가 직접 입력하는 전자 임상결과평가, 웨어러블 기기는 방문을 "날짜 하나와 값 목록"에서 기기 식별자, 펌웨어, 샘플링 주기, 시간대를 가진 흐름으로 바꾼다. v1.0의 measurements 맵은 이런 출처 정보를 표현할 수 없고, §8.3 범위는 진료실의 단일 측정값을 전제로 쓰였다. 개정판에는 모든 관측값의 출처 속성(기관, 방문 간호사, 환자 앱, 기기), 기기 메타데이터, 그리고 연속 측정값을 분석값으로 바꾸는 집계 규칙이 필요하다.
시판 후 연구는 점점 더 전자의무기록, 레지스트리, 청구 데이터를 활용하며, 이 데이터는 CDASH가 아니라 HL7 FHIR 같은 진료 현장 형식으로 들어온다. WIA-BIO-010은 대응 계층을 정의할 수 있고, 무엇보다 실사용 데이터가 해당 질문에 적합한지 심사자가 판단하는 데 필요한 출처·품질 메타데이터를 정의할 수 있다. README가 언급한 WIA-OMNI-API 연계가 자리 잡을 곳도 여기이며, 그러려면 먼저 인터페이스가 명세되어야 한다.
README는 적응형, 바스켓, 엄브렐러 설계를 사용 사례로 꼽지만, 데이터 모델은 시험 하나, 한 번에 하나의 계획서 버전, 두 개의 군을 가정한다. 플랫폼 시험은 마스터 계획서 아래에서 군을 추가하고 종료하며, 적응형 설계는 중간분석에서 배정 비율이나 표본크기를 바꾼다. 이를 지원하려면 군 단위 생애주기 상태, 각 적응 결정과 그 결정에 쓴 중간 데이터 컷의 기록, 적응 단계별로 버전이 매겨진 무작위배정 목록이 필요하다. ICH는 적응형 설계 가이드라인(E20)을 개발 중이므로 개정 시 이를 참고해야 한다.
README는 "AI 기반 임상 분석"을 위해 이 표준을 WIA-AI와 짝지어 둔다. 임상시험에서 모델의 출력은 사람의 결정을 돕는 참고 정보이거나, 그 자체로 다른 측정값처럼 밸리데이션해야 하는 측정값이다. 따라서 개정판은 최소한 모든 출력에 모델 식별자와 버전을 붙이고, 모델을 학습·검증한 데이터를 기록하며, 모델 출력 하나만으로 피험자 기록, 중대성 분류, 분석 집단이 바뀌지 않도록 하는 규칙을 요구해야 한다. 테스트 벡터의 규율도 똑같이 적용된다. 고정 입력, 기대 출력, 문서화된 허용 오차다.
| 영역 | v1.0의 공백 | 다음 개정을 위한 제안 |
|---|---|---|
| 적합성 | 등급, 시험 스위트, 절차 없음 | 기대값을 갖춘 규범적 테스트 벡터와 적합성 선언 양식 공개 |
| 통계 | 고정 Z값, 이분형 공식 결함, 휴리스틱 검정력, §4.2와 부록 A.2 수치 오류 | 정확한 분위수, 올바른 이분형 공식, 지원하지 않는 입력 거부, 예제 수정 |
| 안전성 | 인지일·예상성 없음, 발생일부터 시한 계산 | sponsorAwarenessDate와 expectedness 추가, 신속보고 여부 도출 |
| 유효성 검사 | 범위 위반이 경고에 그침, 네 한계는 미검사 | §8.3에 규범적 오류 의미 부여, 참고 범위 플래그 분리 |
| 품질 관리 | 결정적 데이터·위험 지표 모델 없음 | E6(R3)에 맞춘 결정적 데이터 플래그와 표준 핵심 위험 지표 |
| 추정 대상 | LOCF 수록, 중간사건 없음 | E9(R1)에 따른 중간사건 기록과 평가변수별 추정 대상 전략 |
| 분산형 데이터 | 출처·기기 메타데이터 없음 | 출처·기기 속성, 연속 측정값의 집계 규칙 |
| 시험 설계 | 2군, 단일 계획서 가정 | 군 생애주기, 적응 기록, 버전 관리되는 무작위배정 |
| 참고문헌 | 대체된 가이드라인 판, EudraCT | E6(R3), E9(R1), EU CTR 536/2014와 CTIS 인용, 사전 버전은 "사용 버전"으로 명시 |
test.failing으로 남겨 계속 보이게 하고, 고쳐지면 뒤집히게 한다.이 책의 목표는 WIA-BIO-010을 실제로 쓸 수 있게 만드는 것이었다. 표준 뒤에 있는 임상적·통계적 개념을 설명하고, 산술이나 규정이 다르게 말하는 곳에서는 오류를 바로잡고, 참조 코드 가운데 믿을 수 있는 부분이 어디인지 보이려 했다. 이 표준의 가치는 어느 한 공식보다, 시험·피험자·방문·이상사례를 처음부터 끝까지 확인할 수 있는 공통 모델로 다루라는 규율에 있다. 그 규율은 시험될 때에만 실체가 된다. 이 장의 벡터, 점검표, 추적성 매트릭스는 그것을 실현하는 수단이자 표준의 다음 개정을 시작할 출발점이다.