알피
경력근거: 병원 온프레미스·폐쇄망·클라우드 환경에 AI 솔루션을 구축·운영합니다.
※ 실제 산출물이 아닌 AI 예측 시안입니다.
ARPI 의 ECG Buddy 는 국내 100개 이상 병원에서 쓰이고 월 20만 건 이상의 심전도를 분석하는데, 그 병원들이 저마다 온프레미스·폐쇄망·클라우드로 갈립니다. FDE 의 일은 모델을 고치는 것이 아니라 병원마다 다른 전산실에 같은 솔루션을 올려 놓고 인수인계까지 마치는 것이고, 자격요건이 Linux 운영·폐쇄망 구축·Helm·ArgoCD 를 나란히 세운 이유도 거기 있습니다. 병원 수가 늘수록 설치를 손으로 되풀이하는 비용이 커지는 구조라 이 도구가 붙을 자리가 분명합니다. 다만 이 포트폴리오는 폐쇄망 배포와 설정 자동화를 보여주는 것이고, 심전도 판독이나 EMR 연동 표준의 의료 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
PoC·현장 설치·연동 테스트·검수·운영 전환·인수인계를 한 사람이 맡는다고 적혀 있어, 설치를 돌리고 그 결과를 문서로 남기는 결과물이 직무 전체를 한 번에 보여줍니다.
폐쇄망·망분리 환경 구축 경험과 Kubernetes 클러스터 설정·Helm 차트 작성이 나란히 우대사항이라, 외부가 막힌 클러스터에 차트로 올리는 산출물이 그대로 겹칩니다.
병원 및 EMR/OCS 업체와 소통해 모호한 요구사항을 구체화하는 일이 주요업무라, 담당자가 채운 요건표를 설치 설정 값으로 떨어뜨리는 층이 직무의 한가운데입니다.
병원별 설치 요건표와 전산실 서버 목록 두 파일을 받아 Helm 설정 값을 채우고, 외부가 막힌 클러스터에 분석 서버와 연동 게이트웨이를 실제로 올리는 CLI 도구입니다. 못 올린 병원은 어느 서버의 어느 값이 막았는지 인수인계 문서에 줄로 남습니다.
병원마다 EMR 업체도 다르고 서버 사양도 달라, 갈 때마다 설정 값을 손으로 고쳐 설치하다 가속기가 없는 서버에서 파드가 안 떠 반나절을 태우던 구축 담당자입니다. 무엇이 막힐지는 설치를 돌려 봐야 알고, 인수인계 문서는 현장마다 밤에 새로 씁니다.
설치가 막히는 이유는 대개 요건표와 서버 목록만 봐도 알 수 있는 것들입니다. 가속기 유무, Kubernetes 버전, 스토리지 클래스, 병원 이름 표기를 규칙으로 먼저 맞춰 보면 현장에 가기 전에 막힐 병원이 갈리고, 실제 설치는 남은 병원만 돌리면 됩니다.
요건표와 서버 목록을 넣으면 Helm 설정이 채워지고 분석 서버와 연동 게이트웨이가 실제로 뜹니다. 병원 전산실 대신 노트북에 만든 kind 클러스터와 사내 미러를 흉내 낸 레지스트리 컨테이너를 대상으로 시작하고, 실제 병원 망 연결은 2주차 이후로 둡니다.
정답을 붙인 26건으로 막힘 판정 일치를 자동으로 재는 러너와, 같은 차트를 AWS 관리형 Kubernetes 에 올려 클라우드 병원 건도 같은 명령으로 다루는 형상입니다.
Terraform 으로 클러스터를 만들고 ArgoCD 로 차트를 밀어 넣은 뒤, Prometheus 와 Loki 가 거둔 설치 직후 상태를 인수인계 문서 뒤에 붙이는 형상입니다.
구축 담당자가 요건표와 서버 목록을 넣고 명령을 한 줄 치면 파드가 뜨고 인수인계 문서가 파일로 남습니다. 요건 12건과 서버 14건을 합친 26건에서 그대로 올라가는 병원과 막히는 병원이 갈립니다.
처리 건수가 비어 있는 요건과 병원 이름 표기가 흔들린 줄을 일부러 섞은 입력에서도 실행이 멈추지 않습니다. 서버 목록에 짝이 없는 병원은 미매핑으로 인수인계 문서 아래 확인 목록에 남습니다.
정답을 붙인 26건으로 막힘 판정 일치를 냅니다. 단, 정답도 데이터도 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
요건 12건을 한 번 판정하고 차트를 올리는 데 걸리는 시간을 실행 로그로 잽니다. 측정은 개발 장비의 실행 로그이고 표본은 연속 10회 실행입니다.
위 수치는 더미데이터 자가채점이며 실제 설치 성공률은 병원의 실 전산실과 실제 EMR 연동이 있어야 측정됩니다. 이 포트폴리오는 그 측정을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실측 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 처리 건수가 비어 있는 요건, 병원 표기가 흔들린 줄, 서버 목록에 짝이 없는 병원, 스토리지 클래스가 없는 서버를 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
파드가 떴다는 사실이 곧 연동이 된다는 뜻은 아닙니다. 병원 시스템과 실제로 주고받는 부분은 현장 연동 테스트에서만 확인되므로, 문서에는 올라간 항목과 아직 확인하지 않은 항목을 나눠 적습니다.
실제 도입에서는 병원마다 보안 심의와 반입 절차가 다르고 노트북 반입조차 막히는 곳이 있습니다. 여기서는 폐쇄망을 흉내 낸 로컬 클러스터를 대상으로 돌리는 데까지만 다루고, 실제 망 연결은 도입 단계 과제로 분리합니다.
이 포트폴리오는 폐쇄망 배포와 설정 자동화를 보여줍니다. 심전도 판독이나 의료 연동 표준의 도메인 깊이는 주장하지 않습니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
병원 담당자와 나눈 이야기를 구축 담당자가 정리한 설치 요건표입니다. 판정의 왼쪽 축이고, 하루 처리 건수를 아직 못 받은 줄과 병원 이름을 다르게 적은 줄이 섞여 있습니다.
| site_no | hospital_raw | hospital_id | emr_vendor_raw | network_zone | ecg_source_raw | daily_volume | tls_required | contact |
|---|---|---|---|---|---|---|---|---|
| SITE-041 | 서울성모병원 | HOSP-1001 | 아이티엠 | 폐쇄망 | GE MAC5500 | 620 | true | 정보팀 김수현 |
| SITE-042 | 서울 성모병원 | HOSP-1001 | 아이티엠 | 폐쇄망 | 필립스 PageWriter | 180 | true | 정보팀 김수현 |
| SITE-043 | 부산대학교병원 | HOSP-1002 | 이지케어텍 | 망분리 | GE MAC2000 | 940 | true | 전산실 박지훈 |
| SITE-044 | 대구파티마병원 | HOSP-1003 | 아이티엠 | 폐쇄망 | 쉴러 CARDIOVIT | — | true | 정보팀 이현주 |
| SITE-045 | 인천세종병원 | HOSP-1004 | 비트컴퓨터 | 클라우드 | GE MAC5500 | 310 | true | IT팀 최민석 |
아래 JSON Schema 에 정확히 맞는 병원 설치 요건 16건을 JSON 배열로 생성해 주세요. {"type":"object","properties":{"site_no":{"type":"string"},"hospital_raw":{"type":"string"},"hospital_id":{"type":["string","null"]},"emr_vendor_raw":{"type":"string"},"network_zone":{"type":"string","enum":["폐쇄망","망분리","클라우드"]},"ecg_source_raw":{"type":"string"},"daily_volume":{"type":["integer","null"],"minimum":0},"tls_required":{"type":"boolean"},"contact":{"type":"string"}},"required":["site_no","hospital_raw","emr_vendor_raw","network_zone","ecg_source_raw","tls_required","contact"]} 조건: site_no 는 SITE-0NN 형식으로 서로 다르게 쓰고, 한 병원이 여러 줄로 나올 수 있습니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: 같은 병원을 '서울성모병원' 과 '서울 성모병원' 처럼 다르게 적은 줄 2건, daily_volume 이 null 인 줄 1건, hospital_id 가 null 인 신규 병원 줄 1건, 서버 목록에 짝이 없을 병원 줄 1건. 형식을 깨뜨리는 시도(tls_required 칸에 'O' 입력, 건수 칸에 '하루 300~400' 처럼 구간 넣기, 열 이름을 영문으로 바꾼 CSV)는 데이터가 아니라 별도 3건의 파일로 만들어 읽기 계층을 시험합니다. JSON 배열만 출력해 주세요.{"type":"object","properties":{"site_no":{"type":"string"},"hospital_raw":{"type":"string"},"hospital_id":{"type":["string","null"]},"emr_vendor_raw":{"type":"string"},"network_zone":{"type":"string","enum":["폐쇄망","망분리","클라우드"]},"ecg_source_raw":{"type":"string"},"daily_volume":{"type":["integer","null"],"minimum":0},"tls_required":{"type":"boolean"},"contact":{"type":"string"}},"required":["site_no","hospital_raw","emr_vendor_raw","network_zone","ecg_source_raw","tls_required","contact"]}병원 전산실에서 받은 서버 목록입니다. 판정의 오른쪽 축이고, 가속기가 없는 서버와 스토리지 클래스를 못 받은 서버, 지원 범위 밖 Kubernetes 버전이 섞여 있습니다.
| node_no | hospital_raw | hostname_raw | cpu_cores | memory_gb | gpu_model_raw | k8s_version_raw | storage_class_raw |
|---|---|---|---|---|---|---|---|
| ND-1201 | 서울성모병원 | ecg-gpu01.hosp.local | 16 | 64 | NVIDIA A10 | 1.28 | ceph-rbd |
| ND-1202 | 서울성모병원 | ecg-api01.hosp.local | 8 | 32 | — | 1.28 | ceph-rbd |
| ND-1203 | 서울 성모병원 | ecg-api02.hosp.local | 8 | 32 | — | 1.28 | ceph-rbd |
| ND-1204 | 부산대학교병원 | ecg-gpu01.pnuh.local | 24 | 128 | NVIDIA L4 | 1.29 | longhorn |
| ND-1205 | 부산대학교병원 | ecg-api01.pnuh.local | 8 | 32 | — | 1.29 | longhorn |
아래 JSON Schema 에 정확히 맞는 병원 전산실 서버 18건을 JSON 배열로 생성해 주세요. {"type":"object","properties":{"node_no":{"type":"string"},"hospital_raw":{"type":"string"},"hostname_raw":{"type":"string"},"cpu_cores":{"type":"integer","minimum":1},"memory_gb":{"type":"integer","minimum":4},"gpu_model_raw":{"type":["string","null"]},"k8s_version_raw":{"type":"string"},"storage_class_raw":{"type":["string","null"]}},"required":["node_no","hospital_raw","hostname_raw","cpu_cores","memory_gb","k8s_version_raw"]} 조건: node_no 는 ND-12NN 형식으로 서로 다르게 쓰고, hospital_raw 는 요건표와 같은 병원 이름을 씁니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: gpu_model_raw 가 전부 null 인 병원 1곳, storage_class_raw 가 전부 null 인 병원 1곳, k8s_version_raw 가 1.24 인 병원 1곳, hospital_raw 표기가 요건표와 미세하게 다른 줄 1건. 형식을 깨뜨리는 시도(메모리 칸에 '32GB' 처럼 단위 붙이기, 버전을 'v1.28.3' 으로 적기)는 데이터가 아니라 별도 2건의 파일로 만들어 읽기 계층을 시험합니다. JSON 배열만 출력해 주세요.{"type":"object","properties":{"node_no":{"type":"string"},"hospital_raw":{"type":"string"},"hostname_raw":{"type":"string"},"cpu_cores":{"type":"integer","minimum":1},"memory_gb":{"type":"integer","minimum":4},"gpu_model_raw":{"type":["string","null"]},"k8s_version_raw":{"type":"string"},"storage_class_raw":{"type":["string","null"]}},"required":["node_no","hospital_raw","hostname_raw","cpu_cores","memory_gb","k8s_version_raw"]}| Python | 요건표와 서버 목록을 읽어 Helm 설정 값을 채우고 막힘을 가르는 판정 엔진을 씁니다 |
| Linux(Ubuntu) | 설치 명령과 로그 수집을 셸에서 그대로 이어 붙여 현장 노트북에서 돌게 만듭니다 |
| Docker | 분석 서버와 연동 게이트웨이 이미지를 사내 미러를 흉내 낸 레지스트리에 올려 외부 없이 받습니다 |
| Kubernetes | 병원 전산실을 대신하는 kind 클러스터에 파드를 띄우고 스케줄이 붙는지 확인합니다 |
| Helm | 병원마다 다른 값만 values 로 빼고 차트 한 벌로 모든 병원에 같은 구성을 올립니다 |
| ArgoCD | 차트 변경을 저장소 상태로 두고 클러스터에 밀어 넣어 현장에서 손으로 고치는 자리를 없앱니다 |
| Terraform | 클라우드 병원 건에서 클러스터와 스토리지를 코드로 만들어 같은 구성을 다시 세웁니다 |
| Prometheus | 설치 직후 파드 상태와 응답 지연을 거둬 인수인계 문서에 붙입니다 |
| Grafana | 거둔 지표를 병원별 한 장으로 묶어 운영 담당자에게 넘깁니다 |
| Loki | 설치 중 파드 로그를 모아 실패한 단계의 원문을 문서에 인용합니다 |
| PostgreSQL | 병원별 설치 이력과 채운 설정 값을 쌓아 다음 현장에서 다시 꺼냅니다 |
| REST API | 연동 게이트웨이가 병원 시스템에서 심전도 요청을 받는 창구이며, 버전 경로로 나눠 옛 연동을 깨뜨리지 않습니다 |
| HL7·FHIR | 결과를 병원 시스템에 돌려줄 때 쓰는 표준 이름을 설정 값에 적어 두고, 실제 항목 매핑은 병원별 연동 담당과 정합니다 |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 막힘 판정이 맞았는지 재는 러너와 클라우드 배포를 붙입니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 저장소 상태로 미는 배포와 설치 직후 상태 수집을 붙입니다.
요건표 한 줄이 어느 서버에 어떤 설정으로 올라가는지, 못 올라가면 무엇이 막았는지를 담습니다. 근거로 쓴 서버 번호를 그대로 실어 담당자가 병원 전산실에 근거를 대고 말합니다.
{"type":"object","properties":{"site_no":{"type":"string"},"hospital_name":{"type":["string","null"]},"blocker_code":{"type":"string","enum":["values_ready","gpu_absent","k8s_unsupported","storage_class_missing","nodes_unknown","volume_missing"]},"target_node_no":{"type":["string","null"]},"helm_values_note":{"type":["string","null"]},"summary":{"type":"string"},"evidence":{"type":"array","items":{"type":"object","properties":{"dataset":{"type":"string"},"field":{"type":"string"},"quote":{"type":"string"},"value":{"type":["number","string","null"]}},"required":["dataset","field"]}}},"required":["site_no","blocker_code","summary","evidence"]}[
{
"site_no": "SITE-041",
"hospital_name": "서울성모병원",
"blocker_code": "values_ready",
"target_node_no": "ND-1201",
"helm_values_note": "gpu.enabled true, replicas 2, storageClass ceph-rbd",
"summary": "가속기와 스토리지가 모두 있어 설정 그대로 올라갑니다.",
"evidence": [
{
"dataset": "deploy_nodes",
"field": "gpu_model_raw",
"quote": "NVIDIA A10",
"value": "NVIDIA A10"
},
{
"dataset": "deploy_nodes",
"field": "storage_class_raw",
"quote": "ceph-rbd",
"value": "ceph-rbd"
},
{
"dataset": "site_requirements",
"field": "daily_volume",
"value": 620
}
]
},
{
"site_no": "SITE-042",
"hospital_name": "서울성모병원",
"blocker_code": "values_ready",
"target_node_no": "ND-1203",
"helm_values_note": "gpu.enabled false, replicas 1, storageClass ceph-rbd",
"summary": "병원 이름을 띄어 적었을 뿐 같은 병원이라 서울성모병원 서버에 그대로 올라갑니다.",
"evidence": [
{
"dataset": "site_requirements",
"field": "hospital_raw",
"quote": "서울 성모병원",
"value": "서울 성모병원"
},
{
"dataset": "deploy_nodes",
"field": "hostname_raw",
"quote": "ecg-api02.hosp.local",
"value": "ecg-api02.hosp.local"
}
]
},
{
"site_no": "SITE-045",
"hospital_name": "인천세종병원",
"blocker_code": "storage_class_missing",
"target_node_no": "ND-1208",
"helm_values_note": null,
"summary": "인천세종병원 서버에 스토리지 클래스 이름이 없어 분석 결과를 쌓을 볼륨을 붙이지 못했습니다.",
"evidence": [
{
"dataset": "deploy_nodes",
"field": "storage_class_raw",
"value": null
},
{
"dataset": "deploy_nodes",
"field": "hostname_raw",
"quote": "ecg-cloud01.sejong.local",
"value": "ecg-cloud01.sejong.local"
}
]
},
{
"site_no": "SITE-046",
"hospital_name": "광주기독병원",
"blocker_code": "k8s_unsupported",
"target_node_no": "ND-1210",
"helm_values_note": null,
"summary": "광주기독병원 클러스터가 1.24 라 차트가 요구하는 버전보다 낮아 올리지 못했습니다.",
"evidence": [
{
"dataset": "deploy_nodes",
"field": "k8s_version_raw",
"quote": "1.24",
"value": "1.24"
},
{
"dataset": "deploy_nodes",
"field": "hostname_raw",
"quote": "ecg-app01.kch.local",
"value": "ecg-app01.kch.local"
}
]
},
{
"site_no": "SITE-049",
"hospital_name": "대구파티마병원",
"blocker_code": "gpu_absent",
"target_node_no": "ND-1206",
"helm_values_note": null,
"summary": "대구파티마병원 서버에 가속기가 한 대도 없어 분석 파드를 올리지 못했습니다.",
"evidence": [
{
"dataset": "deploy_nodes",
"field": "gpu_model_raw",
"value": null
},
{
"dataset": "deploy_nodes",
"field": "hostname_raw",
"quote": "ecg-app01.fatima.local",
"value": "ecg-app01.fatima.local"
},
{
"dataset": "site_requirements",
"field": "daily_volume",
"value": 380
}
]
},
{
"site_no": "SITE-044",
"hospital_name": "대구파티마병원",
"blocker_code": "volume_missing",
"target_node_no": null,
"helm_values_note": null,
"summary": "하루 처리 건수가 비어 있어 파드를 몇 개 띄울지 정하지 못했습니다. 정보팀 이현주 님에게 건수를 물어야 합니다.",
"evidence": [
{
"dataset": "site_requirements",
"field": "daily_volume",
"value": null
},
{
"dataset": "site_requirements",
"field": "contact",
"quote": "정보팀 이현주",
"value": "정보팀 이현주"
}
]
},
{
"site_no": "SITE-047",
"hospital_name": null,
"blocker_code": "nodes_unknown",
"target_node_no": null,
"helm_values_note": null,
"summary": "강원대학교병원은 서버 목록을 아직 받지 못해 어느 서버에 올릴지 정하지 못했습니다.",
"evidence": [
{
"dataset": "site_requirements",
"field": "hospital_raw",
"quote": "강원대학교병원",
"value": "강원대학교병원"
},
{
"dataset": "site_requirements",
"field": "hospital_id",
"value": null
}
]
}
][자동 검증 · 러너가 매 실행마다 겁니다] 1. blocker_code 값이 여섯 값 목록에 있는지 확인합니다. 2. site_no·blocker_code·summary·evidence 필수 필드가 모두 존재하는지 확인합니다. 3. blocker_code 가 values_ready 이면 target_node_no 필드가 존재하고 helm_values_note 도 채워져 있는지 확인합니다. 4. evidence 의 field 가 datasets 컬럼에 실재하는지 확인합니다. 5. evidence 의 quote 가 datasets 샘플의 원문 그대로인지 확인합니다. 6. cpu_cores 가 1 이상, memory_gb 가 4 이상 범위인지 확인합니다. 7. summary 가 존댓말 한 문장인지 확인합니다. [수동 검증 · 사람이 한 번 봅니다] 막힘 사유를 잘못 골랐는지, 인수인계 문서의 문장이 병원 운영 담당자가 그대로 읽을 만한지를 표본 열 건에서 눈으로 봅니다. [평가 러너 · 2주차] 정답을 붙인 26건으로 막힘 판정이 정답과 얼마나 겹치는지 재고, 올라갈 수 있는데 막힘으로 잘못 세운 병원이 몇 곳인지 따로 셉니다.