림피드
경력근거: 데이터 기반 맞춤형 영양 설계 로직 을 처리하는 백엔드 API 설계 및 구축
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 킨포라 모바일 앱과 웹 프론트엔드, 그리고 맞춤형 영양 설계 로직을 처리하는 백엔드 API 설계를 한 사람의 업무로 묶고, Claude Code 와 Cursor 로 빠르게 프로토타이핑하는 방식을 전제로 둡니다. 이 포트폴리오는 그 구조를 그대로 씁니다. 영양 계산을 백엔드 규칙으로 두고 앱은 기록과 결과만 담당하며, 사료 성분표가 바뀌어도 계산이 흔들리지 않게 데이터를 분리합니다. 다만 이 포트폴리오는 영양 계산 규칙과 앱 화면 구현을 보여줍니다. 수의영양학 처방이나 질병별 급여 기준에 대한 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
공고는 킨포라 모바일 앱과 웹 프론트엔드 개발, 맞춤형 영양 설계 로직을 처리하는 백엔드 API 설계를 한 자리의 업무로 묶습니다. 화면과 계산이 나뉘지 않아 영양 계산의 모양을 정하는 판단이 그대로 앱 화면이 됩니다.
Claude Code 와 Cursor 로 빠르게 프로토타이핑하고 서비스를 고도화한다고 주요업무에 적고, 자격요건에서도 그 도구를 능숙하게 쓰는 사람을 찾습니다. 손으로 다 짜는 속도가 아니라 만들고 고치는 회전 속도를 봅니다.
일상 관찰 데이터를 분석해 개체별 맞춤형 영양 레시피를 설계하는 흐름을 회사 소개의 중심에 둡니다. 기록이 쌓이는 것으로 끝나지 않고 급여 판단으로 이어져야 하는 제품입니다.
보호자가 오늘 먹인 사료와 간식을 앱에 적으면, 체중과 활동량으로 잡은 하루 목표와 비교해 열량과 단백질이 얼마나 모자라거나 넘쳤는지 보여줍니다. 목표 대비 차이는 사료 성분표를 쓰는 계산 규칙이 정하고, 모델은 어느 영양소가 왜 모자랐는지 보호자가 읽을 한 줄로 요약합니다.
저는 일곱 살 말티즈를 키웁니다. 사료를 바꾼 뒤로 포장에 적힌 급여량을 그대로 줬는데 살이 붙었습니다. 간식까지 더하면 하루에 얼마를 먹은 셈인지 계산이 안 되고, 병원에서 단백질을 늘리라고 해도 지금 사료로 얼마나 되는지 모르겠습니다.
보호자가 급여를 못 맞추는 이유는 성의가 없어서가 아니라 사료와 간식이 각각 다른 단위로 적혀 있어 하루 합계가 안 잡히기 때문입니다. 먹인 것을 그대로 적게 하고 성분표로 합계를 내 목표와의 차이만 보여주면, 다음 끼니에서 무엇을 줄일지가 정해집니다.
보호자가 쓰는 앱 화면 하나가 완결됩니다. 사료와 간식을 적으면 성분표로 하루 합계를 내고 체중 기준 목표와의 차이를 보여주며, 성분을 모르는 항목은 합계에서 빼고 표시로 남깁니다. 사진으로 사료를 알아보는 기능은 목록에서 고르는 것으로 대신하고 3주차로 미룹니다.
하루 판정을 일주일로 모아 열량과 단백질의 목표 대비 추세를 냅니다. 체중 기록 40건과 맞춰 보고 목표 계산이 체중 변화와 어긋나지 않는지 확인합니다.
사료 이름을 검색해 성분을 자동으로 채우고 자주 주는 조합을 저장해 한 번에 적습니다. 기록에 드는 손이 줄어 매일 쓰는 앱이 됩니다.
보호자가 사료와 간식을 적으면 하루 합계가 나오고, 열량과 단백질이 목표에서 얼마나 벗어났는지 앱에서 보입니다.
성분을 모르는 사료가 섞이거나 단위가 비거나 체중 기록이 없는 입력 30종을 넣어도 앱이 죽지 않고, 합계에서 뺀 항목이 표시로 남습니다.
체중 기록 40건과 목표 계산을 맞춰 어긋난 정도를 냅니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 더미데이터 자가채점이며 실제 영양 판정의 타당성은 림피드의 실 급여 데이터와 수의영양학 검수가 있어야 확인됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 성분 미상 사료·단위 결측·체중 미기록을 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
목표를 넘겼다는 계산이 곧 건강 문제를 뜻하지는 않습니다. 활동량이 많은 날과 회복기에는 기준 자체가 달라지므로 판정을 처방으로 읽지 않게 근거 값을 항상 같이 보여줍니다.
실제 도입에서는 사료마다 성분 표기 단위와 수분 함량 기준이 달라 합계가 흔들립니다. 성분표 정규화가 늘어나면 규칙을 코드에서 빼 데이터로 옮겨야 하며, 그것은 도입 단계 과제로 분리합니다.
질병별 처방 급여와 의학적 판단은 이 포트폴리오의 범위 밖입니다. 여기서는 보호자가 적은 기록을 목표와 비교하는 데까지만 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
보호자가 앱에 적은 하루치 급여 기록입니다. 계산의 입력이며 성분 미상 사료와 단위 결측, 체중 미기록을 의도적으로 포함합니다.
| log_no | fed_at | pet_code | food_name_raw | food_kind | amount | amount_unit | weight_kg | activity | memo |
|---|---|---|---|---|---|---|---|---|---|
| FD-0818-001 | 2026-08-18T08:00:00 | PET-201 | 닥터트러스티 관절 케어 | kibble | 45 | g | 4.6 | normal | — |
| FD-0818-002 | 2026-08-18T12:30:00 | PET-201 | 씨위독 해조류 스틱 | treat | 2 | ea | 4.6 | normal | — |
| FD-0818-003 | 2026-08-18T19:00:00 | PET-201 | 닥터트러스티 관절 케어 | kibble | 45 | g | 4.6 | normal | — |
| FD-0818-004 | 2026-08-18T21:10:00 | PET-201 | 이름 모르는 수제 간식 | treat | 15 | g | 4.6 | normal | 성분표 없음 |
| FD-0818-005 | 2026-08-18T08:10:00 | PET-202 | 트러스티푸드 연어 | wet | 120 | ml | 9.2 | high | — |
아래 JSON Schema 에 맞는 급여 기록 행을 더 만들어 주세요. {"type":"object","properties":{"log_no":{"type":"string"},"fed_at":{"type":"string"},"pet_code":{"type":"string"},"food_name_raw":{"type":"string"},"food_kind":{"type":"string","enum":["kibble","wet","treat","topper"]},"amount":{"type":["number","null"]},"amount_unit":{"type":["string","null"],"enum":["g","ml","ea",null]},"weight_kg":{"type":["number","null"]},"activity":{"type":"string","enum":["low","normal","high"]},"memo":{"type":["string","null"]}},"required":["log_no","fed_at","pet_code","food_name_raw","food_kind","activity"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) amount 나 weight_kg 가 null 인 행, (2) amount 가 0 이거나 하루치를 한 번에 준 극단 행, (3) food_name_raw 의 띄어쓰기가 흔들린 행, (4) 성분표에 없는 수제 간식 이름. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"log_no":{"type":"string"},"fed_at":{"type":"string"},"pet_code":{"type":"string"},"food_name_raw":{"type":"string"},"food_kind":{"type":"string","enum":["kibble","wet","treat","topper"]},"amount":{"type":["number","null"]},"amount_unit":{"type":["string","null"],"enum":["g","ml","ea",null]},"weight_kg":{"type":["number","null"]},"activity":{"type":"string","enum":["low","normal","high"]},"memo":{"type":["string","null"]}},"required":["log_no","fed_at","pet_code","food_name_raw","food_kind","activity"]}사료와 간식의 100g 기준 성분표입니다. 합계 계산의 기준이며 단백질 미표기와 단위가 다른 항목을 포함합니다.
| food_key | food_name | basis_unit | kcal | protein_g | fat_g | sodium_mg | updated_on |
|---|---|---|---|---|---|---|---|
| FK-001 | 닥터트러스티 관절 케어 | per_100g | 372 | 28 | 14 | 320 | 2026-06-01 |
| FK-002 | 닥터트러스티 체중 관리 | per_100g | 318 | 30 | 8.5 | 290 | 2026-06-01 |
| FK-003 | 트러스티푸드 치킨 | per_100g | 355 | 26.5 | 12 | 300 | 2026-05-20 |
| FK-004 | 트러스티푸드 연어 | per_100ml | 92 | 8.4 | 4.1 | 110 | 2026-05-20 |
| FK-005 | 씨위독 해조류 스틱 | per_ea | 18 | 1.2 | 0.4 | 45 | 2026-07-02 |
아래 JSON Schema 에 맞는 성분표 행을 더 만들어 주세요. {"type":"object","properties":{"food_key":{"type":"string"},"food_name":{"type":"string"},"basis_unit":{"type":"string","enum":["per_100g","per_100ml","per_ea"]},"kcal":{"type":"number"},"protein_g":{"type":["number","null"]},"fat_g":{"type":["number","null"]},"sodium_mg":{"type":["number","null"]},"updated_on":{"type":"string"}},"required":["food_key","food_name","basis_unit","kcal","updated_on"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) protein_g 나 sodium_mg 가 null 인 미표기 행, (2) basis_unit 이 per_ea 라 무게로 환산되지 않는 행, (3) 급여 기록에는 없는 사료 이름, (4) 같은 제품군의 이름이 한 글자만 다른 행. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"food_key":{"type":"string"},"food_name":{"type":"string"},"basis_unit":{"type":"string","enum":["per_100g","per_100ml","per_ea"]},"kcal":{"type":"number"},"protein_g":{"type":["number","null"]},"fat_g":{"type":["number","null"]},"sodium_mg":{"type":["number","null"]},"updated_on":{"type":"string"}},"required":["food_key","food_name","basis_unit","kcal","updated_on"]}| React Native | 보호자가 쓰는 앱 화면입니다. 기록 입력과 오늘의 차이를 한 화면에 둡니다. |
| React | 성분표와 목표 기준을 관리하는 내부 웹 화면입니다. |
| Flutter | 앱 화면을 다른 프레임워크로 옮길 때를 대비해 계산과 화면의 경계를 API 로 고정합니다. |
| TypeScript | 앱과 백엔드가 같은 타입으로 계산 결과를 주고받게 하는 언어입니다. |
| Claude Code | 화면 골격과 반복되는 기록 폼을 만드는 데 씁니다. 계산 규칙은 직접 쓰고 테스트로 고정합니다. |
| LLM | 차이 계산이 끝난 뒤 어느 영양소가 얼마나 모자랐는지를 한 줄로 요약합니다. 수치와 판정은 규칙이 내고 모델은 여기에 개입하지 않습니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 일주일 추세와 체중 변화를 맞춰 보는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 사료 검색과 조합 저장으로 기록 손을 줄이는 단계로 확장합니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
nutrition_code사유 라벨 · log_no + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
on_target목표 범위calorie_over열량 초과protein_short단백질 부족amount_missing급여량 미기록food_unmapped성분 미상weight_missing체중 미기록한 마리의 하루 급여 판정입니다. 목표 대비 차이와 근거가 된 값을 함께 남깁니다.
{"type":"object","properties":{"check_id":{"type":"string"},"pet_code":{"type":"string"},"log_no":{"type":"string"},"nutrition_code":{"type":"string","enum":["on_target","calorie_over","protein_short","amount_missing","food_unmapped","weight_missing"]},"kcal_gap":{"type":["number","null"]},"evidence":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["feeding","nutrition"]},"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{"type":["string","number","null"]}},"required":["source","field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["check_id","pet_code","log_no","nutrition_code","evidence","summary"]}[
{
"check_id": "nc_0001",
"pet_code": "PET-201",
"log_no": "FD-0818-001",
"nutrition_code": "on_target",
"kcal_gap": null,
"evidence": [
{
"source": "feeding",
"field": "amount",
"agg": null,
"value": 45
},
{
"source": "nutrition",
"field": "kcal",
"agg": null,
"value": 372
}
],
"summary": "아침 급여량이 하루 목표의 절반에 맞아 그대로 두어도 됩니다.",
"hedge": null
},
{
"check_id": "nc_0002",
"pet_code": "PET-205",
"log_no": "FD-0818-012",
"nutrition_code": "on_target",
"kcal_gap": null,
"evidence": [
{
"source": "feeding",
"field": "amount",
"agg": null,
"value": 60
},
{
"source": "nutrition",
"field": "protein_g",
"agg": null,
"value": 26.5
}
],
"summary": "체중 대비 열량과 단백질이 모두 목표 범위 안에 들어옵니다.",
"hedge": null
},
{
"check_id": "nc_0003",
"pet_code": "PET-204",
"log_no": "FD-0818-010",
"nutrition_code": "calorie_over",
"kcal_gap": 213,
"evidence": [
{
"source": "nutrition",
"field": "kcal",
"agg": "sum",
"value": 880
},
{
"source": "nutrition",
"field": "kcal",
"agg": "target",
"value": 667
},
{
"source": "feeding",
"field": "amount",
"agg": null,
"value": 210
}
],
"summary": "하루 섭취가 880kcal 로 목표 667kcal 를 213kcal 넘겼습니다.",
"hedge": "합성 기준 판정이라 활동량이 많은 날에는 목표 자체가 달라질 수 있습니다."
},
{
"check_id": "nc_0004",
"pet_code": "PET-202",
"log_no": "FD-0818-006",
"nutrition_code": "amount_missing",
"kcal_gap": null,
"evidence": [
{
"source": "feeding",
"field": "amount",
"agg": null,
"value": null
},
{
"source": "nutrition",
"field": "kcal",
"agg": null,
"value": 92
}
],
"summary": "저녁에 준 양이 비어 있어 합계에서 빼고 차이를 내지 않았습니다.",
"hedge": null
},
{
"check_id": "nc_0005",
"pet_code": "PET-201",
"log_no": "FD-0818-004",
"nutrition_code": "food_unmapped",
"kcal_gap": null,
"evidence": [
{
"source": "feeding",
"field": "food_name_raw",
"agg": null,
"value": "이름 모르는 수제 간식"
},
{
"source": "nutrition",
"field": "food_name",
"agg": "count",
"value": 0
}
],
"summary": "성분표에 없는 간식이라 합계에서 빼고 확인 대상으로 남깁니다.",
"hedge": null
},
{
"check_id": "nc_0006",
"pet_code": "PET-203",
"log_no": "FD-0818-007",
"nutrition_code": "weight_missing",
"kcal_gap": null,
"evidence": [
{
"source": "feeding",
"field": "weight_kg",
"agg": null,
"value": null
},
{
"source": "feeding",
"field": "activity",
"agg": null,
"value": "low"
}
],
"summary": "체중이 없어 하루 목표를 잡지 못해 차이를 내지 않았습니다.",
"hedge": null
},
{
"check_id": "nc_0007",
"pet_code": "PET-202",
"log_no": "FD-0818-005",
"nutrition_code": "protein_short",
"kcal_gap": null,
"evidence": [
{
"source": "nutrition",
"field": "protein_g",
"agg": null,
"value": 8.4
},
{
"source": "feeding",
"field": "weight_kg",
"agg": null,
"value": 9.2
}
],
"summary": "습식만으로는 체중 대비 단백질이 목표에 못 미칩니다.",
"hedge": "합성 기준 판정이라 실제 필요량은 나이와 건강 상태에 따라 달라집니다."
},
{
"check_id": "nc_0008",
"pet_code": "PET-204",
"log_no": "FD-0818-009",
"nutrition_code": "amount_missing",
"kcal_gap": null,
"evidence": [
{
"source": "feeding",
"field": "amount",
"agg": null,
"value": 0
},
{
"source": "feeding",
"field": "memo",
"agg": null,
"value": "거부해서 못 먹음"
}
],
"summary": "급여량이 0 이라 먹은 것으로 세지 않고 급여 거부로 남깁니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. nutrition_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. check_id·pet_code·log_no·nutrition_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. kcal_gap 이 evidence 에 적힌 섭취 합계와 목표 열량의 차이와 같은지 다시 계산합니다. 양이나 체중이 비면 kcal_gap 도 null 인지 확인합니다. 4. evidence 의 field 이름이 급여 기록과 성분표에 실재하는 컬럼인지 대조합니다. 5. agg 가 없는 evidence 의 value 가 그 행의 값과 같은지 확인합니다. 6. weight_kg 가 null 이면 nutrition_code 는 on_target 이 아닌지 확인합니다. 기록에는 체중이 있는 행과 없는 행이 모두 있습니다. 7. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 체중 기록 40건과 목표 계산을 비교해 목표가 체중 변화 방향과 어긋난 날을 냅니다. [수동 검증 · 사람이 봅니다] 1. 성분표에 없는 간식이 하루에 여러 번 들어온 날을 표본 20건 눈으로 확인합니다. 2. 급여 거부로 0 을 적은 기록을 결측으로 볼지 실제 0 으로 볼지는 운영 기준을 따라 정합니다.