1. 활용한 디자인 시스템
1.1 토큰 체계
예전 팀프로젝트의 컬러 시스템을 차용하고 키컬러를 서비스 성격에 맞게 변경했습니다. Figma Foundations 을 통해 미니멀한 디자인 시스템을 구축하고 코드 레벨에서는 두 파일로 분산하여 관리합니다.
| 위치 | 역할 |
|---|---|
app/globals.css @theme | Tailwind v4 유틸리티 생성용 (bg-primary, text-now-open-ink 등) |
constants/tokens.ts | JS 참조용 미러 + statusToToken() 매핑 |
핵심 토큰:
#0E7C86 · strong #0A5F67 · blue #2E9BD6 · 10 #E5F4F5 · 5 #F2FAFA#2DB16B · upcoming #F0A231 · closed #9797A0 · error #C80000-soft(10% 틴트 배경) + -ink(AA 대비 텍스트)설계 원칙: 상태 컬러 = 비즈니스 로직과 1:1.getPoolNowStatus()의 반환(open/soon/closed)이 디자인 상태 토큰(now-open/upcoming/closed)에 그대로 대응되어, 로직과 디자인이 같은 언어를 쓴다.
1.2 컴포넌트 시스템
Figma Components 페이지의 주요 컴포넌트를 코드로 1:1 구현했습니다.
| Figma 컴포넌트 | 코드 | 비고 |
|---|---|---|
| Button (solid/outline/medium/disabled) | components/ui/Button.tsx | buttonClass() 헬퍼로 <a>/<Link>에도 동일 스타일 적용 |
| StatusBadge (now-open/upcoming/closed) | components/ui/StatusBadge.tsx | NowStatus → 틴트 pill |
| FilterChip (selected/default) | components/ui/Chip.tsx | 필터·제보사유 공용 |
| FreshnessTag | components/ui/FreshnessTag.tsx | "✓ YYYY.MM.DD 확인" |
| PoolCard | components/home/PoolCard.tsx | |
| TabBar (홈/강습알림/더보기) | components/layout/TabBar.tsx | |
| BottomSheet | components/report/ReportSheet.tsx | 제보 시트 |
| Toggle | components/ui/Toggle.tsx | 강습 알림·설정 공용 |
1.3 동시 작업 프로토콜 (Figma ↔ 코드)
토큰·로직·라우트는 디자인과 독립적으로 선행 구현하고, 화면 디자인이 완료되면 노드별 컨텍스트를 요청해 마크업과 스타일만 바인딩했습니다. 덕분에 디자인 진행 속도에 코드가 묶이지 않고 재작업율을 최소화했습니다.
2. 해결한 문제
2.1 데이터 수급
hanamsport.or.kr의 정형 HTML을 크롤링해 4개 시설(미사/풍산/덕풍/감일)의 자유수영 37세션 + 강습 18개를 추출, 공통 스키마로 정규화(data/pools.json).full/half 공통 요금테이블 1개로, 세션은 tier만 참조. 일요일 운영 주차 차이는 weeksOfMonth로, 시설별 변칙(감일=평일 없음, 풍산=금요일 분리)은 세션 단위로 흡수.2.2 데이터 검증
2.3 스코프 정의
data/private-pools.json에 분리 기록(향후 별개 기능 검토용)2.4 지금 상태
2.5 URL Fallback
not-found.tsx의 서버 redirect()가 Next에서 동작하지 않고 404를 그대로 반환(실측 확인).app/[...slug]/page.tsx)에서 서버 redirect('/')(307) + 상세 페이지의 잘못된 id도 redirect('/') + not-found.tsx는 클라이언트 안전망2.6 재사용성·클린코드 리팩토링
Button(+buttonClass), Chip, Toggle 추출 및 lib/format.ts(formatWon, tierLabel)·lib/cn.ts로 공통화. FilterChips·ReportSheet·Detail CTA·FeeCard·PoolCard·DaySchedule를 공용 조각으로 재구성.2.7 데이터 정직성 원칙
분기·시즌 변동이 잦은 도메인이라 신선도를 숨기지 않고 노출("✓ YYYY.MM.DD 확인", "데이터 기준 안내"). 검증되지 않은 값(예: 강습 등록일 D-day)은 날조하지 않고 "시설 확인" 등으로 표기 — 위치/정보 앱의 신뢰가 곧 제품 가치이기 때문.
3. 결과물 구조 (요약)
oneul-swim/ ├── app/ page(홈)·pool/[id](상세)·lessons·more·manifest·[...slug]·not-found ├── components/ │ ├── ui/ Button·Chip·Toggle·StatusBadge·FreshnessTag·icons │ ├── home/ HomeHeader·HomeClient·FilterChips·PoolCard │ ├── pool/ DaySchedule·FeeCard │ ├── report/ ReportSheet(BottomSheet) │ └── layout/ TabBar ├── lib/ pools(전처리)·types·time(dayjs)·format·cn ├── constants/ tokens (Foundations 미러) └── data/ pools.json · private-pools.json