제3장
변환기의 원시 출력이 교정된 수치가 되기까지의 과정, 그리고 그 수치가 얼마나 좋은지를 말해 주는 WIA-BIO-011의 성능 지표(규격 §5–§6, 부록 A)를 다룬다.
제2장은 변환기의 단자에서 끝났다. 거기서 나오는 것은 전류, 전위, 주파수 변화 또는 형광 계수값이다. 이 장은 그 신호를 이어받아 기저선 보정, 필터링, 교정, 역산의 순서로 따라간 뒤, WIA-BIO-011이 바이오센서를 기술하는 데 쓰는 모든 성능 지표를 정의한다. 감도, 검출 한계(LOD), 정량 한계(LOQ), 동적 범위, 선택성, 응답 시간, 정확도, 정밀도, 안정성이 그것이다. 각 지표는 데이터에 대한 계산으로 다룬다. 즉 무엇을 입력하고, 어떤 식을 적용하며, 어떤 단위가 나오고, 규격의 어느 목표값과 비교하는지를 밝힌다. 표준액을 몇 개 쓸지, 언제 재교정할지, 교정을 어떻게 합격·불합격 판정할지 같은 운영 측면은 제6장의 몫이다.
계산 예시는 규격 자체의 부록 A에서 가져온다. 부록에 인쇄된 수치가 그 데이터로부터 재현되지 않는 경우에는 이 장에서 다시 계산하고 그 사실을 밝힌다. 규격, TypeScript SDK(@wia/bio-011), CLI(wia-bio-011)가 서로 어긋나는 곳에서는 불일치를 명시하고, 이 책의 나머지 부분에서 따를 규약 하나를 정한다.
분석물이 없을 때 정확히 0을 읽는 변환기는 거의 없다. 전류법 전극에는 충전(용량성) 전류와 완충액에서 오는 작은 패러데이 배경 전류가 흐르고, 광학 센서는 미광(迷光)과 자가형광을 보며, QCM은 온도와 인식층의 느린 수분 흡수에 따라 표류한다. 그래서 규격 §5.1은 체인의 첫 단계를 뺄셈으로 정의한다.
Signal_corrected = Signal_raw − Baseline
규격은 기저선을 추정하는 네 가지 방법을 든다. 선형 기저선(y = mx + b)은 분석물 응답이 없다고 알려진 구간, 예컨대 시료를 떨어뜨리기 전 몇 초와 흐름 주입 피크가 씻겨 나간 뒤의 꼬리 구간에 직선을 맞춘다. 다항식 기저선은 배경이 휘어질 때 곡선 모델로 같은 일을 한다. 전극에 전위를 건 직후 몇 분 동안 배경이 휘는 경우가 흔하다. 이동 평균은 분석물 응답보다 훨씬 긴 창을 써서 연속 모니터링에서 느린 표류를 추적한다. Savitzky–Golay 필터는 이동하는 창 안에서 저차 다항식을 맞춘다. 본래 평활화 기법으로서 이동 평균보다 피크의 높이와 폭을 잘 보존하고, 해석적 도함수도 제공하므로 응답의 시작과 끝을 찾는 데 유용하다.
이 선택은 보기보다 중요하다. 기저선 창이 응답의 시작과 겹치면 분석물 신호 일부가 빠져 감도가 낮게 편향된다. 배경이 아직 표류하는 동안 측정보다 한참 앞서 기저선을 잡으면 그 오차가 교정의 절편 오프셋으로 나타난다. SDK에서 SignalProcessingParams.baseline은 processSignal이 빼는 단일 숫자다. 그 숫자를 어떻게 얻었는지는 구현자의 책임이며, 측정 기록에 함께 남겨야 한다.
규격 §5.2.1은 SNR = Signal_mean / Noise_stdev로 정의하고, 목표를 SNR > 10, 가능하면 > 100으로 둔다. 이 정의를 실제로 쓰려면 두 가지를 짚어야 한다. 첫째, 분자는 관심 농도에서의 순신호(기저선을 뺀 값)여야 하고, 분모는 같은 조건에서 반복 측정한 값의 표준편차여야 한다. 분모는 보통 블랭크에서 추정한다. 둘째, SNR은 농도에 따라 달라진다. 측정 범위 중간에서 SNR이 800인 센서도 LOD 근처에서는 SNR이 3 정도일 수 있으며, 바로 이것이 LOD의 정의다(3.4절).
부록 A.1의 블랭크(σ ≈ 0.0070 µA)와 부록 A.2의 포도당 5 mM 신호(5.85 µA)를 쓰면, 5 mM에서의 순 SNR은 약 5.8 / 0.0070 ≈ 830이다. 같은 블랭크의 평균/σ는 약 8이지만, 이 비는 신호가 아니라 블랭크의 오프셋을 나타낼 뿐이므로 SNR로 보고해서는 안 된다.
규격 §5.2.2는 가장 단순한 디지털 필터인 1차 무한 임펄스 응답(IIR) 저역통과 필터, 다른 이름으로 지수 이동 평균을 제시한다.
y[n] = α · x[n] + (1 − α) · y[n − 1], 0 < α ≤ 1
α = 1이면 입력이 그대로 통과하고, α가 작을수록 강하게 평활화된다. 규격은 α를 고르는 규칙을 주지 않는다. α는 샘플링 간격 Δt와 원하는 시상수 τf(또는 차단 주파수 fc = 1/(2π τf))에서 정해진다.
α = Δt / (τ_f + Δt) (이산화한 RC 필터)
α ≈ 1 − exp(−Δt / τ_f) (Δt ≪ τ_f 일 때 동등)
f_c = 1 / (2π τ_f)
예를 들어 10 Hz로 샘플링하는(Δt = 0.1 s) 판독기가 0.1 Hz 차단을 원하면 τf ≈ 1.59 s, α ≈ 0.059가 된다. 이 평활화의 대가는 시간이다. 필터 단독으로도 계단 입력의 90 %에 이르는 데 약 2.3 τf ≈ 3.7 s가 걸린다. 필터와 센서의 지연은 더해진다. 센서 자체의 시상수가 예컨대 τ = 10 s라면, 둘을 합친 90 % 도달 시간은 센서 단독의 23 s가 아니라 약 24.8 s다. 그래서 PerformanceMetrics.responseTime에 적는 응답 시간은 화학 반응만으로 인용할 것이 아니라, 실제 제품의 필터 설정을 포함한 전체 경로에서 측정해야 한다.
SDK 현황. SignalProcessingParams는 네 단계를 선언한다. baseline, temperatureCorrection, filtering(moving-average, savitzky-golay, low-pass, median), driftCompensation이다. 그러나 v1.0의 processSignal은 앞의 두 단계만 구현한다. 필터링과 드리프트 필드는 받아들이기만 하고 무시하며, ProcessedSignal.filtered, driftCompensated, snr은 채워지지 않는다. 따라서 필터링은 값이 SDK에 도달하기 전에 판독기 펌웨어나 애플리케이션 코드에서 처리해야 한다.
교정 곡선은 농도 x를 신호 y로 사상하고, 이후 측정에서는 이를 거꾸로 푼다. 규격 §5.3.1은 y = a + b·x에 대한 보통 최소제곱법을 쓴다.
b = Σ[(xᵢ − x̄)(yᵢ − ȳ)] / Σ(xᵢ − x̄)²
a = ȳ − b · x̄
R² = 1 − SS_res / SS_tot, SS_res = Σ(yᵢ − ŷᵢ)², SS_tot = Σ(yᵢ − ȳ)²
x_sample = (y_sample − a) / b
SDK의 비공개 메서드 linearRegression은 이 식을 그대로(동등한 곱의 합 형태로) 구현하며, calibrateSensor와 독립 함수 calculateSensitivity가 모두 이를 사용한다.
부록 A.2는 표준액 여섯 개를 제공한다. 인쇄된 데이터로 회귀를 다시 계산하면 부록이 적은 값과 자릿수가 조금 다르다.
| 표준액 (mM) | 신호 (µA) | 적합값 ŷ (µA) | 잔차 (µA) | 잔차 / 신호 |
|---|---|---|---|---|
| 0 | 0.05 | 0.047 | +0.003 | — |
| 1 | 1.20 | 1.206 | −0.006 | −0.5 % |
| 5 | 5.85 | 5.840 | +0.010 | +0.2 % |
| 10 | 11.50 | 11.633 | −0.133 | −1.2 % |
| 50 | 58.20 | 57.974 | +0.226 | +0.4 % |
| 100 | 115.80 | 115.900 | −0.100 | −0.1 % |
적합 결과는 기울기 b ≈ 1.1585 µA/mM, 절편 a ≈ 0.047 µA, R² ≈ 0.99999이다. 부록은 1.157, 0.043, 0.9998을 적고 있으나 이 값들은 부록의 데이터에서 나오지 않는다. 이 책은 재계산한 값을 쓴다. 실제 영향은 작다. 부록의 시료 신호 23.5 µA는 인쇄된 20.3 mM이 아니라 (23.5 − 0.047)/1.1585 ≈ 20.2 mM으로 역산된다. 그러나 구현의 출력을 인쇄된 자릿수와 비교하는 적합성 시험이라면 올바른 구현을 불합격시키게 된다.
R²는 y의 전체 분산 중 직선이 설명하는 비율이다. 표준액이 두 자릿수(decade)에 걸쳐 있으면 SS_tot는 가장 높은 표준액이 좌우하며, 단조 증가하는 데이터라면 거의 무엇이든 R² > 0.99를 얻는다. A.2의 적합은 R²가 가리는 세 가지를 보여 준다.
따라서 모든 교정 기록에는 잔차 그래프, 즉 농도에 대한 잔차(가능하면 상대 잔차도)가 함께 있어야 한다. SDK는 이미 CalibrationData.qualityControl에 잔차 벡터와 maxResidual을 계산해 넣는다. 이를 사용하는 합격 규칙은 제6장에서 다룬다.
면역분석은 로그 농도 축에서 S자형이다. 높은 농도에서는 결합 부위가 포화되고, 낮은 농도에서는 신호가 배경에 묻히기 때문이다. 규격 §5.3.2는 4-매개변수 로지스틱(4PL) 모델을 규정한다.
y = d + (a − d) / [1 + (x / c)^b]
a : 농도 0에서의 응답
d : 농도 무한대에서의 응답
c : 변곡점 (EC50 / IC50)
b : 기울기 인자 (Hill 기울기)
역산: x = c · [ (a − d) / (y − d) − 1 ]^(1/b)
규격은 a를 "최소", d를 "최대" 점근선이라 부른다. 신호가 농도와 함께 증가하는 샌드위치 분석에서는 맞는 말이다. 그러나 신호가 감소하는 경쟁 분석에서는 a가 위쪽 점근선이 된다. 식은 어느 경우든 같고 이름만 바뀐다. 매개변수를 "농도 0에서의 응답"과 "무한대에서의 응답"으로 읽으면 혼동을 피할 수 있다.
효소 전극은 제1장에서 소개한 겉보기 Michaelis–Menten 형태 I = Imax[S]/(Km,app + [S])를 따르며, 역산식은 [S] = Km,app·I/(Imax − I)이다. 교정 설계에 유용한 성질은 초기 직선에서 벗어나는 비율이 정확히 [S]/(Km,app + [S])라는 점이다. [S] = 0.1 Km,app에서 이미 응답이 초기 기울기보다 9.1 % 낮다. 확산 제한 막으로 Km,app를 끌어올리지 않는 한 효소 전극의 선형 범위가 Km,app보다 한참 아래에서 끝나는 이유가 여기에 있다.
비선형 모델에는 이 책 전체에 해당하는 두 가지 결과가 따른다. 감도는 더 이상 하나의 숫자가 아니라 범위에 따라 달라지는 국소 도함수 dy/dx가 된다. 또 점근선 근처에서는 y의 작은 변화가 x의 큰 변화로 이어지므로 역산이 불안정해질 수 있다. CalibrationData.type에는 '4pl'이나 'michaelis-menten'을 기록할 수 있지만, v1.0의 calibrateSensor는 type과 관계없이 항상 직선을 맞추고, measureAnalyte는 비선형 유형을 거부한다(제5장). 따라서 v1.0에서 비선형 교정은 SDK 밖에서 적합해야 한다.
감도는 교정 곡선의 기울기 S = Δy/Δx다(규격 §6.1). 분석화학에서 "감도"는 이 기울기를 뜻하며, 제7–8장에서 쓰는 임상 민감도(참양성률)와는 다르다. 단위는 언제나 "농도당 신호"이고 변환기에 따라 달라진다.
| 변환기 | 신호 | 대표적 감도 단위 | 면적 정규화 형태 |
|---|---|---|---|
| 전류법 | 전류 | µA/mM, nA/µM | µA·mM⁻¹·cm⁻² |
| 전위차법 | 전위 | mV/decade (로그-선형) | 해당 없음 (면적과 무관) |
| 광학 (형광, 흡광) | RFU, AU | RFU/µM, AU/mM | 기기 고유 |
| 압전 (QCM) | 주파수 | Hz/ng, Hz/(ng/mL) | Hz·cm²/ng (Sauerbrey) |
| 임피던스 (EIS) | ΔRct | Ω/decade 또는 %ΔRct/decade | 보통 상대값으로 보고 |
원시 감도는 큰 전극에 유리하다. 전류법 전류는 전극 면적 A에 비례하므로(I = nFA·D·dC/dx, 제2장), 같은 화학을 쓴 지름 5 mm 원판 전극은 1 mm 미세전극을 언제나 "이긴다". 기하 구조가 아니라 인식층을 비교하려면 기하 면적으로 나누어야 한다. 예를 들어 A.2 센서가 지름 3 mm 원판(A ≈ 0.0707 cm²)을 썼다면 면적 정규화 감도는 1.1585 / 0.0707 ≈ 16.4 µA·mM⁻¹·cm⁻²가 된다. 부록은 전극 면적을 밝히지 않았으며, 이 수치는 계산 방법을 보이기 위한 것일 뿐이다.
전위차법 센서는 다르다. 신호가 활동도의 로그에 비례하므로 자연스러운 감도는 decade당 mV로 나타낸 기울기다(25 °C에서 이상적으로 59.16/n mV/decade). 전위를 농도에 대해 선형 적합하는 것은 잘못된 모델이다.
SDK는 기울기를 CalibrationData.sensitivity에 저장하고, 요청에 주어진 신호 단위와 농도 단위로 sensitivityUnit을 만든다(기본값 "μA/mM"). 단위가 명시적 문자열이므로 RFU/µM 기록이 µA/mM 기록으로 오인될 일은 없다. 다만 호출자가 기본값에 기대지 않고 단위를 직접 지정할 때에 한한다.
LOD는 블랭크와 신뢰성 있게 구별할 수 있는 가장 낮은 농도이고, LOQ는 허용할 만한 정밀도와 정확도로 측정할 수 있는 가장 낮은 농도다. 규격 §6.2–§6.3은 교정 기울기를 이용한 형태를 쓴다.
LOD = 3.3 · σ / S
LOQ = 10 · σ / S
⇒ LOQ / LOD = 10 / 3.3 ≈ 3.03
규격 §6.3은 "LOQ = 3.3 × LOD"라고도 적는다. 이는 오류다. 위의 두 정의에서 나오는 관계는 LOQ ≈ 3.03 × LOD이며, SDK(LOD_MULTIPLIER 3.3, LOQ_MULTIPLIER 10)도 대체식이 아니라 정의를 따른다. 이 책은 LOQ = 10σ/S를 쓴다.
3.3이라는 계수는 임의로 정한 것이 아니다. 잡음이 정규분포를 따른다면 블랭크보다 1.645σ 위에 판정 문턱을 두었을 때 위양성률이 5 %가 되고, LOD를 그 문턱보다 다시 1.645σ 위에 두면 LOD에서의 위음성률이 5 %가 된다. 2 × 1.645 ≈ 3.3이다. 3.3σ/S와 10σ/S의 짝은 ICH Q2 분석법 밸리데이션 지침에서 쓰는 형태이며, IUPAC 계열 문헌은 흔히 계수 3을 쓴다. SDK는 3.3 형태를 'iupac'이라 부르고 '3-sigma'도 제공한다. 어느 것을 쓰든 방법을 결과와 함께 기록해야 하며, SDK는 이를 LODResult.method에 남긴다.
σ는 적어도 세 가지 방법으로 추정할 수 있고, 그 결과는 서로 같지 않다.
calculateLOD의 방식이다. calculateLOD는 blankSignals를 받아 표본 표준편차(분모 n − 1)를 계산한다.A.2 교정에서 sy/x ≈ 0.14 µA이므로 LOD ≈ 3.3 × 0.14 / 1.1585 ≈ 0.40 mM이 되는데, 이는 아래에서 계산할 블랭크 기반 값의 스무 배다. 모순은 아니다. 잔차 표준편차는 50–100 mM에서의 절대 산포가 좌우하며(3.2.2절) 0 근처의 잡음에 대해서는 거의 말해 주지 않는다. 넓은 범위의 교정에서는 블랭크 기반 σ(또는 저농도 표준액의 표준편차)가 적절하며, LOD를 주장할 때는 어떤 σ를 썼는지 밝혀야 한다.
블랭크 10회 측정값(µA): 0.05, 0.06, 0.05, 0.07, 0.05, 0.06, 0.05, 0.06, 0.05, 0.06.
평균 = 0.560 / 10 = 0.056 µA
σ (n−1) = √(0.00044 / 9) ≈ 0.00699 µA
S = 1.15 µA/mM (A.1에 주어진 값)
LOD (3.3σ/S) = 3.3 × 0.00699 / 1.15 ≈ 0.020 mM (20 µM)
LOQ (10σ/S) = 10 × 0.00699 / 1.15 ≈ 0.061 mM (61 µM)
3σ/S = 3 × 0.00699 / 1.15 ≈ 0.018 mM
재계산한 A.2 기울기 1.1585를 쓰면:
LOD ≈ 0.0199 mM, LOQ ≈ 0.0604 mM
부록의 결과(LOD ≈ 0.020 mM)는 확인된다. 두 가지를 덧붙인다. 첫째, 측정값이 0.01 µA 단위로 양자화되어 있는데 이는 σ보다 조금 클 뿐이다. 따라서 LOD 추정값은 판독기의 분해능에 민감하며, 실제 밸리데이션에서는 블랭크 잡음보다 분해능이 충분히 작은 판독기를 써야 한다. 둘째, 여기서 블랭크의 CV는 약 12 %인데 이는 예상된 일이다. CV < 5 % 같은 정밀도 목표는 LOQ보다 높은 농도에 적용하는 것이지 블랭크에 적용하는 것이 아니다.
규격 §6.2는 σ에 Student t값과 예측구간 인자 √(1 + 1/n + (x̄ − x₀)²/Σ(xᵢ − x̄)²)를 곱하는 "EPA" 대체식을 적고 있다. 미국 EPA의 방법 검출 한계 절차(40 CFR Part 136, Appendix B)는 저농도 반복 시료로부터 MDL = t(n−1, 0.99) × s를 쓴다. 규격이 "대표값"이라 부른 3.14는 자유도 6, 즉 반복 횟수가 정확히 n = 7일 때의 단측 99 % t값이다(더 정확히는 3.143). 부록 A.1의 블랭크 10개(자유도 9)에 해당하는 값은 약 2.82다.
SDK의 method: 'epa'는 n과 관계없이 σ/S에 고정값 3.14를 곱할 뿐이며 confidenceLevel도 무시한다. 이는 3σ 규칙의 배율을 바꾼 변형이지 EPA 절차의 구현이 아니므로, EPA 준수라고 설명해서는 안 된다.
규격 §6.4는 동적 범위를 자릿수(decade)로 정의한다.
DR = log₁₀(C_max / LOD)
그런데 규격의 용어집(§1.4)은 "동적 범위"를 센서가 선형으로 응답하는 범위라고 정의한다. 둘은 서로 다른 개념이며, 이 책은 둘을 구분한다.
A.1 블랭크를 적용한 A.2 센서의 DR = log₁₀(100 / 0.0199) ≈ 3.7 decade로, 규격이 효소 센서의 대표값으로 제시한 3–4 decade와 맞는다. SDK가 기록하는 선형 범위는 이와 다르다. calibrateSensor는 linearRange.min을 0이 아닌 가장 낮은 표준액(1 mM)으로, max를 가장 높은 표준액(100 mM)으로 정하므로 두 자릿수에 걸친다. LOD(0.02 mM)와 가장 낮은 표준액(1 mM) 사이에는 센서가 분석물을 검출하지만 정량을 뒷받침할 표준액이 없는 구간이 남는다. updatePerformanceMetrics는 DR을 log₁₀(linearRange.max/LOD)로 계산하므로, SDK의 DR은 교정 범위의 상한을 Cmax로 쓴다.
신호
^
| 포화
| ____.-----'''''''
| _.-'''
| _.-' <-- 선형 범위 -->
| _.-' (교정됨, 표준액 있음)
| _.-'
| _.-'
| _.-'
|.-' 블랭크 ± σ
+--+----+-------+--------------------------+--------------> 농도
LOD LOQ 최저 표준액 최고 표준액
|<------------- 동적 범위 (decade, 로그 척도) ------------->|
규격 §6.5는 선택성 계수를 Kij = Si/Sj로 정의한다. Si는 표적 분석물에 대한 감도, Sj는 간섭 물질에 대한 감도이며 목표는 Kij > 100:1이다. S는 기울기(농도당 신호)이므로, 비를 구하기 전에 각 신호를 그 신호를 만든 농도로 나누어야 한다. 서로 다른 농도에서 측정한 원시 신호를 비교하면 센서가 아니라 시험 설계를 측정하는 셈이다.
규약에도 주의해야 한다. 전위차법에서 IUPAC 선택성 계수 Kpotij(Nikolsky–Eisenman)는 반대 방향으로 정의된다. 주 이온에 대한 간섭 이온의 기여를 나타내므로 값이 작을수록 선택성이 좋다. WIA-BIO-011의 Kij는 클수록 좋은 비다. 두 규약을 섞은 데이터 기록은 의미가 소리 없이 뒤집히므로, 숫자와 함께 규약도 전달되어야 한다.
부록 A.3은 5 mM 포도당 신호를 각각 5, 0.1, 0.5 mM에서 측정한 간섭 물질 신호로 나누어 39:1, 73:1, 49:1을 얻고 결과를 "우수"하다고 평가한다. 농도로 정규화하면 그림이 달라진다.
| 물질 | 농도 (mM) | 신호 (µA) | S = 신호/농도 (µA/mM) | 규격 A.3 (원시 비) | Kij = S포도당/Sj | Kij, 블랭크 차감 |
|---|---|---|---|---|---|---|
| 포도당 | 5 | 5.85 | 1.17 | — | — | — |
| 과당 | 5 | 0.15 | 0.030 | 39:1 | ≈ 39:1 | ≈ 62:1 |
| 아스코르브산 | 0.1 | 0.08 | 0.80 | 73:1 | ≈ 1.5:1 | ≈ 4.8:1 |
| 요산 | 0.5 | 0.12 | 0.24 | 49:1 | ≈ 4.9:1 | ≈ 9:1 |
밀리몰당으로 보면 이 센서는 아스코르브산에 포도당과 거의 같은 세기로 응답하고, 요산에는 약 5분의 1 세기로 응답한다. 세 간섭 물질 중 어느 것도 규격 자신의 목표인 100:1을 만족하지 못한다. 부록이 이를 드러내지 못한 것은 아스코르브산과 요산 시험을 포도당 시험보다 각각 50배, 10배 낮은 농도에서 수행했기 때문이다.
마지막 열은 정규화하기 전에 모든 신호에서 A.1 블랭크 평균(0.056 µA)을 뺀 값이다. 아스코르브산 신호 0.08 µA는 블랭크보다 약 0.024 µA, 즉 약 3.4σ 높을 뿐이어서 검출 한계 바로 위에 있다. 그 결과 계수는 부록이 명시하지 않은 보정 하나에 따라 1.5에서 4.8까지 움직인다. 블랭크 근처에서 한 점만 측정한 간섭 시험으로는 신뢰할 만한 Kij를 얻을 수 없다. 제대로 된 연구(제6장, 그리고 제8장의 CLSI EP07 접근)는 여러 간섭 물질 농도에서 블랭크를 보정한 신호를 쓰고, 결과를 명시된 분석물 농도에서의 편향(bias)으로 보고한다.
보정된 수치는 잘 알려진 전기화학과 들어맞는다. 백금 전극에서 H2O2를 산화하는 데 필요한 +0.6 V(vs Ag/AgCl)(제2장)에서는 아스코르브산과 요산이 효소를 거치지 않고 전극에서 직접 산화된다. 포도당 센서가 선택 투과성 막, 작동 전위를 낮출 수 있는 산화환원 매개체, 또는 비특이 전류를 빼기 위한 효소 없는 보조(sentinel) 전극을 쓰는 진짜 이유가 이것이다. A.3을 원시 비로 읽으면 이 중 어느 것도 필요 없다는 결론이 나온다.
규격 §6.6은 농도가 계단 모양으로 바뀐 뒤 신호가 오르는 과정을 1차 응답으로 모델링한다.
S(t) = S_max · (1 − e^(−t/τ))
t₉₀ = τ · ln 10 ≈ 2.3 τ
t₉₅ = τ · ln 20 ≈ 3.0 τ
t₉₉ = τ · ln 100 ≈ 4.6 τ
예를 들어 τ = 10 s인 센서는 t₉₀ ≈ 23 s, t₉₅ ≈ 30 s다. τ는 기록한 계단 응답에서 ln(1 − S(t)/Smax)를 t에 대해 그려 추정할 수 있다. 진정한 1차 센서라면 점들이 기울기 −1/τ인 직선 위에 놓인다. 이 그래프가 휘어 있으면 응답이 단일 지수 함수가 아니라는 뜻이다. 막으로 덮인 효소 전극은 흔히 빠른 초기 상승 뒤에 느린 확산 꼬리를 보이는데, 이때 τ 하나로는 안정된 판독에 필요한 시간을 과소평가하게 된다. README는 신속형 바이오센서의 대표적 t₉₀로 1–60 s를 들며, 결합 평형에 의존하는 친화형 센서는 보통 수 분이 필요하다.
응답 시간은 updatePerformanceMetrics의 호출자가 초 단위의 단순한 숫자로 넘겨 PerformanceMetrics.responseTime에 저장된다. SDK가 이를 측정하지는 않는다. 3.1.3절에서 보았듯이 응답 시간은 신호 체인 전체를 거쳐 측정해야 한다.
규격 §6.9는 감도 손실을 1차 감쇠로 모델링한다.
S(t) = S₀ · e^(−k t)
t½ = ln 2 / k
드리프트 (%/월) = [1 − S(30일) / S₀] × 100 (t 단위: 일)
규격의 장기 드리프트 목표는 월 5 % 미만이다. 30일 동안 감도를 정확히 5 % 잃는 센서는 k = −ln(0.95)/30 ≈ 0.0017 d⁻¹, t½ ≈ 405일이다. 같은 월 5 %가 여섯 달 누적되면 초기 감도의 0.95⁶ ≈ 74 %만 남는다. 규격이 드리프트 보정보다 재교정을 일차 방어 수단으로 삼는 이유다(제6장).
두 종류의 안정성은 구분해야 하며, PerformanceMetrics.stability에도 각각 별도 필드가 있다.
operationalStability, 측정 횟수 단위): 사용에 따른 응답 손실이다. 효소 비활성화, 전극 오염(파울링), 매개체 용출이 원인이다. 규격의 목표는 100–1000회 측정이다.shelfLife, 일 단위): 첫 사용 전 보관 중의 응답 손실로, 보통 2–8 °C 조건에서 규정한다. 규격은 3–12개월을 제시한다.
세 번째 필드인 drift(%/월)는 연속 사용에서 둘을 잇는 기울기다. README는 t½ 1–365일을 대표 범위로 인용하는데, 이는 참고값일 뿐 WIA 요구사항이 아니다.
규격 §6.7은 정확도를 기준값에 대한 상대 오차로 나타낸다.
정확도 (%) = (측정값 − 참값) / 참값 × 100 목표: ±5 % 이내 (임상용)
회수율 (%) = (C_첨가후 − C_첨가전) / C_첨가량 × 100 목표: 90–110 % (규격 §7.2)
상대 오차에는 "참값"이 필요한데, 실제로는 인증 표준물질이나 기준 측정법을 뜻한다. 회수율은 실제 시료에 알려진 양의 분석물을 첨가하고 그중 얼마가 검출되는지를 물음으로써 이 요구를 피한다. 예를 들어 5.0 mM으로 읽히던 시료에 포도당 5.0 mM을 첨가한 뒤 9.8 mM으로 읽히면 회수율은 (9.8 − 5.0)/5.0 = 96 %다. 회수율은 매트릭스 효과를 확인하는 데 더 나은 시험이다. 시료 자체의 단백질, 점도, 적혈구 용적률이 내재 분석물과 마찬가지로 첨가한 분석물에도 작용하기 때문이다. 규격 §7.2.3은 회수율을 C측정/C첨가 × 100으로 쓰는데, 분석물이 없는 매트릭스에 첨가할 때는 위 식과 같다. 실제 시료에는 위의 차감 형태를 써야 한다(제6장). 합격 한계는 BIOSENSOR_CONSTANTS(MIN_RECOVERY 90, MAX_RECOVERY 110)에 있다.
규격 §6.8은 정밀도를 변동계수로 나타낸다.
CV (%) = σ / μ × 100 목표: 진단용 바이오센서 CV < 5 % (MAX_CV_PERCENT = 5.0)
CV는 LOQ보다 충분히 높은 농도에서만 의미가 있다. LOQ 자체에서는 정의상 CV ≈ 10 %다(σ/S를 10σ/S로 나눈 값). 정밀도를 말할 때는 조건도 함께 밝혀야 한다. 반복성(같은 센서, 같은 회차)은 언제나 중간 정밀도(다른 날짜, 다른 작업자, 다른 센서 로트)보다 좋게 나오므로, 조건 없이 "CV < 5 %"라고만 적으면 검증할 수 없다. 제8장에서 이를 CLSI EP05와 연결한다.
효소 반응 속도, 확산 계수, 막 투과도는 모두 온도와 함께 증가하므로, 효소 센서의 신호는 보통 1 °C당 몇 퍼센트씩 증가한다. 규격(§5.4)은 효소 센서 2–5 %/°C, 전기화학 센서 0.5–2 %/°C를 대표적 계수로 제시하며, SDK 기본값은 ENZYME_TEMP_COEFFICIENT = 3 %/°C다. 이는 기본값일 뿐이다. 실제 값은 효소, 고정화 방식, 막에 따라 달라지므로 센서 설계마다 측정해야 한다.
규격과 코드는 보정 방향에서 어긋난다.
규격 §5.4: Signal_comp = Signal × [1 + α (T − T_ref)]
SDK processSignal: signal = signal / [1 + (coefficient/100) (T − T_ref)]
CLI measure: corrected = signal / (1 + 0.03 × (temp − 25))
둘 다 옳을 수 있지만, α의 의미가 서로 달라야 한다. α가 센서의 온도 계수, 즉 1 °C당 신호의 상대 증가율이라면(ST = Sref[1 + α(T − Tref)]) 판독값을 Tref 기준으로 되돌리려면 SDK처럼 나누어야 한다. 규격의 곱셈은 α가 부호가 반대인 보정 계수일 때만 옳다. 1차 근사로 αcorr = −α/[1 + α(T − Tref)]이다.
그 차이의 크기는 과소평가하기 쉽다. 예를 들어 30 °C에서 20.0 µA를 읽었고, α = 3 %/°C, Tref = 25 °C, 재계산한 A.2 교정을 쓴다고 하자.
| 규약 | 보정 신호 (µA) | 농도 (mM) |
|---|---|---|
| 보정 없음 | 20.00 | ≈ 17.2 |
| 나눗셈 (SDK/CLI, α = 센서 계수) | 20.0 / 1.15 ≈ 17.39 | ≈ 15.0 |
| 곱셈 (규격 본문, α = +0.03) | 20.0 × 1.15 = 23.00 | ≈ 19.8 |
5 °C 차이에서 보정 방향을 잘못 적용하면 결과가 5 mM 가까이 움직이며, 이는 아예 보정하지 않은 경우보다 훨씬 큰 오차다. 이 책의 규약: 이후로 α는 두 개 이상의 온도에서 교정하여 측정한 센서 신호의 온도 계수를 뜻하며, 보상은 SDK와 CLI처럼 [1 + α(T − Tref)]로 나누는 방식으로 한다. 계수를 저장하는 기록에는 어느 규약을 따르는지 적어야 한다. SDK는 시료 온도가 25 °C에서 10 °C 넘게 벗어나면 temperature-out-of-range 플래그도 설정한다. 그만한 폭에서는 선형 보정을 믿어서는 안 된다.
| 지표 | 기호 / 식 | 단위 | 규격 목표 | README 대표값 | 기록 위치 |
|---|---|---|---|---|---|
| 감도 | S = Δy/Δx | 신호/농도 (µA/mM 등) | — | 0.1–1000 | CalibrationData.sensitivity |
| 직선성 | R² = 1 − SS_res/SS_tot | — | > 0.99 | — | CalibrationData.rSquared |
| SNR | 평균/σ | — | > 10 (가능하면 > 100) | — | v1.0에서는 저장 안 됨 |
| 검출 한계 | 3.3σ/S | 농도 | 용도별 | pM–µM | LODResult.lod, PerformanceMetrics.lod |
| 정량 한계 | 10σ/S ≈ 3.03 × LOD | 농도 | — | — | LODResult.loq |
| 동적 범위 | log₁₀(Cmax/LOD) | decade | — | 3–6 | PerformanceMetrics.dynamicRange |
| 선형 범위 | [Clow, Chigh] | 농도 | — | — | CalibrationData.linearRange |
| 선택성 | Kij = Si/Sj | 비 | > 100:1 | > 100:1 | PerformanceMetrics.selectivity |
| 응답 시간 | t₉₀ ≈ 2.3τ | s | — | 1–60 s | PerformanceMetrics.responseTime |
| 정확도 | (측정 − 참)/참 | % | ±5 % | — | PerformanceMetrics.accuracy |
| 회수율 | (C첨가후 − C첨가전)/C첨가량 | % | 90–110 % | — | 밸리데이션 입력값 |
| 정밀도 | CV = σ/µ | % | < 5 % | — | PerformanceMetrics.precision |
| 안정성 | t½ = ln2/k; 드리프트 | 일; %/월 | 보관 3–12개월; 100–1000회; 드리프트 < 5 %/월 | t½ 1–365일 | PerformanceMetrics.stability |
README 열은 참고값일 뿐이며, 규범적 목표는 규격 §6–§7의 값이다.
다음 TypeScript 코드는 A.2 회귀를 손으로 다시 계산해 SDK 결과와 대조하고, A.1 블랭크로부터 LOD, LOQ, 동적 범위를 구한다. 예상 출력은 주석으로 적었다. 생물학적 인식 요소 값은 열거형 멤버 'enzyme'이다. README에 나오는 'glucose-oxidase'는 BiorecognitionElement의 멤버가 아니다.
import { BiosensorSDK, calculateSensitivity } from '@wia/bio-011';
const conc = [0, 1, 5, 10, 50, 100]; // mM (부록 A.2)
const signal = [0.05, 1.20, 5.85, 11.50, 58.20, 115.80]; // µA
const blanks = [0.05, 0.06, 0.05, 0.07, 0.05, 0.06, 0.05, 0.06, 0.05, 0.06]; // A.1
// 1. 손 계산: 중심화한 최소제곱
const mean = (v: number[]) => v.reduce((a, b) => a + b, 0) / v.length;
const xm = mean(conc), ym = mean(signal);
let sxy = 0, sxx = 0;
conc.forEach((x, i) => { sxy += (x - xm) * (signal[i] - ym); sxx += (x - xm) ** 2; });
const b = sxy / sxx; // 1.15853
const a = ym - b * xm; // 0.04736
console.log(b.toFixed(4), a.toFixed(3)); // "1.1585" "0.047"
// 2. 독립 헬퍼 (내부적으로 같은 회귀)
console.log(calculateSensitivity(conc, signal).toFixed(4)); // "1.1585"
// 3. SDK 전체 경로
const sdk = new BiosensorSDK();
const sensor = sdk.createSensor({
type: 'electrochemical',
biorecognitionElement: 'enzyme',
targetAnalyte: 'glucose',
});
const cal = sdk.calibrateSensor({
sensorId: sensor.id,
standardConcentrations: conc,
measuredSignals: signal,
concentrationUnit: 'mM',
signalUnit: 'μA',
});
console.log(cal.rSquared.toFixed(5)); // "0.99999"
console.log(cal.sensitivityUnit); // "μA/mM"
console.log(cal.linearRange); // { min: 1, max: 100, unit: 'mM' }
console.log(cal.qualityControl?.maxResidual.toFixed(3)); // "0.226" (50 mM 지점)
// 4. 블랭크로 LOD / LOQ, 이어서 동적 범위
const lod = sdk.calculateLOD({ blankSignals: blanks, sensitivity: cal.sensitivity });
console.log(lod.blankStdDev.toFixed(5)); // "0.00699"
console.log(lod.lod.toFixed(4), lod.loq.toFixed(4)); // "0.0199" "0.0604"
const perf = sdk.updatePerformanceMetrics(sensor.id, lod, 23 /* s, 실측값 */);
console.log(perf.dynamicRange.toFixed(2)); // "3.70"
// A.2 시료 역산 (25 °C, 보정 없음)
console.log(((23.5 - cal.regression.intercept) / cal.sensitivity).toFixed(1)); // "20.2"
이 코드에서 실무상 중요한 점이 세 가지 있다. 독립 함수 calibrateSensor는 등록된 센서가 없는 새 SDK 인스턴스를 만들기 때문에, 어떤 sensorId로 호출하든 SENSOR_NOT_FOUND(BIO001)를 던진다. 위처럼 센서를 생성한 SDK 인스턴스의 메서드를 써야 한다. LODResult.unit은 문자열 "concentration units" 그대로이므로, 결과를 저장할 때 호출자가 실제 단위(여기서는 mM)를 붙여야 한다. updatePerformanceMetrics에 넘기는 응답 시간은 계산값이 아니라 입력값이다. 위의 23 s는 실측 t₉₀ 자리를 채운 임시값이다.
바이오센서 결과는 기저선 차감, 선택적 필터링, 온도 보상, 교정 모델의 역산을 거친다. 각 단계는 최종 숫자의 의미를 바꾸므로 모두 기록해야 한다. WIA-BIO-011의 성능 지표는 단순한 식이지만, 함정은 입력에 있다. 어떤 σ인가, 어떤 농도인가, 어떤 규약인가. 부록 A에서 다시 계산한 기준 포도당 센서의 값은 S ≈ 1.1585 µA/mM, LOD ≈ 0.020 mM, LOQ ≈ 0.060 mM, 교정된 선형 범위 1–100 mM, 동적 범위 약 3.7 decade다. 선택성을 올바르게 계산하면 이 센서는 아스코르브산과 요산에 대해 규격의 100:1 목표에 한참 못 미친다.
이 장에서 제시한 v1.0 수정 사항, 즉 LOQ ≈ 3.03 × LOD, A.2 회귀의 재계산, A.3 선택성의 농도 정규화, 동적 범위와 선형 범위의 구분, 온도 보상의 나눗셈 규약은 다른 정오표 항목과 함께 제8장에 모아 둔다. 제4장에서는 이 모든 값이 저장되는 기록 구조를 살펴본다.