보살핌
경력근거: 케어아카데미 백엔드를 뜯어보고, 어디를 어떤 순서로 고도화할지 근거와 함께 제안합니다
케어아카데미는 평균 연령 61세인 분들이 새 직무를 배우는 서비스이고, 공고는 합류 직후 할 일로 케어아카데미 백엔드를 뜯어보고 어디를 어떤 순서로 고도화할지 근거와 함께 제안하는 일을 적었습니다. 수료증 발급은 그 백엔드에서 수강 기록, 과정 기준, 시험, 발급이 한곳에서 만나는 자리라, 판정이 어긋나는 쌍을 세어 보면 어느 테이블부터 손봐야 하는지가 숫자로 나옵니다. 공고가 따로 적은 "CX나 운영처럼 개발을 하지 않는 팀이 AI로 자기 백로그를 처리"하는 일도 이 화면에서 시작합니다. 수료 문의는 CX 백로그에서 반복되는 유형이고, 판정 조회를 CX 에이전트 도구로 여는 것이 3주차 단계입니다. 이 포트폴리오는 수강 기록과 이수 기준을 맞대는 판정 규칙과 그 결과를 읽는 화면을 보여줍니다. 직업훈련 인정 요건이나 민간자격 발급 행정의 전문성은 주장하지 않습니다.
공고는 아카데미 스쿼드에서 기능을 개선하면서 백엔드를 어디부터 어떤 순서로 고칠지 근거와 함께 제안하라고 적었습니다. 수료 판정이 어긋나는 쌍을 세면 수강 기록과 기준 표 가운데 어디가 먼저 바뀌어야 하는지 근거가 생깁니다.
장기 업무에 CX 와 운영 팀을 위한 내부 에이전트와 자동화가 있고, 우대사항에 Claude·MCP 로 사내 워크플로우를 자동화한 경험이 있습니다. 수료 문의 조회를 에이전트 도구로 여는 3주차 단계가 이 두 줄에 닿습니다.
공통 인프라 목록에 배치성 작업이 들어 있고 스택은 AWS ECS, GitHub Actions, Sentry, CloudWatch 입니다. 판정을 매일 새벽 도는 배치로 올리고 실패를 모니터링에 거는 2주차가 이 스택 위에서 돕니다.
중장년 직무교육 서비스 케어아카데미의 강의 시청 기록, 과정별 이수 기준, 시험 결과, 수료증 발급 기록 네 표를 받아 수강생·과정 쌍마다 수료증이 기준대로 나갔는지 판정하는 관리자 페이지입니다. 강의마다 최소 시청 시간이 다르고 시험은 여러 번 볼 수 있어, 강의별로 시청 시간을 기준과 맞대고 가장 높은 점수를 합격선과 견줍니다. 기준이 발급 뒤에 바뀐 과정은 따로 세워, 운영·CX 담당자가 "수료증이 안 나와요" 문의를 받았을 때 어느 강의가 몇 분 모자란지 바로 답합니다.
케어아카데미 수강생 문의를 받고 수료 처리를 바로잡는 운영·CX 담당자
수료증 문의가 오면 CX 담당자는 지금 관리 화면에서 강의 시청 기록과 시험 기록을 따로 열어 과정 기준표와 손으로 맞춰 봅니다. 강의가 개편되거나 기준이 바뀐 과정은 어느 기준으로 봐야 하는지도 매번 개발자에게 물어야 합니다. 네 표를 수강생·과정 쌍으로 맞대 판정 다섯 가지로 미리 나눠 두면, 문의 한 건에 드는 확인 시간이 줄고 문의가 오기 전에 발급 누락과 잘못 나간 수료증을 먼저 바로잡습니다.
운영·CX 담당자가 시청 기록, 과정 기준, 시험 결과, 수료증 네 파일을 올리면 수강생·과정 쌍마다 다섯 판정 가운데 하나가 붙고, 한 명을 펼치면 모자란 강의와 점수가 나오는 관리자 페이지입니다. 입력은 운영 DB 에서 내려받은 CSV 로 두고 시작합니다.
판정을 AWS ECS 예약 작업으로 매일 새벽 돌려 전날 발급분을 자동으로 점검하고, 수강생·과정 쌍 180건에 손으로 매긴 정답과 견줘 판정별 일치율을 냅니다. 실패한 배치는 Sentry 와 CloudWatch 로 담당자에게 보입니다.
판정 조회를 MCP 서버의 도구로 열어, CX 담당자가 Claude 에게 "U-10502 수료증 왜 안 나갔어요" 라고 물으면 판정과 근거, 수강생에게 읽어 줄 문장이 돌아옵니다. 발급 누락 쌍은 재발급 요청 목록으로 모입니다.
운영·CX 담당자가 네 파일을 올리면 발급 누락 한 쌍, 시청 미달 발급 세 쌍, 점수 미달 발급 두 쌍, 기준 개정 한 쌍, 정상 발급 네 쌍이 판정별로 화면에 나옵니다.
시청 시간이 기준과 꼭 같은 강의, 합격선과 같은 점수, 채점 전이라 비어 있는 점수, 개편 전 강의 기록, 시청 기록이 한 줄도 없는 수료증을 섞은 파일에서도 판정이 하나씩 붙고 모자란 값이 없는 쌍은 비고 칸에 사정을 적습니다.
열이 빠진 CSV, 날짜 칸에 글자가 든 CSV 20종을 넣어도 화면이 멈추지 않고 어느 파일의 몇째 줄이 문제인지 보입니다.
수강생·과정 쌍 180건에 손으로 매긴 정답과 자동 판정을 견줘 판정별 일치율(자동 판정이 정답과 같은 비율)을 냅니다. 시청 기록과 시험 점수, 정답을 한 사람이 지은 합성 데이터라, 이 비율로 알 수 있는 것은 누락·시청 미달·점수 미달의 차례가 흔들리지 않는다는 점까지입니다.
성능을 말할 수 있는 데는 여기까지입니다. 문의 확인 시간이 실제로 얼마나 줄지는 케어아카데미 운영 DB 와 CX 문의 기록이 있어야 잽니다. 그 기록이 오는 날 바로 돌도록 배치와 채점 러너를 먼저 만들어 두었습니다.
네 표와 정답 판정이 모두 제 손에서 나와 일치율이 높게 나오는 것이 자연스럽습니다. 면접에서는 판정 규칙이 어떤 순서로 도는지와 실데이터가 오면 다시 잰다는 준비 상태로 설명합니다.
제가 만든 기록만 넣으면 규칙이 쉬운 쌍만 맞힙니다. 기준과 꼭 같은 시청 시간, 비어 있는 점수, 개편 전 강의, 시청 기록이 아예 없는 수료증을 일부러 넣어 둔 이유입니다.
발급 당시 기준값이 표에 남지 않는 구조에서는 기준이 바뀐 과정의 옛 수료증을 옳다 그르다 판정하지 못합니다. 이 도구는 그런 쌍을 따로 세울 뿐이고, 기준 이력 표를 두는 일은 백엔드 개선 과제로 제안합니다.
이 포트폴리오는 수강 기록과 이수 기준을 맞대는 판정 규칙을 보여줍니다. 직업훈련 인정 요건이나 자격 발급 행정의 전문성은 주장하지 않습니다.
실서비스에서는 이어 보기, 배속 재생, 앱과 웹을 오간 시청이 한 강의 기록을 여러 줄로 쪼갭니다. 여기서는 강의당 한 줄로 두었으니, 쪼개진 기록을 합치는 일은 도입 단계 과제로 남깁니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
수강생이 강의마다 몇 초를 들었는지 남긴 시청 기록입니다. 완료 시각이 비어 있는 강의, 지금 과정 기준에 없는 개편 전 강의, 완료 표시는 있지만 시청 시간이 짧은 강의를 담았습니다.
| user_id | course_id | lesson_id | watched_sec | completed_at |
|---|---|---|---|---|
| U-10233 | C-HOSP | HOSP-01 | 1,260 | 2026-08-25T20:14:00 |
| U-10233 | C-HOSP | HOSP-02 | 1,530 | 2026-08-27T19:40:00 |
| U-10233 | C-HOSP | HOSP-03 | 1,520 | 2026-08-29T21:02:00 |
| U-10418 | C-CARE | CARE-01 | 1,950 | 2026-09-10T10:22:00 |
| U-10418 | C-CARE | CARE-02 | 1,830 | 2026-09-12T09:48:00 |
아래 JSON Schema 에 맞는 케어아카데미 강의 시청 기록 행을 더 만들어 주세요. {"type":"object","properties":{"user_id":{"type":"string"},"course_id":{"type":"string"},"lesson_id":{"type":"string"},"watched_sec":{"type":"integer","minimum":0},"completed_at":{"type":["string","null"]}},"required":["user_id","course_id","lesson_id","watched_sec","completed_at"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) completed_at 이 null 인 듣는 중 강의, (2) watched_sec 가 course_requirements.min_watch_sec 와 정확히 같은 행, (3) course_requirements 에 없는 lesson_id(개편 전 강의), (4) completed_at 은 있는데 watched_sec 가 기준의 절반 아래인 행. user_id 는 U-5자리, 강의 한 개당 한 줄로 만들고 생성한 행은 반드시 위 스키마를 통과해야 합니다.{"type":"object","properties":{"user_id":{"type":"string"},"course_id":{"type":"string"},"lesson_id":{"type":"string"},"watched_sec":{"type":"integer","minimum":0},"completed_at":{"type":["string","null"]}},"required":["user_id","course_id","lesson_id","watched_sec","completed_at"]}과정의 강의마다 최소 시청 시간과 시험 합격선, 마지막 개정일을 적은 기준 표입니다. 발급이 끝난 뒤에 기준이 바뀐 강의 하나를 담았습니다.
| course_id | course_name | lesson_id | min_watch_sec | pass_score | revised_at |
|---|---|---|---|---|---|
| C-HOSP | 병원동행매니저 | HOSP-01 | 1,200 | 70 | 2026-06-01 |
| C-HOSP | 병원동행매니저 | HOSP-02 | 1,500 | 70 | 2026-06-01 |
| C-HOSP | 병원동행매니저 | HOSP-03 | 1,800 | 70 | 2026-09-15 |
| C-SILV | 실버인지활동지도사 | SILV-01 | 1,500 | 60 | 2026-05-20 |
| C-SILV | 실버인지활동지도사 | SILV-02 | 1,500 | 60 | 2026-05-20 |
아래 JSON Schema 에 맞는 과정 이수 기준 행을 더 만들어 주세요. {"type":"object","properties":{"course_id":{"type":"string"},"course_name":{"type":"string"},"lesson_id":{"type":"string"},"min_watch_sec":{"type":"integer","minimum":0},"pass_score":{"type":"integer","minimum":0,"maximum":100},"revised_at":{"type":"string"}},"required":["course_id","course_name","lesson_id","min_watch_sec","pass_score","revised_at"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 같은 과정 안에서 한 강의만 revised_at 이 최근인 행, (2) pass_score 가 60·70·80 처럼 과정마다 다른 행, (3) 강의가 두 개뿐인 짧은 과정. 한 과정의 모든 행은 pass_score 가 같아야 하고, 생성한 행은 반드시 위 스키마를 통과해야 합니다.{"type":"object","properties":{"course_id":{"type":"string"},"course_name":{"type":"string"},"lesson_id":{"type":"string"},"min_watch_sec":{"type":"integer","minimum":0},"pass_score":{"type":"integer","minimum":0,"maximum":100},"revised_at":{"type":"string"}},"required":["course_id","course_name","lesson_id","min_watch_sec","pass_score","revised_at"]}과정 수료 시험의 응시 기록입니다. 두 번 응시한 수강생, 채점 전이라 점수가 비어 있는 응시, 합격선과 같은 점수, 강의를 다 듣기 전에 본 시험을 담았습니다.
| user_id | course_id | attempt_no | score | taken_at |
|---|---|---|---|---|
| U-10233 | C-HOSP | 1 | 88 | 2026-08-30T20:00:00 |
| U-10418 | C-CARE | 1 | 91 | 2026-09-13T10:00:00 |
| U-10502 | C-CARE | 1 | 86 | 2026-09-17T15:20:00 |
| U-10577 | C-CARE | 1 | 83 | 2026-09-08T11:40:00 |
| U-10610 | C-SILV | 1 | 52 | 2026-08-26T09:30:00 |
아래 JSON Schema 에 맞는 수료 시험 응시 행을 더 만들어 주세요. {"type":"object","properties":{"user_id":{"type":"string"},"course_id":{"type":"string"},"attempt_no":{"type":"integer","minimum":1},"score":{"type":["integer","null"],"minimum":0,"maximum":100},"taken_at":{"type":"string"}},"required":["user_id","course_id","attempt_no","score","taken_at"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 같은 user_id·course_id 에 attempt_no 가 1, 2 인 재응시, (2) score 가 null 인 채점 전 응시, (3) score 가 pass_score 와 정확히 같은 행. 생성한 행은 반드시 위 스키마를 통과해야 합니다.{"type":"object","properties":{"user_id":{"type":"string"},"course_id":{"type":"string"},"attempt_no":{"type":"integer","minimum":1},"score":{"type":["integer","null"],"minimum":0,"maximum":100},"taken_at":{"type":"string"}},"required":["user_id","course_id","attempt_no","score","taken_at"]}수료증 발급 기록입니다. 시청 기록이 한 줄도 없는 수강생에게 나간 수료증과 시험 응시 1분 뒤에 나간 수료증을 담았습니다.
| cert_no | user_id | course_id | issued_at |
|---|---|---|---|
| CA-260902-0233 | U-10233 | C-HOSP | 2026-09-02T09:00:00 |
| CA-260913-0418 | U-10418 | C-CARE | 2026-09-13T10:05:00 |
| CA-260908-0577 | U-10577 | C-CARE | 2026-09-08T11:45:00 |
| CA-260828-0610 | U-10610 | C-SILV | 2026-08-28T09:15:00 |
| CA-260904-0655 | U-10655 | C-CARE | 2026-09-04T14:15:00 |
아래 JSON Schema 에 맞는 수료증 발급 행을 더 만들어 주세요. {"type":"object","properties":{"cert_no":{"type":"string"},"user_id":{"type":"string"},"course_id":{"type":"string"},"issued_at":{"type":"string"}},"required":["cert_no","user_id","course_id","issued_at"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) lesson_progress 에 기록이 없는 user_id 의 수료증, (2) issued_at 이 그 과정 revised_at 보다 앞선 수료증, (3) 시험 응시 직후 몇 분 안에 나간 수료증. cert_no 는 CA-YYMMDD-4자리로 쓰고 생성한 행은 반드시 위 스키마를 통과해야 합니다.{"type":"object","properties":{"cert_no":{"type":"string"},"user_id":{"type":"string"},"course_id":{"type":"string"},"issued_at":{"type":"string"}},"required":["cert_no","user_id","course_id","issued_at"]}| TypeScript | 네 입력 표와 판정 결과의 모양을 한 곳에 정의해 배치 작업과 관리 화면이 같은 타입을 씁니다. |
| NestJS(Prisma) | 판정 규칙을 담는 서비스 계층입니다. Prisma 로 네 표를 읽어 수강생·과정 쌍으로 모읍니다. |
| Ruby on Rails | 기존 수강 기록이 쌓이는 쪽으로 두고, 판정 서비스가 읽을 CSV 내보내기 작업 하나만 붙입니다. |
| PostgreSQL | 네 입력 표와 판정 결과 표를 담고, 과정별 기준 개정일 인덱스로 발급 뒤 개정 조회를 빠르게 합니다. |
| Next.js | 판정별 목록과 수강생 한 명을 펼치는 상세 화면을 그립니다. |
| React | 판정 묶음과 강의별 시청 막대를 그리는 컴포넌트입니다. |
| TailwindCSS | 60대 수강생 기록을 읽는 화면이라 큰 글자와 판정별 색을 한 벌의 규칙으로 맞춥니다. |
| Redis Cache | 3주차 에이전트 조회가 같은 수강생을 여러 번 물을 때 판정 결과를 잠시 담아 둡니다. |
| AWS ECS | 2주차부터 판정 배치를 예약 작업으로 매일 새벽 돌립니다. |
| AWS ECR | 판정 배치 이미지를 올려 두는 저장소입니다. |
| GitHub Actions | 판정 규칙 테스트를 PR 마다 돌리고 통과하면 이미지를 ECR 에 올립니다. |
| Sentry | 배치가 입력 파일을 못 읽거나 판정 도중 멈추면 오류를 모읍니다. |
| CloudWatch | 배치 소요 시간과 판정별 건수를 날마다 남겨 발급 누락이 갑자기 늘면 알 수 있게 합니다. |
| Claude | 판정과 근거 값을 받아 CX 담당자가 수강생에게 읽어 줄 쉬운 말 한 문장을 씁니다. |
| MCP | 3주차에 판정 조회를 에이전트 도구로 열어 CX 담당자가 대화로 수강생을 조회합니다. |
✓ pass → 여기서 멈춰도 CX 담당자가 쓰는 완결품입니다. 더 키우려면 2주차 새벽 배치와 정답 대조로 넘어갑니다.
✓ pass → 3주차 CX 에이전트 연결로 넘어갑니다. 일치율이 낮은 판정이 있으면 그 조건부터 규칙 표를 다시 봅니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · user_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
기준대로 발급됨정상 발급기준을 채웠는데 수료증이 없음발급 누락시청 시간 미달인데 발급됨시청 미달 발급시험 점수 미달인데 발급됨점수 미달 발급발급 뒤 과정 기준이 바뀜발급 후 기준 개정수료증 발급 판정 형식입니다. 수강생·과정 쌍 하나가 과정 기준과 견줘 어떤 판정을 받는지 남기고, 판정은 규칙이 정하며 수강생에게 읽어 줄 문장만 모델이 씁니다.
{"type":"object","properties":{"user_id":{"type":"string"},"course_id":{"type":"string"},"cert_no":{"type":["string","null"],"description":"certificates 에서 찾은 수료증 번호입니다. 수료증이 없으면 비웁니다."},"verdict":{"type":"string","enum":["기준대로 발급됨","기준을 채웠는데 수료증이 없음","시청 시간 미달인데 발급됨","시험 점수 미달인데 발급됨","발급 뒤 과정 기준이 바뀜"]},"short_lesson_id":{"type":["string","null"],"description":"watched_sec 가 min_watch_sec 에 못 미친 강의 가운데 차이가 가장 큰 lesson_id 입니다. 시청 기록이 한 줄도 없으면 course_requirements 의 첫 강의를 적습니다. verdict 가 발급 뒤 과정 기준이 바뀜이면 발급 당시 기준을 알 수 없어 비웁니다."},"watch_gap_sec":{"type":["integer","null"],"description":"short_lesson_id 강의의 watched_sec 에서 min_watch_sec 를 뺀 값입니다. 시청 기록이 없어 watched_sec 가 없거나 short_lesson_id 가 비면 함께 비웁니다."},"best_score":{"type":["integer","null"],"description":"그 user_id·course_id 의 exam_results.score 가운데 가장 높은 값입니다. 채점된 점수가 하나도 없으면 비웁니다."},"score_gap":{"type":["integer","null"],"description":"best_score 에서 pass_score 를 뺀 값입니다. best_score 가 비면 함께 비웁니다."},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"],"enum":["count","sum","avg","min","max",null]},"value":{}},"required":["field","value"]}},"summary":{"type":"string","description":"CX 담당자가 수강생에게 전화로 그대로 읽어 줄 수 있는 쉬운 말 한 문장입니다."},"hedge":{"type":["string","null"]}},"required":["user_id","course_id","cert_no","verdict","evidence","summary"]}[
{
"user_id": "U-10418",
"course_id": "C-CARE",
"cert_no": "CA-260913-0418",
"verdict": "기준대로 발급됨",
"short_lesson_id": null,
"watch_gap_sec": null,
"best_score": 91,
"score_gap": 11,
"evidence": [
{
"field": "user_id",
"value": "U-10418"
},
{
"field": "watched_sec",
"agg": "min",
"value": 1830
},
{
"field": "min_watch_sec",
"value": 1800
},
{
"field": "score",
"agg": "max",
"value": 91
},
{
"field": "pass_score",
"value": 80
}
],
"summary": "두 강의를 모두 기준 이상 들으셨고 시험도 91점으로 통과하셔서 수료증이 제대로 나갔습니다.",
"hedge": null
},
{
"user_id": "U-10655",
"course_id": "C-CARE",
"cert_no": "CA-260904-0655",
"verdict": "기준대로 발급됨",
"short_lesson_id": null,
"watch_gap_sec": null,
"best_score": 80,
"score_gap": 0,
"evidence": [
{
"field": "user_id",
"value": "U-10655"
},
{
"field": "lesson_id",
"value": "CARE-00"
},
{
"field": "score",
"agg": "max",
"value": 80
},
{
"field": "pass_score",
"value": 80
}
],
"summary": "시험 점수가 합격선과 같은 80점이라 통과로 보았고 수료증도 제대로 나갔습니다.",
"hedge": "CARE-00 시청 기록은 지금 과정 기준에 없는 강의라 판정에서 뺐습니다. 개편 전 강의를 들은 기록으로 보입니다."
},
{
"user_id": "U-10502",
"course_id": "C-CARE",
"cert_no": null,
"verdict": "기준을 채웠는데 수료증이 없음",
"short_lesson_id": null,
"watch_gap_sec": null,
"best_score": 86,
"score_gap": 6,
"evidence": [
{
"field": "user_id",
"value": "U-10502"
},
{
"field": "watched_sec",
"value": 1800
},
{
"field": "min_watch_sec",
"value": 1800
},
{
"field": "score",
"agg": "max",
"value": 86
},
{
"field": "pass_score",
"value": 80
},
{
"field": "cert_no",
"agg": "count",
"value": 0
}
],
"summary": "9월 17일에 시험까지 마쳐 수료 조건을 모두 채우셨는데 수료증이 아직 나가지 않았습니다.",
"hedge": "CARE-01 시청 시간이 기준과 같은 1800초라 이상 조건을 채운 것으로 봤습니다."
},
{
"user_id": "U-10577",
"course_id": "C-CARE",
"cert_no": "CA-260908-0577",
"verdict": "시청 시간 미달인데 발급됨",
"short_lesson_id": "CARE-02",
"watch_gap_sec": -1260,
"best_score": 83,
"score_gap": 3,
"evidence": [
{
"field": "user_id",
"value": "U-10577"
},
{
"field": "lesson_id",
"value": "CARE-02"
},
{
"field": "watched_sec",
"value": 540
},
{
"field": "min_watch_sec",
"value": 1800
},
{
"field": "completed_at",
"value": null
},
{
"field": "score",
"agg": "max",
"value": 83
},
{
"field": "pass_score",
"value": 80
}
],
"summary": "2강을 9분만 들으셨는데 수료증이 먼저 나갔습니다. 2강을 마저 들으셔야 합니다.",
"hedge": "CARE-02 는 완료 시각도 비어 있어 아직 듣는 중인 강의입니다."
},
{
"user_id": "U-10610",
"course_id": "C-SILV",
"cert_no": "CA-260828-0610",
"verdict": "시험 점수 미달인데 발급됨",
"short_lesson_id": null,
"watch_gap_sec": null,
"best_score": 58,
"score_gap": -2,
"evidence": [
{
"field": "user_id",
"value": "U-10610"
},
{
"field": "attempt_no",
"agg": "max",
"value": 2
},
{
"field": "score",
"agg": "max",
"value": 58
},
{
"field": "pass_score",
"value": 60
}
],
"summary": "두 번 보신 시험 가운데 높은 점수가 58점으로 합격선 60점에 2점 모자란데 수료증이 나갔습니다.",
"hedge": null
},
{
"user_id": "U-10702",
"course_id": "C-CARE",
"cert_no": "CA-260920-0702",
"verdict": "시험 점수 미달인데 발급됨",
"short_lesson_id": null,
"watch_gap_sec": null,
"best_score": null,
"score_gap": null,
"evidence": [
{
"field": "user_id",
"value": "U-10702"
},
{
"field": "score",
"value": null
},
{
"field": "taken_at",
"value": "2026-09-20T20:30:00"
},
{
"field": "issued_at",
"value": "2026-09-20T20:31:00"
}
],
"summary": "시험 점수가 아직 매겨지지 않았는데 응시 1분 뒤에 수료증이 나갔습니다.",
"hedge": "점수가 비어 있어 모자란 점수를 계산하지 않았습니다. 채점이 끝나면 다시 판정합니다."
},
{
"user_id": "U-10233",
"course_id": "C-HOSP",
"cert_no": "CA-260902-0233",
"verdict": "발급 뒤 과정 기준이 바뀜",
"short_lesson_id": null,
"watch_gap_sec": null,
"best_score": 88,
"score_gap": 18,
"evidence": [
{
"field": "user_id",
"value": "U-10233"
},
{
"field": "lesson_id",
"value": "HOSP-03"
},
{
"field": "revised_at",
"value": "2026-09-15"
},
{
"field": "issued_at",
"value": "2026-09-02T09:00:00"
},
{
"field": "score",
"agg": "max",
"value": 88
},
{
"field": "pass_score",
"value": 70
}
],
"summary": "수료증은 9월 2일에 나갔고 3강 기준이 9월 15일에 바뀌어, 지금 기준으로 다시 볼지 운영팀이 정해야 합니다.",
"hedge": "지금 기준으로는 HOSP-03 시청 1520초가 1800초에 모자라지만, 발급 당시 기준값은 이 표에 남아 있지 않아 미달로 보지 않았습니다."
},
{
"user_id": "U-10845",
"course_id": "C-CARE",
"cert_no": "CA-260720-0845",
"verdict": "시청 시간 미달인데 발급됨",
"short_lesson_id": "CARE-01",
"watch_gap_sec": null,
"best_score": null,
"score_gap": null,
"evidence": [
{
"field": "user_id",
"value": "U-10845"
},
{
"field": "watched_sec",
"agg": "count",
"value": 0
},
{
"field": "issued_at",
"value": "2026-07-20T09:00:00"
}
],
"summary": "수료증은 7월 20일에 나갔는데 이 과정 강의를 들은 기록이 한 줄도 없습니다.",
"hedge": "시청 기록이 아예 없어 모자란 시간을 초로 계산하지 않았습니다. 시험 기록도 없어 점수는 보지 않았습니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 다섯 값 가운데 하나인지 확인합니다. 2. user_id·course_id·cert_no·verdict·evidence·summary 필드가 빠짐없이 존재하는지 확인합니다. 3. watched_sec 와 min_watch_sec 는 0 이상, score 와 pass_score 는 0~100 범위인지 확인합니다. 4. watch_gap_sec 는 evidence 의 watched_sec 에서 min_watch_sec 를 뺀 값과 같은지, score_gap 은 evidence 의 score 최댓값에서 pass_score 를 뺀 값과 같은지 차이를 다시 계산합니다. 5. certificates.issued_at 보다 뒤인 revised_at 이 그 과정에 하나라도 있으면 verdict 는 발급 뒤 과정 기준이 바뀜입니다. 6. 수료증이 있고 course_requirements 의 lesson_id 가운데 watched_sec 가 min_watch_sec 에 못 미치거나 시청 기록이 없는 강의가 있으면 verdict 는 시청 시간 미달인데 발급됨입니다. 7. 수료증이 있고 score 최댓값이 pass_score 에 못 미치거나 점수가 비어 있으면 verdict 는 시험 점수 미달인데 발급됨입니다. 8. 수료증이 없고 모든 lesson_id 의 watched_sec 가 min_watch_sec 이상이며 score 최댓값이 pass_score 이상이면 verdict 는 기준을 채웠는데 수료증이 없음입니다. 9. 5~7번이 둘 이상 걸리면 5, 6, 7번 차례로 하나만 남기고, 수료증이 있는데 하나도 안 걸리면 verdict 는 기준대로 발급됨입니다. 10. evidence 의 field 이름이 네 입력 표의 컬럼에 실재하는지 대조합니다. 11. agg 가 없는 evidence 의 value 가 수록된 행의 원문 값과 같은지 확인합니다. 12. summary 와 hedge 가 존댓말로 끝나는지 확인합니다. [판정 대상에서 빠지는 행] 수료증이 없고 기준도 못 채운 쌍(U-10811 처럼 수강 중인 사람)은 판정하지 않습니다. course_requirements 에 없는 lesson_id(U-10655 의 CARE-00)는 과정 개편 전 강의라 시청 여부를 따지지 않고 hedge 에 적습니다. 시청 시간이 기준과 꼭 같으면(U-10502 의 CARE-01) 이상 조건이라 채운 것으로 봅니다. 발급 뒤 과정 기준이 바뀜으로 나간 쌍(U-10233)은 지금 기준으로 모자란 강의가 있어도 short_lesson_id 와 watch_gap_sec 를 비우고, 그 강의와 값은 hedge 에만 적습니다. [평가 러너 · 2주차] 수강생·과정 쌍 180건에 손으로 정답 판정을 매겨 자동 판정과 대조하고, 판정별 일치율과 어긋난 쌍 목록을 냅니다. [수동 검증 · 사람이 봅니다] 1. 발급 뒤 과정 기준이 바뀜으로 나온 쌍은 운영 담당자가 옛 기준으로 발급이 맞았는지 과정 개편 기록을 보고 정합니다. 2. summary 를 60대 수강생에게 읽어 줬을 때 어려운 말이 없는지 CX 담당자가 스무 건을 골라 읽어 봅니다.