피트윈
경력근거: 측정 시스템 결과, 로봇 상태 정보, 센서와 회원 데이터를 연결합니다
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 측정 시스템 결과와 로봇 상태와 센서를 회원 데이터에 연결하는 일을 주요업무에 두고, 설계부터 현장 검증까지 한 사람이 주도한다고 적습니다. 이 포트폴리오는 그 연결이 끊기는 자리를 화면으로 만듭니다. 기기 로그와 회원 기록을 맞추는 규칙을 코드에 두어 현장에서 확인할 목록이 매일 나옵니다. 다만 이 포트폴리오는 결합 판정 규칙과 화면 구현을 보여줍니다. 헬스케어 측정 방법이나 의료기기 규제에 대한 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
웹 서비스와 안드로이드 기능을 개발하면서 측정 시스템 결과와 로봇 상태 정보, 센서와 회원 데이터를 연결한다고 주요업무에 적습니다. 기기에서 나온 값이 사람의 기록으로 이어지는 자리입니다.
요구사항과 아키텍처 설계부터 배포와 현장 검증과 장애 해결까지 제품 개발 전 과정을 주도한다고 적습니다. 화면만 만들고 끝나지 않는 자리입니다.
Codex 와 Claude Code 를 구현과 리팩터링과 테스트에 활용하되 운영에 반영되는 모든 변경 사항을 직접 검토한다고 못박습니다. 빠르게 만들되 책임은 사람이 지는 방식입니다.
운영 담당자가 하루치 측정 세션을 열면, 기기가 남긴 결과를 회원 기록과 맞춰 어느 회원의 것인지 정해지지 않은 세션을 사유별로 보여줍니다. 어느 회원 기록으로 갈지는 회원 코드 대조와 측정 완결성을 보는 규칙이 정합니다. 모델은 그 판정에 손대지 않고, 규칙이 고른 사유와 근거 값을 담당자가 읽는 한 문장으로 요약합니다.
저는 측정 기기가 놓인 센터의 운영을 맡고 있습니다. 회원이 측정을 마치고 앱에서 결과를 못 본다고 문의하면, 기기 로그와 회원 기록을 각각 열어 어디서 끊겼는지 찾습니다. 회원 코드가 잘못 찍힌 건지 측정이 중간에 멈춘 건지 매번 다릅니다.
결과가 안 보이는 문의가 반복되는 이유는 기기가 고장이라서가 아니라 회원 기록에 남지 않은 세션이 어디에도 모이지 않기 때문입니다. 회원 코드와 측정 완결성을 같이 보고 사유별로 모으면, 현장에서 다시 받을 건과 코드에서 고칠 건이 갈립니다.
운영 담당자가 쓰는 화면 하나가 완결됩니다. 측정 세션을 회원 기록과 맞춰 어느 회원의 것인지 정해지지 않은 건을 사유별로 보여주고, 판정 근거가 된 값을 같이 냅니다. 기기에서 직접 받아 오는 연결은 세션 내보내기 파일을 올리는 것으로 대신하고 3주차로 미룹니다.
재측정 대상으로 표시한 세션 140건이 다음 날 회원 기록에 남았는지 맞춰 보고, 아직 남지 않은 사유를 유형별로 냅니다. 현장 안내가 필요한 유형이 그 표에서 드러납니다.
측정이 끝나는 대로 결과를 받아 판정이 돌고, 회원 기록으로 이어지지 못한 세션이 생기면 현장 담당자에게 알립니다. 회원이 문의하기 전에 먼저 확인합니다.
운영 담당자가 하루치 세션을 열면 회원 기록과의 결합 결과가 사유별로 갈리고, 판정 근거가 같이 보입니다.
회원 코드가 비거나 측정이 중간에 멈추거나 같은 세션이 두 번 올라온 입력 30종을 올려도 화면이 죽지 않고, 판정할 수 없는 세션은 미판정으로 남습니다.
재측정 대상 140건이 다음 날 회원 기록에 남았는지 맞춰 봅니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 더미데이터 자가채점이며 실제 결합 성공률은 피트윈의 실 측정 로그와 회원 데이터가 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 회원 코드 결측·측정 중단·같은 세션 중복 적재를 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
값이 범위를 벗어났다는 사실이 곧 기기 고장을 뜻하지는 않습니다. 자세가 흐트러진 측정도 같은 모양으로 남으므로 판정을 결론으로 읽지 않게 근거 값을 항상 같이 보여줍니다.
실제 도입에서는 측정 항목마다 정상 범위와 필수 단계가 달라 기준이 하나로 서지 않습니다. 항목이 늘어나면 규칙을 코드에서 빼 설정으로 옮겨야 하며, 그것은 도입 단계 과제로 분리합니다.
측정값을 건강 상태로 해석하는 일은 의료 판단이라 이 포트폴리오의 범위 밖입니다. 여기서는 결과가 회원 기록에 남았는지까지만 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
기기가 남긴 하루치 측정 세션입니다. 판정의 입력이며 회원 코드 결측과 측정 중단, 같은 세션의 중복 적재를 의도적으로 포함합니다.
| session_no | measured_at | device_code | member_code_raw | metric | value | unit | session_state | robot_state | note |
|---|---|---|---|---|---|---|---|---|---|
| MS-0818-001 | 2026-08-18T09:05:00 | DV-01 | MB-3001 | grip | 34.2 | kg | done | online | — |
| MS-0818-002 | 2026-08-18T09:20:00 | DV-01 | MB-3002 | balance | 71 | score | done | online | — |
| MS-0818-003 | 2026-08-18T09:41:00 | DV-02 | — | gait | 88 | score | done | online | 회원 인식 실패 |
| MS-0818-004 | 2026-08-18T10:02:00 | DV-02 | MB-3003 | body_fat | — | — | aborted | online | 중간에 내려옴 |
| MS-0818-005 | 2026-08-18T10:30:00 | DV-03 | MB-3004 | grip | 0 | kg | done | offline | 기기 통신 끊김 상태 |
아래 JSON Schema 에 맞는 측정 세션 행을 더 만들어 주세요. {"type":"object","properties":{"session_no":{"type":"string"},"measured_at":{"type":"string"},"device_code":{"type":"string"},"member_code_raw":{"type":["string","null"]},"metric":{"type":"string","enum":["grip","balance","gait","body_fat"]},"value":{"type":["number","null"]},"unit":{"type":["string","null"],"enum":["kg","score","pct",null]},"session_state":{"type":"string","enum":["done","aborted","unknown"]},"robot_state":{"type":["string","null"],"enum":["online","offline",null]},"note":{"type":["string","null"]}},"required":["session_no","measured_at","device_code","metric","session_state"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) member_code_raw 나 value 가 null 인 행, (2) value 가 0 이거나 정상 범위를 크게 벗어난 행, (3) 같은 session_no 가 두 번 적재된 행, (4) member_code_raw 의 구분자가 흔들리거나 회원 목록에 없는 행. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"session_no":{"type":"string"},"measured_at":{"type":"string"},"device_code":{"type":"string"},"member_code_raw":{"type":["string","null"]},"metric":{"type":"string","enum":["grip","balance","gait","body_fat"]},"value":{"type":["number","null"]},"unit":{"type":["string","null"],"enum":["kg","score","pct",null]},"session_state":{"type":"string","enum":["done","aborted","unknown"]},"robot_state":{"type":["string","null"],"enum":["online","offline",null]},"note":{"type":["string","null"]}},"required":["session_no","measured_at","device_code","metric","session_state"]}| TypeScript | 판정 규칙과 화면을 함께 쓰는 언어입니다. 결합 규칙을 한 곳에 두고 러너와 화면이 같은 코드를 씁니다. |
| Python | 센서 값의 범위 검사와 집계를 맡습니다. |
| AWS | 측정 결과 접수와 판정 실행과 배포를 맡습니다. |
| Kotlin | 안드로이드 기기에서 결과를 보내는 형식을 그대로 받도록 접수 규격을 맞춥니다. |
| ROS | 3주차에 로봇 상태 정보를 함께 받아 판정 근거로 씁니다. |
| LLM | 판정이 끝난 뒤 규칙이 고른 사유와 근거 값을 담당자가 읽는 한 문장으로 요약합니다. 어느 회원 기록으로 갈지는 규칙이 정하고 모델은 그 판정에 손대지 않습니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 재측정 표시가 다음 날 해소됐는지 확인하는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 기기 결과를 바로 받아 확인하고 알리는 단계로 확장합니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
link_code사유 라벨 · session_no + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
linked회원 기록 연결member_unmapped회원 미매핑session_aborted측정 중단value_out_of_range값 범위 이탈duplicate_session세션 중복state_unknown상태 미확인측정 세션 한 건의 결합 판정입니다. 사유 코드와 근거가 된 값을 함께 남깁니다.
{"type":"object","properties":{"result_id":{"type":"string"},"session_no":{"type":"string"},"member_code":{"type":["string","null"]},"link_code":{"type":"string","enum":["linked","member_unmapped","session_aborted","value_out_of_range","duplicate_session","state_unknown"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{"type":["string","number","null"]}},"required":["field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["result_id","session_no","link_code","evidence","summary"]}[
{
"result_id": "lk_0001",
"session_no": "MS-0818-001",
"member_code": "MB-3001",
"link_code": "linked",
"evidence": [
{
"field": "member_code_raw",
"agg": null,
"value": "MB-3001"
},
{
"field": "value",
"agg": null,
"value": 34.2
}
],
"summary": "회원 코드가 그대로 읽혀 결과가 회원 기록에 남았고 측정값도 정상 범위입니다.",
"hedge": null
},
{
"result_id": "lk_0002",
"session_no": "MS-0818-012",
"member_code": "MB-3010",
"link_code": "linked",
"evidence": [
{
"field": "session_state",
"agg": null,
"value": "done"
},
{
"field": "value",
"agg": null,
"value": 22.4
}
],
"summary": "측정이 끝까지 진행돼 결과가 회원 기록으로 그대로 이어졌습니다.",
"hedge": null
},
{
"result_id": "lk_0003",
"session_no": "MS-0818-003",
"member_code": null,
"link_code": "member_unmapped",
"evidence": [
{
"field": "member_code_raw",
"agg": null,
"value": null
},
{
"field": "device_code",
"agg": null,
"value": "DV-02"
}
],
"summary": "회원 인식이 안 돼 어느 회원의 기록으로 보낼지 정하지 못합니다.",
"hedge": null
},
{
"result_id": "lk_0004",
"session_no": "MS-0818-008",
"member_code": null,
"link_code": "member_unmapped",
"evidence": [
{
"field": "member_code_raw",
"agg": null,
"value": "MB-9999"
},
{
"field": "note",
"agg": null,
"value": "회원 목록에 없는 코드"
}
],
"summary": "회원 목록에 없는 코드가 찍혀 맞는 기록을 찾지 못했습니다.",
"hedge": null
},
{
"result_id": "lk_0005",
"session_no": "MS-0818-004",
"member_code": "MB-3003",
"link_code": "session_aborted",
"evidence": [
{
"field": "session_state",
"agg": null,
"value": "aborted"
},
{
"field": "value",
"agg": null,
"value": null
}
],
"summary": "측정이 중간에 멈춰 남길 값이 없습니다.",
"hedge": null
},
{
"result_id": "lk_0006",
"session_no": "MS-0818-007",
"member_code": "MB-3006",
"link_code": "value_out_of_range",
"evidence": [
{
"field": "value",
"agg": null,
"value": 92
},
{
"field": "metric",
"agg": null,
"value": "body_fat"
}
],
"summary": "체지방률이 92% 로 나와 값 자체를 믿을 수 없어 기록에 넣지 않습니다.",
"hedge": "합성 기준 판정이라 실제로는 단위가 잘못 들어온 경우일 수 있습니다."
},
{
"result_id": "lk_0007",
"session_no": "MS-0818-009",
"member_code": "MB-3007",
"link_code": "duplicate_session",
"evidence": [
{
"field": "session_no",
"agg": "count",
"value": 2
},
{
"field": "value",
"agg": null,
"value": 28.7
}
],
"summary": "같은 세션이 두 번 올라와 한 건으로 묶어 기록에 남깁니다.",
"hedge": null
},
{
"result_id": "lk_0008",
"session_no": "MS-0818-010",
"member_code": "MB-3008",
"link_code": "state_unknown",
"evidence": [
{
"field": "session_state",
"agg": null,
"value": "unknown"
},
{
"field": "robot_state",
"agg": null,
"value": null
}
],
"summary": "종료 신호가 오지 않아 측정이 끝났는지 확인하지 못합니다.",
"hedge": null
},
{
"result_id": "lk_0009",
"session_no": "MS-0818-005",
"member_code": "MB-3004",
"link_code": "value_out_of_range",
"evidence": [
{
"field": "value",
"agg": null,
"value": 0
},
{
"field": "robot_state",
"agg": null,
"value": "offline"
}
],
"summary": "기기가 끊긴 상태에서 0 이 기록돼 측정으로 세지 않습니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. link_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. result_id·session_no·link_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. metric 이 body_fat 이면 value 가 0 이상 60 이하인지, grip 이면 0 이상 100 이하인지 확인합니다. 4. evidence 의 field 이름이 측정 세션에 실재하는 컬럼인지 대조합니다. 5. agg 가 없는 evidence 의 value 가 그 session_no 행의 값과 같은지 확인합니다. 6. session_state 가 aborted 이면 link_code 는 linked 가 아닌지 확인합니다. 세션에는 끝난 건과 중단된 건이 모두 있습니다. 7. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 재측정 대상으로 표시한 세션 140건이 다음 날 회원 기록에 남았는지 비교해 해소율과 남은 사유를 냅니다. [수동 검증 · 사람이 봅니다] 1. 값이 범위를 벗어난 세션이 기기 문제인지 자세 문제인지 표본 20건을 현장 기록과 눈으로 확인합니다. 2. 종료 신호가 없는 세션을 측정으로 셀지는 운영 기준을 따라 담당자가 정합니다.