GPSTracker was a team build, and I owned the frontend's API and mock layer. For the first couple of weeks the screens ran on JSON I'd hardcoded, because the backend endpoints didn't exist yet. Then I added MSW and turned that dummy data into a contract the frontend could develop against, before a single endpoint was live. The catch is that a mock only helps if it reflects the real contract.
GPSTracker는 팀 프로젝트였고, 저는 프론트엔드의 API·mock 계층을 맡았어요. 처음 2주쯤은 백엔드 엔드포인트가 아직 없어서 화면이 제가 하드코딩한 JSON으로 돌아갔어요. 그러다 MSW를 붙여서, 그 더미데이터를 프론트가 기준으로 삼을 계약으로 바꿨어요. 엔드포인트가 하나도 안 열린 상태에서요. 함정은, mock이 실제 계약을 반영할 때만 도움이 된다는 거예요.
Before MSW, my screens ran on JSON I'd hardcoded: a car list, driving logs, start and end overlays on the map, enough to build the tables against. It works until it doesn't. Every time the backend settled a field name or a response shape, my dummy data was suddenly wrong, and I was patching fixtures to chase a contract that lived only in someone else's head. Hardcoded fixtures don't unblock you; they just move the coupling somewhere you can't see it.
MSW를 붙이기 전에는, 제 화면이 제가 하드코딩한 JSON으로 돌아갔어요. 차량 목록, 운행일지, 지도 위의 출발·도착 오버레이까지, 테이블을 붙여볼 만큼요. 그럭저럭 돌아가다가 결국 무너져요. 백엔드가 필드 이름이나 응답 형태를 정할 때마다 제 더미데이터가 갑자기 틀렸고, 저는 누군가의 머릿속에만 있는 계약을 쫓아 fixture를 계속 고치고 있었어요. 하드코딩한 fixture는 프론트를 풀어주지 못해요. 결합을 눈에 안 보이는 곳으로 옮길 뿐이에요.
So I flipped the order. On the api-mocking branch I added MSW and moved the dummy data behind real
handlers, each typed against the same Response interface the backend would implement later:
getCarListResponse, getLogListResponse, and so on. The frontend now called
/api/vehicle and /api/record for real, and MSW answered them in the browser.
Same contract, both sides.
그래서 순서를 뒤집었어요. api-mocking 브랜치에서 MSW를 붙이고, 더미데이터를 진짜 핸들러 뒤로 옮겼어요.
핸들러 하나하나를 나중에 백엔드가 구현할 바로 그 Response 인터페이스에 맞춰 타입을 걸었고요.
getCarListResponse, getLogListResponse 같은 것들요. 이제 프론트는
/api/vehicle, /api/record를 진짜로 호출하고, MSW가 브라우저에서 그걸 대신
응답했어요. 양쪽이 같은 계약을 보는 거예요.
The rule that made it worth doing: the mock had to behave like the server, not like a happy path. My
handlers did the server's real work: paginate by page and size, sort ascending
or descending, filter the vehicle list by number and status. Delete a car that wasn't there and the mock
didn't gloss it with a 200; it returned the server's actual error body,
{ timeStamp, status, error, path }, the exact shape my response hook had to parse. So I
built and tested the frontend's error handling against the real error contract weeks before the backend
could send one.
할 가치가 있으려면 규칙이 하나 있었어요. mock이 happy-path가 아니라 서버처럼 굴어야 한다는 거예요.
제 핸들러는 서버가 하는 일을 그대로 했어요. page·size로 페이지를 나누고,
오름차순·내림차순으로 정렬하고, 차량 목록을 번호와 상태로 걸렀어요. 없는 차량을 삭제하면 mock은 200으로
얼버무리지 않았어요. 서버의 실제 에러 응답인 { timeStamp, status, error, path }를, 제 응답
훅이 파싱해야 할 바로 그 형태로 돌려줬어요. 덕분에 백엔드가 에러를 보낼 수 있기 몇 주 전에, 저는 실제
에러 계약을 기준으로 프론트의 에러 처리를 만들고 테스트했어요.
A mock's job isn't to impersonate the backend; it's to pin the contract down first. An honest mock isn't one whose data looks real, it's one that behaves exactly as the contract will. When mine was missing a field the UI needed, I backfilled it rather than let a screen depend on a fiction.
mock이 할 일은 백엔드를 그럴듯하게 흉내 내는 게 아니라, 계약을 먼저 고정하는 거예요. '정직한 mock'은 데이터가 진짜 같은 mock이 아니라, 실제 계약과 똑같이 움직이는 mock이고요. mock에 UI가 필요로 하는 필드가 빠져 있으면, 화면이 허구에 기대게 두지 않고 그 필드를 채웠어요.
The honest scope: this was a team build (Kernel360, 2025), and I wasn't the whole frontend, but the API
and mock layer was mine, so this lesson is first-hand. When the real endpoints landed, integration was
boring: mostly turning MSW off. The UI didn't get rewritten, because it had been built against the shape
the backend actually shipped. The one thing that bit me wasn't the contract at all, it was a
circular-reference bug in the shared response hook, which I fixed by injecting logout from
the outside. A mock isn't a shortcut around API design. It is the API design, written
down early.
정직한 범위예요. 이건 팀 프로젝트였고(Kernel360, 2025) 제가 프론트 전부는 아니었지만, API와 mock 계층은
제 몫이라 이 교훈은 직접 겪은 거예요. 실제 엔드포인트가 나왔을 때, 통합하는 날이 지루했어요. 대부분 MSW를
끄는 일이었거든요. UI를 다시 짜지 않았어요. 백엔드가 실제로 내보낸 형태에 맞춰 이미 만들어 뒀으니까요.
정작 발목을 잡은 건 계약이 아니라, 공용 응답 훅의 순환 참조 버그였어요. logout을 바깥에서
주입하는 방식으로 고쳤고요. mock은 API 설계를 건너뛰는 지름길이 아니에요. 일찍 적어둔 API 설계
그 자체예요.
GPSTracker is a fleet driving-analytics dashboard built with a small team at Kernel360 (React, TypeScript, Zustand, TanStack Query, MSW, Docker, Jenkins). Screenshots and the stack are on the project card.
GPSTracker는 Kernel360에서 소규모 팀으로 만든 차량 주행 분석 대시보드예요 (React, TypeScript, Zustand, TanStack Query, MSW, Docker, Jenkins). 스크린샷과 스택은 프로젝트 카드에 있어요.