🏊
기획부터 서버 운영까지 혼자 끝낸 개인 프로덕트입니다. 디자인 판단을 토큰과 컴포넌트 구조로 코드에 남긴 과정이 문서로 남아 있습니다.
배경
"오늘 이 시간에 자유수영을 할 수 있는 수영장이 어디지?"라는 생활 속 질문에서 시작한 개인 프로젝트입니다. 수영장의 자유수영 가능 여부·요금·위치를 한 화면에서 보여주며, 기획·디자인·프론트엔드·백엔드·인프라를 전부 혼자 맡았습니다.
한 일
- 데이터를 먼저 확보하고 검증: 공공 API로 전국 17개 시도의 공공 수영장 602곳을 적재했습니다. 자유수영 시간표와 요금처럼 공공데이터에 없는 값은 시설 페이지에서 직접 모아 정규화하고 교차 확인해 시설명 오류를 바로잡았습니다. 확인하지 못한 값은 지어내는 대신 화면에 "시설 확인"으로 명시하고 확인 날짜를 함께 노출합니다.
- iOS 스타일 디자인 시스템과 다크모드를 토큰으로 직접 설계:
:root에 시맨틱 CSS 변수(색·간격·elevation)를 정의하고prefers-color-scheme: dark에서 값만 뒤집어 다크모드가 자동 전환되게 했고, 수동 토글은[data-theme]훅으로 병설했습니다. 중복되던 그림자를 elevation 토큰으로 통합하고, 상태색(영업중·곧열림·마감)을 soft와 ink 페어로 묶어 라이트·다크 양쪽에서 대비 기준(AA)을 지켰습니다. - 목록 중심 화면을 지도 중심으로 재설계: 홈을 내 위치 기반 풀스크린 지도로 바꾸고, 상태색 마커에 클러스터러를 붙여 600곳 이상을 끊김 없이 보여줍니다. 필터는 지도 위 글라스 플로팅 패널로, 목록은 지도 앱식 3단(peek/half/full) 드래그 바텀시트로 재구성했습니다.
- 디자인을 살리려고 구현 방식을 바꿔 판단: 지도 SDK의
yAnchor가 React 포털 렌더 전 0×0 div에서 오프셋이 굳는 문제를 만났습니다. 디자인을 포기하고 마커 위치를 타협하는 대신 CSStransform앵커로 바꿔 렌더 타이밍과 무관하게 마커 카드가 정확히 붙게 했습니다. 지도 키나 API가 죽어도 정적 데이터로 항상 동작하는 폴백도 뒀습니다. - 백엔드는 필요해진 시점에 증설: MVP는 백엔드 없이 정적 JSON으로 검증하고, 백엔드가 있어야만 가능한 기능(오류 제보 수집, 내 제보 내역, 아침 요약 푸시)이 생긴 시점에 NestJS + Prisma + PostgreSQL을 증설했습니다. 로그인 대신 익명 기기 ID로 제보를 묶어 가입 마찰을 없앴습니다.
- 무료 인프라를 직접 운영: Oracle Cloud 무료 ARM 서버에 Docker Compose(Caddy HTTPS 자동 발급 + API + DB)로 배포했습니다. 실수 과금을 막으려고 쿼터 잠금과 예산 알림을 걸어 월 0원으로 운영합니다. 매일 오전 8시 크론이 오늘의 자유수영 세션을 계산해 Web Push로 발송합니다.
결과
- 602곳: 공공 API로 전국 수영장 적재 (17개 시도)
- 단독 완주: 기획·디자인·프론트엔드·백엔드·인프라·배포
- 지도 중심 재설계: 리스트 중심 화면을 내 위치 기반 지도로 전환
- 다크모드 AA 대비: 시맨틱 토큰으로 라이트·다크 양쪽 유지
- 월 0원 인프라: Oracle Cloud ARM + Docker Compose 직접 운영
검증은 typecheck·lint·테스트·빌드 게이트를 CI에 걸고, Playwright로 라이트·다크 화면을 시각 확인하는 방식으로 진행합니다.
정리
화면보다 데이터의 정확성이 제품 신뢰를 만든다는 걸 확인한 프로젝트입니다. 처음부터 풀스택으로 짓는 대신 정적 MVP로 먼저 검증하고 필요가 생긴 시점에 백엔드를 증설하면서, 제품의 생애주기를 기획부터 서버 운영까지 혼자 겪어봤습니다.