스탯
경력근거: 데이터를 계산·가공·분석하여 대시보드, 통계, 그리드 및 차트 등으로 시각화
※ 실제 산출물이 아닌 AI 예측 시안입니다.
스탯은 주문과 재고와 광고와 손익과 정산 관리를 시작으로 셀러 업무를 하나로 잇는 운영 플랫폼을 만들고 있고, 이번 채용은 그 화면을 0에서 설계할 네 번째 팀원을 찾는 자리입니다. 흩어진 판매 숫자를 한 화면의 계산 결과로 바꾸는 문제가 스탯의 첫 제품 범위와 그대로 겹칩니다. 이 포트폴리오가 보여주는 것은 기능 단위 프론트엔드 구조 설계와 대량 행 그리드 처리와 서버 상태 분리이며, 이커머스 정산 실무나 세무 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
화면 구현을 넘어 구조를 설계하고 배포와 운영까지 끌고 가는 자리라서, 기능 단위로 폴더와 의존 방향을 정한 구조를 코드로 보입니다.
주문과 재고와 광고와 손익과 정산이 한 화면에 모이는 제품이라서, 대량 행을 끊김 없이 다루는 그리드와 집계 차트를 제품의 중심에 둡니다.
디자이너와 함께 디자인 시스템을 세우는 업무가 명시돼 있어, 화면마다 다시 만들지 않는 공통 컴포넌트 층을 분리해 둡니다.
온라인 셀러의 주문과 정산과 광고비 더미를 받아 상품별 손익을 계산하고, 수천 행을 끊김 없이 다루는 그리드와 기간별 차트로 보여주는 대시보드입니다. 판매액에서 원가와 판매 수수료와 광고비를 뺀 값이 정산 금액과 어긋나는 행을 규칙으로 짚어 어느 항목 때문인지 근거를 붙여 표시하고, 자연어로 쓴 조건은 그리드 필터로 바꿔 적용합니다. 주문과 정산 데이터는 실제 판매 데이터 대신 더미를 가정합니다.
판매 채널에서 받은 주문 파일과 정산 내역을 표 계산 프로그램에 붙여 상품별 손익을 매번 손으로 맞추고, 광고비를 반영하면 숫자가 어긋나는 상품을 눈으로 찾던 온라인 셀러 운영 담당자가, 어긋난 행의 근거와 계산된 손익을 한 화면에서 보고 싶은 사람입니다.
주문과 정산과 광고비를 하나의 계산 규칙으로 묶어 상품별 손익을 산출하고 차이가 나는 행을 규칙으로 판정하면, 셀러가 표 계산 프로그램을 오가지 않고 어느 항목에서 손익이 새는지 한 화면에서 확인할 수 있다는 가정입니다.
주문과 정산과 광고비 더미를 넣으면 상품별 손익을 계산해 수천 행 그리드와 기간별 차트로 보여주고, 정산 금액이 계산값과 어긋난 행을 규칙으로 짚어 근거를 붙여 표시하는 화면이 배포까지 끝난 완결품입니다. 기능 단위 폴더 구조와 공통 컴포넌트 층을 이 주차에 확정합니다.
1주차 그리드 위에서 광고비 대비 손익이 나쁜 상품처럼 말로 쓴 조건을 필터 조건으로 바꿔 적용하고, 자주 쓰는 조건을 저장해 다시 부르는 기능입니다. 변환 결과는 사람이 확인한 뒤 적용됩니다.
손익 계산 규칙과 어긋난 행 판정을 단위 테스트로 고정하고, 가짜 서버로 응답 지연과 실패를 흉내 내 화면이 어떻게 견디는지 확인하는 검증 묶음입니다.
주문과 정산과 광고비 더미를 올리면 상품별 손익 그리드와 기간별 차트가 그려지고, 어긋난 행마다 사유가 표시되는 흐름을 화면으로 시연합니다.
정산 누락과 환불로 인한 음수 금액과 수수료율 차이를 심어 둔 더미에서 그 세 유형이 모두 어긋난 행으로 잡히는 것을 보입니다. 다만 정답과 데이터를 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
값이 비어 있거나 상품코드가 마스터에 없거나 옵션 표기가 흔들리는 입력 30종을 넣어도 화면이 죽지 않고 해당 행을 미매핑으로 표시하는 것을 보입니다.
5천 행 규모의 주문 더미에서 정렬과 그룹 집계를 화면 멈춤 없이 돌려 보입니다.
위 수치는 더미데이터 자가채점이며, 실제 손익 정확도는 스탯의 실제 판매 데이터와 채널별 정산 규정이 있어야 측정 가능합니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모가 도는 상태와 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 정산이 비어 있는 행과 환불로 금액이 음수인 행과 마스터에 없는 상품코드를 더미에 일부러 섞어 두었습니다.
채널마다 수수료 산정 방식과 정산 주기가 달라, 실제로는 산식이 하나가 아닙니다. 채널별 규칙 분리는 도입 단계 과제로 따로 둡니다.
말로 쓴 조건을 필터로 바꾸는 변환은 틀릴 수 있어 사람 확인 단계를 두었습니다. 확인 없이 자동 적용하면 잘못된 조건으로 걸러진 화면을 사실로 믿게 됩니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
채널에서 받은 주문과 정산 내역을 흉내 낸 더미입니다. 상품별 손익을 계산하고 정산 금액이 어긋난 행을 찾기 위한 입력입니다.
| order_id | channel | sku_raw | sku_id | qty | sale_amount | cost_amount | fee_rate | settled_amount | ordered_at |
|---|---|---|---|---|---|---|---|---|---|
| O-1001 | 채널A | 머그컵 화이트 | SKU-mug-w | 2 | 24,000 | 9,000 | 0.11 | 21,360 | 2026-07-01 |
| O-1002 | 채널A | 머그컵 화이트(신) | SKU-mug-w | 1 | 12,000 | 4,500 | 0.11 | 10,680 | 2026-07-01 |
| O-1003 | 채널B | 텀블러 블랙 | SKU-tmb-b | 3 | 57,000 | 24,000 | 0.13 | — | 2026-07-02 |
| O-1004 | 채널B | 텀블러 블랙 | SKU-tmb-b | 1 | 19,000 | — | 0.13 | 16,530 | 2026-07-02 |
| O-1005 | 채널C | 보온병 500ml | — | 1 | 32,000 | 14,000 | 0.09 | 29,120 | 2026-07-03 |
아래 JSON Schema에 맞는 셀러 주문 행을 30건 더 만들어 주세요. {"type":"object","properties":{"order_id":{"type":"string"},"channel":{"type":"string","enum":["채널A","채널B","채널C"]},"sku_raw":{"type":"string"},"sku_id":{"type":["string","null"]},"qty":{"type":"integer"},"sale_amount":{"type":"integer"},"cost_amount":{"type":["integer","null"]},"fee_rate":{"type":["number","null"]},"settled_amount":{"type":["integer","null"]},"ordered_at":{"type":"string"}},"required":["order_id","channel","sku_raw","qty","sale_amount","ordered_at"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 정산이 아직 안 끝나 settled_amount가 null인 행, 둘째 원가나 수수료율이 비어 있는 행, 셋째 환불이라 settled_amount가 음수이거나 수량이 0인 행, 넷째 sku_raw는 같은 상품인데 표기가 달라 sku_id가 null인 미매핑 행. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"order_id":{"type":"string"},"channel":{"type":"string","enum":["채널A","채널B","채널C"]},"sku_raw":{"type":"string"},"sku_id":{"type":["string","null"]},"qty":{"type":"integer"},"sale_amount":{"type":"integer"},"cost_amount":{"type":["integer","null"]},"fee_rate":{"type":["number","null"]},"settled_amount":{"type":["integer","null"]},"ordered_at":{"type":"string"}},"required":["order_id","channel","sku_raw","qty","sale_amount","ordered_at"]}상품별 일자별 광고비 더미입니다. 손익에서 광고비를 빼고 광고비 대비 손익이 나쁜 상품을 찾기 위한 입력입니다.
| spend_id | sku_id | channel | spend_amount | clicks | spent_on |
|---|---|---|---|---|---|
| A-001 | SKU-mug-w | 채널A | 12,000 | 240 | 2026-07-01 |
| A-002 | SKU-mug-w | 채널A | 9,000 | 180 | 2026-07-02 |
| A-003 | SKU-tmb-b | 채널B | 30,000 | — | 2026-07-02 |
| A-004 | — | 채널C | 15,000 | 90 | 2026-07-03 |
| A-005 | SKU-btl-750 | 채널C | 0 | 0 | 2026-07-04 |
아래 JSON Schema에 맞는 광고비 행을 25건 더 만들어 주세요. {"type":"object","properties":{"spend_id":{"type":"string"},"sku_id":{"type":["string","null"]},"channel":{"type":"string","enum":["채널A","채널B","채널C"]},"spend_amount":{"type":"integer"},"clicks":{"type":["integer","null"]},"spent_on":{"type":"string"}},"required":["spend_id","channel","spend_amount","spent_on"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 상품을 특정하지 못해 sku_id가 null인 행, 둘째 클릭 수집이 안 돼 clicks가 null인 행, 셋째 집행액이 0이거나 클릭 대비 과도하게 큰 극단 행, 넷째 주문 데이터에 없는 상품코드를 가리키는 미매핑 행. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"spend_id":{"type":"string"},"sku_id":{"type":["string","null"]},"channel":{"type":"string","enum":["채널A","채널B","채널C"]},"spend_amount":{"type":"integer"},"clicks":{"type":["integer","null"]},"spent_on":{"type":"string"}},"required":["spend_id","channel","spend_amount","spent_on"]}| TypeScript | 손익 계산 입력과 결과의 형태를 타입으로 고정해, 채널마다 다른 주문 필드가 계산기로 흘러드는 것을 막습니다. |
| React 18 / Next.js 14 | 대시보드 화면과 데이터 조회 경로를 만들고 Vercel 배포까지 이 위에서 끝냅니다. |
| FSD | 손익 계산과 그리드와 필터를 기능 단위 폴더로 나누고 의존 방향을 한쪽으로 고정해, 초기 제품이 커져도 화면끼리 얽히지 않게 합니다. |
| AG Grid | 수천 행 주문과 정산을 정렬과 그룹 집계와 함께 끊김 없이 보여주는 화면의 중심입니다. |
| TanStack Query | 주문과 정산과 광고비 조회를 서버 상태로 두고 캐시와 재조회를 관리합니다. |
| Zustand | 선택한 기간과 필터 조건처럼 화면 전반이 공유하는 값만 담아 서버 상태와 분리합니다. |
| Tailwind / Shadcn | 표와 카드와 배지 같은 공통 컴포넌트를 하나의 규칙으로 맞춰 디자인 시스템의 시작점을 만듭니다. |
| Vitest / MSW | 손익 계산 규칙을 단위 테스트로 고정하고, 가짜 서버로 지연과 실패 응답을 흉내 내 화면 동작을 검증합니다. |
| Vercel / pnpm | 1주차부터 배포된 주소로 바로 열리게 만들어, 화면을 말로 설명하지 않고 링크로 보입니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차에서 말로 쓴 조건을 필터로 바꿉니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 3주차에서 계산 규칙과 화면 동작을 테스트로 고정합니다.
상품별 손익 계산 결과와 정산 금액이 어긋난 행의 판정입니다. 어느 항목 때문에 차이가 났는지 근거를 함께 담아 셀러가 바로 확인하게 합니다.
{"type":"object","properties":{"order_id":{"type":"string"},"sku_id":{"type":["string","null"]},"computed_margin":{"type":["integer","null"]},"verdict":{"type":"string","enum":["ok","settle_missing","fee_mismatch","cost_missing","sku_unmapped"]},"gap_amount":{"type":["integer","null"]},"evidence":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["order_id","verdict","evidence"]}[
{
"order_id": "O-1001",
"sku_id": "SKU-mug-w",
"computed_margin": 12360,
"verdict": "ok",
"gap_amount": 0,
"evidence": "판매 24000원에서 원가 9000원과 수수료 11퍼센트인 2640원을 빼면 12360원이고, 정산 21360원에서 원가를 뺀 값과 일치합니다.",
"hedge": null
},
{
"order_id": "O-1003",
"sku_id": "SKU-tmb-b",
"computed_margin": null,
"verdict": "settle_missing",
"gap_amount": null,
"evidence": "정산 금액이 비어 있어 손익을 확정하지 못합니다. 정산 주기가 끝나지 않은 주문으로 보입니다.",
"hedge": "합성 데이터 기준 판정이라 실제 정산 주기와 다를 수 있습니다."
},
{
"order_id": "O-1009",
"sku_id": "SKU-mug-w",
"computed_margin": 61800,
"verdict": "fee_mismatch",
"gap_amount": -10800,
"evidence": "수수료 11퍼센트를 적용하면 정산이 106800원이어야 하는데 실제 정산은 96000원이라 10800원이 적습니다.",
"hedge": "합성 기준 추정이며 채널별 추가 수수료 규정은 반영하지 않았습니다."
},
{
"order_id": "O-1005",
"sku_id": null,
"computed_margin": null,
"verdict": "sku_unmapped",
"gap_amount": null,
"evidence": "보온병 500ml라는 표기가 상품 마스터에 없어 상품을 특정하지 못했습니다. 광고비를 붙일 수 없어 손익을 확정하지 않습니다.",
"hedge": null
}
][자동 검증] 1. 판정 JSON이 위 스키마를 통과하는지 확인합니다. verdict가 정해진 다섯 값 밖이면 그 행을 버립니다. 2. verdict가 ok인데 gap_amount가 0이 아니면 계산과 판정이 어긋난 것이므로 오류로 잡습니다. 3. computed_margin이 null인 행은 verdict가 ok가 아니어야 합니다. 손익을 못 구했는데 정상으로 표시하는 일을 막기 위해서입니다. 4. 각 행의 evidence가 비어 있지 않은지 확인합니다. [수동 검증] 1. fee_mismatch로 잡힌 행 20건을 뽑아 채널 수수료율이 실제로 다른 것인지 산식이 틀린 것인지 확인합니다. 2. sku_unmapped로 잡힌 표기가 정말 새 상품인지, 같은 상품의 다른 표기인지 확인합니다.