데이원컴퍼니
경력근거: 3단계 루틴 학습 구조를 통해 바쁜 일상 속에서도 실질적인 말하기 실력을 체화할 수 있도록 돕습니다
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 8분 예습과 25분 실시간 수업과 자가 복습으로 이어지는 3단계 루틴을 제품의 뼈대로 설명하고, 기능마다 성공을 판정할 지표와 이벤트를 함께 설계하며 비개발 직군도 검증에 참여하는 툴을 만들라고 적습니다. 이 포트폴리오는 그 셋을 한 화면에 겹칩니다. 단계별 이벤트를 이어 붙여 끊긴 자리를 지표로 만들고, 운영팀이 직접 여는 화면으로 냅니다. 다만 이 포트폴리오는 이벤트 연결과 판정 화면 구현을 보여줍니다. 외국어 교수법이나 튜터 매칭 기준에 대한 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
포도 스피킹은 8분 예습과 25분 실시간 수업과 자가 복습으로 이어지는 3단계 루틴 학습 구조를 제품 설명의 중심에 둡니다. 기능을 붙일 때도 이 세 단계 중 어디를 다루는지가 먼저 정해집니다.
서비스 가설을 정의하고 가장 빠르게 확인할 방법을 골라 구현하며, 기능 개발 시 성공을 판정할 지표와 이벤트를 함께 설계한다고 적혀 있습니다. 만들고 끝내는 자리가 아니라 지표를 같이 놓는 자리입니다.
비개발 직군도 개발과 검증 사이클에 참여할 수 있는 툴을 만든다고 업무에 적습니다. 운영팀이 직접 쓰는 화면이 결과물이 되는 자리라 화면의 읽기 쉬움이 그대로 성과가 됩니다.
운영 담당자가 주간 수업 기록을 열면, 예습과 수업과 복습 세 단계를 한 줄로 이어 붙여 어느 단계에서 끊겼는지 단계별로 나눠 보여줍니다. 끊김 판정은 단계별 기록 유무와 소요 시간을 비교하는 규칙이 정하고, 모델은 어느 단계가 왜 끊겼는지 담당자가 읽을 한 줄로 요약합니다.
저는 회화 서비스 운영을 맡고 있습니다. 이탈이 보이면 연락을 돌리는데, 목록에는 출석률만 있어 예습을 안 하고 들어와 수업이 겉돈 수강생과 수업은 못 왔지만 복습을 챙긴 수강생이 같은 줄로 보입니다. 누구에게 무슨 말을 해야 하는지가 목록에서 안 나옵니다.
이탈 대응이 늦는 이유는 지표가 없어서가 아니라 세 단계가 하나의 출석률로 뭉개지기 때문입니다. 단계별로 끊긴 자리를 나눠 주면, 예습을 건너뛴 사람에게는 예습 안내를 수업을 빠진 사람에게는 일정 조정을 보내는 식으로 연락 내용이 달라집니다.
운영 담당자가 쓰는 화면 하나가 완결됩니다. 주간 수업 기록을 올리면 예습·수업·복습을 한 줄로 이어 붙여 끊긴 단계를 나눠 보여주고, 판정 근거가 된 기록을 같이 냅니다. 실제 서비스 이벤트를 붙이는 일은 파일 업로드로 대신하고 3주차로 미룹니다.
이전 주 판정과 다음 주 수업 참여 기록 100건을 맞춰 보고, 끊김 유형별로 실제 이탈로 이어진 비율을 표로 냅니다. 어느 유형에 먼저 연락해야 하는지가 그 표에서 나옵니다.
예습 최소 시간과 복습 마감 같은 기준을 화면에서 바꿔 저장하면 판정이 다시 돌아 이전 기준과 나란히 비교됩니다. 개발자를 거치지 않고 운영팀이 기준을 조정합니다.
운영 담당자가 주간 수업 기록을 올리면 예습·수업·복습이 한 줄로 이어지고, 끊긴 단계가 수강생별로 갈라져 보입니다.
예습 기록이 비거나 같은 회차가 두 번 올라오거나 등록되지 않은 수강생 번호가 섞인 입력 30종을 올려도 화면이 죽지 않고, 판정할 수 없는 회차는 미판정으로 남습니다.
이전 주 판정과 다음 주 참여 기록 100건을 맞춰 유형별 이탈 비율을 냅니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 더미데이터 자가채점이며 실제 이탈 예측력은 데이원컴퍼니의 실 수업 이벤트가 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 예습 기록 결측·같은 회차 중복 업로드·등록되지 않은 수강생 번호를 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
예습을 건너뛰었다는 사실이 곧 이탈을 뜻하지는 않습니다. 이미 말하기가 익은 수강생은 예습 없이도 수업을 끌고 가므로 판정을 결론으로 읽지 않게 근거 기록을 항상 같이 보여줍니다.
실제 도입에서는 언어와 레벨마다 예습 분량과 복습 마감이 달라 기준이 하나로 서지 않습니다. 기준이 늘어나면 규칙을 코드에서 빼 설정으로 옮겨야 하며, 그것은 도입 단계 과제로 분리합니다.
수강생에게 연락을 보내는 발송은 이 포트폴리오의 범위 밖입니다. 여기서는 누구에게 무슨 말을 할지 정하는 목록을 내는 데까지만 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
한 주간의 수업 회차 기록입니다. 판정의 입력이며 예습 기록 결측과 같은 회차 중복, 등록되지 않은 수강생 번호를 의도적으로 포함합니다.
| session_no | scheduled_at | learner_code_raw | language | level | prep_minutes | attended | talk_minutes | review_done_at | tutor_code | note |
|---|---|---|---|---|---|---|---|---|---|---|
| SS-2026W33-001 | 2026-08-10T20:00:00 | LN-1001 | en | B1 | 8.5 | true | 24 | 2026-08-10T22:10:00 | TU-31 | — |
| SS-2026W33-002 | 2026-08-10T21:00:00 | LN-1002 | en | A2 | 0 | true | 11.5 | — | TU-31 | 예습 없이 입장 |
| SS-2026W33-003 | 2026-08-11T07:00:00 | LN-1003 | ja | B2 | 9 | false | — | — | TU-07 | 당일 취소 |
| SS-2026W33-004 | 2026-08-11T20:00:00 | LN-1004 | en | — | — | true | 25 | 2026-08-12T09:30:00 | TU-12 | 레벨 미배정 |
| SS-2026W33-005 | 2026-08-12T06:30:00 | LN-1005 | ja | A1 | 12 | true | 25 | 2026-08-12T08:00:00 | TU-07 | — |
아래 JSON Schema 에 맞는 수업 회차 행을 더 만들어 주세요. {"type":"object","properties":{"session_no":{"type":"string"},"scheduled_at":{"type":"string"},"learner_code_raw":{"type":"string"},"language":{"type":"string","enum":["ko","en","ja"]},"level":{"type":["string","null"]},"prep_minutes":{"type":["number","null"]},"attended":{"type":"boolean"},"talk_minutes":{"type":["number","null"]},"review_done_at":{"type":["string","null"]},"tutor_code":{"type":"string"},"note":{"type":["string","null"]}},"required":["session_no","scheduled_at","learner_code_raw","language","attended","tutor_code"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) prep_minutes 나 level 이 null 인 행, (2) prep_minutes 가 0 이거나 talk_minutes 가 0 인 경계 행, (3) 같은 session_no 가 두 번 올라온 중복 행, (4) learner_code_raw 의 구분자와 대소문자가 흔들린 행과 등록 목록에 없는 번호. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"session_no":{"type":"string"},"scheduled_at":{"type":"string"},"learner_code_raw":{"type":"string"},"language":{"type":"string","enum":["ko","en","ja"]},"level":{"type":["string","null"]},"prep_minutes":{"type":["number","null"]},"attended":{"type":"boolean"},"talk_minutes":{"type":["number","null"]},"review_done_at":{"type":["string","null"]},"tutor_code":{"type":"string"},"note":{"type":["string","null"]}},"required":["session_no","scheduled_at","learner_code_raw","language","attended","tutor_code"]}| TypeScript | 화면과 판정 규칙을 함께 쓰는 언어입니다. 단계 연결 규칙을 한 곳에 두고 화면과 러너가 같은 코드를 씁니다. |
| React | 운영 담당자가 보는 어드민 화면입니다. 끊김 유형별 묶음과 근거 기록을 한 화면에 둡니다. |
| Node.js | 기록 업로드와 판정 실행을 맡는 백엔드입니다. |
| PostgreSQL | 수업 기록과 단계별 이벤트를 담습니다. 수강생과 회차를 키로 세 단계를 잇는 스키마가 판정의 정확도를 좌우합니다. |
| AWS | 3주차부터 배포와 운영을 맡습니다. 기준 변경 이력과 판정 결과를 남깁니다. |
| LLM | 끊김 판정이 끝난 뒤 그 이유를 담당자가 읽을 한 줄로 요약합니다. 어느 단계에서 끊겼는지는 규칙이 정하고 모델은 여기에 개입하지 않습니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 그 판정이 다음 주 이탈로 이어졌는지 확인하는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 운영팀이 기준을 직접 바꿔 결과를 비교하는 단계로 확장합니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
break_code사유 라벨 · session_no + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
on_track정상 진행prep_skipped예습 누락class_missed수업 결석review_missed복습 미완duplicate_session회차 중복learner_unmapped수강생 미매핑한 회차의 루틴 판정입니다. 끊긴 단계 코드와 근거가 된 기록을 함께 남깁니다.
{"type":"object","properties":{"check_id":{"type":"string"},"session_no":{"type":"string"},"learner_code":{"type":["string","null"]},"break_code":{"type":"string","enum":["on_track","prep_skipped","class_missed","review_missed","duplicate_session","learner_unmapped"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{"type":["string","number","boolean","null"]}},"required":["field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["check_id","session_no","break_code","evidence","summary"]}[
{
"check_id": "ck_0001",
"session_no": "SS-2026W33-001",
"learner_code": "LN-1001",
"break_code": "on_track",
"evidence": [
{
"field": "prep_minutes",
"agg": null,
"value": 8.5
},
{
"field": "review_done_at",
"agg": null,
"value": "2026-08-10T22:10:00"
}
],
"summary": "예습과 수업과 복습이 모두 남아 있어 루틴이 이어졌습니다.",
"hedge": null
},
{
"check_id": "ck_0002",
"session_no": "SS-2026W33-005",
"learner_code": "LN-1005",
"break_code": "on_track",
"evidence": [
{
"field": "prep_minutes",
"agg": null,
"value": 12
},
{
"field": "talk_minutes",
"agg": null,
"value": 25
}
],
"summary": "예습을 채우고 수업도 끝까지 이어져 조치가 필요하지 않습니다.",
"hedge": null
},
{
"check_id": "ck_0003",
"session_no": "SS-2026W33-002",
"learner_code": "LN-1002",
"break_code": "prep_skipped",
"evidence": [
{
"field": "prep_minutes",
"agg": null,
"value": 0
},
{
"field": "talk_minutes",
"agg": null,
"value": 11.5
}
],
"summary": "예습 없이 들어와 발화가 절반에 그쳤습니다.",
"hedge": null
},
{
"check_id": "ck_0004",
"session_no": "SS-2026W33-003",
"learner_code": "LN-1003",
"break_code": "class_missed",
"evidence": [
{
"field": "attended",
"agg": null,
"value": false
},
{
"field": "prep_minutes",
"agg": null,
"value": 9
}
],
"summary": "예습은 마쳤는데 수업에 오지 못해 일정 조정이 필요합니다.",
"hedge": null
},
{
"check_id": "ck_0005",
"session_no": "SS-2026W33-006",
"learner_code": "LN-1006",
"break_code": "review_missed",
"evidence": [
{
"field": "review_done_at",
"agg": null,
"value": null
},
{
"field": "talk_minutes",
"agg": null,
"value": 25
}
],
"summary": "수업은 끝까지 했지만 복습 기록이 남지 않았습니다.",
"hedge": null
},
{
"check_id": "ck_0006",
"session_no": "SS-2026W33-010",
"learner_code": "LN-1009",
"break_code": "duplicate_session",
"evidence": [
{
"field": "session_no",
"agg": "count",
"value": 2
},
{
"field": "talk_minutes",
"agg": null,
"value": 0
}
],
"summary": "같은 회차가 두 번 올라와 한 건으로 묶고 발화 0분은 따로 확인 대상으로 둡니다.",
"hedge": "합성 기준 판정이라 실제로는 재접속 기록이 두 줄로 남은 것일 수 있습니다."
},
{
"check_id": "ck_0007",
"session_no": "SS-2026W33-011",
"learner_code": null,
"break_code": "learner_unmapped",
"evidence": [
{
"field": "learner_code_raw",
"agg": null,
"value": "LN-9999"
},
{
"field": "note",
"agg": null,
"value": "등록 목록에 없는 수강생 번호"
}
],
"summary": "등록 목록에 없는 번호라 어느 수강생인지 붙지 않아 판정을 미룹니다.",
"hedge": null
},
{
"check_id": "ck_0008",
"session_no": "SS-2026W33-008",
"learner_code": "LN-1007",
"break_code": "prep_skipped",
"evidence": [
{
"field": "prep_minutes",
"agg": null,
"value": 0.5
},
{
"field": "talk_minutes",
"agg": null,
"value": 25
}
],
"summary": "예습이 기준에 못 미쳤지만 발화는 가득 채워 연락 우선순위는 낮게 둡니다.",
"hedge": "합성 기준 판정이라 상위 레벨 수강생에게는 예습 기준을 달리 잡아야 할 수 있습니다."
}
][자동 검증 · 코드가 판정합니다] 1. break_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. check_id·session_no·break_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. prep_minutes 가 0 이상이고 talk_minutes 가 0 이상 30 이하인지 확인합니다. 4. evidence 의 field 이름이 수업 기록에 실재하는 컬럼인지 대조합니다. 5. agg 가 없는 evidence 의 value 가 그 session_no 행의 값과 같은지 확인합니다. 6. attended 가 false 이면 break_code 는 prep_skipped 가 아닌지 확인합니다. 기록에는 결석 행과 출석 행이 모두 있습니다. 7. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 이전 주 판정과 다음 주 수업 참여 기록 100건을 비교해 끊김 유형별로 실제 이탈로 이어진 비율을 냅니다. [수동 검증 · 사람이 봅니다] 1. 예습이 짧아도 발화가 충분한 회차를 끊김으로 볼지 표본 20건을 눈으로 확인합니다. 2. 결석했지만 복습을 마친 회차의 연락 문구는 운영 기준을 따라 담당자가 정합니다.