고위드
경력근거: [고위드 소개] 고위드는 ‘혁신성장기업에게 실시간 데이터를 기반으로, 최적의 타이밍에 최적의 금융과 프로세스를 공급하여, 효율적
※ 실제 산출물이 아닌 AI 예측 시안입니다.
고위드의 지출최적화 스쿼드는 기업의 구독 지출에서 절감 기회를 찾아내는 데서 멈추지 않고 결제 방식과 계약 구조를 다시 짜서 실제 절감 실행까지 만드는 팀이라, 계약 조건을 금액으로 환산하고 사람의 결정을 계산 입력으로 되먹이는 이 화면이 그 일의 첫 단계와 같은 모양입니다. 공고가 한도와 지출 데이터 기반의 복잡한 금융 정책을 제품으로 구현하는 것을 주요 업무로 못박았기 때문에, 최소 좌석 약정과 중도 해지 위약 조항 같은 계약 제약을 규칙으로 코드에 박고 그 제약에 걸릴 때 화면이 진행을 막는 흐름까지 만들었습니다. 결제와 정산 연동은 가맹점 심사와 회사 명의가 있어야 계정이 열리므로 넣지 않고, 그 직전 단계인 금액 계산과 실행안 확정까지를 완결합니다. 이 포트폴리오는 계약 조건을 도메인 모델로 다루고 사람의 선택마다 즉시 다시 계산되는 화면을 백엔드부터 배포까지 혼자 만드는 풀스택 기술을 보여주며, 소프트웨어 벤더 협상이나 회계와 세무 처리 같은 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
지출 데이터를 들여다보는 데서 끝나지 않고 결제 방식과 계약 구조를 다시 짜서 실제 절감까지 만드는 팀이라서, 계약 조건을 잔여 개월과 단가와 약정 제약으로 환산해 절감액을 계산하는 층을 제품의 골조로 둡니다.
한도와 지출 데이터 위의 복잡한 정책을 제품으로 구현하는 자리라서, 최소 좌석 약정과 중도 해지 위약 조항 같은 계약 제약을 규칙으로 코드에 박고 그 제약에 걸리면 화면이 진행을 막게 만듭니다.
React 프런트엔드를 중심에 두고 Java 와 Spring 백엔드까지 함께 다루며 API 와 데이터베이스 설계를 요구하는 자리라서, 선택 화면과 계산 API 와 계약 도메인 스키마를 한 사람이 끝까지 만든 결과물로 보입니다.
회사가 쓰는 구독 계약 조건과 좌석별 사용 기록을 한 모델로 묶어 좌석 회수와 연납 전환과 등급 조정과 중복 구독 통합을 절감 후보로 계산하고, 재무 담당자가 회수할 좌석을 하나씩 고르고 전환 시점을 정하면 12개월 순절감액과 위약금과 월별 현금 영향이 그 자리에서 다시 계산되어 실행안으로 확정되는 1주 포트폴리오입니다. 확정 전에는 실행안이 만들어지지 않고, 최소 좌석 약정에 걸리거나 후보끼리 겹치면 화면이 진행을 막고 사람이 풀어야 계산이 이어집니다.
구독 계약서와 벤더 청구서와 좌석 목록을 스프레드시트에 옮겨 절감액을 손으로 더하다가, 최소 좌석 약정을 놓쳐 이미 안 쓰는 좌석에 석 달치를 더 낸 적이 있는 스타트업 재무 담당자입니다. 어떤 좌석을 실제로 회수해도 되는지는 본인만 알기 때문에, 계산은 자동으로 하되 결정은 본인이 화면에서 쥐고 싶은 사람입니다.
계약 조건과 좌석 사용 기록을 한 모델로 묶으면 절감액과 위약금은 규칙으로 정확히 계산되고, 사람은 회수할 좌석과 전환 시점만 고르면 되므로, 스프레드시트로 반나절 걸리던 절감안 검토가 화면 앞에서 몇 번의 선택으로 끝난다는 가정입니다.
구독 계약과 좌석 사용 기록을 넣으면 좌석 회수와 연납 전환과 등급 조정과 중복 구독 통합이 각각 절감액과 위약금과 함께 후보로 뜨고, 재무 담당자가 회수할 좌석을 개별로 체크하고 즉시와 다음 갱신일 중 전환 시점을 고르면 12개월 순절감액이 그 자리에서 다시 계산되며, 확정 버튼을 눌러야 실행안이 만들어지는 웹 화면이 배포까지 끝난 완결품입니다. 만드는 덩어리는 절감 후보 계산 엔진과 선택 화면 둘이며 평일 5일에 혼자 끝나는 범위입니다.
지난 분기에 실제로 갱신된 계약의 청구 결과를 넣어 예측 절감액과의 오차를 항목별로 보여 주고, 어긋난 항목의 원인이 계약 조건 누락인지 좌석 변동인지 가려주는 대조 화면입니다. 이 주차부터 서비스를 Google Cloud 에 올려 실제 주소로 운영합니다.
같은 구독 목록에서 만든 실행안 두 개를 나란히 두고 월별 순현금 영향을 곡선으로 겹쳐 보여 주며, 확정한 실행안을 항목별 근거가 붙은 승인 요청 문서로 내려받는 비교 화면입니다.
구독 계약 12건과 좌석 14건을 넣으면 절감 후보가 금액과 함께 뜨고, 좌석 체크와 전환 시점을 바꿀 때마다 12개월 순절감액과 위약금이 다시 계산되며, 확정해야 실행안이 나오는 흐름을 처음부터 끝까지 시연합니다.
손계산으로 만든 정답표 40건과 절감액과 위약금과 12개월 순액이 원 단위까지 일치하는 것을 자동 대조로 보입니다. 다만 정답과 데이터를 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
최소 좌석 약정에 걸린 회수와 이미 연납인 계약의 연납 전환과 약정이 남은 즉시 해지와 같은 구독에 두 후보가 겹치는 충돌을 포함한 하드 케이스 25종에서, 화면이 계산을 멈추고 사유와 사람이 할 일을 표시하는 것을 보입니다.
지난 분기에 실제로 갱신된 계약을 넣어 예측 절감액과 실제 청구액의 오차율을 냅니다 (측정: 항목별 예측액과 청구액의 차이, 표본: 계약 20건). 2주차 결과입니다.
위 수치는 더미데이터 자가채점이며, 실제 절감액과 담당자의 검토 시간 단축은 고위드 고객사의 실제 계약서와 청구 내역과 재무 담당자의 실사용이 있어야 측정 가능합니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모가 도는 상태와 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 최소 좌석 약정이 현재 좌석 수와 같아 회수가 불가능한 계약과 이미 연납이라 전환 이득이 없는 계약과 표기가 흔들려 통합 후보로 잡히는 같은 벤더 계약과 존재하지 않는 구독에 붙은 좌석을 더미에 일부러 섞어 두었습니다.
회수해도 되는 좌석인지는 계산으로 결정되지 않습니다. 90일 미접속이라도 휴직 복귀 예정이거나 이번 주 입사한 사람일 수 있어서, 시스템은 후보와 근거까지만 내고 회수 여부는 사람이 화면에서 체크해야 다음이 진행되게 두었습니다.
이 포트폴리오는 계약 조건을 도메인 모델로 다루고 선택마다 다시 계산되는 화면을 혼자 만드는 기술을 보여주며, 벤더 협상 조건이나 회계와 세무 처리 같은 도메인 전문성은 주장하지 않습니다. 실제 도입에서는 벤더별 약정 조항의 해석과 계정과목 처리 기준을 재무 담당자와 다시 맞춰야 합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
회사가 쓰는 소프트웨어 구독 계약 한 건입니다. 절감 후보의 금액과 위약금이 전부 이 표의 단가와 잔여 약정과 위약률에서 산술로 나오므로, 계산이 막히는 조건을 일부러 섞어 두었습니다.
| sub_id | vendor | product_raw | tier | seats | unit_price_krw | billing_cycle | annual_discount_pct | min_seats | commit_end | early_exit_penalty_pct |
|---|---|---|---|---|---|---|---|---|---|---|
| sub_001 | Slack | Slack Business+ | business_plus | 42 | 15,000 | monthly | 15 | 30 | 2027-03-31 | 40 |
| sub_002 | Slack | 슬랙 비즈니스 플러스 | business_plus | 8 | 15,000 | monthly | 15 | — | — | — |
| sub_003 | Atlassian | Jira Software Premium | premium | 55 | 21,000 | annual | 20 | 50 | 2026-12-31 | 100 |
| sub_004 | Figma | Figma Organization | organization | 30 | 22,000 | monthly | 18 | — | 2026-08-31 | 30 |
| sub_005 | Notion | Notion Business | business | 60 | 18,000 | monthly | 20 | — | — | — |
아래 Zod 스키마에 맞는 기업 소프트웨어 구독 계약 60행을 JSON 배열로 생성합니다.
{"type":"object","properties":{"sub_id":{"type":"string"},"vendor":{"type":"string"},"product_raw":{"type":"string"},"tier":{"type":"string"},"seats":{"type":"integer"},"unit_price_krw":{"type":"integer"},"billing_cycle":{"type":"string","enum":["monthly","annual"]},"annual_discount_pct":{"type":"number"},"min_seats":{"type":["integer","null"]},"commit_end":{"type":["string","null"]},"early_exit_penalty_pct":{"type":["number","null"]}},"required":["sub_id","vendor","product_raw","tier","seats","unit_price_krw","billing_cycle","annual_discount_pct","min_seats","commit_end","early_exit_penalty_pct"]}
쉬운 데이터만 만들지 않습니다. 하드 케이스를 반드시 섞습니다: (1) min_seats 가 seats 와 같아 좌석 회수가 한 석도 불가능한 계약 2건, (2) billing_cycle 이 이미 annual 이라 연납 전환 이득이 없는 계약 3건, (3) annual_discount_pct 가 0 이라 전환해도 절감이 없는 계약 1건, (4) 같은 vendor 에 product_raw 표기만 다른 중복 계약 1쌍(영문 표기와 한글 음차), (5) commit_end 가 이번 달 말이라 다음 갱신일 선택이 즉시와 거의 같아지는 계약 1건, (6) commit_end 와 early_exit_penalty_pct 가 모두 null 인 무약정 계약 2건, (7) unit_price_krw 가 0 인 무료 플랜 1건, (8) early_exit_penalty_pct 가 100 이라 즉시 해지 이득이 사라지는 계약 2건. JSON 배열만 출력합니다.{"type":"object","properties":{"sub_id":{"type":"string"},"vendor":{"type":"string"},"product_raw":{"type":"string"},"tier":{"type":"string"},"seats":{"type":"integer"},"unit_price_krw":{"type":"integer"},"billing_cycle":{"type":"string","enum":["monthly","annual"]},"annual_discount_pct":{"type":"number"},"min_seats":{"type":["integer","null"]},"commit_end":{"type":["string","null"]},"early_exit_penalty_pct":{"type":["number","null"]}},"required":["sub_id","vendor","product_raw","tier","seats","unit_price_krw","billing_cycle","annual_discount_pct","min_seats","commit_end","early_exit_penalty_pct"]}구독별 좌석 배정과 최근 90일 사용 기록입니다. 어떤 좌석이 유휴 후보로 뜨는지를 결정하는 입력이며, 계산만으로는 회수 여부를 정할 수 없는 행을 일부러 섞어 사람이 화면에서 골라야 하는 이유를 만듭니다.
| seat_id | sub_id | member_email | assigned_at | last_active_at | active_days_90d | status |
|---|---|---|---|---|---|---|
| seat_0001 | sub_001 | ops1@example.com | 2025-04-01 | 2026-07-31 | 62 | active |
| seat_0002 | sub_001 | dev7@example.com | 2025-06-10 | 2026-03-02 | 0 | active |
| seat_0003 | sub_001 | leaver2@example.com | 2024-11-02 | 2026-02-14 | 0 | offboarded |
| seat_0004 | sub_001 | newjoiner@example.com | 2026-07-28 | — | 0 | invited |
| seat_0005 | sub_003 | pm2@example.com | 2025-02-20 | 2026-07-30 | 71 | active |
아래 Zod 스키마에 맞는 구독 좌석 배정과 최근 90일 사용 기록 200행을 JSON 배열로 생성합니다.
{"type":"object","properties":{"seat_id":{"type":"string"},"sub_id":{"type":"string"},"member_email":{"type":"string"},"assigned_at":{"type":"string"},"last_active_at":{"type":["string","null"]},"active_days_90d":{"type":"integer"},"status":{"type":"string","enum":["active","invited","offboarded"]}},"required":["seat_id","sub_id","member_email","assigned_at","last_active_at","active_days_90d","status"]}
쉬운 데이터만 만들지 않습니다. 하드 케이스를 반드시 섞습니다: (1) status 가 offboarded 인데 좌석이 회수되지 않은 행 3건, (2) status 가 invited 이고 last_active_at 이 null 이면서 assigned_at 이 최근 7일 이내인 신규 입사 행 2건(회수 후보로 보이지만 회수하면 안 되는 행), (3) status 가 invited 이고 배정한 지 6개월이 넘도록 접속이 없는 행 2건, (4) active_days_90d 가 0 또는 1 인 경계값 각 3건, (5) 같은 member_email 이 서로 다른 sub_id 두 곳에 좌석을 가진 중복 배정 2쌍, (6) 구독 목록에 없는 sub_id 를 가리키는 행 1건, (7) assigned_at 보다 last_active_at 이 앞서는 모순 행 1건. JSON 배열만 출력합니다.{"type":"object","properties":{"seat_id":{"type":"string"},"sub_id":{"type":"string"},"member_email":{"type":"string"},"assigned_at":{"type":"string"},"last_active_at":{"type":["string","null"]},"active_days_90d":{"type":"integer"},"status":{"type":"string","enum":["active","invited","offboarded"]}},"required":["seat_id","sub_id","member_email","assigned_at","last_active_at","active_days_90d","status"]}| JAVA / Spring Boot 3.0 이상 (Spring Data JPA) | 구독 계약과 좌석과 사용 기록을 도메인 모델로 두고 절감 후보 계산과 후보 간 충돌 판정과 선택 반영 재계산을 처리하는 백엔드를 구현합니다. |
| QueryDSL | 구독과 좌석과 90일 사용 기록을 조건별로 조인해 유휴 좌석과 최소 좌석 약정 여유분을 뽑는 조회를 작성합니다. |
| MySQL | 계약 조건과 좌석 배정과 사람이 확정한 실행안 이력을 저장하고, 같은 구독에 두 후보가 겹칠 때 무엇을 골랐는지 남깁니다. |
| Redis | 선택 조합을 키로 삼아 재계산 결과를 캐시해 좌석 체크를 연달아 바꿔도 화면이 즉시 반응하게 만듭니다. |
| Typescript, React, Vite, Tanstack-Query, Styled Components | 절감 후보 목록과 좌석 체크와 전환 시점 선택과 12개월 순절감액이 한 화면에서 맞물리는 선택 화면을 만들고, 선택이 바뀔 때마다 계산 결과를 다시 받아 옵니다. |
| Google Cloud | 2주차부터 서비스를 Google Cloud 에 올려 실제 주소로 운영하고 3주차 비교 화면까지 같은 환경에서 돌립니다. |
| AI 도구 활용 | 구독명 표기 정규화 후보와 벤더 요청 문구 초안을 Claude 로 만들고 금액은 전부 규칙 계산에 맡기는 경계를 저장소의 커밋과 문서로 남깁니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차에서 지난 갱신 결과와 대조하는 화면을 붙입니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 3주차에서 실행안 두 개를 현금흐름 곡선으로 겹쳐 보는 화면을 만듭니다.
재무 담당자가 화면에서 좌석과 전환 시점을 골라 확정한 절감 실행안입니다. 항목마다 무엇을 하기로 했는지와 그 금액이 어느 계약 필드에서 나왔는지를 함께 담아, 승인자가 숫자를 손으로 검산할 수 있게 합니다.
{"type":"object","properties":{"plan_id":{"type":"string"},"confirmed_by":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"sub_id":{"type":"string"},"action":{"type":"string","enum":["seat_reclaim","billing_switch","tier_downgrade","consolidate","keep"]},"effective":{"type":"string","enum":["immediate","next_renewal"]},"reclaimed_seats":{"type":"array","items":{"type":"string"}},"monthly_saving_krw":{"type":"integer"},"penalty_krw":{"type":"integer"},"twelve_month_net_krw":{"type":"integer"},"basis":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"value":{"type":"string"}},"required":["field","value"]},"minItems":1},"blocked_reason":{"type":["string","null"]}},"required":["sub_id","action","effective","reclaimed_seats","monthly_saving_krw","penalty_krw","twelve_month_net_krw","basis","blocked_reason"]},"minItems":1},"twelve_month_net_krw":{"type":"integer"},"needs_human_confirm":{"type":"array","items":{"type":"string"}}},"required":["plan_id","confirmed_by","items","twelve_month_net_krw","needs_human_confirm"]}[
{
"plan_id": "plan_2026_08",
"confirmed_by": "finance@example.com",
"items": [
{
"sub_id": "sub_001",
"action": "seat_reclaim",
"effective": "next_renewal",
"reclaimed_seats": [
"seat_0002",
"seat_0003"
],
"monthly_saving_krw": 30000,
"penalty_krw": 0,
"twelve_month_net_krw": 360000,
"basis": [
{
"field": "unit_price_krw",
"value": "15000"
},
{
"field": "min_seats",
"value": "30"
}
],
"blocked_reason": null
},
{
"sub_id": "sub_007",
"action": "keep",
"effective": "next_renewal",
"reclaimed_seats": [],
"monthly_saving_krw": 0,
"penalty_krw": 0,
"twelve_month_net_krw": 0,
"basis": [
{
"field": "min_seats",
"value": "25"
},
{
"field": "seats",
"value": "25"
}
],
"blocked_reason": "최소 좌석 약정이 현재 좌석 수와 같아 회수 가능한 좌석이 없습니다"
},
{
"sub_id": "sub_011",
"action": "billing_switch",
"effective": "immediate",
"reclaimed_seats": [],
"monthly_saving_krw": 137500,
"penalty_krw": 330000,
"twelve_month_net_krw": 1320000,
"basis": [
{
"field": "annual_discount_pct",
"value": "25"
},
{
"field": "early_exit_penalty_pct",
"value": "60"
}
],
"blocked_reason": null
}
],
"twelve_month_net_krw": 1680000,
"needs_human_confirm": [
"seat_0004 는 배정한 지 나흘 된 좌석이라 회수 후보에서 사람이 직접 뺐습니다",
"sub_002 는 sub_001 과 같은 벤더의 같은 등급이라 통합 후보로 잡혔고, 계약이 실제로 별개인지 확인이 필요합니다"
]
},
{
"plan_id": "plan_2026_09",
"confirmed_by": "finance@example.com",
"items": [
{
"sub_id": "sub_006",
"action": "hold",
"effective": "next_renewal",
"reclaimed_seats": [],
"monthly_saving_krw": 0,
"penalty_krw": 0,
"twelve_month_net_krw": 0,
"basis": [
{
"field": "sub_id",
"value": "sub_006"
}
],
"blocked_reason": "이 계약에 배정된 좌석 기록이 없어 회수 대상을 정하지 못했습니다."
}
],
"twelve_month_net_krw": 0,
"needs_human_confirm": true
},
{
"plan_id": "plan_2026_10",
"confirmed_by": "finance@example.com",
"items": [
{
"sub_id": "sub_004",
"action": "downgrade",
"effective": "immediate",
"reclaimed_seats": [
"seat_0005"
],
"monthly_saving_krw": 18000,
"penalty_krw": 0,
"twelve_month_net_krw": 216000,
"basis": [
{
"field": "unit_price_krw",
"value": "18000"
},
{
"field": "min_seats",
"value": "10"
}
],
"blocked_reason": null
}
],
"twelve_month_net_krw": 216000,
"needs_human_confirm": false
}
]자동: (1) Zod parse, (2) items 의 twelve_month_net_krw 합이 최상위 twelve_month_net_krw 와 원 단위까지 일치하는지, (3) reclaimed_seats 개수가 seats 에서 min_seats 를 뺀 여유분을 넘지 않는지, (4) basis 의 field 가 subscription_contracts 스키마의 실제 필드명인지, (5) blocked_reason 이 있는 항목의 monthly_saving_krw 가 0 인지, (6) 손계산 정답표와 절감액과 위약금이 원 단위까지 같은지 대조합니다. 사람 확인: 회수 대상 좌석이 실제로 회수해도 되는 좌석인지와 통합 후보의 계약이 정말 중복인지는 재무 담당자가 화면에서 판단합니다.
근거: [고위드 소개] 고위드는 ‘혁신성장기업에게 실시간 데이터를 기반으로, 최적의 타이밍에 최적의 금융과 프로세스를 공급하여, 효율적