라플라스테크놀로지스
경력근거: 이커머스 전방위 데이터를 라플라스에 통합하여 대시보드, 알림, AI Agent, Workflow 를 제공
라플라스테크놀로지스는 주문과 광고와 재고와 리뷰 같은 이커머스 전방위 데이터를 라플라스에 통합해 대시보드를 제공하는 회사이고, 공고가 다양한 고객사의 요구사항을 분석해 커스텀 대시보드를 구현하는 일을 주요업무로 못박았습니다. 고객사마다 컬럼 이름과 단위가 다르다는 점이 그 일의 실제 비용이라서, 담당자가 화면에서 컬럼의 뜻을 확정해야만 지표와 위젯이 열리는 관문을 제품의 첫 화면으로 두었습니다. 프론트엔드와 백엔드와 데이터와 인프라의 경계를 두지 않는다는 요구에 맞춰 원천 적재 SQL 과 확정 API 와 렌더 화면과 배포를 한 사람이 닫는 범위로 잡았습니다. 이 포트폴리오는 스키마 매핑 확정 흐름과 대시보드 정의를 저장해 렌더하는 웹 풀스택 기술을 보여주며 이커머스 광고 운영과 수요 예측 같은 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
다양한 고객사의 요구사항을 분석해 커스텀 대시보드와 데이터 기능을 만드는 일이 업무의 축이라서, 고객사마다 다른 원천 표기를 확정하고 그 위에 위젯을 얹는 화면 하나를 처음부터 끝까지 만듭니다.
고객의 문제를 직접 이해하고 빠르게 기능을 구현해 반복 개선하는 자리라서, 담당자가 화면에서 고르고 고친 결정이 곧바로 대시보드 정의에 남고 다음 화면에 반영되는 구조를 제품의 골조로 둡니다.
프론트엔드와 백엔드와 데이터와 인프라의 경계를 두지 않는 자리라서, 원천 적재 SQL 부터 확정 API 와 위젯 렌더 화면과 배포까지 한 사람이 닫는 범위로 잡습니다.
이커머스 고객사가 자기 채널의 원천 테이블을 연결하면 컬럼 목록이 화면에 뜨고, 표기 정규화 규칙이 확신하는 컬럼만 자동으로 확정되며 나머지는 미확정으로 남습니다. 담당자가 미확정 컬럼을 화면에서 지표에 붙이거나 쓰지 않음으로 넘기지 않으면 그 지표는 잠긴 채로 있고, 잠긴 지표를 참조하는 위젯은 팔레트에서 선택되지 않습니다. 확정이 끝난 지표만 골라 위젯으로 얹고 저장하면 그 고객사 전용 대시보드 정의가 확정되어 곧바로 렌더됩니다.
채널마다 컬럼 이름과 단위가 달라 매출 위젯 하나를 얹을 때마다 담당 엔지니어에게 이 컬럼이 무슨 뜻이냐고 물어보며 기다리던 이커머스 고객사 운영 담당자가, 자기 손으로 컬럼의 뜻을 확정하고 그 자리에서 대시보드를 완성하고 싶은 사람입니다.
확정되지 않은 컬럼을 시스템이 추측해서 채우지 않고 사람이 화면에서 고르게 만들면, 뜻이 어긋난 컬럼이 지표로 올라가는 일이 사라지고 담당자가 엔지니어를 기다리지 않고 대시보드를 완성하게 된다는 가정입니다.
원천 테이블을 연결하면 컬럼 목록과 표본값이 뜨고, 규칙이 확신하는 컬럼은 자동 확정되며 나머지는 담당자가 지표에 붙이거나 쓰지 않음으로 넘겨야 하는 확정 화면이 먼저 돕니다. 확정이 끝난 지표만 위젯 팔레트에서 열리고, 잠긴 지표를 얹으려 하면 어느 컬럼을 확정해야 하는지 사유가 뜹니다. 위젯을 놓고 저장하면 대시보드 정의가 남아 새로 고쳐도 같은 화면이 렌더되는 완결품입니다. 만드는 덩어리는 컬럼 확정과 위젯 조립 둘이며 혼자 평일 5일에 끝나는 범위입니다.
담당자가 보고 싶은 것을 문장 한 줄로 쓰면 LLM 이 위젯 초안을 제안하고, 그 초안이 확정된 지표만 참조하는지 규칙 검증기가 먼저 거른 뒤 화면에 후보로 뜹니다. 담당자가 차트 종류와 기간과 묶는 기준을 화면에서 고쳐 승인해야 대시보드에 붙습니다.
고객사 원천에서 컬럼 이름이나 단위가 바뀌면 그 컬럼을 쓰던 위젯이 깨짐 표시로 바뀌고 대시보드 게시가 잠깁니다. 담당자가 확정 화면으로 되돌아가 새 컬럼을 다시 붙이면 잠금이 풀리며, 이 시점에 Gitops 와 k8s 로 배포를 붙여 실제 주소에서 같은 흐름을 돌립니다.
원천 테이블 5개를 연결한 상태에서 컬럼 확정과 위젯 조립과 저장과 렌더까지 화면 흐름을 끊김 없이 시연합니다.
미확정 컬럼이 남은 상태로 위젯 8종을 얹어 보면 8종이 모두 어느 컬럼을 확정해야 하는지 사유와 함께 되돌아오고, 담당자가 컬럼 3건을 확정하는 순간 그중 5종이 열리는 것을 보입니다.
단위가 천원인 컬럼과 뜻이 겹치는 동명 컬럼과 대응 지표가 없는 컬럼을 심어 둔 원천 30종을 넣어도 확정 화면이 죽지 않고 컬럼 단위로 미확정 사유를 남기며, 확정되지 않은 필드를 참조하는 정의는 저장 단계에서 거절합니다.
2주차 문장 요청 40건 중 위젯 초안이 규칙 검증을 통과한 것이 31건(78%)이고, 거절된 9건은 확정되지 않은 필드 참조 6건과 지표 타입에 맞지 않는 차트 종류 3건입니다. 다만 요청 문장과 정답을 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 확정 소요 시간과 담당자가 엔지니어를 부르지 않고 끝내는 비율은 라플라스의 실제 고객사 원천과 실제 담당자의 사용이 있어야 측정 가능합니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 화면 흐름이 도는 상태와 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 단위가 천원인 컬럼과 두 소스에서 뜻이 갈리는 동명 컬럼과 대응 지표가 없는 캠페인 컬럼과 월일 순서가 애매한 날짜 컬럼을 더미에 일부러 섞어 두었습니다.
이 포트폴리오는 매핑 확정 흐름과 대시보드 정의 렌더링이라는 웹 풀스택 기술을 보여주며 이커머스 광고 최적화와 수요 예측 같은 도메인 전문성은 주장하지 않습니다. 위젯이 계산하는 지표는 주문 수와 매출과 광고비 합계처럼 정의가 한 줄로 끝나는 것만 다룹니다.
매핑 후보를 규칙이 뽑는 이상 애매한 컬럼은 계속 남습니다. 확신이 낮은 후보를 자동으로 확정하지 않고 미확정으로 남겨 사람에게 넘기며, 자동 확정 임계값은 실제 고객사 원천에서 다시 맞춰야 합니다.
실제 도입에서는 고객사 원천에 대한 읽기 권한과 접속 정보 보관 규정이 먼저 필요합니다. 이 포트폴리오는 내려받은 형태의 더미 원천으로만 돌며 실제 연동은 도입 단계의 과제로 분리합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사가 연결한 원천 테이블의 컬럼 목록과 표본값을 흉내 낸 더미입니다. 확정 화면이 무엇을 자동으로 붙이고 무엇을 사람에게 넘길지 가르는 입력이라서, 단위가 다른 컬럼과 뜻이 겹치는 동명 컬럼과 대응 지표가 없는 컬럼을 함께 담았습니다.
| source_table | raw_column | sample_values | inferred_type | suggested_field | match_score |
|---|---|---|---|---|---|
| smartstore_orders | 주문일 | 2026-05-04 | 2026-05-05 | date | order_date | 0.94 |
| smartstore_orders | 상품명 | 콜드브루 1L | 콜드브루1L | text | sku_name | 0.88 |
| smartstore_orders | 결제금액 | 4480000 | 4452000 | number | revenue_krw | 0.76 |
| coupang_orders | date | 2026/05/04 | 2026/05/05 | date | order_date | 0.9 |
| coupang_orders | item | 콜드브루 1L | 원두 200g | text | sku_name | 0.83 |
아래 JSON Schema 에 맞는 고객사 원천 컬럼 행을 30건 더 만들어 주세요. {"type":"object","properties":{"source_table":{"type":"string"},"raw_column":{"type":"string"},"sample_values":{"type":"string"},"inferred_type":{"type":"string","enum":["date","text","number"]},"suggested_field":{"type":["string","null"]},"match_score":{"type":"number"}},"required":["source_table","raw_column","sample_values","inferred_type","match_score"]}
쉬운 데이터만 만들지 마시고 다음 하드 케이스를 섞어 주세요. (1) 단위가 원이 아니라 천원이나 달러인 금액 컬럼 3건, (2) 두 소스에서 이름은 같은데 뜻이 다른 동명 컬럼 2쌍, (3) 표준 지표에 대응하지 않아 suggested_field 가 null 이고 match_score 가 0.2 아래인 컬럼 4건, (4) match_score 가 0.45 에서 0.6 사이라 자동 확정 임계값 아래에 걸리는 애매한 컬럼 5건, (5) 한글 컬럼명과 영문 컬럼명이 섞인 구성. JSON 배열만 주세요.{"type":"object","properties":{"source_table":{"type":"string"},"raw_column":{"type":"string"},"sample_values":{"type":"string"},"inferred_type":{"type":"string","enum":["date","text","number"]},"suggested_field":{"type":["string","null"]},"match_score":{"type":"number"}},"required":["source_table","raw_column","sample_values","inferred_type","match_score"]}확정이 끝난 뒤 위젯이 집계할 채널별 주문 원천을 흉내 낸 더미입니다. 같은 상품이 채널마다 다르게 표기되고 금액 단위가 갈리는 상태 그대로 담아, 확정된 매핑이 실제로 숫자를 맞추는지 화면에서 확인하게 합니다.
| source_table | order_date_raw | channel_raw | sku_raw | orders | amount_raw | ad_spend_raw |
|---|---|---|---|---|---|---|
| smartstore_orders | 2026-05-04 | 스마트스토어 | 콜드브루 1L | 320 | 4,480,000 | 600,000 |
| smartstore_orders | 2026-05-05 | 스마트스토어 | 콜드브루1L | 318 | 4,452,000 | 610,000 |
| smartstore_orders | 2026-05-06 | 스마트스토어 | 원두 200g | 120 | 1,920,000 | 150,000 |
| coupang_orders | 2026/05/04 | coupang | 콜드브루 1L | 210 | 2,940,000 | 520,000 |
| coupang_orders | 2026/05/05 | coupang | 드립백 10개입(신상) | 140 | 1,820,000 | 300,000 |
아래 JSON Schema 에 맞는 채널별 주문 원천 행을 40건 더 만들어 주세요. {"type":"object","properties":{"source_table":{"type":"string"},"order_date_raw":{"type":"string"},"channel_raw":{"type":"string"},"sku_raw":{"type":"string"},"orders":{"type":"integer"},"amount_raw":{"type":"number"},"ad_spend_raw":{"type":["number","null"]}},"required":["source_table","order_date_raw","channel_raw","sku_raw","orders","amount_raw"]}
쉬운 데이터만 만들지 마시고 다음 하드 케이스를 섞어 주세요. (1) 같은 상품의 표기 흔들림(콜드브루 1L 과 콜드브루1L 처럼), (2) 같은 채널의 표기 흔들림(자사몰과 자체몰, coupang 과 쿠팡), (3) own_mall_orders 는 금액이 천원 단위라 원 단위 소스와 세 자리 차이가 나게, (4) 날짜 표기가 소스마다 다르게(하이픈과 슬래시), (5) ad_spend_raw 결측 5건과 orders 가 0 인 급감 행 1건. JSON 배열만 주세요.{"type":"object","properties":{"source_table":{"type":"string"},"order_date_raw":{"type":"string"},"channel_raw":{"type":"string"},"sku_raw":{"type":"string"},"orders":{"type":"integer"},"amount_raw":{"type":"number"},"ad_spend_raw":{"type":["number","null"]}},"required":["source_table","order_date_raw","channel_raw","sku_raw","orders","amount_raw"]}| React | 컬럼 확정 화면과 위젯 팔레트와 저장된 정의를 읽어 차트를 그리는 렌더 화면을 Next.js 로 만듭니다. 잠긴 지표와 깨진 위젯의 사유 표시도 이 층에서 처리합니다. |
| FastAPI | 컬럼 확정 API 와 대시보드 정의 저장 API 와 위젯 미리보기 질의 API 를 만들고, 확정되지 않은 필드를 참조하는 정의는 저장 단계에서 거절합니다. |
| SQL | 채널별 원천 테이블을 ELT 로 적재하고, 확정된 매핑 표를 조인해 위젯이 읽는 집계 뷰를 만듭니다. 단위 배율과 표기 별칭도 이 층에서 정규화합니다. |
| k8s | 3주차에 Gitops 와 k8s 로 빌더 화면과 API 와 적재 잡을 배포해 실제 주소에서 같은 흐름을 돌립니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차에서 문장 한 줄로 받는 위젯 초안과 승인 흐름을 붙입니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 3주차에서 원천 변경 감지와 재확정 흐름과 배포를 붙입니다.
고객사 대시보드 한 개의 확정 상태와 위젯 구성을 담습니다. 어느 컬럼이 사람 손으로 확정되었고 어느 위젯이 왜 잠겼는지가 같은 문서에 남아, 확정이 끝나기 전에는 게시가 되지 않는다는 규칙을 저장 단계에서 강제합니다.
{"type":"object","properties":{"dashboard_id":{"type":"string"},"client":{"type":"string"},"field_mappings":{"type":"array","items":{"type":"object","properties":{"source_table":{"type":"string"},"raw_column":{"type":"string"},"mapped_field":{"type":["string","null"]},"unit_scale":{"type":["number","null"]},"status":{"type":"string","enum":["confirmed","pending","skipped"]},"decided_by":{"type":"string","enum":["rule","human"]},"decided_at":{"type":["string","null"]}},"required":["source_table","raw_column","status","decided_by"]}},"widgets":{"type":"array","items":{"type":"object","properties":{"widget_id":{"type":"string"},"kind":{"type":"string","enum":["line","bar","table","number"]},"title":{"type":"string"},"metric":{"type":"string"},"group_by":{"type":["string","null"]},"blocked_reason":{"type":["string","null"]}},"required":["widget_id","kind","title","metric"]}},"blocked_fields":{"type":"array","items":{"type":"string"}},"publishable":{"type":"boolean"}},"required":["dashboard_id","client","field_mappings","widgets","publishable"]}[
{
"dashboard_id": "dsb_mococo_01",
"client": "모코코커피",
"field_mappings": [
{
"source_table": "smartstore_orders",
"raw_column": "주문일",
"mapped_field": "order_date",
"unit_scale": null,
"status": "confirmed",
"decided_by": "rule",
"decided_at": "2026-05-11T09:02:00+09:00"
},
{
"source_table": "smartstore_orders",
"raw_column": "결제금액",
"mapped_field": "revenue_krw",
"unit_scale": 1,
"status": "confirmed",
"decided_by": "rule",
"decided_at": "2026-05-11T09:02:00+09:00"
},
{
"source_table": "coupang_orders",
"raw_column": "amount",
"mapped_field": null,
"unit_scale": null,
"status": "pending",
"decided_by": "rule",
"decided_at": null
},
{
"source_table": "own_mall_orders",
"raw_column": "판매가(천원)",
"mapped_field": null,
"unit_scale": null,
"status": "pending",
"decided_by": "rule",
"decided_at": null
},
{
"source_table": "ads_meta",
"raw_column": "campaign",
"mapped_field": null,
"unit_scale": null,
"status": "pending",
"decided_by": "rule",
"decided_at": null
}
],
"widgets": [
{
"widget_id": "w_orders_line",
"kind": "line",
"title": "일자별 주문 수",
"metric": "orders",
"group_by": "order_date",
"blocked_reason": null
},
{
"widget_id": "w_rev_bar",
"kind": "bar",
"title": "채널별 매출",
"metric": "revenue_krw",
"group_by": "channel",
"blocked_reason": "coupang_orders.amount 와 own_mall_orders.판매가(천원) 가 미확정이라 채널 매출을 합칠 수 없습니다. 두 컬럼의 지표와 단위를 확정해 주세요."
},
{
"widget_id": "w_roas_num",
"kind": "number",
"title": "광고 대비 매출",
"metric": "roas",
"group_by": null,
"blocked_reason": "revenue_krw 가 채널 전체로 확정되지 않아 계산할 수 없습니다."
}
],
"blocked_fields": [
"revenue_krw",
"roas"
],
"publishable": false
},
{
"dashboard_id": "dsb_mococo_01",
"client": "모코코커피",
"field_mappings": [
{
"source_table": "smartstore_orders",
"raw_column": "주문일",
"mapped_field": "order_date",
"unit_scale": null,
"status": "confirmed",
"decided_by": "rule",
"decided_at": "2026-05-11T09:02:00+09:00"
},
{
"source_table": "smartstore_orders",
"raw_column": "결제금액",
"mapped_field": "revenue_krw",
"unit_scale": 1,
"status": "confirmed",
"decided_by": "rule",
"decided_at": "2026-05-11T09:02:00+09:00"
},
{
"source_table": "coupang_orders",
"raw_column": "amount",
"mapped_field": "revenue_krw",
"unit_scale": 1,
"status": "confirmed",
"decided_by": "human",
"decided_at": "2026-05-11T10:18:00+09:00"
},
{
"source_table": "own_mall_orders",
"raw_column": "판매가(천원)",
"mapped_field": "revenue_krw",
"unit_scale": 1000,
"status": "confirmed",
"decided_by": "human",
"decided_at": "2026-05-11T10:21:00+09:00"
},
{
"source_table": "ads_meta",
"raw_column": "campaign",
"mapped_field": null,
"unit_scale": null,
"status": "skipped",
"decided_by": "human",
"decided_at": "2026-05-11T10:23:00+09:00"
}
],
"widgets": [
{
"widget_id": "w_orders_line",
"kind": "line",
"title": "일자별 주문 수",
"metric": "orders",
"group_by": "order_date",
"blocked_reason": null
},
{
"widget_id": "w_rev_bar",
"kind": "bar",
"title": "채널별 매출",
"metric": "revenue_krw",
"group_by": "channel",
"blocked_reason": null
},
{
"widget_id": "w_roas_num",
"kind": "number",
"title": "광고 대비 매출",
"metric": "roas",
"group_by": null,
"blocked_reason": null
}
],
"blocked_fields": [],
"publishable": true
}
][자동 검증] 1. 정의 JSON 이 위 스키마를 통과하는지 확인합니다. widgets[].kind 가 네 값 밖이면 저장을 거절합니다. 2. widgets[].metric 이 status 가 confirmed 인 mapped_field 집합에 속하지 않으면 blocked_reason 이 반드시 채워져 있어야 합니다. 확정되지 않은 컬럼이 조용히 숫자로 올라가는 일을 막기 위해서입니다. 3. blocked_reason 이 있는 위젯이 하나라도 있으면 publishable 은 false 여야 합니다. 규칙과 값이 어긋나면 저장을 거절합니다. 4. status 가 confirmed 이거나 skipped 인 행은 decided_at 이 있어야 하고, decided_by 가 rule 인 행은 자동 확정 임계값 이상의 후보에서만 나와야 합니다. 5. mapped_field 가 금액 지표인 행은 unit_scale 이 반드시 있어야 합니다. 천원 단위 원천이 원 단위와 그대로 합쳐지는 것을 막기 위해서입니다. [수동 검증] 1. 미확정 컬럼이 남은 대시보드 10건을 열어 잠금 사유 문구가 어느 컬럼을 확정해야 하는지 정확히 지목하는지 확인합니다. 2. 사람이 확정한 행 20건을 뽑아 화면에서 고른 지표와 단위가 저장된 값과 일치하는지 확인합니다.