지바이크
경력근거: 배터리 스와핑 스테이션(BSS), 모빌리티 렌탈 등 신산업
※ 실제 산출물이 아닌 AI 예측 시안입니다.
지바이크는 배터리 스와핑 스테이션을 신사업으로 돌리고 있고, 이 포지션은 기획서가 없는 단계에서 문제를 직접 정의해 앱부터 서버까지 만들라고 적혀 있습니다. 스와핑은 갔는데 못 바꾸면 그 순간 이용이 끊기는 사업이라, 첫 제품이 풀어야 할 문제가 재고 가시성으로 좁혀집니다. 5개국 운영이라 스테이션 표기가 나라마다 다른 것도 처음부터 다뤄야 합니다. 이 포트폴리오는 기기 상태를 규칙으로 판정해 앱 화면까지 잇는 기술을 보여줍니다. 배터리 하드웨어나 BLE 통신 자체의 전문성은 주장하지 않습니다. 기기 연동의 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
공고가 배터리 스와핑 스테이션을 지금 동시에 돌리는 신규 프로젝트로 듭니다. 정해진 기획서가 없는 단계라 제일 먼저 필요한 것은 라이더가 헛걸음하지 않게 하는 최소 흐름입니다.
모바일 앱과 백엔드를 아우르는 end-to-end 개발이 주요 업무이고, 앱과 서버와 기기로 이어지는 인터페이스를 RESTful API로 구현한다고 적혀 있습니다.
5개국 140개 도시에서 운영하며 다국가·다국어 서비스 구조 설계가 업무로 적혀 있습니다. 스테이션 이름 표기가 나라마다 달라 처음부터 원문과 표준 코드를 나눠 다뤄야 합니다.
가까운 배터리 스와핑 스테이션의 교환 가능 여부를 보여주고 도착 전까지 한 칸을 잡아 두는 모바일 앱입니다. 판정은 충전 상태와 갱신 시각의 규칙으로 정합니다.
지쿠를 타다 배터리가 떨어진 라이더입니다. 앱에 스테이션 위치는 나오는데 막상 가 보면 충전 중인 배터리뿐이라 다시 다음 스테이션까지 밀고 가야 합니다.
스테이션마다 칸별 충전 상태와 갱신 시각이 남아 있으면, 도착했을 때 바꿀 수 있는지는 출발 전에 판정됩니다.
스테이션 목록에서 교환 가능 여부와 남은 칸 수를 보여주고 한 칸을 잡아 두는 모바일 앱입니다. 기기와 직접 통신하지 않고 서버가 받아 둔 칸 상태를 읽는 것을 전제로 시작합니다.
예약에 만료 시간을 두고 지난 예약을 자동으로 풀어 다른 라이더에게 돌려주는 처리입니다.
스테이션 이름 원문을 나라별 사전으로 표준 코드에 잇고 앱 표시 언어를 나눠 주는 구성입니다.
칸 상태 20건을 넣어 스테이션별 교환 가능 여부와 남은 칸 수가 앱 화면에 나오고 한 칸이 잡히는 것을 시연합니다.
결측·중복·미등록 스테이션 표기가 섞인 입력 30종을 넣어도 화면 형식이 깨지지 않고, 판정하지 못한 스테이션은 사유가 붙어 남습니다.
갱신이 오래된 스테이션은 남은 칸 수를 숫자로 보이지 않고 확인 필요로 바꿉니다. 라이더를 헛걸음시키는 숫자를 내놓지 않습니다.
판정과 손으로 만든 정답표를 맞춰 사유별로 어긋난 건을 셉니다. 단, 칸 상태와 정답을 모두 작성자가 만든 합성 기준이라 성능 측정이 아니라 데모 조건 동작입니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 더미데이터 자가채점이며, 실제 판정 정확도는 지바이크의 실제 스테이션 기기 상태와 실제 라이더 이동이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 결측·중복·미등록 표기·오래된 갱신을 더미에 일부러 섞어 쉬운 문제만 푸는 것을 피합니다.
충전이 다 됐다고 해서 실제로 뺄 수 있다고 단정할 수 없습니다. 칸이 물리적으로 걸리는 경우가 있어, 이 앱은 서버가 받은 상태까지만 답하고 현장 실패는 별도로 다뤄야 합니다.
실제 도입하면 기기에서 상태가 올라오는 주기가 스테이션마다 다릅니다. 오래된 것으로 볼 기준을 현장 데이터로 정하는 일이 도입 단계의 실제 과제입니다.
1주차는 서버가 이미 받아 둔 칸 상태를 그대로 믿습니다. 기기와 직접 통신하는 부분은 범위 밖이라 통신이 끊긴 상황은 갱신 시각으로만 짐작합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
스테이션 칸마다 올라온 배터리 상태입니다. 충전 정도와 갱신 시각이 함께 있어 교환 가능 여부를 판정합니다. 결측과 중복, 사전에 없는 스테이션 표기를 일부러 섞었습니다.
| slot_no | station_name_raw | station_id | battery_soc_pct | slot_state | updated_min_ago | country |
|---|---|---|---|---|---|---|
| S-001-01 | 강남역 3번출구 | ST-GN-03 | 98 | ready | 2 | KR |
| S-001-02 | 강남역 3번출구 | ST-GN-03 | 100 | ready | 2 | KR |
| S-001-03 | 강남역 3번출구 | ST-GN-03 | 43 | charging | 2 | KR |
| S-002-01 | 역삼 센터 | ST-YS-01 | 21 | charging | 4 | KR |
| S-002-02 | 역삼 센터 | ST-YS-01 | 35 | charging | 4 | KR |
아래 JSON Schema에 맞는 스테이션 칸 상태 행을 더 만들어 주세요. {"type":"object","properties":{"slot_no":{"type":"string"},"station_name_raw":{"type":"string"},"station_id":{"type":["string","null"]},"battery_soc_pct":{"type":["integer","null"]},"slot_state":{"type":"string","enum":["charging","ready","empty","fault"]},"updated_min_ago":{"type":["integer","null"]},"country":{"type":"string"}},"required":["slot_no","station_name_raw","station_id","battery_soc_pct","slot_state","updated_min_ago","country"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 결측으로 battery_soc_pct나 updated_min_ago가 null인 행, 둘째 중복으로 같은 slot_no가 두 행에 있는 재전송 건, 셋째 미매핑으로 station_name_raw가 사전에 없어 station_id가 null인 행, 넷째 표기흔들림으로 같은 스테이션을 강남역 3번출구와 강남역3번출구처럼 띄어쓰기만 다르게 적은 행, 다섯째 경계극단으로 충전이 0인 칸과 100인 칸, 여섯째 갱신이 세 시간 넘게 지난 행. 다섯 나라의 스테이션이 섞이게 해 주세요. 또한 형식 깨뜨리기 시도 3건을 별도 파일로 만들어 slot_state에 enum 밖 값을 넣어 스키마 검증이 걸러내는지 확인하게 해 주세요.{"type":"object","properties":{"slot_no":{"type":"string"},"station_name_raw":{"type":"string"},"station_id":{"type":["string","null"]},"battery_soc_pct":{"type":["integer","null"]},"slot_state":{"type":"string","enum":["charging","ready","empty","fault"]},"updated_min_ago":{"type":["integer","null"]},"country":{"type":"string"}},"required":["slot_no","station_name_raw","station_id","battery_soc_pct","slot_state","updated_min_ago","country"]}나라별 스테이션 이름 사전입니다. 원문 표기를 표준 스테이션 코드에 잇습니다. 신규 개설 스테이션을 일부러 등록하지 않아 미매핑 상황을 재현했습니다.
| station_name_raw | station_id | display_name_ko | display_name_en | country | slot_capacity |
|---|---|---|---|---|---|
| 강남역 3번출구 | ST-GN-03 | 강남역 3번출구 | Gangnam Stn Exit 3 | KR | 8 |
| 강남역3번출구 | ST-GN-03 | 강남역 3번출구 | Gangnam Stn Exit 3 | KR | 8 |
| 역삼 센터 | ST-YS-01 | 역삼 센터 | Yeoksam Center | KR | 6 |
| 홍대입구 2번출구 | ST-HD-02 | 홍대입구 2번출구 | Hongdae Stn Exit 2 | KR | 6 |
| 판교 테크노밸리 | ST-PG-01 | 판교 테크노밸리 | Pangyo Techno Valley | KR | 10 |
아래 JSON Schema에 맞는 스테이션 사전 행을 더 만들어 주세요. {"type":"object","properties":{"station_name_raw":{"type":"string"},"station_id":{"type":"string"},"display_name_ko":{"type":"string"},"display_name_en":{"type":"string"},"country":{"type":"string"},"slot_capacity":{"type":"integer"}},"required":["station_name_raw","station_id","display_name_ko","display_name_en","country","slot_capacity"]} 같은 station_id에 띄어쓰기가 다른 원문 표기를 여러 개 두어 표기흔들림을 흡수하게 하고, 칸 상태에는 나오지만 사전에는 없는 신규 스테이션이 남도록 일부는 일부러 등록하지 말아 주세요. 다섯 나라를 섞어 주세요. 또한 형식 깨뜨리기 시도 3건을 별도 파일로 만들어 slot_capacity에 문자열을 넣어 스키마 검증이 걸러내는지 확인하게 해 주세요.{"type":"object","properties":{"station_name_raw":{"type":"string"},"station_id":{"type":"string"},"display_name_ko":{"type":"string"},"display_name_en":{"type":"string"},"country":{"type":"string"},"slot_capacity":{"type":"integer"}},"required":["station_name_raw","station_id","display_name_ko","display_name_en","country","slot_capacity"]}| React Native | 스테이션 목록과 예약 화면을 그리는 모바일 앱입니다. |
| Kotlin | 칸 상태를 받아 교환 가능 여부를 판정하고 예약을 처리하는 서버입니다. |
| RESTful API | 앱과 서버 사이에서 스테이션 목록과 예약을 주고받는 규격입니다. |
| RDBMS | 스테이션 칸 상태와 예약을 담고 만료 처리를 거는 저장소입니다. |
| IoT 기기 상태 | 칸마다 올라오는 충전 상태와 갱신 시각이 판정의 입력이며, 오래된 값은 숫자 대신 확인 필요로 바꿉니다. |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 잡아 둔 칸에 만료를 걸어 되돌립니다.
✓ pass → 예약 만료까지가 한 덩어리입니다. 더 키우려면 나라별 표기를 사전으로 맞춥니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · station_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
available교환 가능all_charging충전 중slot_fault고장 칸 있음data_stale상태 확인 필요station_unmapped미등록 스테이션duplicate_slot중복 기록스테이션 한 곳의 교환 가능 여부입니다. 지금 바꿀 수 있는지, 안 되면 무엇 때문인지를 근거와 함께 담습니다.
{"type":"object","properties":{"station_id":{"type":["string","null"]},"verdict":{"type":"string","enum":["available","all_charging","data_stale","station_unmapped","slot_fault","duplicate_slot"]},"ready_slot_count":{"type":["integer","null"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{},"quote":{"type":["string","null"]}},"required":["field","value"]}},"next_action":{"type":"string"}},"required":["station_id","verdict","ready_slot_count","evidence","next_action"]}[
{
"station_id": "ST-GN-03",
"verdict": "available",
"ready_slot_count": 2,
"evidence": [
{
"field": "slot_state",
"agg": "count",
"value": 2
},
{
"field": "updated_min_ago",
"value": 2
}
],
"next_action": "지금 두 칸을 바꿀 수 있습니다. 한 칸을 잡아 두고 출발하시면 됩니다."
},
{
"station_id": "ST-PG-01",
"verdict": "available",
"ready_slot_count": 1,
"evidence": [
{
"field": "slot_state",
"agg": "count",
"value": 1
},
{
"field": "battery_soc_pct",
"value": 100
}
],
"next_action": "한 칸이 남았습니다. 먼저 잡아 두시면 도착까지 유지됩니다."
},
{
"station_id": "ST-YS-01",
"verdict": "all_charging",
"ready_slot_count": 0,
"evidence": [
{
"field": "battery_soc_pct",
"value": 35
},
{
"field": "slot_state",
"value": "charging"
}
],
"next_action": "지금은 모두 충전 중입니다. 다른 스테이션을 안내합니다."
},
{
"station_id": "ST-GU-01",
"verdict": "data_stale",
"ready_slot_count": null,
"evidence": [
{
"field": "updated_min_ago",
"value": 180
}
],
"next_action": "상태가 세 시간 전 값입니다. 숫자를 보이지 않고 확인이 필요하다고 알립니다."
},
{
"station_id": null,
"verdict": "station_unmapped",
"ready_slot_count": null,
"evidence": [
{
"field": "slot_no",
"value": "S-004-02"
},
{
"field": "station_name_raw",
"value": "Bangkok Sukhumvit 21",
"quote": "Bangkok Sukhumvit 21"
},
{
"field": "station_id",
"value": null
}
],
"next_action": "사전에 없는 스테이션입니다. 목록에 올리기 전에 운영팀이 등록합니다."
},
{
"station_id": "ST-HD-02",
"verdict": "slot_fault",
"ready_slot_count": 1,
"evidence": [
{
"field": "slot_state",
"value": "fault"
},
{
"field": "battery_soc_pct",
"value": 100
}
],
"next_action": "충전은 다 됐지만 고장난 칸이 있습니다. 남은 한 칸만 안내하고 고장은 운영팀에 알립니다."
},
{
"station_id": "ST-JS-01",
"verdict": "data_stale",
"ready_slot_count": null,
"evidence": [
{
"field": "updated_min_ago",
"value": null
}
],
"next_action": "갱신 시각이 없습니다. 숫자를 보이지 않고 확인이 필요하다고 알립니다."
},
{
"station_id": "ST-DN-01",
"verdict": "available",
"ready_slot_count": 1,
"evidence": [
{
"field": "slot_state",
"agg": "count",
"value": 1
},
{
"field": "updated_min_ago",
"value": 11
}
],
"next_action": "한 칸을 바꿀 수 있습니다. 잡아 두고 출발하시면 됩니다."
}
][자동 검증 · 1주차] 1. verdict가 enum 여섯 값 안에 있는지 확인합니다. 2. station_id·verdict·ready_slot_count·evidence·next_action이 모두 있는지 확인합니다. 3. battery_soc_pct가 0 이상 100 이하인지 확인합니다. 4. updated_min_ago가 null이거나 기준을 넘으면 verdict가 data_stale이고 ready_slot_count가 null인지 확인합니다. 5. evidence의 field가 두 입력 표의 컬럼에 실재하는지 확인합니다. 6. evidence의 quote가 그 행 station_name_raw의 일부인지 확인합니다. 7. next_action이 존댓말로 끝나는지 확인합니다. [수동 검증] 1. 오래된 것으로 볼 기준 시간이 현장 갱신 주기에 맞는지 운영팀이 확인합니다. 2. S-005-01처럼 띄어쓰기만 다른 표기가 사전으로 정규화된 행은 사유를 달지 않는 것이 맞는지 확인합니다. [평가 러너 · 2주차] 1. 손으로 만든 정답표와 verdict를 맞춰 사유별로 어긋난 건을 셉니다. 2. 칸 상태와 정답을 작성자가 만든 합성 기준이라 이 수치는 성능 측정이 아니라 데모 조건 동작입니다.