누비랩
인턴·신입근거: 문제 정의부터 실험·구현·배포·개선까지 전 과정을 책임집니다
※ 실제 산출물이 아닌 AI 예측 시안입니다.
이 파이프라인이 보여주는 것은 백엔드 데이터 모델링과 규칙 기반 집계·정합성 검사, 검수 큐 설계 기술이며, 영양학이나 급식 운영 전문성을 주장하지 않습니다. 다만 이 사슬이 말이 되려면 누비랩이라는 맥락, 즉 식판 스캔 데이터로 섭취·잔반 인사이트를 만드는 푸드테크라는 문제가 있어야 의미가 있습니다. ‘규칙으로 확정 안 되는 정합성은 지어내지 않는다’ 는 각도는 다른 도메인 데이터로도 옮길 수 있지만, 급식 데이터의 결측·별칭·경계값 형태가 있어야 검사 규칙이 구체화됩니다. 실제 영양 인사이트의 타당성은 영양 전문가 검수가 있어야 하고, 면접에서 모르는 부분은 모른다고 말하는 편이 낫습니다.
‘Product Engineer는 제품 문제를 End-to-End로 책임지며 백엔드 경험을 바탕으로 문제 정의부터 배포까지 책임진다’ 가 주요업무입니다. 데이터 수집부터 정합성·검수 큐까지 한 흐름을 끝내는 백엔드가 보여줄 대상입니다.
이력서 안내에 ‘데이터베이스 설계, API 설계, 동시성 처리, 트랜잭션 관리 등 기초 설계 역량’ 을 강조합니다. 집계·정합성 검사는 이 설계 역량을 그대로 드러냅니다.
‘AI가 최고 속도로 문제를 해결하도록 개발 환경과 시스템을 설계하고 마찰을 제거’ 가 업무입니다. 정합성 검사를 규칙으로 자동화하고 애매한 것만 사람에게 남기는 구조가 그 방향입니다.
식판을 스캔한 원시 이벤트를 메뉴 마스터와 대조해 일별 섭취량·잔반을 규칙으로 집계하고, 미등록 메뉴·중복 스캔·섭취량이 제공량을 넘는 불가능 값 같은 정합성 오류를 자동으로 잡아, 어떤 규칙에 왜 걸렸는지 근거를 붙여 검수 큐에 올립니다. 집계·검사는 규칙(SQL·결정론)으로 확정되고 LLM 은 걸린 케이스를 사람이 읽을 설명으로 바꾸는 보조만 맡아, 근거 없는 정합성 판정을 하지 못하게 합니다. 1주차만으로 완성품이고, 검사 규칙 검토(2주차)·스트림 실시간화(3주차)로 키웁니다. (근거는 채용공고 정렬입니다.)
누비랩 백엔드 PE. 매일 들어오는 식판 스캔 데이터에서 이상한 값을 손으로 걸러 집계를 고치던 일을 끝내고, 정합성 오류를 규칙으로 자동으로 잡아 근거를 붙여 검수 큐로 받되 애매한 것만 사람이 보게 하고 싶은 사람입니다.
스캔 이벤트를 메뉴 마스터와 대조해 규칙으로 집계·검사하면, (a) 일별 섭취·잔반이 규칙으로 정확히 집계되고, (b) 미등록·중복·불가능 값 같은 정합성 오류가 규칙으로 잡혀 근거를 달고 큐에 오르며, (c) 규칙으로 확정 안 되는 건 지어내지 않고 사람 검수로 넘어가, 운영자가 이상값 전수 확인 없이 애매한 것만 보면 되는 신뢰할 파이프라인을 만든다는 걸 보여줄 수 있습니다.
스캔 이벤트를 메뉴 마스터와 대조해 일별 섭취·잔반을 집계하고, 정합성 오류를 규칙으로 잡아 근거를 붙여 검수 큐 API 로 올리는 동작하는 백엔드입니다. 규칙으로 확정 안 되는 건 사람 검수로 넘깁니다.
급식 운영자 페르소나 2명이 큐에 오른 오류가 진짜 오류인지, 놓친 오류는 없는지 라벨링하고, 규칙의 정밀도·재현율을 대조하는 대시보드입니다.
배치 집계를 Kafka 스트림으로 바꿔 스캔 즉시 집계·검사하고, 동시 스캔의 중복·경합을 트랜잭션으로 다루는 단계입니다.
30일치 스캔 이벤트를 넣으면, 일별 섭취·잔반을 집계하고 정합성 오류에 근거를 붙여 검수 큐에 올리는 걸 시연합니다
집계 수치가 규칙(SQL) 계산과 100% 일치하는지 자동 대조합니다. LLM 이 판정을 바꾸면 차단되는 걸 보여줍니다
일부러 심은 정합성 오류(미등록·중복·불가능 값) 30종 중 규칙이 잡은 비율(재현율)과 거짓 경보(정밀도)를 보여줍니다
내가 만든 정답 30일치 중 집계·오류 탐지가 일치한 게 26일(87%). 단, 정답과 데이터를 모두 내가 만든 합성 기준이라 ‘성능 검증’ 이 아니라 ‘데모 조건 동작’ 임을 밝힙니다
위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 정합성·영양 인사이트 타당성은 누비랩 실 스캔 데이터와 급식 운영자·영양 전문가가 있어야 측정 가능합니다. 이 파이프라인은 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
이건 운영·실데이터 없이 혼자 만든 포트폴리오라, 수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서 ‘데모 동작 + 실검증 준비 상태’ 로 솔직히 설명합니다
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 별칭 메뉴·중복 스캔·경계값을 더미에 일부러 섞어, 깔끔한 데이터만 다루는 게 아님을 보여줍니다
이 포트폴리오가 보여주는 것은 백엔드 집계·정합성·큐 설계 기술이지 영양학이나 급식 운영 전문성이 아닙니다. 영양 인사이트의 타당성은 영양 전문가 검수가 있어야 하고, 면접에서 모르는 부분은 모른다고 말하는 편이 낫습니다
규칙이 모든 이상을 잡지는 못합니다(새 유형의 오류). 규칙 밖은 자동 판정하지 않고 사람 검수로 넘겨, 잘못된 자동 판정으로 집계를 오염시키지 않습니다
실제 도입 시 달라지는 것 명시합니다. 실 스캔 데이터의 분포·메뉴 체계는 더미와 다르므로 정합성 규칙·중복 임계값을 다시 맞춰야 하고, 검토자도 실 운영자로 교체해야 합니다(도입 단계 과제로 분리)
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
식판 스캔 이벤트. 집계·정합성 검사의 원천입니다. 미등록 메뉴·중복 스캔·섭취량이 제공량을 넘는 불가능 값·결측 무게를 일부러 섞어, 규칙이 이상값을 잡고 애매한 건 사람에게 넘기는지 보여줍니다.
| event_id | tray_id | menu_code | served_g | consumed_g | ts |
|---|---|---|---|---|---|
| s_001 | t_01 | M-rice | 210 | 180 | 2026-05-01T12:00:00 |
| s_002 | t_01 | M-soup | 300 | 300 | 2026-05-01T12:00:05 |
| s_003 | t_02 | M-rice | 210 | 250 | 2026-05-01T12:01:00 |
| s_004 | t_03 | M-ZZZ | 150 | 120 | 2026-05-01T12:02:00 |
| s_005 | t_04 | M-rice | 210 | — | 2026-05-01T12:03:00 |
아래 JSON Schema 에 맞는 식판 스캔 이벤트 40행 JSON 배열로 생성합니다. 식판 여러 개, 메뉴 코드 5~8종.
{"type":"object","properties":{"event_id":{"type":"string"},"tray_id":{"type":"string"},"menu_code":{"type":["string","null"]},"served_g":{"type":["integer","null"]},"consumed_g":{"type":["integer","null"]},"ts":{"type":"string"}},"required":["event_id","tray_id","ts"]}
쉬운 데이터만 만들지 말 것. 하드 케이스 의도적 혼입: (1) menu_master 에 없는 menu_code(M-ZZZ) 또는 null 인 미등록 1~2건, (2) 같은 tray·menu 5초 이내 중복 스캔 1~2쌍, (3) consumed_g > served_g 인 불가능 값 1~2건, (4) served_g 또는 consumed_g 가 null 인 결측 각 1~2건, (5) consumed_g=0(전량 잔반) 경계 1건. JSON 배열만.{"type":"object","properties":{"event_id":{"type":"string"},"tray_id":{"type":"string"},"menu_code":{"type":["string","null"]},"served_g":{"type":["integer","null"]},"consumed_g":{"type":["integer","null"]},"ts":{"type":"string"}},"required":["event_id","tray_id","ts"]}메뉴 마스터. 이벤트의 menu_code 를 메뉴명·기준 제공량으로 잇습니다. 별칭 코드·기준량 결측·비활성 메뉴를 섞어, 미등록·별칭을 규칙이 어떻게 다루는지 보여줍니다.
| menu_code | name | category | standard_g | active |
|---|---|---|---|---|
| M-rice | 백미밥 | rice | 210 | true |
| M-soup | 된장국 | soup | 300 | true |
| M-kimchi | 배추김치 | side | 80 | true |
| M-side | 계란말이 | side | — | true |
| M-main | 제육볶음 | main | 160 | true |
아래 JSON Schema 에 맞는 메뉴 마스터 20행 JSON 배열로 생성합니다. category 를 고루 섞습니다.
{"type":"object","properties":{"menu_code":{"type":"string"},"name":{"type":"string"},"category":{"type":"string","enum":["main","soup","side","rice"]},"standard_g":{"type":["integer","null"]},"active":{"type":"boolean"}},"required":["menu_code","name","category","active"]}
쉬운 데이터만 만들지 말 것. 하드 케이스 의도적 혼입: (1) standard_g 가 null 인 결측 1~2건, (2) 같은 메뉴의 구코드·별칭(M-rice/M-rice2) 1쌍, (3) active=false 비활성 메뉴 1~2건, (4) scan_event 에는 나오지만 마스터에 없는 코드를 만들어 미등록 유발. JSON 배열만.{"type":"object","properties":{"menu_code":{"type":"string"},"name":{"type":"string"},"category":{"type":"string","enum":["main","soup","side","rice"]},"standard_g":{"type":["integer","null"]},"active":{"type":"boolean"}},"required":["menu_code","name","category","active"]}| Kotlin | Spring Boot 집계·정합성 검사 API 서버(우대 스택 반영) |
| Spring Batch | 일별 섭취·잔반 배치 집계 잡 |
| Node.js | 검수 큐 조회·처리 게이트웨이(Nest.js) |
| PostgreSQL | scan_event / menu_master / integrity_issue 단일 진실원과 집계·제약 |
| Redis | 동시 스캔 중복 억제(idempotency key)와 검수 큐 버퍼 |
| Apache Kafka | 3주차 스트림 처리로 실시간 집계·검사 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차(검사 규칙 검토)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(스트림 실시간화)로 진행합니다.
정합성 검사에 걸린 케이스. 위반 규칙(rule)·근거 값(observed)·근거 이벤트(evidence)·설명(explanation)·자동 확정 여부(auto_resolved)를 한 스키마에 담아, 규칙 없는 판정을 지어내지 않고 애매한 건 사람 검수로 넘겼는지 객관 검증합니다.
{"type":"object","properties":{"event_id":{"type":"string"},"rule":{"type":"string","enum":["unregistered_menu","duplicate_scan","consumed_gt_served","missing_weight","inactive_menu"]},"observed":{"type":"object"},"explanation":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["queue_for_review","auto_drop","auto_dedup"]},"auto_resolved":{"type":"boolean"}},"required":["event_id","rule","observed","explanation","evidence","action","auto_resolved"]}[
{
"input_ref": "s_003: consumed 250 > served 210 (불가능 값)",
"output": {
"event_id": "s_003",
"rule": "consumed_gt_served",
"observed": {
"served_g": 210,
"consumed_g": 250
},
"explanation": "섭취량(250g)이 제공량(210g)을 초과해 물리적으로 불가능합니다. 스캔 오차로 보이나 규칙만으로 올바른 값을 확정할 수 없어 검수 큐로 올립니다.",
"evidence": [
"s_003"
],
"action": "queue_for_review",
"auto_resolved": false
}
},
{
"input_ref": "s_007: t_05 M-kimchi 2초 내 재스캔 (중복)",
"output": {
"event_id": "s_007",
"rule": "duplicate_scan",
"observed": {
"gap_seconds": 2,
"prev_event": "s_006"
},
"explanation": "같은 식판·메뉴가 2초 간격으로 재스캔됐습니다. 중복 임계(5초) 안이라 규칙으로 자동 병합하되 근거를 남깁니다.",
"evidence": [
"s_006",
"s_007"
],
"action": "auto_dedup",
"auto_resolved": true
}
},
{
"input_ref": "s_004: M-ZZZ (마스터 미등록)",
"output": {
"event_id": "s_004",
"rule": "unregistered_menu",
"observed": {
"menu_code": "M-ZZZ"
},
"explanation": "메뉴 코드 M-ZZZ 가 마스터에 없습니다. 어느 메뉴인지 확정할 수 없어 집계에서 제외하고 검수 큐로 올립니다.",
"evidence": [
"s_004"
],
"action": "queue_for_review",
"auto_resolved": false
}
}
][자동 검증] 1. 출력이 스키마를 통과하는가(형식·필수 필드). 2. rule 판정이 규칙 엔진 결과와 일치하는가(LLM 이 판정을 바꾸지 못함). 3. evidence 의 event_id 가 scan_event 에 실재하고 observed 값이 실제 이벤트 값과 일치하는가(지어낸 근거 차단). 4. 규칙으로 값을 확정할 수 없는 케이스(consumed_gt_served·unregistered)는 auto_resolved=false 이고 action=queue_for_review 인가. [수동 검증] 1. 급식 운영자 페르소나 2명이 큐 오류 타당성 라벨링 → bootstrap Cohen’s κ 하한 ≥ 0.6. 2. 놓친 오류(재현율) 5% 사람 점검.