블록에스
경력근거: 에이전트가 만든 결과물을 시스템 무결성 기준으로 검증해 프로덕션 품질까지 끌고 가는
'기획서, 화면 흐름, API 요구사항, 작업 지시서를 AI와 사람이 모두 이해할 수 있는 형태로 정리합니다' 라는 공고 문장이 이 보드의 산출물을 그대로 정의합니다. 코드 작성 전에 만들지 않아도 되는 기능을 식별하라는 요구도 카드를 만들지 않음 열로 옮기고 그 이유를 지시서에 남기는 화면 동작으로 옮겼습니다. 이 포트폴리오가 보여주는 것은 요구 조각을 원문 인용에 묶어 사람이 화면에서 확정하게 만드는 제품 설계와 확정 결과·산출물 일치를 규칙으로 대조하는 구현이며, 블록에스의 사업 도메인 전문성은 주장하지 않습니다. 우선순위가 갈리는 실제 사업 맥락은 면접에서 모른다고 말하는 편이 낫습니다.
공고가 모호한 요구사항을 빠르게 구조화하고 MVP 범위와 우선순위를 설정하는 사람을 찾는다고 밝힙니다. 요청 원문을 조각 카드로 펼쳐 사람이 화면에서 범위를 나누는 이 보드가 그 판단을 그대로 제품화합니다.
코드 작성 전에 만들지 않아도 되는 기능을 식별하고 왜 필요한가를 먼저 판단하라고 요구합니다. 카드를 만들지 않음 열로 옮기고 그 이유를 지시서에 남기는 화면 동작이 이 요구와 같습니다.
기획서와 작업 지시서를 AI 와 사람이 모두 이해할 수 있는 형태로 정리하고 MCP 서버·디스패치 시스템을 설계한 경험을 우대합니다. 확정 결과를 지시서로 굳혀 에이전트에 넘기는 이 보드의 구조가 그 방식과 같습니다.
자사 서비스에 들어온 기능 요청 한 건을 붙여넣으면 요구가 섞인 문장을 조각 카드로 펼쳐 주고, 사람이 그 카드를 이번 범위·다음·만들지 않음 세 열로 옮기며 빠진 조건에 답해야 코딩 에이전트에 넘길 작업 지시서가 만들어지는 보드입니다. 미결 카드가 하나라도 남으면 지시서 생성이 잠기고, 지시서에는 사람이 이번 범위로 옮긴 카드만 담습니다.
자사 서비스에 들어온 기능 요청을 받아 이번에 무엇을 만들지 정하고 코딩 에이전트에 넘기는 프로덕트 엔지니어입니다. 슬랙 한 줄과 회의 메모에 요구가 서너 개씩 뒤섞여 있어 그대로 던지면 에이전트가 시키지 않은 것까지 만들어 오고 정작 빠진 조건은 알아서 지어내던 사람이라, 넘기기 전에 범위를 자기 손으로 확정해 두고 싶어 합니다.
요청 원문을 문장 조각 카드로 펼쳐 사람이 화면에서 직접 범위를 나누고 빠진 조건에 답하게 하면, 지시서에 들어갈 항목과 빠질 항목이 사람 손에서 확정되므로 에이전트가 범위를 스스로 넓히거나 조건을 지어낼 자리가 사라집니다.
요청 원문을 인용 구간이 붙은 조각 카드로 쪼개는 분해기와 세 열 보드, 확인 질문 답변 입력, 확정분만 담은 작업 지시서 생성까지 Next.js 한 앱으로 묶어 Vercel 에 배포한 완결품입니다. 미결 카드가 남으면 생성 버튼이 잠깁니다. 실제 에이전트 연동 대신 더미 기능 요청을 입력으로 시작합니다.
원문 문장 중 어느 카드에도 담기지 않은 구간을 표시하는 커버리지 검사와 서로 어긋나는 카드 짝 탐지, 사람 라벨 대비 조각 누락 재현율 리포트가 남은 상태입니다.
확정 지시서와 범위 밖 카드 목록을 MCP 서버로 코딩 에이전트에 넘기고, 돌아온 변경이 만들지 않음으로 분류한 카드를 건드렸는지 보드에서 대조해 사람이 반려하는 흐름이 붙은 상태입니다.
요구 네 개가 섞인 요청을 넣고 카드를 세 열로 나눈 뒤 빠진 조건에 답해야 지시서가 열리는 흐름을 화면에서 시연합니다.
요구가 뒤엉키거나 서로 어긋나거나 이미 있는 기능을 다시 요청하는 더미 30건을 넣어도 카드의 인용 구간이 항상 원문의 실제 부분 문자열이고 지시서 항목이 확정 카드 집합과 어긋나지 않음을 보여줍니다.
원문 중 어느 카드에도 담기지 않은 구간을 잡아내는 재현율을 보여줍니다(표본: 라벨 요청 60건, 자가 라벨, 재현율은 사람이 요구로 표시한 구간 중 카드로 잡힌 비율).
위 수치는 작성자가 만든 더미 요청과 자가 라벨 기준이라 성능 증명이 아니라 데모 조건 동작이며, 실제 요청 분포에서 쪼개기가 얼마나 맞는지는 블록에스에 실제로 들어오는 요청 기록이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
재현율은 작성자가 만든 더미 요청과 자가 라벨 기준의 자가채점이라 성능 증명이 아닙니다. 면접에서는 화면 동작과 검증 구조로 솔직하게 설명합니다.
정답을 내가 만들었으니 쪼개기가 잘 되는 게 당연해지는 함정이 있어, 요구와 배경 설명이 한 문장에 섞인 경우와 요구처럼 보이지만 불평인 문장을 더미에 일부러 넣습니다.
이 포트폴리오가 보여주는 것은 사람이 화면에서 확정해야 산출물이 나오는 제품 설계와 확정 결과 대조 구현이며, 블록에스의 사업 도메인 전문성은 주장하지 않습니다. 우선순위 판단에 필요한 실제 사업 맥락은 면접에서 모른다고 말하는 편이 낫습니다.
쪼개기는 문장 단위 인용까지만 책임지고 요구 사이의 숨은 의존 관계는 잡지 못하므로, 보드가 결정을 대신하지 않고 사람 결정을 붙잡아 두는 자리에 있음을 분명히 둡니다.
실서비스 전환 시 팀 백로그 도구 연결과 조직별 확인 질문 체크리스트 반영이 필요합니다. 이는 도입 단계 과제로 분리합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
보드의 입력이 되는 자사 서비스 기능 요청 원문입니다. 요구가 한 문단에 여러 개 섞이거나 조건이 빠지거나 서로 어긋나는 경우를 일부러 담아 분해기와 화면 잠금 분기를 시험합니다.
| request_id | source | product_area | requester_role | raw_text | due_hint |
|---|---|---|---|---|---|
| R-001 | slack | 검색 | CS 매니저 | 검색이 너무 느리다고 CS 에 계속 들어와요. 결과도 오래된 게 위로… | 이번 주 |
| R-002 | cs_inquiry | 알림 | 고객 | 주문 상태가 바뀌었는데 알림이 안 와요. 앱을 껐다 켜야 반영됩니다.… | — |
| R-003 | meeting_note | 결제 | 제품 리드 | 결제 실패했을 때 다시 시도하는 버튼을 넣기로 했습니다. 실패 사유도… | 다음 스프린트 |
| R-004 | backlog_comment | 로그인 | 기획자 | 소셜 로그인 붙여주세요. 카카오랑 네이버요. 기존 이메일 계정과 같은… | — |
| R-005 | backlog_comment | 정산 | 운영 담당 | 정산 화면에서 엑셀로 내려받게 해달라는 요청이 이번 달만 세 번입니다… | 이번 달 안 |
다음 JSON Schema 를 그대로 만족하는 기능 요청 행을 30개 더 만들어 주세요: {"type":"object","properties":{"request_id":{"type":"string"},"source":{"type":"string","enum":["slack","cs_inquiry","meeting_note","backlog_comment"]},"product_area":{"type":"string"},"requester_role":{"type":"string"},"raw_text":{"type":"string"},"due_hint":{"type":["string","null"]}},"required":["request_id","source","product_area","requester_role","raw_text"]} raw_text 는 실제 사람이 채널에 쓴 것처럼 다듬어지지 않은 한국어 문단이어야 합니다. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: (1) 한 문단에 서로 다른 요구가 셋 이상 섞인 요청 6건, (2) 수치·시각·범위 조건이 빠져 그대로는 만들 수 없는 요청 6건, (3) 같은 요청 안에서 서로 어긋나는 두 요구가 함께 나오는 경우 4건, (4) 이미 있는 기능을 없다고 여겨 다시 요청하는 경우 3건, (5) 요구처럼 들리지만 실은 불평·감상이라 만들 것이 없는 문단 3건, (6) 요구 문장과 배경 설명이 한 문장에 붙어 인용 구간을 어디서 끊을지 애매한 경우 3건.{"type":"object","properties":{"request_id":{"type":"string"},"source":{"type":"string","enum":["slack","cs_inquiry","meeting_note","backlog_comment"]},"product_area":{"type":"string"},"requester_role":{"type":"string"},"raw_text":{"type":"string"},"due_hint":{"type":["string","null"]}},"required":["request_id","source","product_area","requester_role","raw_text"]}사람이 보드 화면에서 내린 결정 기록입니다. 카드가 어느 열로 갔는지와 확인 질문에 답했는지를 담아 지시서 생성 잠금과 집합 일치 검사의 정답이 됩니다.
| card_id | request_id | quote | lane | open_question | answer | confirmed |
|---|---|---|---|---|---|---|
| C-001 | R-001 | 검색이 너무 느리다고 CS 에 계속 들어와요 | this_sprint | 느리다는 기준이 몇 초인지 원문에 없습니다. | 목록 첫 화면이 1초 안에 뜨는 것을 기준으로 합니다. | true |
| C-002 | R-001 | 결과도 오래된 게 위로 올라오고요 | this_sprint | 정렬을 최신순으로 바꿀지 관련도순을 유지할지 정해지지 않았습니다. | 관련도순을 기본으로 두고 최신순을 선택지로 넣습니다. | true |
| C-003 | R-001 | 검색어 자동완성도 있으면 좋겠는데 | next | — | — | true |
| C-004 | R-002 | 주문 상태가 바뀌었는데 알림이 안 와요 | this_sprint | — | — | true |
| C-005 | R-002 | 알림은 밤에는 안 왔으면 좋겠어요 | next | 밤의 시작과 끝 시각이 원문에 없습니다. | — | false |
다음 JSON Schema 를 그대로 만족하는 범위 결정 행을 30개 더 만들어 주세요: {"type":"object","properties":{"card_id":{"type":"string"},"request_id":{"type":"string"},"quote":{"type":"string"},"lane":{"type":"string","enum":["this_sprint","next","wont_do"]},"open_question":{"type":["string","null"]},"answer":{"type":["string","null"]},"confirmed":{"type":"boolean"}},"required":["card_id","request_id","quote","lane","confirmed"]} quote 는 반드시 같은 request_id 의 raw_text 에 그대로 들어 있는 부분 문자열이어야 합니다. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: (1) open_question 이 있는데 answer 가 비어 confirmed 가 false 인 미결 카드 6건(지시서 생성이 잠기는 경우), (2) 한 요청에서 나온 카드가 서로 다른 lane 으로 갈라지는 짝 5건, (3) 요구가 아니라 불평이라 wont_do 로 간 카드 4건, (4) 이미 있는 기능 재요청이라 wont_do 로 가고 answer 에 이유가 적힌 카드 3건, (5) 같은 요청 안 두 카드가 서로 어긋나 answer 로 절충한 짝 3건.{"type":"object","properties":{"card_id":{"type":"string"},"request_id":{"type":"string"},"quote":{"type":"string"},"lane":{"type":"string","enum":["this_sprint","next","wont_do"]},"open_question":{"type":["string","null"]},"answer":{"type":["string","null"]},"confirmed":{"type":"boolean"}},"required":["card_id","request_id","quote","lane","confirmed"]}| Claude | Claude Sonnet 이 요청 원문을 조각 카드 초안으로 쪼개고 확정된 카드를 지시서 문장으로 다듬습니다. 범위 결정은 사람이 화면에서 내리므로 모델은 초안만 냅니다. 개발도 Claude Code 로 진행해 공고가 요구하는 에이전트 활용 방식을 그대로 씁니다 |
| TypeScript | 카드 분해와 커버리지 대조, 지시서 생성 로직과 보드 화면이 함께 쓰는 언어입니다. 확정 집합과 지시서 항목이 일치하는지 확인하는 단위 테스트를 여기서 돌립니다 |
| Next.js | 세 열 보드와 확인 질문 답변, 지시서 미리보기를 한 앱으로 묶는 프레임워크이며 1주차 완성품을 Vercel 로 바로 배포합니다 |
| MCP | 3주차에 확정 지시서와 범위 밖 카드 목록을 코딩 에이전트가 읽도록 넘기는 서버입니다 |
| 컨테이너 | 3주차에 에이전트 실행을 격리해 돌리고 돌아온 변경만 보드로 회수하는 실행 환경입니다 |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 원문 커버리지 대조와 모순 카드 탐지로 넘어갑니다.
✓ pass → MCP 로 확정 지시서를 에이전트에 넘기고 돌아온 변경의 범위 이탈을 대조하는 단계로 넘어갑니다.
사람이 보드에서 확정한 결정 그대로 만들어진 코딩 에이전트 작업 지시서입니다. 담긴 항목은 이번 범위로 옮겨 확정된 카드와 정확히 같고, 각 항목은 원문 인용 구간에 묶입니다.
{"type":"object","properties":{"request_id":{"type":"string"},"scope":{"type":"array","items":{"type":"object","properties":{"card_id":{"type":"string"},"task_title":{"type":"string"},"acceptance":{"type":"array","items":{"type":"string"}},"quote":{"type":"string"}},"required":["card_id","task_title","acceptance","quote"]}},"out_of_scope":{"type":"array","items":{"type":"object","properties":{"card_id":{"type":"string"},"reason":{"type":"string"}},"required":["card_id","reason"]}},"resolved_questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"}},"required":["question","answer"]}},"summary":{"type":"string"},"hedge":{"type":"string"}},"required":["request_id","scope","out_of_scope","resolved_questions","summary","hedge"]}[
{
"request_id": "R-001",
"scope": [
{
"card_id": "C-001",
"task_title": "검색 목록 첫 화면 응답을 1초 안으로 줄입니다",
"acceptance": [
"검색어를 넣고 목록 첫 화면이 1초 안에 뜹니다",
"느려지는 구간을 재현하는 측정 스크립트가 함께 남습니다"
],
"quote": "검색이 너무 느리다고 CS 에 계속 들어와요"
},
{
"card_id": "C-002",
"task_title": "검색 정렬을 관련도순 기본에 최신순 선택으로 나눕니다",
"acceptance": [
"정렬 선택 없이 들어오면 관련도순으로 보입니다",
"최신순을 고르면 등록일 내림차순으로 다시 정렬됩니다"
],
"quote": "결과도 오래된 게 위로 올라오고요"
}
],
"out_of_scope": [
{
"card_id": "C-003",
"reason": "요청자가 급하지 않다고 밝혔고 다음 열로 옮겨졌습니다."
}
],
"resolved_questions": [
{
"question": "느리다는 기준이 몇 초인지 원문에 없습니다.",
"answer": "목록 첫 화면이 1초 안에 뜨는 것을 기준으로 합니다."
},
{
"question": "정렬을 최신순으로 바꿀지 관련도순을 유지할지 정해지지 않았습니다.",
"answer": "관련도순을 기본으로 두고 최신순을 선택지로 넣습니다."
}
],
"summary": "검색 속도와 정렬 두 가지만 이번 범위로 확정했고 자동완성은 제외했습니다. 두 항목 모두 답변으로 채워진 기준을 수용 조건에 담았습니다.",
"hedge": "이 지시서는 합성 요청 더미와 작성자의 화면 결정 기준으로 만들어졌으며, 실제 팀의 우선순위 기준은 반영되지 않았습니다."
},
{
"request_id": "R-004",
"scope": [
{
"card_id": "C-006",
"task_title": "카카오 소셜 로그인을 먼저 붙입니다",
"acceptance": [
"카카오 계정으로 가입과 로그인이 끝까지 됩니다",
"네이버 버튼은 이번 범위에 넣지 않습니다"
],
"quote": "소셜 로그인 붙여주세요"
},
{
"card_id": "C-007",
"task_title": "같은 메일 주소면 기존 이메일 계정에 소셜 로그인을 연결합니다",
"acceptance": [
"기존 계정과 같은 메일이면 새 계정을 만들지 않고 연결합니다",
"연결 시 기존 주문 내역이 그대로 보입니다"
],
"quote": "기존 이메일 계정과 같은 메일 주소로 가입하면 어떻게 되는지는 정해진 게 없네요"
}
],
"out_of_scope": [],
"resolved_questions": [
{
"question": "카카오와 네이버를 한 번에 붙일지 하나만 먼저 붙일지 정해지지 않았습니다.",
"answer": "카카오만 먼저 붙이고 네이버는 다음으로 미룹니다."
},
{
"question": "같은 메일 주소일 때 계정을 합칠지 따로 둘지 결정이 필요합니다.",
"answer": "기존 이메일 계정에 소셜 로그인을 연결하는 방식으로 합칩니다."
}
],
"summary": "원문에 비어 있던 두 결정을 화면에서 답한 뒤 카카오 연동과 계정 연결만 범위로 확정했습니다. 네이버 연동은 지시서에서 빠졌습니다.",
"hedge": "계정 연결 방식은 화면에서 사람이 고른 값이며 합성 기준이라, 실제 보안·약관 검토 결과가 반영되지 않았습니다."
},
{
"request_id": "R-008",
"scope": [],
"out_of_scope": [
{
"card_id": "C-011",
"reason": "만들 대상이 지목되지 않은 감상이라 요구로 세우지 못했습니다."
},
{
"card_id": "C-012",
"reason": "비교 대상과 시점이 없어 확인 질문으로도 좁혀지지 않았습니다."
}
],
"resolved_questions": [],
"summary": "이 요청에는 이번에 만들 항목이 없습니다. 두 조각 모두 만들지 않음으로 확정했고 요청자에게 무엇이 답답한지 되물을 문장을 남깁니다.",
"hedge": "빈 범위 판정은 합성 요청 기준이며, 실제로는 같은 문장이 사용성 조사로 이어질 수 있습니다."
}
][자동 검증 · 스크립트가 전수 수행] 1. 출력이 JSON Schema 형식을 통과하는지 확인합니다(형식 깨진 지시서가 에이전트로 나가는 것을 차단). 2. scope 의 card_id 집합이 lane 이 this_sprint 이면서 confirmed 가 true 인 카드 집합과 정확히 같은지 확인합니다(범위는 사람 결정이 정답). 3. wont_do 와 next 로 분류된 card_id 가 scope 에 하나도 없는지 확인합니다. 4. scope 의 모든 quote 가 해당 request_id 의 raw_text 에 실재하는 부분 문자열인지 확인합니다(지어낸 요구 차단). 5. confirmed 가 false 인 카드가 남아 있으면 지시서 생성 자체를 막고 화면의 잠금 상태와 일치하는지 확인합니다. [수동 검증 · 사람이 표본 확인] 1. task_title 과 acceptance 가 인용 구간과 답변을 넘어 요구를 부풀리지 않는지 지시서 30건을 표본 검토합니다. 2. out_of_scope 의 reason 이 화면에서 사람이 그 열로 옮긴 이유와 어긋나지 않는지 확인합니다.