실천
경력근거: 설치·업데이트의 안정성, 오프라인 동작, 로컬 기기 연동이 핵심인 제품입니다
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고가 제품을 소개하며 '설치·업데이트의 안정성, 오프라인 동작, 로컬 기기 연동이 핵심인 제품입니다' 라고 못박았고, 주요업무에는 '메인/렌더러 프로세스 분리, IPC 통신, 로컬 데이터 저장소' 설계를 적었습니다. 오프라인 동작이 핵심이면 연결이 돌아온 순간 무엇을 보낼지가 곧 제품 품질이고, 이 포트폴리오는 그 판단을 종류별 규칙표로 옮깁니다. '외부 공공기관(심평원 등) 연동 구조를 설계 및 고도화합니다' 도 늦게 보낸 요청이 무의미해지는 성질을 그대로 가리킵니다. 이 포트폴리오가 보여주는 것은 재전송 기준을 데이터로 두고 밀린 목록과 맞춰 보낼 것을 가르는 기술이며, 의약품 안전 판단이나 청구 규정 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
'설치·업데이트의 안정성, 오프라인 동작, 로컬 기기 연동이 핵심인 제품입니다'. 오프라인 뒤 복구가 그 핵심이 부딪히는 자리입니다.
'메인/렌더러 프로세스 분리, IPC 통신, 로컬 데이터 저장소, 자동 업데이트 구조를 설계'. 밀린 요청이 사는 곳이 그 로컬 저장소입니다.
'외부 공공기관(심평원 등) 연동 구조를 설계 및 고도화합니다'. 늦게 보낸 요청이 무의미해지는 것도 그 연동의 성질입니다.
연결이 끊긴 동안 현장 PC 에 쌓인 요청 중 다시 보낼 것과 접을 것을 가릅니다. 요청 종류별 규칙에 유효 시간과 재시도 상한, 순서를 지켜야 하는지를 두고 밀린 목록과 맞춰, 다 맞으면 다시 보냄으로 넘기고 그렇지 않으면 유효 시간 지남·같은 건 중복·앞 순번 막힘·시도 소진 중 어느 쪽인지 사유로 남깁니다. 규칙에 없는 종류는 미선언으로 남겨 어떻게 할지 먼저 정하게 합니다. 밀린 목록은 실 현장 자료 대신 지원자가 클라이언트를 만들어 연결을 끊었다 붙이며 만든 것을 가정합니다.
약국과 병원 데스크 PC 에 깔리는 클라이언트를 만드는 개발자입니다. 인터넷이 끊겼다 붙은 뒤 밀린 요청이 한꺼번에 나가 같은 건이 두 번 올라가거나 이미 지난 점검이 뒤늦게 도착하는 것을 현장 연락으로 알게 되던 사람입니다.
요청 종류마다 유효 시간과 재시도 상한, 순서 보장 여부를 규칙으로 두고 밀린 목록과 맞추면, 다시 보낼 것과 접을 것이 복구 시점에 갈리고 접는 이유가 근거로 남는다는 가정입니다. 규칙에 없는 종류는 미선언으로 빠지므로 아직 정하지 않은 요청이 그대로 드러납니다.
밀린 목록과 종류별 규칙, 복구 시각을 받아 항목마다 재전송·보류를 판정하고, 보류면 유효 시간 지남·중복·앞 순번 막힘·시도 소진·미선언 중 어느 쪽인지 붙여 단말별로 묶은 복구 계획을 터미널과 Markdown 파일로 내는 CLI 가 도는 완결품입니다.
작은 클라이언트를 만들어 연결을 끊은 채로 요청 400건을 쌓고, 복구 시점의 목록을 뽑아 손으로 붙인 정답 판정과 대조해 일치율을 냅니다.
재전송 대상만 순서대로 보내고 접은 항목은 사유와 함께 로컬에 남기며, 보낸 결과를 다시 읽어 남은 목록이 줄어드는지 확인하는 단계입니다.
현장 담당자가 밀린 목록과 규칙표, 복구 시각을 넣으면 항목마다 재전송 여부와 보류 사유가 단말별로 묶여 나오는 과정을 시연합니다.
직접 만든 클라이언트로 쌓은 항목 400건과 손으로 붙인 정답 판정을 대조해 일치율을 냅니다. 단, 정답을 작성자가 붙인 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
시도 횟수가 비었거나 종류 표기가 흔들리거나 규칙에 없는 종류가 섞인 입력 30종을 넣어도 출력 형식이 안 깨지고 사유별로 빠지는 비율을 보여줍니다.
유효 시간과 재시도 상한을 바꿨을 때 옛 판정이 어떻게 뒤집히는지 회귀로 확인합니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 작성자가 만든 목록의 자가채점이며, 실제 복구 정확도는 실천의 실 현장 단말과 실 연동 규격이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 작성자가 만든 목록의 자가채점이라 성능 증명이 아니므로, 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 붙였으니 잘 맞는 게 당연하므로, 시도 횟수 결측과 같은 키 중복, 규칙에 없는 종류를 일부러 섞어 쉬운 항목만 푸는 함정을 피합니다.
이 포트폴리오가 보여주는 것은 재전송 기준을 데이터로 두고 밀린 목록과 맞춰 보낼 것을 가르는 기술이며, 의약품 안전 판단이나 청구 규정 전문성은 주장하지 않습니다. 판정은 시각 계산과 횟수 비교가 하고 LLM 은 담당자가 읽을 사유 문장만 맡습니다.
같은 업무 키가 두 번이라고 늘 중복은 아니어서, 중복으로 걸린 항목은 쌓인 시각을 함께 실어 담당자가 보게 둡니다.
실제 도입 때는 요청 종류와 유효 시간이 연동 규격에 따라 달라지므로, 규칙표를 새로 채우는 일을 도입 단계 과제로 둡니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
연결이 끊긴 동안 현장 PC 로컬 저장소에 쌓인 요청 목록입니다. 항목 번호와 단말 번호, 요청 종류 표기, 업무 키, 단말 안 순번, 쌓인 시각, 시도 횟수, 마지막 결과를 한 행에 둡니다. 종류 표기는 만든 화면에 따라 'dur_check'와 'DUR CHECK'처럼 흔들립니다. 시도 횟수가 빈 행, 같은 업무 키가 두 번 쌓인 행, 유효 시간을 갓 넘긴 행, 규칙에 없는 종류를 일부러 섞어 두어 사유가 갈리게 둡니다.
| item_no | client_no | kind_raw | payload_key | client_seq | queued_at | attempt_count | last_result_raw |
|---|---|---|---|---|---|---|---|
| QI-001 | PH-001 | dur_check | RX-20260822-0011 | 1 | 2026-08-22T09:10:00 | 0 | — |
| QI-002 | PH-001 | dur_check | RX-20260822-0012 | 2 | 2026-08-22T09:11:00 | 1 | TIMEOUT |
| QI-003 | PH-001 | DUR CHECK | RX-20260822-0013 | 3 | 2026-08-22T09:12:00 | 0 | — |
| QI-004 | PH-002 | dur_check | RX-20260822-0104 | 1 | 2026-08-22T06:40:00 | 2 | TIMEOUT |
| QI-005 | PH-002 | dispense_log | DP-20260822-0104 | 2 | 2026-08-22T06:41:00 | 0 | — |
아래 JSON Schema 를 그대로 만족하는 밀린 요청 행을 400개 더 만들어 주세요. 단말을 열댓 대 두고 종류를 대여섯 가지로 섞으며, 종류 표기는 몇 행에서 띄어쓰기와 대문자로 흔들리게 둡니다. 단말마다 순번을 1부터 늘리고, 2할은 시도 횟수나 순번을 비우며, 몇 행은 같은 업무 키를 두 번 넣고 몇 행은 쌓인 시각을 세 시간 전으로 두세요. 규칙표에 없는 종류도 몇 행 넣습니다. 판정 결과는 넣지 마세요.
{"type": "object", "properties": {"item_no": {"type": "string"}, "client_no": {"type": "string"}, "kind_raw": {"type": "string"}, "payload_key": {"type": "string"}, "client_seq": {"type": ["integer", "null"]}, "queued_at": {"type": "string"}, "attempt_count": {"type": ["integer", "null"]}, "last_result_raw": {"type": ["string", "null"]}}, "required": ["item_no", "client_no", "kind_raw", "payload_key", "client_seq", "queued_at", "attempt_count", "last_result_raw"]}{"type": "object", "properties": {"item_no": {"type": "string"}, "client_no": {"type": "string"}, "kind_raw": {"type": "string"}, "payload_key": {"type": "string"}, "client_seq": {"type": ["integer", "null"]}, "queued_at": {"type": "string"}, "attempt_count": {"type": ["integer", "null"]}, "last_result_raw": {"type": ["string", "null"]}}, "required": ["item_no", "client_no", "kind_raw", "payload_key", "client_seq", "queued_at", "attempt_count", "last_result_raw"]}요청 종류별 재전송 규칙표입니다. 규칙 번호와 종류, 유효 시간, 재시도 상한, 순서 보장 여부, 담당을 한 행에 둡니다. 종류마다 유효 시간이 크게 달라 같은 시각에 쌓여도 어떤 요청은 살아 있고 어떤 요청은 접힙니다. 밀린 목록에 나오는 종류 중 일부는 일부러 넣지 않아 미선언이 도출되게 둡니다.
| policy_no | kind_raw | valid_minutes | max_attempt | keep_order | owner_name |
|---|---|---|---|---|---|
| RS-01 | dur_check | 120 | 3 | true | 임상서비스 |
| RS-02 | dispense_log | 1,440 | 5 | true | 임상서비스 |
| RS-03 | stock_sync | 720 | 5 | false | 운영 |
| RS-04 | receipt_print | 30 | 2 | false | 운영 |
| RS-05 | claim_submit | 2,880 | 3 | true | 청구 |
아래 JSON Schema 를 그대로 만족하는 재전송 규칙 행을 30개 더 만들어 주세요. 종류마다 유효 시간을 30분에서 사흘 사이로 다르게 두고 재시도 상한은 2에서 5 사이로 둡니다. 순서를 지켜야 하는 종류와 아닌 종류를 섞으세요. 밀린 목록에 나오는 종류 중 일부는 일부러 넣지 마세요 — 미선언이 있어야 규칙표의 구멍이 드러납니다. 판정 결과는 넣지 마세요.
{"type": "object", "properties": {"policy_no": {"type": "string"}, "kind_raw": {"type": "string"}, "valid_minutes": {"type": "integer"}, "max_attempt": {"type": "integer"}, "keep_order": {"type": "boolean"}, "owner_name": {"type": "string"}}, "required": ["policy_no", "kind_raw", "valid_minutes", "max_attempt", "keep_order", "owner_name"]}{"type": "object", "properties": {"policy_no": {"type": "string"}, "kind_raw": {"type": "string"}, "valid_minutes": {"type": "integer"}, "max_attempt": {"type": "integer"}, "keep_order": {"type": "boolean"}, "owner_name": {"type": "string"}}, "required": ["policy_no", "kind_raw", "valid_minutes", "max_attempt", "keep_order", "owner_name"]}| TypeScript | 밀린 목록과 규칙을 맞춰 유효 시간과 순서를 견주는 판정기를 맡습니다. |
| Electron | 로컬 저장소에 요청을 쌓았다 복구 시 목록을 뽑는 클라이언트를 만듭니다. |
| SQLite | 현장 PC 안에서 밀린 요청을 담는 로컬 저장소로 씁니다. |
| Markdown | 단말별 복구 계획과 접은 항목 목록을 파일로 냅니다. |
✗ fail → 미선언이 절반을 넘으면 규칙표를 먼저 채웁니다.
✓ pass → 2주차 실제 클라이언트 연결로 넘어갑니다.
✗ fail → 어긋난 종류를 모아 유효 시간과 순서 규칙을 다시 봅니다.
✓ pass → 3주차 재전송기 연결로 넘어갑니다.
밀린 항목 하나와 재전송 규칙이 만나는 자리마다 다시 보낼지 정합니다. 접으면 유효 시간 지남·중복·앞 순번 막힘·시도 소진·미선언 중 어느 쪽인지 싣습니다. 복구 계획은 이 사유 코드로 묶습니다.
{"type": "object", "properties": {"verdict_no": {"type": "string"}, "item_no": {"type": "string"}, "client_no": {"type": "string"}, "kind_raw": {"type": "string"}, "policy_no": {"type": ["string", "null"]}, "resend_code": {"type": "string", "enum": ["resend", "expired", "duplicate_key", "order_blocked", "attempt_exhausted", "kind_undeclared"]}, "waited_minutes": {"type": ["integer", "null"], "description": "복구 시각에서 queued_at 을 뺀 분입니다. 복구 시각은 실행 인자로 받습니다."}, "valid_minutes": {"type": ["integer", "null"]}, "owner_name": {"type": ["string", "null"]}, "evidence": {"type": "array", "items": {"type": "object", "properties": {"field": {"type": "string"}, "agg": {"type": ["string", "null"]}, "value": {}}, "required": ["field", "value"]}}, "summary": {"type": "string"}, "hedge": {"type": ["string", "null"]}}, "required": ["verdict_no", "item_no", "client_no", "kind_raw", "resend_code", "evidence", "summary"]}[
{
"verdict_no": "RQ-001",
"item_no": "QI-001",
"client_no": "PH-001",
"kind_raw": "dur_check",
"policy_no": "RS-01",
"resend_code": "resend",
"waited_minutes": 50,
"valid_minutes": 120,
"owner_name": "임상서비스",
"evidence": [
{
"field": "attempt_count",
"agg": null,
"value": 0
},
{
"field": "max_attempt",
"agg": null,
"value": 3
}
],
"summary": "유효 시간 안이고 시도 횟수도 남아 순서대로 먼저 보냅니다.",
"hedge": null
},
{
"verdict_no": "RQ-002",
"item_no": "QI-003",
"client_no": "PH-001",
"kind_raw": "DUR CHECK",
"policy_no": "RS-01",
"resend_code": "resend",
"waited_minutes": 48,
"valid_minutes": 120,
"owner_name": "임상서비스",
"evidence": [
{
"field": "kind_raw",
"agg": null,
"value": "DUR CHECK"
},
{
"field": "client_seq",
"agg": null,
"value": 3
}
],
"summary": "종류를 띄어쓰기로 적어 왔으나 접으면 같은 값이라 같은 규칙을 씁니다.",
"hedge": null
},
{
"verdict_no": "RQ-003",
"item_no": "QI-004",
"client_no": "PH-002",
"kind_raw": "dur_check",
"policy_no": "RS-01",
"resend_code": "expired",
"waited_minutes": 200,
"valid_minutes": 120,
"owner_name": "임상서비스",
"evidence": [
{
"field": "queued_at",
"agg": null,
"value": "2026-08-22T06:40:00"
},
{
"field": "valid_minutes",
"agg": null,
"value": 120
}
],
"summary": "두 시간이 넘게 묵어 지금 보내면 이미 조제가 끝난 처방을 점검하게 됩니다.",
"hedge": null
},
{
"verdict_no": "RQ-004",
"item_no": "QI-005",
"client_no": "PH-002",
"kind_raw": "dispense_log",
"policy_no": "RS-02",
"resend_code": "order_blocked",
"waited_minutes": 199,
"valid_minutes": 1440,
"owner_name": "임상서비스",
"evidence": [
{
"field": "client_seq",
"agg": null,
"value": 2
},
{
"field": "keep_order",
"agg": null,
"value": true
}
],
"summary": "앞 순번이 만료로 빠져 순서를 지키는 종류라 뒤 항목도 함께 멈춥니다.",
"hedge": null
},
{
"verdict_no": "RQ-005",
"item_no": "QI-006",
"client_no": "PH-002",
"kind_raw": "dispense_log",
"policy_no": "RS-02",
"resend_code": "duplicate_key",
"waited_minutes": 199,
"valid_minutes": 1440,
"owner_name": "임상서비스",
"evidence": [
{
"field": "payload_key",
"agg": "count",
"value": 2
},
{
"field": "client_seq",
"agg": null,
"value": 3
}
],
"summary": "같은 조제 건이 두 번 쌓여 있어 한 번만 보내야 합니다.",
"hedge": null
},
{
"verdict_no": "RQ-006",
"item_no": "QI-007",
"client_no": "PH-003",
"kind_raw": "dur_check",
"policy_no": "RS-01",
"resend_code": "attempt_exhausted",
"waited_minutes": 40,
"valid_minutes": 120,
"owner_name": "임상서비스",
"evidence": [
{
"field": "attempt_count",
"agg": null,
"value": 5
},
{
"field": "max_attempt",
"agg": null,
"value": 3
}
],
"summary": "다섯 번 실패해 상한 세 번을 넘겨 자동으로 다시 보내지 않습니다.",
"hedge": "같은 오류가 이어지면 서버 쪽 문제일 수 있어 담당자가 봅니다."
},
{
"verdict_no": "RQ-007",
"item_no": "QI-009",
"client_no": "PH-004",
"kind_raw": "device_ping",
"policy_no": null,
"resend_code": "kind_undeclared",
"waited_minutes": 35,
"valid_minutes": null,
"owner_name": null,
"evidence": [
{
"field": "kind_raw",
"agg": null,
"value": "device_ping"
},
{
"field": "policy_no",
"agg": "count",
"value": 12
}
],
"summary": "규칙 열두 행에 없는 종류라 다시 보낼지 먼저 정해야 합니다.",
"hedge": null
},
{
"verdict_no": "RQ-008",
"item_no": "QI-011",
"client_no": "PH-005",
"kind_raw": "receipt_print",
"policy_no": "RS-04",
"resend_code": "expired",
"waited_minutes": 30,
"valid_minutes": 30,
"owner_name": "운영",
"evidence": [
{
"field": "valid_minutes",
"agg": null,
"value": 30
},
{
"field": "queued_at",
"agg": null,
"value": "2026-08-22T09:30:00"
}
],
"summary": "영수증 출력은 유효 시간이 딱 차서 다시 보내지 않고 접습니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. resend_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. verdict_no·item_no·client_no·kind_raw·resend_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. item_no 가 밀린 목록에 실재하는 번호인지 대조합니다. 4. policy_no 가 있으면 규칙표에 실재하는 행 번호인지 대조합니다. 5. policy_no 가 있으면 valid_minutes·owner_name 이 그 규칙 행의 값과 같은지 대조합니다. 6. kind_raw 를 대소문자와 띄어쓰기를 접어 규칙표와 맞추고, 맞는 행이 없으면 resend_code 가 kind_undeclared 인지 확인합니다. 7. waited_minutes 를 실행 인자로 받은 복구 시각에서 queued_at 을 빼 분으로 다시 계산합니다. 8. waited_minutes 가 valid_minutes 이상이면 resend_code 가 expired 인지 다시 계산합니다. 9. attempt_count 가 max_attempt 이상이면 resend_code 가 attempt_exhausted 인지 다시 확인합니다. 10. 같은 client_no 안에 같은 payload_key 가 둘이면 뒤 순번의 resend_code 가 duplicate_key 인지 다시 셉니다. 11. keep_order 가 참이고 같은 단말의 앞 순번이 재전송에서 빠졌으면 resend_code 가 order_blocked 인지 확인합니다. 12. resend_code 가 resend 이면 위 검사를 모두 통과하는지 다시 확인합니다. 13. evidence 의 field 이름이 밀린 목록과 규칙표에 실재하는 컬럼인지 대조합니다. 14. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 행 값과 같은지 확인합니다. 15. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 연결을 끊은 채로 쌓은 항목 400건에 손으로 붙인 정답 판정과 대조해 일치율과 사유 코드별 분포를 내고, 미선언이 몰리는 종류를 목록으로 냅니다. [수동 검증 · 사람이 봅니다] 1. 규칙에 없는 요청 종류를 어떻게 할지 담당 조직이 정합니다. 2. 시도를 다 쓴 항목을 손으로 다시 보낼지 담당자가 정합니다.
근거: 설치·업데이트의 안정성, 오프라인 동작, 로컬 기기 연동이 핵심인 제품입니다