상용·오픈소스 LIMS 연동
📍 현재 위치: Part IV · 현실과 마주하기 — 이제 우리 플랫폼은 시계열, 배치 모델, 그리고 PI와 DCS/MES/ERP로 이어지는 다리를 갖추었습니다. 이 장에서는 *결정(decision)*을 소유한 마지막 시스템 — 배치를 출하해도 되는지를 판정하는 실험실 — 을 연결하고, 진짜 기록 시스템(system of record)이 누구인지에 대해 정직하게 짚어 봅니다.
당신의 데이터 플랫폼을, 모든 배치에 대해 매일 신문을 찍어 내는 분주한 편집국이라고 생각해 보세요. 이 편집국은 바이오리액터(bioreactor), 크로마토그래피 스키드(chromatography skid), 대시보드 등 곳곳에서 사실을 수집합니다. 하지만 지어내서는 안 되는 사실이 하나 있습니다 — 바로 그 의약품이 출하 시험을 통과했는지 여부입니다. 그 판정은 실험실 자체의 서류 캐비닛 — LIMS(Laboratory Information Management System, 실험실 정보 관리 시스템) — 안에 기록되고, 서명되고, 잠겨 보관됩니다. 우리의 일은 그 캐비닛을 빼앗는 것이 아닙니다. 정중하게 그 인증서의 사본 한 장을 요청해, 나머지 모든 것 옆에 가지런히 철해 두되, 우리의 사본이 절대 원본 행세를 하지 못하게 하는 것입니다. 실험실이 마음을 바꿀 때 — 결과가 정정되거나 배치가 부적합 처리될 때 — 우리의 사본도 충실하게 갱신되어야 합니다. 그러지 않으면 그것은 위험한 소문이 되어 버립니다. 이 장에서는 그 정중하고 충실한 양방향 복사기를 만듭니다.
이 장에서 다루는 내용
앞선 다리들은 플랜트를 감시하는 시스템에서 공정(process) 데이터를 끌어왔습니다. 실험실은 다릅니다. 단지 관찰하는 데 그치지 않고 **판정(adjudicate)**합니다. 최종 출하 시험 — 단일클론항체(monoclonal antibody, mAb) 로트가 출하 적합한지를 말해 주는 SEC, CEX, 숙주세포단백질(host-cell-protein), 잔류 Protein A, DNA, 엔도톡신(endotoxin) 분석 — 은 대개 상용 LIMS에 자리 잡고 있으며, 그것이 발행하는 **분석성적서(Certificate of Analysis, CofA)**는 규제 대상 기록입니다. 이 장에서 다루는 내용은 다음과 같습니다.
- 실험실 정보학의 어휘 — LIMS, ELN, SDMS — 와 각 오픈소스 도구가 어디에 들어맞는지.
- OSS 스택이 이미 실험실 결과를 담을 자리를 가지고 있다는 점:
lab.sample/lab.test/lab.result테이블, 그리고 LIMS와 REST/JSON 및 파일로 주고받는 방식. - 모의(mock) 상용 LIMS를 상대로 한 실제 CofA-in 동기화, 그리고 멱등성(idempotency)과 충돌 해결을 갖춰 재실행해도 안전하게 만드는 법.
- 정직한 오픈소스 지형도: QC/출하용 SENAITE, 공정 개발용 openBIS, 그리고 LabKey의 Part 11 기능이 왜 유료 장벽 뒤에 있는지.
- 당신의 사본이 섀도 레코드(shadow record) — 규제기관이 가장 신경 쓰는 실패 양상 — 가 되지 않게 지켜 주는 규율.
노트북에서 돌릴 수 있는 모든 것은 이미 당신이 가지고 있는 테이블과 모의 객체를 상대로 실행됩니다. 진짜 LIMS는 우리가 배포할 수 없는 유일한 것이므로, 그 API를 모의로 만들고 그 사실을 명시합니다.
같지 않은 세 글자: LIMS, ELN, SDMS
무엇이든 연결하기 전에 어휘부터 분명히 합시다. 통합 계약(integration contract)이 여기에 달려 있기 때문입니다. 실험실 정보학의 표준 지침인 ASTM E1578은 업계가 사용하는 경계선을 그어 줍니다 [1]. LIMS는 시료 중심(sample-centric)입니다. 시료를 등록하고, 시험을 일정에 올리고, 규격(specification) 대비 결과를 기록하며, 출하 워크플로(release workflow)를 주도합니다. ELN(Electronic Lab Notebook, 전자 실험 노트)은 실험 중심(experiment-centric)입니다. 과학자가 무엇을 왜 했는지, 종이 노트가 담아 오던 서사를 기록합니다. SDMS(Scientific Data Management System, 과학 데이터 관리 시스템)는 파일 중심(file-centric)입니다. 크로마토그램, 스펙트럼 같은 원시 기기 출력을 손대지 않은 원본 그대로 보관합니다.
지금 다루는 사례는 이 셋 모두에 닿습니다. 출하의 경우 LIMS가 SEC/CEX/엔도톡신 판정을 소유합니다. 공정 개발에서는 ELN이 배지(media) 조정 뒤의 근거를 기록합니다. 그리고 역가(titer) 수치 뒤에 있는 원시 HPLC 트레이스(trace)는 SDMS 방식의 아카이브에 속합니다. 이것들을 혼동하는 것이 바로 통합이 썩어 가는 방식입니다. 자유 서술형 실험 서사를 LIMS 결과 필드에 밀어 넣고서 반대편에서 깔끔한 출하 결정이 나오기를 기대할 수는 없습니다.
OSS 스택에는 이미 실험실 스키마가 있다
실험실 데이터를 위한 새 데이터베이스는 필요 없습니다. 8장에서 이미 하나를 마련했습니다. examples/platform/db/30-lab-events.sql에는 어떤 LIMS 교환이든 매핑해야 하는 시료-에서-결과(sample-to-result)의 척추를 모델링하는 세 테이블이 있습니다.
-- examples/platform/db/30-lab-events.sql
CREATE TABLE lab.sample (
sample_id text PRIMARY KEY,
batch_id text REFERENCES s88.batch,
sample_time timestamptz NOT NULL,
sample_point text NOT NULL,
sample_type text NOT NULL DEFAULT 'in_process' -- in_process | release | stability
);
CREATE TABLE lab.test (
test_id text PRIMARY KEY,
name text NOT NULL,
unit text,
spec_low numeric,
spec_high numeric
);
CREATE TABLE lab.result (
result_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sample_id text NOT NULL REFERENCES lab.sample,
test_id text REFERENCES lab.test,
value numeric,
text_value text,
unit text,
result_ts timestamptz NOT NULL DEFAULT now(),
analyst text,
instrument_id text,
status text NOT NULL DEFAULT 'preliminary', -- preliminary | verified | rejected
UNIQUE (sample_id, test_id, result_ts)
);
CREATE INDEX ON lab.result (sample_id);
이 스키마의 두 가지 설계 선택이 충실한 다리를 만드는 전부입니다. 첫째, 모든 결과는 자신의 출처(provenance) — analyst, instrument_id, result_ts — 와 함께, preliminary 값과 verified 값을 구분하는 status를 지닙니다. LIMS의 출하 결정은 바로 그 구분에 달려 있으므로, 우리의 사본도 그것을 지녀야 합니다. 그러지 않으면 확정되지 않은 수치를 슬그머니 사실로 격상시키게 됩니다. 둘째, UNIQUE (sample_id, test_id, result_ts) 제약 조건은 우리의 **멱등성 키(idempotency key)**입니다. 같은 결과를 몇 번을 다시 보내도 행(row)이 중복되지 않게 해 줍니다. 바로 그 단 하나의 제약 조건이, 깨지기 쉬운 일회성 가져오기를 크래시 후에도 재실행할 수 있는 동기화로 바꿔 줍니다.
제조로 돌아가는 연결 고리는 lab.sample.batch_id로, 3장에서 시드(seed)했던 ISA-88/95의 s88.batch 테이블을 가리키는 외래 키(foreign key)입니다. 바로 그것이 실험실 결과를 맥락이 있는(contextual) 것으로 만듭니다. CofA 값은 떠다니는 데이터가 아니라, BR101 유닛에서 돌아가 MAB-001 제품을 만든 BATCH-2026-001에 붙어 있는 값입니다.
분석성적서는 실제로 어떻게 생겼나
시뮬레이터는 이미 6개 배치 캠페인에 대한 출하 데이터셋을 생성해 두었습니다. 아래는 examples/datasets/hplc_results.csv에서 가져온 첫 배치의 대표적인 출하 시험들로(부분 발췌 — 실제 파일에는 이 사이에 SEC_LMW_pct, CEX_acidic_pct, CEX_basic_pct, bioburden_CFU_per_10mL 행도 들어 있습니다), CofA 교환이 전달하는 모양 그대로입니다.
batch_id,test,value,unit,spec_low,spec_high,result
BATCH-2026-001,SEC_monomer_pct,98.611,%,95.0,100.0,PASS
BATCH-2026-001,SEC_HMW_pct,1.287,%,0.0,3.0,PASS
BATCH-2026-001,CEX_main_pct,70.686,%,60.0,80.0,PASS
BATCH-2026-001,HCP_ng_per_mg,28.203,ng/mg,0.0,100.0,PASS
BATCH-2026-001,residual_ProteinA_ng_per_mg,1.149,ng/mg,0.0,20.0,PASS
BATCH-2026-001,host_cell_DNA_ng_per_dose,0.939,ng/dose,0.0,10.0,PASS
BATCH-2026-001,endotoxin_EU_per_mL,0.215,EU/mL,0.0,5.0,PASS
# ... (SEC_LMW_pct, CEX_acidic_pct, CEX_basic_pct, bioburden_CFU_per_10mL omitted)
열(column)을 눈여겨보세요. 값, 단위, 규격 범위(specification window), 그리고 그 범위 대비 계산된 PASS/OOS 판정입니다. 배치 전체는 모든 시험이 통과할 때에만 통과합니다. 우리 캠페인은 의도적으로 하나의 실패를 포함합니다. BATCH-2026-004는 규격 상한 100.0에 대해 HCP_ng_per_mg,128.0을 반환해 OOS — 규격 이탈(out of specification) — 로 표시되며, 이것이 그 배치의 s88.batch.status가 released가 아니라 rejected인 이유입니다. 그 한 수치를 조용히 누락하거나 반올림하는 다리는 부적합 로트를 숨기는 것입니다. 그것이 바로 악몽입니다. 여기서 충실한 동기화는 있으면 좋은 것이 아니라, 환자 안전(patient safety) 그 자체입니다.
출하 결정은 규제 대상 행위입니다. 21 CFR 211.165에 따르면 의약품은 실험실 시험으로 최종 규격 적합이 확인되기 전까지는 출하될 수 없으며 [2], 21 CFR 211.194는 CofA의 모든 값 뒤에 있어야 하는 완전한 실험실 기록 — 방법, 원시 데이터, 누가 시험했고 누가 검토했는지 — 을 정의합니다 [3]. LIMS는 그 완전한 기록을 보유합니다. 우리 테이블은 플랫폼이 맥락화하고 시각화하는 데 필요한 부분의 *진정한 사본(true copy)*을 보유합니다. 이 둘의 차이가 이 장 전체의 척추입니다.
충실하고 멱등한 CofA-in 동기화
실제 상용 LIMS(LabWare, STARLIMS, SampleManager)는 독점 소프트웨어이고 라이선스로 잠겨 있어, 노트북에서 돌릴 공개 이미지가 없습니다. 그래서 17장에서 AVEVA PI를 다룰 때 했던 것과 똑같이, 같은 REST 계약을 지키는 모의(mock) 객체를 향해 다리를 겨눕니다 — 그리고 다리 코드는 진짜이지만 상대편은 시뮬레이션이라는 점을 명시합니다. 모의 객체는 동반 저장소(companion repo)에 services/lims-cofa-adapter로 들어 있고 commercial Compose 프로파일 뒤에서 올라옵니다(docker compose --profile commercial up -d lims-cofa-adapter). 실제 다리 클라이언트는 bridges/lims_cofa_adapter.py이며, 운영 환경에서는 베이스 URL과 자격 증명만 바뀝니다. 이 어댑터는 CofA를 JSON으로 노출합니다 — 이것이 실제 LIMS의 CofA 엔드포인트가 반환하는 계약입니다.
// GET /api/v1/cofa/BATCH-2026-001 (served by services/lims-cofa-adapter; a real LIMS returns the same shape)
{
"batch_id": "BATCH-2026-001",
"lot": "L26001",
"disposition": "released",
"results": [
{"test": "SEC_monomer_pct", "value": 98.611, "unit": "%",
"spec_low": 95.0, "spec_high": 100.0, "result": "PASS",
"analyst": "j.okafor", "instrument_id": "HPLC-07", "status": "verified",
"result_ts": "2026-01-20T10:15:00Z"},
{"test": "HCP_ng_per_mg", "value": 28.203, "unit": "ng/mg",
"spec_low": 0.0, "spec_high": 100.0, "result": "PASS",
"analyst": "j.okafor", "instrument_id": "ELISA-02", "status": "verified",
"result_ts": "2026-01-20T11:02:00Z"}
]
}
다리의 임무는 그 행들을 lab.result에 멱등하게(idempotently) 안착시키는 것입니다. 그래야 동기화를 일정에 맞춰 실행할 수 있고, 어떤 실패 후에 재실행해도 중복이나 — 더 나쁘게는 — 서로 어긋나는 두 번째 사본을 만들지 않습니다. 패턴은 PostgreSQL의 INSERT ... ON CONFLICT이며, 스키마가 이미 정의해 둔 고유 제약 조건을 키로 삼습니다.
-- examples/bridges/lims_cofa_adapter.py — the heart of the idempotent CofA-in upsert
INSERT INTO lab.result
(sample_id, test_id, value, unit, result_ts, analyst, instrument_id, status)
VALUES
(%(sample_id)s, %(test_id)s, %(value)s, %(unit)s,
%(result_ts)s, %(analyst)s, %(instrument_id)s, %(status)s)
ON CONFLICT (sample_id, test_id, result_ts)
DO UPDATE SET
value = EXCLUDED.value,
status = EXCLUDED.status,
analyst = EXCLUDED.analyst
WHERE lab.result.status <> 'verified'; -- never overwrite a verified record in place
그 WHERE 절을 주의 깊게 읽으세요. 한 줄로 된 **충돌 해결 정책(conflict-resolution policy)**이기 때문입니다. preliminary 행은 더 새로운 값으로 갱신되도록 허용하되, 이미 verified로 표시된 행을 덮어쓰는 것은 거부합니다. 왜일까요? GxP 세계에서는 확정된 기록을 *편집(edit)*하지 않기 때문입니다 — 대신 그것을 대체(supersede)하면서 옛 기록은 그대로 보이게 둡니다. 검증된(verified) 결과에 대한 정정은 새로운 result_ts(새 행)로 도착해야지, 조용한 제자리 변경으로 도착해서는 안 됩니다. 그래야 실험실이 언제 무엇을 사실로 여겼는지의 이력이 결코 지워지지 않습니다. 20장에서는 시스템 버전 관리(system-versioned) 이력 테이블로 그 추가 전용(append-only) 규율을 구조적으로 만들 것입니다. 여기서는 단지 파괴적 갱신을 거부할 뿐입니다.
가장 풍부한 단일 기기 기록은 본래의 벤더 중립(vendor-neutral) 형식으로 아카이브됩니다. 시뮬레이터는 Allotrope Simple Model 문서 하나, examples/datasets/hplc_titer.asm.json도 내보내므로, 원시 HPLC 역가 측정값이 맥락이 벗겨진 수치가 아니라 자기 기술적(self-describing) 원본 — SDMS 역할 — 으로 살아남습니다.
// examples/datasets/hplc_titer.asm.json (Allotrope Simple Model — the "original" the LIMS value derives from)
{
"$asm.manifest": "http://purl.allotrope.org/manifests/core/REC/2024/06/manifest.schema",
"measurement aggregate document": {
"measurement document": [{
"sample document": {"batch identifier": "BATCH-2026-001",
"sample identifier": "BATCH-2026-001-DS"},
"device system document": {"device identifier": "HPLC-07",
"model number": "OpenHPLC-1"},
"measurement identifier": "BATCH-2026-001-titer",
"protein concentration": {"value": 5.877, "unit": "g/L"},
"measurement time": "2026-01-20T10:15:00Z"
}]
}
}
CofA 값과 이 ASM 문서는 batch identifier, instrument_id/device identifier(HPLC-07), 그리고 타임스탬프를 공유합니다 — 그래서 플랫폼은 언제든 출하된 수치에서 그것이 비롯된 원시 측정값까지 거슬러 올라갈 수 있습니다. 그 추적 가능한 실 한 가닥이 바로 규제기관이 말하는 재구성 가능성(reconstructable)입니다.
다리의 전체 모양, 처음부터 끝까지
이를 종합하면 흐름은 작고 의도적입니다. 아래 다이어그램은 결정이 어디에 있고 우리의 사본이 어디에 있는지를 보여 줍니다.
LIMS가 판정하고 서명합니다. 오픈소스 스택은 배치에 결합된, 읽기 위주의 충실한 진정 사본을 유지합니다. 권위의 화살표는 한 방향만 가리킵니다. Original diagram by the authors, created with AI assistance.
이 그림에서 가장 중요한 단 하나의 속성은 권위 화살표의 방향입니다. 데이터는 우리 스택 안으로 흐르고, 결정은 결코 밖으로 흘러 나가지 않습니다. MHRA의 데이터 무결성(data-integrity) 지침은 우리가 피하려는 함정에 대해 단호합니다. 규제 대상 기록의 사본은 검증된 **진정 사본(true copy)**으로서만 허용되며, 원본의 통제 없이 권위 있는 것으로 취급되는 병렬 기록은 섀도 레코드(shadow record) — 기능이 아니라 지적 사항(finding) — 입니다 [4]. 우리의 status 열, 검증된 행을 덮어쓰기를 거부하는 태도, 그리고 ASM 원본의 보존이 바로 그 사본을 정직하게 유지하는 통제입니다.
정직한 오픈소스 LIMS 지형도
상용 LIMS가 없다면 오픈소스가 그 자리를 채울 수 있을까요? 부분적으로는 가능합니다 — 그리고 정직한 답은 어느 슬롯이냐에 달려 있습니다.
SENAITE는 QC와 출하 시험에 가장 잘 들어맞는 오픈소스입니다. Plone/Zope 위에 구축된 성숙한 LIMS로, 시료 등록부터 공표된 결과 보고서까지를 아우르고 REST를 구사합니다. senaite.jsonapi가 생성/조회/갱신 엔드포인트를 노출하므로 우리 스택은 (AnalysisRequest로서) 시료를 등록하고 평이한 HTTP/JSON으로 결과를 끌어올 수 있습니다 [5] [6]. 다만 성숙도에 대해서는 정확해야 합니다. SENAITE는 GPL v2.0으로 공개되며, 기본 설치 상태로는 Part 11 준수 시스템이 아닙니다 — 반복적으로 나타나는 "GxP 라스트 마일(last mile)" 항목들(설정된 변조 방지 감사 추적, 서명 시점 전자 서명, 검증된 보존 통제, 문서화된 비밀번호 정책, IQ/OQ/PQ 패키지)은 운영자가 책임지는 설정·절차·검증 작업입니다. 그 간극의 모양을 가르치기 위해, 저장소에는 examples/compliance/gap-analyses/senaite-part11-gap.md에 예시용 갭 레지스터(gap register)가 들어 있으며, SENAITE를 기본 설치만으로 준수되는 시스템이 아니라 교육용 LIMS로 다룹니다. 훌륭한 QC 척추이지만, 내려받기만 하면 Part 11을 만족시키는 물건은 아닙니다.
ETH 취리히의 openBIS는 공정 개발과 R&D 슬롯에 들어맞습니다 — 풍부한 메타데이터와 함께 실험, 시료, 데이터셋을 등록합니다. V3 API와 pyBIS 파이썬(Python) 클라이언트를 통해 프로그래밍 친화적이어서, 몇 줄의 파이썬으로 PD 시료를 등록하고 끌어올 수 있습니다 [7]. Apache-2.0 라이선스이며, 질문이 "이 로트가 출하 가능한가"가 아니라 "우리가 무엇을 시도했고 무슨 일이 일어났는가"일 때 알맞은 도구입니다.
LabKey는 마케팅이 경계선을 흐리기 때문에 분명히 짚어 둘 만합니다. LabKey Community Edition은 정말로 무료이지만, 출하 LIMS에 실제로 필요한 기능 — 21 CFR Part 11 준수를 위해 설계된 전자 서명 — 은 명시적으로 프리미엄 기능이며 Enterprise Edition에서만 제공된다고 표기되어 있고 [8], 에디션 페이지도 준수 기능에는 유료 라이선스가 필요함을 확인해 줍니다 [9]. 이것이 이 책 전체의 주제를 축소판으로 보여 줍니다. 순수 OSS는 데이터 모델과 워크플로를 제공합니다. 규제 대상 라스트 마일 — 서명, 검증된 감사 추적, 벤더 책임(vendor accountability) — 은 유료이거나 하이브리드입니다. 이를 솔직히 말하는 편이 아닌 척하는 것보다 더 유용합니다.
왜 중요한가
"이 로트가 통과했는가?"에 답하지 못하는 바이오공정 데이터 플랫폼은 시스템이 아니라 구경거리입니다. 그러나 출하 결정은 플랫폼이 결코 *작성(author)*해서는 안 되는 단 하나의 사실입니다. 다리를 손실이 나는 방향으로 잘못 만들면 OOS 결과를 숨기게 되고, 권위를 부여하는 방향으로 잘못 만들면 규제기관이 인용할 수 있는 섀도 레코드를 만들게 됩니다. 이 장의 스키마 선택들 — 출처 열, status 생애주기, 멱등성을 위한 고유 키, 그리고 덮어쓰기 대신 대체하는 충돌 정책 — 은 데이터베이스의 사소한 사항이 아닙니다. 그것들은 충실한 사본과 책임 부담(liability) 사이의 차이입니다. 그것들은 플랫폼이 잘하는 일(출하된 수치를 배치 전체와 견주어 맥락화하고 시각화하고 분석하는 일)을 하게 해 주면서, 작성 권한은 법적으로 그것이 속한 곳에 그대로 남겨 둡니다.
실제 현장에서는
실제 유가식(fed-batch) CHO + Protein A 시설에서는 LIMS가 QC의 중심에 앉아 있고 OSS 플랫폼이 그 주위를 돕니다. 이 통합은 화려할 일이 좀처럼 없습니다. 야간 REST 풀(pull) 또는 감시 중인 CofA 파일 드롭(drop), 시료와 시험을 키로 한 업서트(upsert), 그리고 배치가 OOS로 뒤집힐 때의 경보. 강화/연속(intensified/continuous) 변형 — 다중 컬럼 포집을 동반한 관류(perfusion) — 은 부담을 키울 뿐입니다. 거의 연속적인 수확(harvest)은 거의 연속적인 샘플링을 의미하므로, 동기화 주기가 배치 단위에서 교대 근무(shift) 단위로 옮겨 갑니다. 패턴은 바뀌지 않습니다. 빈도가 바뀔 뿐입니다.
미국의 민관 제조 혁신 연구소(Manufacturing Innovation Institute)인 NIIMBL은 바로 이런 통합 이음매(seam)가 바이오제조가 시간과 신뢰를 잃는 지점이기 때문에 존재합니다. 그곳의 SABRE 시설 — 델라웨어 대학교에 건설 중인 파일럿 규모의 현행 우수 의약품 제조 및 품질 관리 기준(current Good Manufacturing Practice, cGMP) 시설로, 2024년 4월에 착공 — 은 현대적 데이터 플랫폼이 QC가 실제로 운영하는 검증된 LIMS를 대체하는 것이 아니라 그것과 상호 운용될 수 있음을 입증하는 장소입니다. (SABRE는 데이터 프로그램이 아니라 시설입니다. 데이터 규율은 우리가 구축해야 할 몫입니다.) GAMP 5 제2판은 위험 그림을 깔끔하게 잡아 줍니다. 출하 결정을 보유한 LIMS는 엄격한 보증을 요구하는 더 높은 위험의 기록 시스템인 반면, 읽기 위주의 통합 계층은 더 가벼운 의도된 용도에 비례하여(proportionately) 위험 평가되고 검증됩니다 [10]. 그리고 규제의 닻은 결코 움직이지 않습니다. LIMS가 출하 기록을 보유하는 곳이라면 어디서든 그 전자 기록과 서명은 21 CFR Part 11을 충족해야 하며, OSS 계층은 그 곁에서 통제되지 않은 병렬 기록이 되어서는 안 됩니다 [11]. 정직한 결론은 이렇습니다. 오픈소스는 탁월한 QC·PD 데이터 모델과 깔끔한 교환 계약을 제공하지만, 검증되고 Part 11로 서명된 출하 시스템을 제공하지는 않습니다. 그 라스트 마일은 하이브리드이며, 아닌 척하는 것이야말로 이 책이 한사코 거부하는 바로 그 실수입니다.
핵심 용어
- LIMS(Laboratory Information Management System, 실험실 정보 관리 시스템): 시료를 등록하고, 시험을 일정에 올리고, 규격 대비 결과를 기록하며, 출하를 주도하는 시료 중심 소프트웨어. 대개 출하 결정의 기록 시스템.
- ELN(Electronic Lab Notebook, 전자 실험 노트): 과학자가 무엇을 왜 했는지를 기록하는 실험 중심 소프트웨어.
- SDMS(Scientific Data Management System, 과학 데이터 관리 시스템): 원시 기기 출력을 손대지 않은 원본으로 보관하는 파일 중심 아카이브.
- CofA(Certificate of Analysis, 분석성적서): 로트의 출하 시험 결과를 규격 대비로 요약하고 합격/불합격 처분을 담은 규제 대상 문서.
- OOS(Out Of Specification, 규격 이탈): 규격 범위를 벗어난 결과. 이 캠페인에서는 로트를 불합격시키는
BATCH-2026-004의 HCP 값. - 진정 사본 / 섀도 레코드(true copy / shadow record): 규제 대상 원본의 검증된 충실한 사본(허용됨) 대 권위 있는 것으로 취급되는 통제되지 않은 병렬 기록(데이터 무결성 지적 사항).
- 멱등 동기화(idempotent sync): 행을 중복시키거나 손상시키지 않고 안전하게 재실행할 수 있는 가져오기. 여기서는
(sample_id, test_id, result_ts)에 대한ON CONFLICT로 강제됨. - ASM(Allotrope Simple Model): 분석 측정을 위한 벤더 중립 JSON 형식. 원시 HPLC 원본을 자기 기술적으로 유지하는 데 사용.
- cGMP: 현행 우수 의약품 제조 및 품질 관리 기준(current Good Manufacturing Practice). 의약품 제조에 대한 FDA의 집행 가능한 품질 체계.
다음 이야기
이 장 내내 우리는 덮어쓰기 대신 대체하고, 기록을 verified로 표시하며, 원본 측정값을 추적 가능하게 유지하려고 조심했습니다 — 하지만 지금까지 그 규율은 우리의 습관과 단 하나의 WHERE 절 속에 살고 있었습니다. 다음 장 구조로 만드는 ALCOA+: 코드 속의 무결성은 그것을 구조적으로 만듭니다. 추가 전용 패턴, PostgreSQL 트리거 기반 시스템 버전 관리 이력 테이블, 그리고 감사 로그 위의 해시 체인(hash-chain)에, 그 보증을 단언하는 테스트 스위트(test suite)를 더합니다. 우리는 사본이 충실하다고 약속하기를 멈추고 그것을 증명하기 시작합니다.