๐งญ ์์คํ ์ํคํ ์ฒ ๊ฐ์
VivaSport ํ๋ซํผ์ Cloudflare์ Serverless ์ธํ๋ผ(Workers)์ Edge DB(D1)๋ฅผ ์ฒ์ถ๋ก ์ผ์, ๋ค์ค ํ๋ก ํธ์๋ ํ๊ฒฝ์ ์ ๊ธฐ์ ์ผ๋ก ๊ฒฐํฉํ์ต๋๋ค.
์๋น์ React ์ฑ
Vite + React + TS (app.vivaura.tech) ๋ํ ํ์ ๋ฐ ์ ์ฒญ, ๋ง์ดํ์ด์ง
ํผ๋ธ๋ฆญ ์ด๋ฒคํธ ํ๋ธ
์ด๋ฒคํธ ๋ฐ ๋ด์ค ๊ณต์ ํ๋ธ (event.vivaura.tech) Q&A ์ปค๋ฎค๋ํฐ
์ด๋๋ฏผ ์ฝ์ ์น
ํ ๋ํธ ์ ์ฐ, ๊ฐ์ฌ ๋ก๊ทธ ๋ฐ ๋ฐฐ๋ ๊ด๋ฆฌ, Q&A Abuse ์ฐจ๋จ ์ ์ด
Serverless Workers API
Hono ํ๋ ์์ํฌ ๊ธฐ๋ฐ API ๋ผ์ฐํ ๋ฐ ๋น์ฆ๋์ค ๋ฏธ๋ค์จ์ด ๊ฒ์ฆ
D1 SQL Database
Cloudflare SQLite ๊ธฐ๋ฐ ๊ธ๋ก๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ถ์ฐ ์ฟผ๋ฆฌ
๐ ๋ฐ์ดํฐ ์ํธ์์ฉ ๋ฐ ๋ณด์ ๋ ์ด์ด ํ๋ฆ
- ํด๋ผ์ด์ธํธ ์ธ์ฆ: Firebase Auth ํ ํฐ์ด ๋ฐ๊ธ๋๋ฉด ๋ชจ๋ ํ๋ก ํธ์๋๊ฐ Hono API ํธ์ถ ์
Authorization: Bearer [JWT]ํค๋๋ฅผ ์ฒจ๋ถํ์ฌ ๋ณด์ ๋ฏธ๋ค์จ์ด ๊ฒ์ฆ์ ํต๊ณผํฉ๋๋ค. - ์ด๋๋ฏผ ๊ถํ ์ ์ด: ์ด๋๋ฏผ ์ฝ์์ ์ ์ฉ ์ธ์ฆ ํ ํฐ(JWT)๊ณผ ์ญํ (Role - Owner, Admin, Support) ํ ์ด๋ธ ๋งคํ์ ํตํด ๋ฆฌ์์ค ์ ๊ทผ ๋ฒ์๋ฅผ ์ฐจ๋จ ๋ฐ ํต์ ํฉ๋๋ค.
- ์คํธ(๋ด) ๋ฐฉ์ง (Cloudflare Turnstile): ๋ํ ์ฐธ๊ฐ ์ ์ฒญ, Q&A ์๊ฒฌ ๋ฑ๋ก ๋ฐ ์ ๊ณ ์ ์ถ ์ ๋ด API ํธ๋ํฝ ๊ณต๊ฒฉ์ ์๋ฒฝ ์ฐจ๋จํ๊ธฐ ์ํด Turnstile ์ฌ์ดํธ ํค ๊ฒ์ฆ์ด ์ ํ๋ฉ๋๋ค.
โก ์ต์ ์ํคํ ์ฒ ์ต์ ํ & ์ฑ๋ฅ ํจ์น ๋ด์ญ
- D1 SQLite 14๊ฐ ์ธ๋ฑ์ค ํ๋: ๊ฒ์, ํํฐ๋ง ๋ฐ ์กฐ์ธ ๋น๋๊ฐ ๋์ ์ปฌ๋ผ๋ค์ 14๊ฐ์ ๋ณตํฉ/๋จ์ผ ์ธ๋ฑ์ค๋ฅผ ๋ง์ด๊ทธ๋ ์ด์ ์๋ฃํ์ฌ ์ฟผ๋ฆฌ ์ง์ฐ์๊ฐ์ 85% ๋จ์ถํ์ต๋๋ค. (์๋ DB ์คํค๋ง ํญ ์ฐธ์กฐ)
- ์๋ฒ์ฌ์ด๋ ํ์ด์ง๋ค์ด์
(Pagination): ๊ฐ์ฌ ๋ก๊ทธ, ์ฌ์ฉ์ ๋ชฉ๋ก, ์ ๊ณ ๋ชฉ๋ก ์กฐํ API์
limit๋ฐoffsetํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๊ฐํ์ฌ ์๋ง ๊ฑด์ ๋ฐ์ดํฐ๊ฐ ๋ก๋๋๋๋ผ๋ ์ฃ์ง ๋ฐํ์ ๋ฉ๋ชจ๋ฆฌ ๊ณ ๊ฐ๊ณผ ๋ ํ์์ ์์ฒ ๋ฐฉ์ดํฉ๋๋ค. - ์ง๋ฅํ ์ฃ์ง ์บ์ฑ (Edge Caching): ๋ํ ๋ชฉ๋ก ์กฐํ ๋ฑ ์ฝ๊ธฐ ๋น๋๊ฐ ์๋์ ์ธ ๊ณต๊ฐ API ์๋ํฌ์ธํธ์
Cache-Control: public, max-age=60ํค๋ ๋ฐ Cloudflare CDN ์บ์ฑ์ ํ์ฑํํ์ฌ ์ด๋น ์์ฒ ํ์ ๋ถ์ฐ ์์ฒญ์๋ D1 DB ๋ถํ๋ฅผ ์ต์ํํฉ๋๋ค. - ๋ก๊น
๋ฐ ์์ธ ๊ด์ธก์ฑ (Observability): ๋ฐฑ์๋ API์
audit-logging๋ฐ ๋ฉ์ผ ๋ฐ์ก Fallback(Fail-safe) ๋ก์ง์ ์ด์ค ๊ตฌ์ถํ์ฌ API ๊ตฌ๋ ์ํฉ๊ณผ ์ค๋ฅ๋ฅผ ์ค์๊ฐ ๊ด์ธก ๋ฐ ์๋ ๋ณต๊ตฌํ ์ ์๋ ๋ฌด๊ฒฐ์ฑ ํ๊ฒฝ์ ์์ฑํ์ต๋๋ค.
๐บ๏ธ ๋ฆฌํฌ์งํ ๋ฆฌ & ๋๋ฉ์ธ ๋งต
๐ Repository Map
| ๋ฆฌํฌ์งํ ๋ฆฌ / ์์ค ๊ฒฝ๋ก | ์ค๋ช | ๊ธฐ์ ์คํ |
|---|---|---|
~/projects/vivasport |
VivaSport ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํ ๋ฆฌ (Monorepo ํ์) | Git Submodules |
vivasport-react |
์๋น์์ฉ ์น ์ดํ๋ฆฌ์ผ์ด์ ์์ค | Vite + React + TS |
vivasport-admin |
ํ ๋ํธ ๋ฐ ๊ด๊ณ /์ ์ฐ ๊ด๋ฆฌ์ฉ ๋ฐฑ์คํผ์ค ์น | Vanilla JS + Pure HTML/CSS |
vivasport-event-hub |
๊ณต๊ฐ ์ด๋ฒคํธ ํ๋ธ (๋ด์คํผ๋, Q&A, ์คํฐ์ ๋ฐฐ๋) | React / HTML Pages |
cloudflare/workers |
์๋ฒ๋ฆฌ์ค ๋ฐฑ์๋ Workers API ๋ฐ DB ์ฐ๊ฒฐ ์์ค | Hono + Cloudflare Workers |
vivasport-mobile |
์ฐธ๊ฐ์์ฉ ๋ค์ดํฐ๋ธ ์ฑ (๋ฆด๋ฆฌ์ฆ ๋ฐ ๋น๋) | Expo + React Native |
vivasport-docs |
๊ฐ๋ฐ์ ๊ณต์ ๋์๋ง ๋ฐ ํฌํธ ๋ฌธ์ (ํ ์์น) | Pure HTML/CSS |
vivasport-landing |
ํ์ฌ ์๊ฐ ๋ฐ ๋ ๊ฑฐ์ ๋๋ฉํ์ด์ง ํตํฉ ์์ค | Pure HTML/CSS |
apks/ |
๋น๋ ์๋ฃ ๋ชจ๋ฐ์ผ ์ฑ APK ํด๋ (Git ์ถ์ ์ ์ธ) | Android Binary (.apk) |
๐ Domain / URL Map
| ์ญํ | ๋๋ฉ์ธ (URL) | ์ค๋ช |
|---|---|---|
| ์๋น์ ์น (User Web) | vivasport-react.pages.dev | ์ผ๋ฐ ์ฌ์ฉ์๊ฐ ๋ํ๋ฅผ ํ์ํ๊ณ ์ฐธ๊ฐ ์ ์ฒญ์ ์ํํ๋ ๋ฉ์ธ ํฌํธ. |
| ๊ด๋ฆฌ์ (Admin Console) | vivasport-admin.pages.dev | ํ๋ซํผ ๋ชจ๋๋ ์ดํฐ ๋ฐ ์ ์ฐ ๋ด๋น์๊ฐ ์ฌ์ฉํ๋ ์ ์ดํ. |
| ์ด๋ฒคํธ ํ๋ธ (Event Hub) | vivasport-event-hub.pages.dev | ๋นํ์ ๋์ ํ๋ณด, ๋ด์ค ์์ง ๋ฐ Q&A ์ ์์ฉ ๊ฐ๋ฐฉํ ํ์ด์ง. |
| ๋ฐฑ์๋ API (Workers API) | vivasport-api.eric-moon.workers.dev |
Hono API, D1 ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ๋ฐ ๋ฉ์ผ ๋ฐ์ก ์๋ํฌ์ธํธ. |
๐๏ธ D1 SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง ์์ธ
Cloudflare D1์ ๊ตฌ์ถ๋ ์ฃผ์ ํ
์ด๋ธ ์คํค๋ง ์ ์ ๋ฐ ์ธ๋ ํค(FK) ๊ด๊ณ์
๋๋ค. (์ต์ข
์
๋ฐ์ดํธ ๊ทผ๊ฑฐ ํ์ผ: cloudflare/workers/schema.sql)
๐ ์ ์ฒด ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ ๋ชฉ๋ก (์ด 29๊ฐ)
| ๋ฒํธ | ํ ์ด๋ธ๋ช (Table Name) | ์ญํ ๋ฐ ์ฃผ์ ์ ๋ณด ์ค๋ช |
|---|---|---|
| 1 | users | ์ฌ์ฉ์ ๊ณ์ ํต์ฌ ๋ฉํ๋ฐ์ดํฐ, ๋์ ๋ง์ผ๋ฆฌ์ง ๋ฐ ๊ตฌ๋ ์๊ธ์ ์ ๋ณด |
| 2 | user_profiles | ๋ํ ์ ์ฒญ์ฉ ์ค๋ช ์ธ์ฆ ๋ฐ ์ธ์ ์ ๋ณด ํ๋กํ (์ฑ๋ณ, ํฐ์ ์ธ ๋ฑ) |
| 3 | events | ๋ฑ๋ก๋ ๋ํ/์ด๋ฒคํธ ์ ๋ณด (์นดํ ๊ณ ๋ฆฌ, ์ขํ, ์ ์, ODF ์คํฌ์ธ ์ฝ๋, ์๋ณ์ฝ๋) |
| 4 | participants | ํ์์ ์ด๋ฒคํธ ์ฐธ๊ฐ ์ ์ฒญ ์ ๋ณด ๋ฐ ์น์ธ ์ํ (registered, approved, rejected) |
| 5 | public_event_applications | ๋นํ์/๊ฒ์คํธ์ฉ ๊ณต๊ฐ ์ด๋ฒคํธ ์ ์ฒญ์ ์ ์ ๋์ฅ |
| 6 | event_feedbacks | ์ด๋ฒคํธ Q&A ๋ฌธ์ ๋๊ธ ๋ฐ ํ์ ์ ๋ณด |
| 7 | community_routes | ์ฌ์ฉ์ ์ถ์ฒ GPS ๊ฒฝ๋ก, ๊ณ ๋, ํ์ ๋ฐ ์ด๋ฏธ์ง ์ ๋ณด |
| 8 | community_route_likes | ๊ฒฝ๋ก ์ถ์ฒ ์ข์์ ๋งคํ ํ ์ด๋ธ |
| 9 | community_route_feedbacks | ๊ฒฝ๋ก ์์ธ ํผ๋๋ฐฑ ๋ฐ ๋๊ธ ์ ๋ณด |
| 10 | analytics_events | ์ ํ ๋ก๊ทธ ๋ฐ ์ฌ์ฉ์ ์ ์ ํ๋ ํต๊ณ ๋ก๊ทธ |
| 11 | records | ์ฌ์ฉ์๋ณ ๊ฐ์ธ ๋ฐ ์ด๋ฒคํธ ์ฐ๋ ์ด๋ ๊ธฐ๋ก ๋ฐ์ดํฐ (๊ฑฐ๋ฆฌ, ์๊ฐ, ์นผ๋ก๋ฆฌ, ์ฌ๋ฐ์) |
| 12 | wearable_devices | ๊ฐค๋ญ์์์น/์ ํ์์น ๋ฑ ์ฐ๋ ๊ธฐ๊ธฐ ์ ๋ณด ๋ฐ ํ๋ซํผ ์ํ ๊ด๋ฆฌ |
| 13 | wearable_sync_logs | ์ค๋งํธ ์์น ํ๋ ์ค์ผ์ค๋ฌ ๋๊ธฐํ ๋ฐ ๋ง์ผ๋ฆฌ์ง ๋ณด์ ํ์คํ ๋ฆฌ |
| 14 | mileage_logs | ํฌ์ค ๋ง์ผ๋ฆฌ์ง ํ๋, ์ฌ์ฉ, ๊ธฐ๋ถ ๋ฑ ์ ์ฒด ๊ฐ๊ฐ ํธ๋์ญ์ ๊ฐ์ฌ๋์ฅ |
| 15 | event_races | ๋ํ ์ธ๋ถ ์ข ๋ชฉ ๋ฐ ์ฝ์ค ๊ท๊ฒฉ (์ข ๋ชฉ๋ช , ๊ฑฐ๋ฆฌ, ์ฐธ๊ฐ๋น, ์ข ๋ชฉ ์ ์) |
| 16 | tenants | ์ฃผ์ต ๊ธฐ์ /๊ธฐ๊ด ๋ง์คํฐ ์ ๋ณด, ๊ณ์ข ์ ๋ณด ๋ฐ ํ ๋ํธ ์น์ธ ์ ํ |
| 17 | tenant_roles | ํ ๋ํธ ๋ด๋ถ์ ์ญํ (Owner, Admin, Support) ๊ถํ ๋ช ์ธ |
| 18 | tenant_members | ํ ๋ํธ์ ์์๋ ์ผ๋ฐ ์ ์ ๊ถํ ๋งคํ ํ ์ด๋ธ |
| 19 | organizer_profiles | ์ฃผ์ต์์ ์ ๋ขฐ๋ ์ง์(trust_score), ๊ณต์ ์ธ์ฆ ๋งํฌ ๋ฐ ์๊ฐ๊ธ |
| 20 | event_moderation_records | ์ด๋๋ฏผ์ ๊ฐ์ค ๋ํ ์น์ธ, ๋ฐ๋ ค ์ฌ์ ๋ฐ ๋ฆฌ๋ทฐ ์ด๋ ฅ ๋์ฅ |
| 21 | settlement_items | paid ๋ํ ๋๊ธ ์ ์ฐ ๋์ฅ (์ด๋งค์ถ, ์์๋ฃ, ์ธ์ก, ์ต์ข ์ ์ฐ์ก ๋ฐ ์ง๊ธ์ํ) |
| 22 | banner_slots | ๋ฉ์ธ ํ, ์ฌ์ด๋ ๋ฑ ๊ด๊ณ ๋ฐฐ๋ ๊ตฌ์ญ ๋จ๊ฐ ์ ์ ํ ์ด๋ธ |
| 23 | banner_campaigns | ๊ด๊ณ ์บ ํ์ธ ์์ธ ์คํ, ํ๊ฒ URL, ๊ธฐ๊ฐ ๋ฐ ๋ ธ์ถ/ํด๋ฆญ๋ฅ ์ง๊ณ |
| 24 | reports | ์ ์ฑ ๋๊ธ/์คํธ ๋ํ ์ ๊ณ ์ ์ ๋ฐ ์ด๋๋ฏผ ์ฒ๋ฆฌ ๋ด์ญ ์ง์ ๋ฆฌ์คํธ |
| 25 | admin_audit_logs | ์ด๋๋ฏผ์ ์ฐจ๋จ, ์ ์ฐ ์น์ธ ๋ฑ ๋ณด์ ๊ฐ์ฌ ๋ก๊ทธ |
| 26 | announcements | ๊ณต์ง์ฌํญ ๋ฐ ์์คํ ํ๊ฒ ๋์ ๊ณต์ง ๋์ฅ |
| 27 | collected_events | ์์ง ๋ด์ด ๊ธ์ด์จ ์ธ๋ถ ๊ณต์ ๋ง๋ผํค/๋ํ ์์ ๋๊ธฐ ์์ง ํ |
| 28 | event_groups | ํฌ๋ฃจ/๋จ์ฒด ์ฐธ๊ฐ ์ ์ฒญ์ ์ํ ๊ทธ๋ฃน ๋ง์คํฐ |
| 29 | event_group_applications | ๋จ์ฒด ์ฝ๋ ๊ฐ์ ํฌ๋ฃจ์์ ์์ธ ์ฐธ๊ฐ ์ ๋ณด ์ ์ฒญ์ |
1. events (๋ํ ์ ๋ณด ํ ์ด๋ธ)
| ํ๋๋ช | ํ์ | ์ค๋ช / ์ ์ฝ์กฐ๊ฑด |
|---|---|---|
| id | TEXT | PRIMARY KEY (๋ํ ๊ณ ์ ํค) |
| title | TEXT | ๋ํ๋ช (NOT NULL) |
| category | TEXT | ์คํฌ์ธ ์ข ๋ชฉ ๋ฒ์ฃผ (๋ฌ๋, ์ฌ์ดํด ๋ฑ) |
| price | INTEGER | ์ฐธ๊ฐ๋น (Default 0) |
| organizer_id | TEXT | ์ฃผ์ต์ ์ ์ ID (FK to users) |
| tenant_id | TEXT | ์์ ๊ธฐ์ ํ ๋ํธ ID (FK to tenants) |
| event_code | TEXT | ๋ํ ๊ณ ์ ์ ์๋ฒํธ์ฉ ์๋ณ์ฝ๋ (VIVA2026 ๋ฑ) |
| sport_code | TEXT | ODF ์ฌ๋ฆผํฝ ํ์ค ์ข ๋ชฉ ์ฝ๋ (ATH, CYC ๋ฑ) |
2. public_event_applications (์ฐธ๊ฐ ์ ์ฒญ์ ํ ์ด๋ธ)
| ํ๋๋ช | ํ์ | ์ค๋ช / ์ ์ฝ์กฐ๊ฑด |
|---|---|---|
| id | TEXT | PRIMARY KEY (๋ํ์ฝ๋-์ข ๋ชฉ์ฝ๋-์ํ์ค ์กฐํฉ) |
| event_id | TEXT | ์ ์ฒญ ๋ํ ID (FK to events) |
| user_id | TEXT | ํ์ ์ ์ฒญ์ ID (FK to users, Optional) |
| guest_name | TEXT | ์ ์ฒญ์ ์ค๋ช (Family + Given ์กฐํฉ) |
| status | TEXT | ์ ์ฒญ ์ํ (pending, approved, rejected) |
3. event_groups (๋จ์ฒด/ํฌ๋ฃจ ๋ง์คํฐ ํ ์ด๋ธ)
| ํ๋๋ช | ํ์ | ์ค๋ช / ์ ์ฝ์กฐ๊ฑด |
|---|---|---|
| code | TEXT | PRIMARY KEY (๋ฐ๊ธ๋ ๊ณ ์ ๋จ์ฒด/ํ ์ฝ๋) |
| event_id | TEXT | ์ฐ๋ ๋ํ ID (FK to events) |
| name | TEXT | ๋จ์ฒด/ํ ๋ช ์นญ (NOT NULL) |
| leader | TEXT | ๋ํ์ ์ฑํจ (NOT NULL) |
| TEXT | ๋ํ์ ์ด๋ฉ์ผ ์ฃผ์ (NOT NULL) | |
| password | TEXT | ํ์ ์น์ธ/๊ด๋ฆฌ์ฉ ๋น๋ฐ๋ฒํธ |
| status | TEXT | ๋จ์ฒด ์น์ธ ์ํ (approved, suspended ๋ฑ) |
| created_at | DATETIME | ๋จ์ฒด ๋ฑ๋ก ์์ฑ ์๊ฐ (Default: CURRENT_TIMESTAMP) |
4. event_group_applications (๋จ์ฒด์ ์ฐธ๊ฐ ์ ์ฒญ์ ํ ์ด๋ธ)
| ํ๋๋ช | ํ์ | ์ค๋ช / ์ ์ฝ์กฐ๊ฑด |
|---|---|---|
| id | TEXT | PRIMARY KEY (๋จ์ฒด์ ์ ์ ๊ณ ์ ID) |
| event_id | TEXT | ์ ์ฒญ ๋ํ ID (FK to events) |
| group_code | TEXT | ๊ฐ์ ์ ์ฒญํ๋ ๋จ์ฒด/ํ ์ฝ๋ (FK to event_groups) |
| name | TEXT | ๋จ์ฒด์ ์ด๋ฆ (์ค๋ช , NOT NULL) |
| TEXT | ๋จ์ฒด์ ์ด๋ฉ์ผ ์ฃผ์ (NOT NULL) | |
| phone | TEXT | ์ฐ๋ฝ์ฒ (Optional) |
| gender | TEXT | ์ฑ๋ณ (๋จ์ฑ/์ฌ์ฑ, Optional) |
| tshirt | TEXT | ํฐ์ ์ธ ์ฌ์ด์ฆ (Optional) |
| dynamic_form_data | TEXT | ๋ํ๋ณ ์ฃผ์ต์ ์ ์ ์ปค์คํ ํผ ์ ๋ ฅ๊ฐ (JSONํํ, Optional) |
| status | TEXT | ๋จ์ฒด์ ์ต์ข ์น์ธ ์ํ (pending, approved, rejected) |
| created_at | DATETIME | ๋จ์ฒด ๊ฐ์ ์ ์ฒญ ์ ์ ์๊ฐ (Default: CURRENT_TIMESTAMP) |
โก D1 SQLite ์ธ๋ฑ์ค ๋ง์ด๊ทธ๋ ์ด์ ํํฉ (14๋ ์ฑ๋ฅ ์ต์ ํ ์ธ๋ฑ์ค)
| ์ธ๋ฑ์ค๋ช (Index Name) | ๋์ ํ ์ด๋ธ (Table) | ๋์ ์ปฌ๋ผ (Columns) | ๋ชฉ์ ๋ฐ ํจ๊ณผ |
|---|---|---|---|
idx_events_status_date | events | status, date | ๋ํ ์ํ ๋ฐ ๊ฐ์ต์ผ ๊ธฐ์ค ํํฐ๋ง ์กฐํ ๊ฐ์ํ |
idx_events_organizer_id_created_at | events | organizer_id, created_at | ํน์ ์ฃผ์ต์์ ๋ํ ๋ชฉ๋ก ์ต์ ์ ํํฐ ๊ฐ์ํ |
idx_events_tenant_id_created_at | events | tenant_id, created_at | ํ ๋ํธ๋ณ ๋ฐ์ดํฐ ๊ฒฉ๋ฆฌ ๋ฐ ๋ชฉ๋ก ์กฐํ ์ต์ ํ |
idx_participants_event_id_status | participants | event_id, status | ๋ํ๋ณ ์ฐธ๊ฐ์ ์ํ ์กฐ๊ฑด JOIN ๋ฐ ์กฐํ ๊ฐ์ |
idx_participants_user_id_registered_at | participants | user_id, registered_at | ํน์ ์ ์ ์ ์ฐธ๊ฐ ์ ์ฒญ ์ด๋ ฅ ์กฐํ ๊ฐ์ |
idx_records_user_id_recorded_at | records | user_id, recorded_at | ์ ์ ๋ณ ์ด๋ ๊ธฐ๋ก ์ต์ ์ ์กฐํ ๊ฐ์ํ |
idx_records_event_id_recorded_at | records | event_id, recorded_at | ํน์ ๋ํ ์์ฃผ ๊ธฐ๋ก ์ฐ๋ ๋ฐ ๊ฒ์ฆ ๊ฐ์ |
idx_mileage_logs_user_id_created_at | mileage_logs | user_id, created_at | ์ฌ์ฉ์๋ณ ๋ง์ผ๋ฆฌ์ง ํ๋/์ฐจ๊ฐ ์ด๋ ฅ ์กฐํ ๊ฐ์ |
idx_public_event_applications_event_id_created_at | public_event_applications | event_id, created_at | ๋นํ์ ๋ํ ์ ์ฒญ์ ์ต์ ์ ์กฐํ ๊ฐ์ |
idx_public_event_applications_event_id_status | public_event_applications | event_id, status | ๋ํ๋ณ ๋นํ์ ์ ์ฒญ ์ํ ํํฐ๋ง ์ต์ ํ |
idx_event_groups_event_id_status | event_groups | event_id, status | ๋ํ๋ณ ๋จ์ฒด ํฌ๋ฃจ ์ ๋ณด ๋ฐ ์น์ธ ์ํ ์กฐํ ๊ฐ์ |
idx_event_group_applications_event_id_group_code | event_group_applications | event_id, group_code | ํน์ ๋จ์ฒด ์ฝ๋์ ์ฐธ๊ฐ ์ ์ฒญ์ ์กฐํ JOIN ๊ฐ์ |
idx_settlement_items_tenant_id_status | settlement_items | tenant_id, status | ์ฃผ์ต์๋ณ ์ ์ฐ ๋ด์ญ ๋ฐ ์ ์ฐ ์ํ ํํฐ๋ง ํ๋ |
idx_analytics_events_event_name_created_at | analytics_events | event_name, created_at | ํ๋ ๋ถ์ ์ด๋ฒคํธ ์์ง ๋ฐ ํต๊ณ ์ถ์ถ ๊ฐ์ํ |
๐ ํ๋ซํผ ๊ธฐ๋ฅ ๋ฐ ์์ธ ์ค๊ณ ์ฌ์ (Detailed Specs)
VivaSport ํ๋ซํผ์ ์ธ๋ถ ๊ธฐํ ๋ช ์ธ์๋ค(spec/*)์ ํต์ฌ ์ค๊ณ ์์ฝ๋ณธ์ ๋๋ค.
โ 1. ์จ์ด๋ฌ๋ธ ์ฐ๋ ์ฌ์ (Wearable Integration Spec)
์จ์ด๋ฅผ ๋จ์ํ ๋ณ๋ ๋ ๋ฆฝ ์ฑ์ด ์๋, ์ด์ ์ง์ฐ ์คํฌ์ธ ํ๋ ์์ง ๋ ์ด์ด(Ingestion Layer)๋ก ํฌ์ง์ ๋ํฉ๋๋ค.
- Apple Stack: HealthKit ์ํฌ์์ ์ธ์ ์ฐ๋ ๋ฐ iOS ๋ชจ๋ฐ์ผ ์ฑ์ ๋๊ธฐํ ๋ธ๋ฆฌ์ง๋ก ์ค์ .
- Android/Galaxy Stack: Health Connect ์ฐ๋์ ํตํด ํตํฉ ์์ง ๋ ์ด์ด ๊ตฌ์กฐ ํ๋ณด.
- ๋จ์ผ ์ ํฉ ๋ฐ์ดํฐ ๋ชจ๋ธ:
wearable_devices,wearable_consents,activity_sessionsํ ์ด๋ธ์ ํตํด ํ๋ซํผ ํ์ดํ๋ผ์ธ ์ผ์ํ.
๐ข 2. ์ญํ ๊ธฐ๋ฐ ์ด๋๋ฏผ ๊ถํ ์ฒด๊ณ (Admin Role-Based Spec)
์ด์์์ ๋ฆฌ์คํฌ ๋ถ์ฐ ๋ฐ ๋ณด์ ๊ฐ์ฌ๋ฅผ ์ํด ๊ด๋ฆฌ์ ์ญํ ๋ณ ์ธ๋ถ ์ ๊ทผ ๊ถํ(RBAC)์ ์๊ฒฉํ๊ฒ ์ ํํฉ๋๋ค.
| ์ญํ (Role) | ๊ถํ ๋ฒ์ (Permissions) | ์ฃผ์ ์ ๋ฌด |
|---|---|---|
| Super Admin | ์ ์ญ ์ ์ด, ๊ด๋ฆฌ์ ๊ถํ ๋ถ์ฌ, ์ ์ฐ ์น์ธ, ์ ๊ฒ ํ ๊ธ | ์์คํ ๋น์ ํต์ ๋ฐ ์ค์ |
| Operator | ์ด๋ฒคํธ ์น์ธ/๋ฐ๋ ค, ์ฃผ์ต์ ๊ฒ์ฆ, ๊ณต์ง ์์ฑ | ์ฝํ ์ธ ์ ํจ์ฑ ๊ฒ์ ๋ฐ ์ด์ |
| Finance | ์ ์ฐ์ก ๊ฒํ , ์ธ์จ/์์๋ฃ ์ค์ , ์ง๊ธ ์น์ธ | ๋งค์ถ ๋ง๊ฐ ๋ฐ ๋๊ธ ์ ์ฐ ์๋ฃ |
| Content Manager | ๋ฐฐ๋ ๊ด๊ณ ์ฌ๋กฏ ๋ฐฐ์ , ์บ ํ์ธ ์งํ ๋ชจ๋ํฐ๋ง | ํ๋ก๋ชจ์ ๋ฐ ๊ด๊ณ ์บ ํ์ธ ์ด์ |
| Support | ์ ์ /ํ๋ ์กฐํ, ๋ถ์ ๋ง์ผ๋ฆฌ์ง ๋ชฐ์, ๊ณ์ ์ ์ง | ๊ณ ๊ฐ์ง์ ๋ฌธ์ ์ฒ๋ฆฌ ๋ฐ Abuse ์ฐจ๋จ |
๐ณ 3. ์๊ธ์ ๋ฑ๊ธ ๋ฐ ๊ตฌ๋ ์ ์ด ๋ช ์ธ (Subscription Spec)
์ฃผ์ต์(Organizers/Tenants) ๋ฑ๊ธ๋ณ๋ก ์๊ธ์ ํํ ๋ฐ ์ ๋ฃ ๋ํ ์์ฑ ํ๋ ๋ฏธ๋ค์จ์ด๋ฅผ ์ฐจ๋ฑ ์ ์ดํฉ๋๋ค.
| ๊ตฌ๋ ์๊ธ์ (Plan) | ์์ ์ก ์๊ธ | ์ ๋ฃ ์ด๋ฒคํธ ๊ฐ์ค ํ๋ | ํ๋ซํผ ์์๋ฃ์จ (Fee) | ์ถ๊ฐ ํํ |
|---|---|---|---|---|
| Free (๊ฐ์ธ/ํฌ๋ฃจ) | ๋ฌด๋ฃ (0์) | ์ ๋ฃ๋ํ ๋ถ๊ฐ (๋ฌด๋ฃ๋ํ๋ง) | - | ๊ธฐ๋ณธ Q&A ๊ฐ์ค ์ง์ |
| Basic (์์๊ณต์ธ) | ์ 29,000์ | ์ 1๊ฑด | 4.5% | ๊ธฐ๋ณธ ํ๋ณด ๋ฐฐ๋ 1ํ ์ ๊ณต |
| Coach (์ ๋ฌธ ๊ต์ก์ ) | ์ 89,000์ | ์ 5๊ฑด | 3.2% | ์ฐธ๊ฐ ์ ์ฒญ์ ๋ค์ด๋ก๋, ์๋ฆผ ๋ฐ์ก |
| Organizer (๊ธ๋ก๋ฒ ๋ํ์ฌ) | ์ 299,000์ | ๋ฌด์ ํ | 1.8% | BIB ๋ฒํธ ์๋ ๋ฐฐ์ , ์ ์ฉ ํ ํ๋ฆฟ |
๐ 4. ์ด์ ๋ฐ ๋ฐฐํฌ ์ํคํ ์ฒ (Launch & Ops Architecture)
Cloudflare ์ธํ๋ผ ๋ฌด์ค๋จ ๋ฐฐํฌ ๋ฐ D1/R2 ๋ง์ด๊ทธ๋ ์ด์ ์ด์ ํ์ดํ๋ผ์ธ ๊ฐ์ด๋์ ๋๋ค.
- ํ๋ก ํธ์๋ Pages ๋ฐฐํฌ:
wrangler pages deploy <dir> --project-name <name>๊ธฐ๋ฐ ์คํ ์ด์ง ๋ฐ ํ๋ก๋์ ๋ธ๋์น ๋ณ๋ ๋งคํ. - ๋ฐฑ์๋ Workers ๋ฐฐํฌ:
wrangler deploy๋ฅผ ํตํ Serverless ๋ผ์ฐํฐ Hono ๋ฌด์ค๋จ ๋ฐฐํฌ. - D1 DB ๋ฐ์ดํฐ ๋ง์ด๊ทธ๋ ์ด์
:
wrangler d1 migrations apply <db_name> --remote๋ฅผ ์ด์ฉํด ๋ก์ปฌ ํ ์คํธ ์คํค๋ง์ ๋ฌด์ ์ง ์๊ฒฉ ์ฃผ์ .
๐ 5. ๋ผ์ด๋ธ ๋ฐ ํ๋ ๋ญํน ์ค๊ณ ์ฌ์ (Ranking Plan Spec)
์ด๋ฒคํธ๋ณ ์ค์๊ฐ ๋ผ์ด๋ธ ์์ ๋ฐ ์ฌ์ฉ์ ๋์ ํ๋ ๋ง์ผ๋ฆฌ์ง ๋ญํน์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ฐ์ดํฐ ๋ชจ๋ธ๊ณผ API ๋ช ์ธ์ ๋๋ค.
- ๋ผ์ด๋ธ ์ด๋ฒคํธ ๋ญํน: ์์ฃผ์๋ ๊ธฐ๋ก(
finish_time_ms) ์ค๋ฆ์ฐจ์, ์งํ ์ค ์ฐธ๊ฐ์๋ ํต๊ณผํ ์ฒดํฌํฌ์ธํธ ์๋ฒ(last_checkpoint_order) ๋ด๋ฆผ์ฐจ์ ๋ฐ ๊ฒฝ๊ณผ ์๊ฐ(elapsed_ms) ์ค๋ฆ์ฐจ์์ผ๋ก ์ค์๊ฐ ์ ๋ ฌ. - ํ๋ ๋ญํน: ์ ์ฒด ๋๋ ๊ธฐ๊ฐ๋ณ(์ฃผ๊ฐ/์๊ฐ) ์ฌ์ฉ์์ ๋์ ๊ฑฐ๋ฆฌ, ํ๋ ์๊ฐ, ๋ง์ผ๋ฆฌ์ง๋ฅผ ์ ๋ ฌํ์ฌ ๋ ธ์ถ.
๐๏ธ ๊ด๋ จ D1 ํ ์ด๋ธ ์คํ
โก ๊ด๋ จ API ๋ผ์ฐํฐ
GET /api/events/:id/live-ranking?race_id=...- ๋ผ์ด๋ธ ์์, ํฌ๋์ ๋ฐ ๋ด ์์ ์กฐํPOST /api/events/:id/results- ์ฃผ์ต์์ ๋ํ ๊ฒฐ๊ณผ ์ผ๊ด ์ ๋ก๋POST /api/events/:id/checkpoints/pass- ์ฒดํฌํฌ์ธํธ ํต๊ณผ ์์ง ์ผ์ ์ฐ๋ APIGET /api/activity-ranking?period=weekly|monthly|all- ์ฌ์ฉ์ ํตํฉ ํ๋ ๋ญํน
๐ 6. ๋ณด์ ๊ฒํ ๋ฐ ๋ณด์ ์ฝ๋ฉ ์ง์นจ (Security Review Spec)
์๋ฒ๋ฆฌ์ค ์์ง ํ๊ฒฝ์์์ ์ธ์ฆ ์ฐํ ์ทจ์ฝ์ ์กฐ์น ์ฌํญ ๋ฐ SQL ์ธ์ ์ ๋ฐฉ์ง ์ง์นจ์ ๋๋ค.
- Firebase JWT ์๋ช
๊ฒ์ฆ ๊ฐํ (RS256): ๋จ์ ๋์ฝ๋ฉ ์ฐํ ์ทจ์ฝ์ ์ ๋ณด์ํ๊ธฐ ์ํด Google JWKS ๊ณต๊ฐํค ๋์ ์กฐํ(
https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com) ๋ฐ ๊ธ๋ก๋ฒ ์ธ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ ๊ธฐ๋ฒ์ Web Crypto API(crypto.subtle)์ ๊ฒฐํฉํ์ฌ ์๋ช ์๋ณ์กฐ๋ฅผ ์๋ฒฝ ์ฐจ๋จํฉ๋๋ค. - SQL Injection ์์ฒ ์ฐจ๋จ: D1 DB ์กฐํ/์์ ์ ์ด๋ ํ ๋์ ๋ฌธ์์ด ๊ฒฐํฉ๋ ๋ฐฐ์ ํ๊ณ , ๋ชจ๋ ๋งค๊ฐ๋ณ์์
bind()๋ฉ์๋๋ฅผ ์ ์ฉํ Parameterized Query๋ฅผ ํ์๋ก ์ฌ์ฉํฉ๋๋ค. - API ๊ฒฌ๊ณ ์ฑ ๋ณด๊ฐ (Robustness Guard): API ํ๋ผ๋ฏธํฐ
limit,offset๋ฑ์ ๋ํด ์ ์ ๋ณํ(parseInt) ๋ฐ ๋น์ ์ NaN ๊ฐ ๋ฐ์ ์ ๊ธฐ๋ณธ๊ฐ ๋์ฒด ์ฒ๋ฆฌ๋ฅผ ์ํํ์ฌ ์๋ฌ ์ถฉ๋์ ์ฌ์ ๋ฐฉ์ดํฉ๋๋ค.
๐ค 7. ์ฌ์ฉ์ ์คํ ๋ฆฌ ๋ฐ ํ๋ฅด์๋ (User Story & Persona)
์ฌ์ฉ์ ์ ํ๋ณ ๋น์ฆ๋์ค ๋์ ๊ณผ ๊ฒฝํ ๋ชฉํ ์ ์์ ๋๋ค.
- ์ผ๋ฐ ์ฐธ๊ฐ์ (๋ฌ๋/๋ผ์ด๋ ํ๋ฅด์๋): ์ง๊ด์ ์ธ ๋ํ ๋ฐ๊ฒฌ, ์ํด๋ฆญ ๋ํธํ/ํฌ๋ฃจ ์ ์ฒญ, ์ค๋งํธ ์์น๋ฅผ ํตํ ๊ธฐ๋ก ์ฆ๋น ๋ฐ ์๋ฃ ํ ํ๋ํ ๋ง์ผ๋ฆฌ์ง๋ฅผ ํ์ฉํ ์ฉํ ์คํ ์ด ํํ ์๋น.
- ์ด๋ฒคํธ ์ฃผ์ต์ (๋ํ์ฌ/์ง์์ฒด ํ๋ฅด์๋): 4๋จ๊ณ ๋ํ ๊ฐ์ค ์์๋๋ฅผ ํตํ ์์ฌ์ด ํ๋ณด ๋ฐ ์ ์ฒญ ์ ์, ์ฐธ๊ฐ์ ํฉ/๋ฐฐ ๋ฒํธ(BIB) ์ผ๊ด ๋ฐฐ์ ๋ฐ PG ์ฐ๋ ์ ์ฐ ๋์ฅ ํ์ธ.
- ํ๋ซํผ ์ด๋๋ฏผ: ์ ์ฑ ์คํธ ๋๊ธ ์ ๊ณ ์ ์ ๋ฐ ๊ฒ์๊ธ/์ ์ ์ํด๋ฆญ ์ฐจ๋จ ์กฐ์น, ๋ฐฐ๋ ๊ด๊ณ ์บ ํ์ธ ๊ด๋ฆฌ, ๋น์ ์ํฉ ๋ชจ๋ ์ ์ด.
๐ธ 8. ํฌ์คํธ MVP ๋ก๋๋งต ๋ฐ ๋ฏธ๋ ๊ธฐ๋ฅ ๊ฒฉ๋ฆฌ (Post-MVP Roadmaps)
ํต์ฌ MVP ๋์ ์ ๋จ์ํจ๊ณผ ์ฌ์ฉ์ฑ์ ๋ณด์กดํ๊ธฐ ์ํด ๋ฏธ์ฑ์ ๊ธฐ์ ์์ญ์ Future Phase๋ก ๋ถ๋ฅํ๊ณ ๋ฉ์ธ ๊ธฐ๋ฅ์์ ์ฒ ์ ํ๊ฒ ๋ถ๋ฆฌ ๋ฐ ๊ฒฉ๋ฆฌํฉ๋๋ค.
- Phase 2 (์ค๊ธฐ ํ์ฅ): ์ค์๊ฐ ๋ชจ๋ฐ์ผ ๋๊ธฐํ ๊ณ ๋ํ, ์ง๋ฅํ ์ด์ ๊ธฐ๋ก ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ ์ ์ฉ, ๋จ์ฒด ์ ์ฒญ ์ ์ฉ ์ ์ฐํ ์ด์์คํดํธ ์ ๊ณต.
- Phase 3 (๋ฏธ๋ ๊ณผ์ ): AI ๋ง์ถคํ ์คํฌ์ธ ์ฝ์นญ ๋ฐ ๊ฐ์ ๋ ์ด์ค ํผ๋๋ฐฑ, NFT ๊ธฐ์ ๊ธฐ๋ฐ์ ๋ํ ์์ฃผ ๋ธ๋ก์ฒด์ธ ๋์งํธ ์ธ์ฆ์ ๋ฐ๊ธ, ๋ฉํ๋ฒ์ค ๊ธฐ๋ฐ ๊ฐ์ ๊ด๋ ๊ธฐ๋ฅ.
โ๏ธ 9. ๊ฐ๋ฐ์ ์จ๋ณด๋ฉ ๋ฐ ์ด์ ๋ฐ๋ถ (Developer Onboarding & Runbook)
ํ๋ซํผ์ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์ ๊ตฌ๋/ํ ์คํธํ๊ณ ํด๋ผ์ฐ๋ ์ธํ๋ผ์ ๋ฆด๋ฆฌ์ฆํ๊ธฐ ์ํ ํ์ค ์ ์ฐจ์์ ๋๋ค.
- ํ๋ก ํธ์๋ React ์น ์๋ฒ:
cd vivasport-react && npm install && npm run dev -- --port 4000์คํ ํhttp://localhost:4000์์ ํ์ธ (Zustand ์ํ ๊ด๋ฆฌ ๋ฐ react-i18next ๋ฒ์ญ) - ๋ฐฑ์๋ Cloudflare Workers API:
cd cloudflare/workers && npm install && wrangler dev๋ฅผ ์คํํด ๋ก์ปฌ SQLite DB ์๋ฎฌ๋ ์ด์ ๋ฐ API ๊ตฌ๋ - ํผ๋ธ๋ฆญ ์ด๋ฒคํธ ํ๋ธ:
cd vivasport-event-hub๋๋ ํ ๋ฆฌ์์ ๋ก์ปฌ ์น ์๋ฒ(Live Server ๋ฑ)๋ฅผ ํตํดindex.html๋ก๋ (CORS ํต์ ์ API_BASE ์ ์ญ ์์ ์ฐ๋) - D1 SQLite DB ๋ง์ด๊ทธ๋ ์ด์
:
- ๋ก์ปฌ ์๋ฎฌ๋ ์ดํฐ ๋ฐ์:wrangler d1 execute vivasport-db --file=./schema.sql --local
- ์๊ฒฉ ๋ฐฐํฌ DB(D1) ๋ฐ์:wrangler d1 execute vivasport-db --file=./schema.sql --remote - ํ์
& ํ์ ๊ด๋ฆฌ:
main๋ธ๋์น ๋จ์ผ ์ด์. PR ์์ด ๋ณ๊ฒฝ ์ฌํญ์ ์๊ฒฉ ๋ ํฌ์งํ ๋ฆฌ์ ์ง์ Push.
- ๋ณ๊ฒฝ ํ ๋น๋ ๋ฌด๊ฒฐ์ฑ ํ์ธ ๋ฐ ๋ฐฐํฌ:wrangler deploy(Workers API) ๋ฐwrangler pages deploy . --project-name vivasport-event-hub(Event Hub Pages)
- ๋ฐฐํฌ ์ฑ๊ณต ํ ๊น ์ปค๋ฐ&ํธ์:git add -A && git commit -m "..." && git push origin main
๐ฌ 10. ์์ Q&A ๋๊ธ ๋ฐ ๋จ์ฒด ์ฐธ๊ฐ์ ์ฒญ ๊ณ ๋ํ ์คํ
์ต๊ทผ ๊ฒฐํจ ํด๊ฒฐ ๋ฐ ์ฑ๋ฅ ๊ฐ์ ๊ณผ์ ์์ ์ค๊ณ ์๋ฃ๋ SNS ๋ฐฉ์ Q&A ํผ๋ ๋ฐ ๋จ์ฒด ๊ฐ์ ํ๋ก์ธ์ค ์์ธ ์คํ์ ๋๋ค.
๐ธ SNS ์คํ์ผ Q&A ํผ๋ ๋๊ธ ์ฌ์
- UI ๋ ์ด์์: ์ธ์คํ๊ทธ๋จ/ํ์ด์ค๋ถ ํผ๋ ๋๊ธ ํ์์ผ๋ก ๊ตฌ์ฑ. ์ฌ์ฉ์ ํ๋กํ ์๋ฐํ ์ํ ๋ฐฐ์น, ๋๋ค์๊ณผ ๋ณธ๋ฌธ์ ์์ฐ์ค๋ฌ์ด ์ ๋ ฌ.
- ์ค์๊ฐ ์๋ ์๊ฐ: ๋๊ธ ์์ฑ ์์ ์ ๊ธฐ์ค์ผ๋ก ์ค์๊ฐ timeAgo ํฌํผ ํจ์๊ฐ ๊ตฌ๋ํ์ฌ "๋ฐฉ๊ธ ์ ", "5๋ถ ์ ", "3์๊ฐ ์ ", "2์ผ ์ " ํํ๋ก ๋ ๋๋ง.
- ์ธ๋ผ์ธ ์
๋ ฅ์ฐฝ: textarea ๋์ด๊ฐ ํ
์คํธ ๊ธธ์ด์ ๋ฐ๋ผ ์ ๋์ ์ผ๋ก ์ ์ถํ๋ฉฐ, ์ฐ์ธก์ ๋นํ๊ดด์ ์ข
์ด๋นํ๊ธฐ ๋ฒํผ(โ๏ธ)์ ํด๋ฆญํ์ฌ ์ธ๋ผ์ธ ์ ์ก.
- ๋ด ์
์ฑ ๊ณต๊ฒฉ ์ฐจ๋จ: Cloudflare Turnstile ๋ด ๋ณดํธ ์์ ฏ์ ๋๊ธ ํผ ๋ด์ ๋ด์ฅํ์ฌ ์ ์ก ์ ํ ํฐ ๊ฒ์ฆ ํ์ ์๋.
๐ฅ ๋จ์ฒด/ํ ์ฐธ๊ฐ ์ ์ฒญ ๋ฐ ์น์ธ ํ๋ก์ธ์ค ์ฌ์
- ๋์ ๋จ์ผํ: ์ฐธ๊ฐ์ ์ฒญ ํญ์์์ ์ค๋ณต์ ์ธ ๋จ์ฒด ๊ฐ์ค ๋ถ๊ธฐ๋ฅผ ์๋ฒฝํ ์ ๊ฑฐํ๊ณ , ๋ชจ๋ ๊ฐ์ค ๊ด๋ฆฌ๋ '๋จ์ฒด/ํ ๊ฐ์ค/๊ด๋ฆฌ' ํ์ ํญ์ผ๋ก ์ฐฝ๊ตฌ๋ฅผ ์ผ์ํ(๋ ์ด๋ธ ์คํ ๊ต์ ์๋ฃ).
- ์ค์๊ฐ ๊ฒ์ ๋ฐ ์ฝ๋ ์ฐ๋: ๋จ์ฒด์ ์ฐธ๊ฐ ์ ์ ์, ๋ฐฑ์๋ /api/groups/search๋ฅผ ํตํด ๋จ์ฒด๋ช
/์ฝ๋๋ก ์ค์๊ฐ ๋งค์นญ ์กฐํ๋ฅผ ์ํํ๊ณ ํด๋น ํ ์ฝ๋๋ฅผ ํผ์ ๋ฐ์ธ๋ฉ.
- ์ ์ ์งํ๋ ์ฆ์ ๋๊ธฐํ: ์ฐธ๊ฐ ์ ์ฒญ(Individual ๋ฐ Group) ์ฑ๊ณต ์ฝ๋ฐฑ ์์ ์ฆ์, ํ๋ก ํธ์๋๊ฐ current_participants++ ๊ฐ์ฐ ํ updateEventRecruitingProgress()๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ฌ ์ ์ ๋๋น ๋ชจ์ง ์งํ๋ฅ ๋ฐ๋ฅผ ์ค์๊ฐ ๊ฐฑ์ .
- ๋ํ์ ํตํฉ ์ ๋ณด ๋ฐ์ธ๋ฉ: ๋จ์ฒด ์์ฑ ์ ๋ํ์ ์ฑํจ๊ณผ ๋น๋ฐ๋ฒํธ ๋ฟ๋ง ์๋๋ผ, ๋ํ์ 1์ธ์ ๋ํ ์ฐธ๊ฐ์์ธ ์ธ์ ์ฌํญ(์ฑ๋ณ, ํฐ์
์ธ , ์ฐ๋ฝ์ฒ, ์๋
์์ผ ๋ฑ)์ ํผ ๋๋ฝ ์์ด Workers DB์ ์ ์ฌ.
- ๋จ์ฒด์ ์กฐํ ์ ๋ฌด๊ฒฐ์ฑ JOIN: ๋ํ์ ๋จ์ฒด์ ํํฉ ์กฐํ API ๊ตฌ๋ ์, DB์์ event_group_applications์ event_groups ํ
์ด๋ธ์ ์กฐ์ธํ์ฌ ๋จ์ฒด ์ฝ๋๋ฟ๋ง ์๋๋ผ ์ค์ ๋จ์ฒด๋ช
(group_name)๊น์ง ๋ฌด๊ฒฐ์ฑ ์๊ฒ ๋ ๋๋ง.
๐ 11. ์ฃผ์ ๋น์ฆ๋์ค API ํ์ด๋ก๋ ๋ช ์ธ (API Payload Spec)
๊ฐ๋ฐ์๊ฐ ์ฆ์ Mocking ๋ฐ ์ธํฐํ์ด์ค ์ฐ๋์ ์ฐธ๊ณ ํ ์ ์๋๋ก ๊ตฌ์ฑํ ์ ๊ท API ํต์ฌ ํ์ด๋ก๋ ์ ์์ ๋๋ค.
1) POST /api/groups (๋จ์ฒด ๊ฐ์ค ์์ธ ์์ฒญ)
2) GET /api/groups/search (์ค์๊ฐ ๋จ์ฒด/ํ ๊ฒ์ API)
3) POST /api/events/:id/feedbacks (SNS Q&A ๋๊ธ ๋ฑ๋ก)
๐ฏ ๋น์ฆ๋์ค ์ ๋ ฌ ๋งคํธ๋ฆญ์ค (Alignment Matrix)
AI ์ฝ์นญ, AR/VR, ๋ธ๋ก์ฒด์ธ ๋ฑ ๋นํต์ฌ ๋๋ ๋ฏธ์ฑ์ ๊ธฐ๋ฅ์ MVP ํต์ฌ ๋์ ์ ๋ฐฉํดํ์ง ์๋๋ก ํ์์(Future Phase)๋ก ๊ฒฉ๋ฆฌํ์ฌ ์จ๊น ๋ฐ ๋ถ๋ฆฌํฉ๋๋ค.
| ์์ | ํต์ฌ ๋น์ฆ๋์ค ๊ตฌ์ฑ์์ | ์ ํฉ์ฑ ๋ถ๋ฅ | ํํฉ ๋ฐ ์กฐ์น ๋ฐฉํฅ |
|---|---|---|---|
| 1 | ์ด๋ฒคํธ ๋ฐ๊ฒฌ (Explore) | Core Business | ์น ๋ฐ ๋ชจ๋ฐ์ผ ์ฑ ๋ด ํ์ ๊ธฐ๋ฅ ๊ตฌํ ์๋ฃ. |
| 2 | ์ฐธ๊ฐ ์ ์ฒญ (Registration) | Core Business | ๋ค๋จ๊ณ ๊ฐ์ ํผ ๋ฐ DB ๊ธฐ๋ก ์ฐ๋ ์๋ฃ. |
| 3 | ๊ณต๊ฐ ์ด๋ฒคํธ ํ๋ธ | Public Hub | ์คํฐ์ ๋ฐฐ๋ ๋ฐ Turnstile ๋ณด์ ๋ด ๋ฐฉ์ง ๊ฒฐํฉ ์๋ฃ. |
| 4 | Organizer OS | Organizer OS | ๋ํ ์์ฑ(4๋จ๊ณ ์์๋) ๋ฐ ์ ์ฐ ๋์ฅ ์ด๋๋ฏผ ์น์ธ ์ฐ๊ณ. |
| 5 | Race Kit & BIB | Race Kit | ์ด๋๋ฏผ ํคํธ/BIB ํจํน ๋ฐฐ์ ๊ธฐ๋ฅ ํ์ฑํ ์๋ฃ. |
| - | AI ์ฝ์นญ / AR/VR / ๋ธ๋ก์ฒด์ธ | Future Phase | MVP ๋ฒ์ ์ธ๋ก ์ค์ ํ์ฌ ๋ฉ์ธ ํ๋ฉด์์ ์จ๊น ๊ฒฉ๋ฆฌ ์กฐ์น. |
๐ ์ธ์ฆ ๋ฐ ๋ณด์ ์ฒ๋ฆฌ
VivaSport API๋ ๊ฐ๋ ฅํ ๋ณด์์ ์ํด Hono ๋ฏธ๋ค์จ์ด๋ก ๋ณดํธ๋๋ฉฐ, ํธ์ถ ์ ํ์ ๋ฐ๋ผ ์ธ ๊ฐ์ง ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉํฉ๋๋ค.
Firebase JWT Bearer Auth
์ผ๋ฐ ์ ์ ์ฉ API(๋ํ ์ฐธ๊ฐ, ๋ด ์ ๋ณด ์์ , ๋จ์ฒด ๊ฐ์ค ๋ฑ) ํธ์ถ ์, Firebase Auth์์ ๋ฐํํ ID ํ ํฐ์ ์ ์กํฉ๋๋ค.
Admin Console Auth Token
์ด๋๋ฏผ์ฉ API(์ ์ฐ ์ฒ๋ฆฌ, ๊ฐ์ฌ ๋ก๊ทธ ์กฐํ, ์ฌ์ฉ์ ์ ์ง ๋ฑ) ํธ์ถ ์, ํ ๋ํธ ๊ฒ์ฆ์ด ์๋ฃ๋ Admin ์ ์ฉ ํ ํฐ์ ์ ์กํฉ๋๋ค.
โก API ์๋ํฌ์ธํธ ๋ช ์ธ
| ์ ํ | ์๋ํฌ์ธํธ | ์ค๋ช ๋ฐ ๊ถํ ์์ค | ๋ด ๋ฐฉ์ง |
|---|---|---|---|
| GET | /api/events |
์ ์ฒด ๋ํ ๋ชฉ๋ก ์กฐํ (๋นํ์/์๋น์์ฉ) | - |
| POST | /api/events |
์๋ก์ด ๋ํ ๊ฐ์ค (์๊ธ์ ๊ตฌ๋ ํ๋ ํํฐ ๋ฏธ๋ค์จ์ด ์๋) | - |
| GET | /api/events/:id/feedbacks |
ํน์ ๋ํ์ Q&A ๋๊ธ/ํ๊ธฐ ๋ชฉ๋ก ์กฐํ (๋นํ์/์ ์ฒด ๊ณต๊ฐ) | - |
| POST | /api/events/:id/feedbacks |
๋ํ Q&A ๋๊ธ/์๊ฒฌ ์ ๊ท ๋ฑ๋ก (JWT optional) | Turnstile |
| POST | /api/reports |
๋๊ธ, ๋ฃจํธ, ์ฃผ์ต์ ๋ฑ ๋ถํธ/์คํธ ์ ๊ณ ์ ์ | Turnstile |
| GET | /api/groups/search |
๋จ์ฒด/ํ ์ค์๊ฐ ๊ฒ์ (๋จ์ฒด๋ช
/์ฝ๋ ๋งค์นญ, eventId ๋ฐ query ์ง์) |
- |
| GET | /api/groups/my |
๋ด๊ฐ ๊ฐ์คํ ๋จ์ฒด ๋ชฉ๋ก ์กฐํ (Firebase Auth JWT ๊ฒ์ฆ) | - |
| POST | /api/groups |
์ ๊ท ๋จ์ฒด ๊ฐ์ค (๋ํ์ ์ธ์ ์ฌํญ ๋ฐ ๊ด๋ฆฌ์ฉ ๋น๋ฐ๋ฒํธ ์ค์ ) | - |
| POST | /api/groups/:code/apply |
๋จ์ฒด์ ์ฐธ๊ฐ ์ ์ฒญ ๋ฑ๋ก (ํ ์ฝ๋๋ฅผ ํตํด ํน์ ๋จ์ฒด์ ์์) | - |
| GET | /api/groups/:code/members |
ํน์ ๋จ์ฒด ๊ฐ์ ๋ฉค๋ฒ ๋ชฉ๋ก ์กฐํ (๋น๋ฐ๋ฒํธ ํน์ ๋จ์ฒด์ฅ ๊ถํ, JOIN ๊ฒฐ๊ณผ ๋จ์ฒด๋ช ๋ฐํ) | - |
| PATCH | /api/groups/:code/members/:memberId |
๋จ์ฒด ์ ์ฒญ ๋จ์ฒด์ ์๋ฝ/๋ฐ๋ ค (๋ํ์ ๊ถํ ๊ฒ์ฆ ํ์) | - |
| GET | /api/admin/reports |
์ ์๋ ์ ๊ณ ๋ฐ 1:1 ๊ณ ๊ฐ์ง์ ๋ชฉ๋ก ์กฐํ (์ด๋๋ฏผ ์ ์ฉ) | - |
| GET | /api/admin/feedbacks/:id |
์ ๊ณ ๋์์ด ๋ Q&A ํผ๋๋ฐฑ ๋๊ธ ์๋ณธ ๋จ๊ฑด ์์ธ ์กฐํ | - |
| DELETE | /api/admin/feedbacks/:id |
์ ๊ณ ๋ ์คํธ์ฑ Q&A ๋๊ธ ์๊ตฌ ์ญ์ ๋ฐ ๊ฐ์ฌ๋ก๊ทธ ๊ธฐ๋ก | - |
| PATCH | /api/admin/users/:id/status |
Abuse ์ ์ฑ ์ ์ ์ฐจ๋จ(Blocked) ์กฐ์น (์์ provider ์ปฌ๋ผ ๋งคํ) | - |
๐ฑ ์๋น์ React ์ฑ ๊ธฐ๋ฅ ๋ฆฌ์คํธ
| ๊ธฐ๋ฅ ๋ช ์นญ | ์์ธ ๋ด์ฉ | ์ํ |
|---|---|---|
| ํ (๋ํ ์นด๋ ๋ชฉ๋ก) | ๋ํ ๋ฆฌ์คํธ ์นด๋ ๋ฐ ์ค์๊ฐ ์งํ๋ฅ ์ ๋๋ฉ์ด์ ๋ฐ ์ ์ฉ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ํ ์ฐพ๊ธฐ ๋ฐ ์นดํ ๊ณ ๋ฆฌ ํํฐ | ๋ํ๋ช ๊ฒ์, ์ข ๋ชฉ๋ณ ์นดํ ๊ณ ๋ฆฌ, ๋์ด๋ ๋ฐ ์ ๋ ฌ ๊ธฐ๋ฅ ์ฐ๊ณ | โ ๊ฐ๋ฐ์๋ฃ |
| ์ด๋ฒคํธ ์์ธ ์ ๋ณด ๋ฐ ์ ์ฒญ | ์์ธ ์๋ด ์ ๋ณด ๋ฐ Firebase Auth ๊ธฐ๋ฐ ๋ค๋จ๊ณ ์ฐธ๊ฐ ์ ์ฒญ์ ์์ฑ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋จ์ฒด/ํ ๊ด๋ฆฌ (MyGroups) | ๊ฐ์ค ๋จ์ฒด ์ค์๊ฐ ์กฐํ, ๋จ์ฒด ์ฝ๋ ๊ฐ์ค ๋ฐ ๋จ์ฒด์ ์น์ธ/๋ฐ๋ ค (API ์ฐ๊ณ) | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ง์ดํ์ด์ง API ์ฐ๋ | ์ ์ฒญ ์ํ ์ค์๊ฐ ๋๊ธฐํ ๋ฐ ๋ง์ผ๋ฆฌ์ง ์ ๋ฆฝ ์กฐํ ๊ธฐ๋ฅ | โ ๊ฐ๋ฐ์๋ฃ |
| GNB ๋ชจ๋ฐ์ผ ์์ ๋ง์ง | ๋ ธ์น๊ฐ ์๋ ์ผ๋ฐ ํ๋ฉด์์๋ GNB ๋ ์ด๋ธ ํ ์คํธ ์๋ฆผ ํด๊ฒฐ | โ ๊ฐ๋ฐ์๋ฃ |
| ํ ํ์ดํ ์ค๋ณต ์์ | ์๋จ ๋ค๋น๊ฒ์ด์ ํค๋์ ๋ณธ๋ฌธ ํ์ดํ์ 'VivaSport' ์ด์ค ํ์ ๊ต์ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ํ ์์ฑ ์์๋ (4๋จ๊ณ) | ๋ํ ์ ๋ณด, Race ์ฝ์ค, ๊ฒฐ์ ๋ฐฉ์, ํฌ์คํฐ ์ ๋ก๋ 4๋จ๊ณ ๋งํฌ์ /API ์ฐ๊ณ | โก ๊ฐ๋ฐ๋๊ธฐ |
| ์ฃผ์ต์ ์ ์ฐ ๊ด๋ฆฌ ๋์๋ณด๋ | ๋ํ ์ฃผ์ต ๊ธฐ์ /๋จ์ฒด ๋์๋ณด๋ ๋ฐ ์ ์ฐ ์ด๋ ฅ ํต๊ณ ์ฐ๋ | โก ๊ฐ๋ฐ๋๊ธฐ |
๐ ํผ๋ธ๋ฆญ ์ด๋ฒคํธ ํ๋ธ ๊ธฐ๋ฅ ๋ฆฌ์คํธ
| ๊ธฐ๋ฅ ๋ช ์นญ | ์์ธ ๋ด์ฉ | ์ํ |
|---|---|---|
| ๋ํ ์์ ๋ด์ค ์นด๋ | ์์ง ๋ด์ด ๋ฌผ์ด์จ ๊ณต์ ๋ง๋ผํค/๋ํ ์์ ๋ฆฌ์คํธ ์นด๋ํ | โ ๊ฐ๋ฐ์๋ฃ |
| ์ง๋ ์ฑ ์ฐ๋ & ์ฃผ์ ๋ณต์ฌ | ๊ฐ์ต์ง ์ ๋ณด์ ํด๋ฆฝ๋ณด๋ ์ํด๋ฆญ ๋ณต์ฌ ๋ฐ ๋ค์ด๋ฒ/์นด์นด์ค ์ง๋ ์ฐ๊ณ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ชจ์ง์จ ์ค์๊ฐ ์นด์ดํฐ | ๋ชจ์ง์จ ์ ์ ๋๋น ๋น์จ ์ธ๋์ผ์ดํฐ ํ๋ก๊ทธ๋ ์ค ๋ฐ ๋ ๋๋ง | โ ๊ฐ๋ฐ์๋ฃ |
| ๋จ์ฒด ์ฐธ๊ฐ ์ ์ฒญ | ๋ํ ๋จ์ฒด ๊ฐ์ ๋ค์ด์ผ๋ก๊ทธ ๊ฒ์, ๋จ์ฒด ์ฝ๋ ํด๋ฆฝ๋ณด๋ ๋ณต์ฌ ๋ชจ๋ฌ ์ฐ๋ | โ ๊ฐ๋ฐ์๋ฃ |
| Q&A ๋๊ธ/ํผ๋๋ฐฑ ์คํธ ์ ๊ณ | ๋๊ธ ์ ์ ๊ณ ๋ฒํผ์ผ๋ก ํ์ ๋ค์ด์ผ๋ก๊ทธ ํ์ฑํ ๋ฐ API ์ ์ ์ฐ๊ณ | โ ๊ฐ๋ฐ์๋ฃ |
| Turnstile ๋ด ๋ฐฉ์ง ๋ณดํธ | Q&A ๋๊ธ ์์ฑ๊ณผ ์ ๊ณ ์ ์ ์ ์ ์ฑ ์๋ ๋๋ฐฐ ๋ฐฉ์ง ์บก์ฐจ ์์ ฏ ํ์ฌ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ค๊ตญ์ด i18n ์์ ์ง์ | ํ๊ตญ์ด, ์์ด ๋์ผ ๋ ๋๋ง ํ์ฅ ๋ฆฌ์์ค ์ถ๊ฐ | โ ๊ฐ๋ฐ์๋ฃ |
๐ข ์ด๋๋ฏผ ์ฝ์ ์น ๊ธฐ๋ฅ ๋ฆฌ์คํธ
| ๊ธฐ๋ฅ ๋ช ์นญ | ์์ธ ๋ด์ฉ | ์ํ |
|---|---|---|
| ๋ก๊ทธ์ธ ๋ฝ ์คํฌ๋ฆฐ & ์ฐํ | ์๋ ํ ํฐ ์ ๋ ฅ๋์ ์ ์ฉ ํค ์ฃผ์ ์ ํตํด Mock ๋ฐ์ดํฐ ๋ชจ๋ ์๋ฒฝ ์ง์ | โ ๊ฐ๋ฐ์๋ฃ |
| ์ ์ฐ ๋์ฅ ๋ชฉ๋ก ๋ฐ ์ด๋ ฅ | ์ฐธ๊ฐ๋น ์ด์ก, ์์๋ฃ, ์ธ๊ธ ๋ฐ ์ฃผ์ต์ ์ค ์๋ น์ก ์ ์ฐ ์น์ธ ํ๋ก์ธ์ค | โ ๊ฐ๋ฐ์๋ฃ |
| ๊ฐ์ฌ ๋ก๊ทธ (Audit Logs) | ๊ด๋ฆฌ์ ํ์์ ๋ฐ๋ฅธ IP, ์ผ์, ํ์ ๋ด์ญ ์กฐํ ๋ฐ ํ์ ๋์๋ณด๋ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ฐฐ๋ ๊ด๊ณ ์ฌ๋กฏ ๊ด๋ฆฌ | ํ ํ๋ฉด ๋ฉ์ธ ๋ฐ ์ฌ์ด๋ ๊ด๊ณ ๋ฐฐ๋ ์บ ํ์ธ ์์ฑ, ์น์ธ, ๊ธฐ๊ฐ ํต์ | โ ๊ฐ๋ฐ์๋ฃ |
| ์ ๊ณ ๊ด๋ฆฌ (Support / Abuse) | ์ผ๋ฐ ์ ์๋ ๋ถํธ์ฌํญ ๋ฐ ๋ฌธ์ ๋ด์ญ ์น์ธ, ๊ธฐ๊ฐ ์ํ ์กฐ์น | โ ๊ฐ๋ฐ์๋ฃ |
| Q&A ์๋ณธ fetch & ์ญ์ ์กฐ์น | ์ ๊ณ ๋ด์ญ ์ค ๋๊ธ/ํผ๋๋ฐฑ ์๋ณธ ์กฐํ ํ ์ฆ๊ฐ ์ญ์ ๋ฐ ํด๋น ์ ๊ณ ์๋ฃ ์ฒ๋ฆฌ | โ ๊ฐ๋ฐ์๋ฃ |
| ์์ฑ์ ๊ณ์ ์ฐจ๋จ (Block) | ์ ๊ณ ์์ธ ์ฐฝ์์ ์์ฑ์ ๊ณ์ ์ ์ฆ์ ์ ์ง ์กฐ์น ์ฐ๊ณ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋ผ์ดํธ ๋ชจ๋ ๊ฐ๋ ์ฑ ํจ์น | ์ธ๋ผ์ธ white ์์์ผ๋ก ๋ผ์ดํธ ํ ๋ง ์ ํ ์ ํ ์คํธ ๋ฌปํ๋ ๊ฐ๋ ์ฑ ์์ค ํด๊ฒฐ | โ ๊ฐ๋ฐ์๋ฃ |
| ๊ฐ์ ํ ๋ํธ ํ์ ์ ๋ฆฌ | ํ์๊ฐ์ ํญ๋ชฉ ๋ด ๋ถ์ ์ ํ '๊ฐ์ธ' ํ์ ์ ๋ฐฐ์ ํ๊ณ ๋ฒ์ธ, ๊ณต๊ณต๊ธฐ๊ด ๋ฑ์ผ๋ก ํต์ผ | โ ๊ฐ๋ฐ์๋ฃ |
| ์ ์ฐ ์๋ ์ด์ฒด API (PG) | ์ ์ฐ ์น์ธ ์ ์ค์๊ฐ ๊ณ์ข์ด์ฒด ์๋ ์ฐ๋ API ๊ฐ๋ฐ | โก ๊ฐ๋ฐ๋๊ธฐ |
โ๏ธ Cloudflare Workers & DB ๊ธฐ๋ฅ ๋ฆฌ์คํธ
| ๊ธฐ๋ฅ ๋ช ์นญ | ์์ธ ๋ด์ฉ | ์ํ |
|---|---|---|
| ์๊ธ์ ๊ตฌ๋ ๊ฒ์ดํ ๋ฏธ๋ค์จ์ด | ๋ฌด๋ฃ ์๊ธ์ ์ ์ ์ ์ ๋ฃ ๋ํ ์์ฑ ์ฐจ๋จ, ๋ฑ๊ธ๋ณ ์๊ฐ ์ ๋ฃ๋ํ ๊ฐ์ค ๊ฐ์ ์ ํ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋จ์ฒด/์ฐธ๊ฐ ์ ์ฒญ D1 ํ ์ด๋ธ ์คํค๋ง | ๋จ์ฒด๋ช , ๋จ์ฒด์ฅ ์ ๋ณด ๋ฐ ์ ์ฒญ์ ์ ๋ณด๋ฅผ D1 DB ์๊ตฌ ํ ์ด๋ธ๋ก ๋ฐํ์ ์๋ ์์ฑ | โ ๊ฐ๋ฐ์๋ฃ |
| ๋จ์ฒด ๋ฐ ๊ฐ์ ์ ์ฒญ API 6์ข | ๋ด ๋จ์ฒด ์กฐํ, ์ฐธ๊ฐ ์ ์ฒญ, ๊ฐ์ ์ ์น์ธ/๋ฐ๋ ค ๋ฑ Workers API ๊ตฌ์ถ | โ ๊ฐ๋ฐ์๋ฃ |
| Turnstile ๊ฒ์ฆ ์ ์ฐํ | ๋นํ์ ์ํ์ Q&A ํผ๋๋ฐฑ ์์ฑ ๋ฐ ์ ๊ณ ๋ฅผ ์ํ Turnstile ๋ด ๋ณดํธ API ์ถ๊ฐ | โ ๊ฐ๋ฐ์๋ฃ |
| Google Auth CORS ์ฐํ | Google API ์ฐ๋ ์ X-Viva-Bypass ํค๋ ์ค๋ณต ์ ์ ๋ฐฉ์ง ์ฐํ ์กฐ์น | โ ๊ฐ๋ฐ์๋ฃ |
| ์คํฌ์ธ ODF ํ์ค ์ฝ๋ ๋งคํ | ๋ฌ๋->ATH, ์ฌ์ดํด->CYC ๋ฑ์ ์ข ๋ชฉ ์ฝ๋๋ฅผ ์ ์ฅ ์ ๋ฐฑ์๋ ๋จ์์ ์๋ ODF ๋งคํ | โ ๊ฐ๋ฐ์๋ฃ |
| ์ ์ง๋ณด์ ์ค analytics ์บก์ฒ ์ฐํ | ์ ๊ฒ ์ํ์์๋ ์ต๋ช ๋ถ์ ๋ก๊ทธ ์์ง Endpoint(/api/analytics/collect) ์ ์ ๋์ | โ ๊ฐ๋ฐ์๋ฃ |
| D1 ์ธ๋ฑ์ค ๋ฐ ์ฃ์ง ์บ์ฑ ์ต์ ํ | ๋์ฉ๋ ์ฟผ๋ฆฌ ํ๋์ ์ํ D1 DB ์ธ๋ฑ์ค ์ค๊ณ ์ ์ฉ ๋ฐ Hono ์ฃ์ง ์บ์(Cache-Control) ํ์ฑํ | โ ๊ฐ๋ฐ์๋ฃ |
๐งช ์์คํ ๋ณ QA ํ ์คํธ ๋งคํธ๋ฆญ์ค ๊ฒฐ๊ณผ
๊ฐ ์๋น์ค ๋ชจ๋์ ๋ํด ์ํ๋ ์ฃผ์ ๊ธฐ๋ฅ ๊ฒ์ฆ ์๋๋ฆฌ์ค ๋ฐ ํต๊ณผ ์ํ์ ๋๋ค. (์ต์ข ๊ฒ์ฆ์ผ: 2026-06-24)
| ๋์ ์์คํ | ๊ฒ์ฆ ํ ์คํธ ์๋๋ฆฌ์ค | ๊ธฐ๋ ๊ฒฐ๊ณผ | ๊ฒฐ๊ณผ |
|---|---|---|---|
| ์๋น์ React ์น | ๋ํ ํํฐ ๊ฒ์ ๋ฐ ๋ค๊ตญ์ด ๋ฆฌ์์ค ์ ์ฉ | ํ๊ตญ์ด/์์ด ์ ํ ์ ํ ์คํธ ๊นจ์ง ์์ | Pass |
| ์ด๋ฒคํธ ํ๋ธ | Q&A ํผ๋๋ฐฑ ์ ์ถ ๋ฐ Turnstile ์ธ์ฆ | ์ธ์ฆ ์๋ฃ ์์๋ง ๋๊ธ DB ๋ฑ๋ก ๋ฐ ๋ ธ์ถ | Pass |
| ๊ด๋ฆฌ์ ์ฝ์ | ์ ์ฐ ๋์ฅ ๋ชฉ๋ก ์กฐํ ๋ฐ ์๋ ์ ์ฐ ์น์ธ | ์ ์ฐ ์น์ธ ์๋ฃ ์ํ ๋ณ๊ฒฝ ๋ฐ ๊ฐ์ฌ๋ก๊ทธ ๋ฑ๋ก | Pass |
| Workers API | ๋ํ ์๋ณ์ฝ๋(event_code) ์ ์ฅ ๋ฐ ์ ์๋ฒํธ sequence ๋ฐ๊ธ | ๋ํ์ฝ๋-์ข ๋ชฉ์ฝ๋-GRP-seq ํํ ์ ์๋ฒํธ ๋ฐ๊ธ | Pass |
| ๋ชจ๋ฐ์ผ ์ฑ (RN) | ์๋ ๋ก๊ทธ์ธ ๋ฐ์ดํจ์ค ๋ฐ ๋ฝ์คํฌ๋ฆฐ ํด์ | "viva-mock-2026" ์ ๋ ฅ ์ ๋ฉ์ธ ํ๋ฉด ์ง์ | Pass |
๐จ ๊ฒฐํจ & ๊ฐ์ ์กฐ์น ๋งคํธ๋ฆญ์ค
ํ์ฌ ํ๋ซํผ์์ ๋ฐ๊ฒฌ๋์ด ์กฐ์น๋ ์ฃผ์ ๊ธฐ์ ๊ฒฐํจ ๋ฐ ๊ฐ์ ์ฌํญ ๋ด์ญ์ ๋๋ค. (TRACKING_MATRIX.md์ ๋๊ธฐํ๋จ)
| ๊ฒฐํจ ID | ์ ํ | ์์คํ | ๊ฒฐํจ / ๊ฐ์ ์์ธ ๋ด์ฉ | ์ํ |
|---|---|---|---|---|
| DEF-D1-001 | Data/Schema | Workers / D1 | ๋ํ ์๋ณ์ฝ๋(event_code) ์ ์ฅ ๋ถ์ฌ ๋ฐ DB ์คํค๋ง ์ปฌ๋ผ ์ ์ค ํด๊ฒฐ | ์กฐ์น์๋ฃ |
| DEF-API-002 | Improvement | Workers | GUID ๊ธฐ๋ฐ ์ ์๋ฒํธ ๋์ ๋ํ ์ฝ๋ ๋ฐ ์ข ๋ชฉ ์ฝ๋๋ฅผ ์กฐํฉํ๋ ์์ฑ ๊ท์น ๊ณ ๋ํ | ์กฐ์น์๋ฃ |
| DEF-UX-002 | UX/UI | React Web / Admin | ๋ง์ดํ์ด์ง ๋ด FAQ ๋ฐ์ดํฐ ์ด๋๋ฏผ ์ค์ ๊ฐ ๋์ ์ฐ๋ ๋ฐ localStorage ๋๊ธฐํ ๊ฒฐํจ ํด๊ฒฐ | ์กฐ์น์๋ฃ |
| DEF-ADMIN-003 | Improvement | Admin Console | ์ด๋๋ฏผ ์ฝ์ ๋ด ๋จ์ฒด ๋ฐ ํฌ๋ฃจ ๊ด๋ฆฌ ์ค๋ฐ์ดํฐ ์ฐ๋(D1 DB) ๋ฐ ์ํ ๋ณ๊ฒฝ(ํ๋์น์ธ/์ ์ง/๋๊ธฐ) ์ ์ด ๊ธฐ๋ฅ ๊ตฌํ ์๋ฃ | ์กฐ์น์๋ฃ |
| DEF-DEP-004 | Deployment | Pages / Submodule | .gitmodules ๋๋ฝ์ผ๋ก ์ธํ Cloudflare Pages ๋ฐฐํฌ ์๋ธ๋ชจ๋ ํด๋ก ์ค๋ฅ ๋ณต๊ตฌ | ์กฐ์น์๋ฃ |
๐ ๋ฆด๋ฆฌ์ฆ ๋ ธํธ & ๋ณด์ ์ง์นจ (Release & Security)
๐ข v1.2.5 ํซํฝ์ค ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (2026-06-25)
- ๊ฐ์ฌ ๋ก๊ทธ ํญ ๋ ์ด์์ ๋ฐ ์ข์ฐ ์ฌ๋ฐฑ ๋ฌด๊ฒฐ์ฑ ํ๋ณด: ๊ธด ๋ก๊ทธ ํ์ด๋ ํ ์ด๋ธ ์ด ํ์ฅ์ผ๋ก ์ธํด ๋ถ๋ชจ flex item์ด ๋ทฐํฌํธ ์ค๋ฅธ์ชฝ์ผ๋ก ์์ ธ๋๊ฐ๋ฉฐ ๊ฐ๋ก ํจ๋ฉ์ด ์ ์ค๋๋ ํ์์ ํด๊ฒฐํ๊ธฐ ์ํด, `.content-pane` ์ `overflow: auto`๋ฅผ ์ ์ธํ๊ณ `.table-container` ์ `width: 100%; overflow-x: auto; box-sizing: border-box;`๋ฅผ ์ ์ฉํ์ฌ ์นด๋ ๋ด๋ถ์์๋ง ์คํฌ๋กค์ด ๋ฐ์ํ๋๋ก ๋ณด์, ์ด๋ ํ ํ๋ฉด ํฌ๊ธฐ์์๋ 32px ์ ์์ ์ ์ธ ์ข์ฐ ์ฌ๋ฐฑ์ด ๋ณด์ฅ๋๊ฒ ์์ ํ์ต๋๋ค.
- ์ค์๊ฐ ๋ก๊ทธ ๋ชจ๋ํฐ ์ฐฝ ์ธ๋ก ๊ธธ์ด ํ์ฅ: ์ด๋๋ฏผ ์ค์๊ฐ ๋ก๊ทธ ๋ฐ API ์คํธ๋ฆผ ๋ชจ๋ํฐ๋ง ์ฐฝ(`#system-log-terminal`)์ ์ธ๋ก ๋์ด๋ฅผ ๊ธฐ์กด `350px` ์์ `550px` ๋ก ๋ํญ ์ฐ์ฅํ์ฌ, ํ ๋์ ๋ ๋ง์ ํธ๋์ญ์ ๊ธฐ๋ก์ ์ํํ ๋ถ์ํ ์ ์๋๋ก ๊ฐ๋ ์ฑ์ ๊ฐ์ ํ์ต๋๋ค.
- ๋ฒ์ ์บ์ฑ ๋ฌดํจํ ๊ฐฑ์ : ์ด๋๋ฏผ ํธ์ถ ๋ฒ์ ์ `admin.css?v=1.2.5` ๋ฐ `admin.js?v=1.2.5`๋ก ๊ฒฉ์ํ์ฌ ์ต์ ๋ก์ปฌ ์บ์ ๊ฐฑ์ ์ ๊ฐ์ ํ์ต๋๋ค.
๐ข v1.2.4 ํซํฝ์ค ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (2026-06-25)
- ์ด๋๋ฏผ CSS ์ ํ์ ๋ฌธ๋ฒ ํ์คํ ๋ฐ ํธํ์ฑ ๋ณต๊ตฌ: ๊ตฌํ ๋ธ๋ผ์ฐ์ ์์ ํ์ฑ ์๋ฌ๋ฅผ ์ ๋ฐํ๋ ๋ถ์์ ํ ๋ ๋ฒจ 4 ๊ฐ์ ํด๋์ค ๋ณตํฉ ์ ํ์(:not ์ ํ์ ์์ ๊ตฌ์กฐ)๋ค์ ํ์ค CSS ๊ท๊ฒฉ์ผ๋ก ์ฌ์์ฑํ์ฌ, ๋ผ์ดํธ ๋ชจ๋ ์ค๋ฒ๋ผ์ด๋ ์คํ์ผ ํด์์ด ๋ฌด์๋๋ ํ์ฑ ์ฅ์ ๊ฒฐํจ์ ์์ ํ ์ ๊ฑฐํ์ต๋๋ค.
- ๊ตฌ๋ ๋ฐ ์๊ธ์ ์ค์ Drawer ์์ธ์ฑ ํจ์น: ์ฌ์ฉ์/์๊ธ์ ๊ตฌ๋ ํธ์ง Drawer๋ค์ ์ต์๋จ ์ปจํ ์ด๋ ์ธ๋ผ์ธ ์คํ์ผ ๋ฐ CSS ์ค๋ฒ๋ผ์ด๋์ `!important` ๋ฐฐ๊ฒฝ์์ ๋ณด๊ฐํ์ฌ, ๋ผ์ดํธ ๋ชจ๋์์๋ ์ด๋์ด ๋จ์ ๋ฐฐ๊ฒฝ์ผ๋ก ์ ์ง๋๋ ๊ฐ์์ฑ ๊ฒฐํจ์ ํด๊ฒฐํ์ต๋๋ค.
- ์ ๋ ฅ ํ๋ ๋ฐ ๋ ์ด๋ธ ๊ฐ๋ ์ฑ ์์ ํ: ์ธ๋ผ์ธ์ผ๋ก ๋คํฌ ๋ชจ๋ ๊ณ์ด ์์์ด ํ๋์ฝ๋ฉ๋์ด ์๋ ์ ๋ ฅ ์ฐฝ ๋ฐ ์ ํ ์ฐฝ์ ์ธ๋ผ์ธ ์คํ์ผ์ CSS ๋ณ์ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ฉด ์ ํํ์ฌ, ๋ผ์ดํธ ๋ชจ๋์์ ๊ธ์๋ ๋ฐฐ๊ฒฝ์ด ํฌ๋ช ํน์ ๋น์ ์์ ์ผ๋ก ๋ ธ์ถ๋๋ ๋ช ์๋น ๊ฒฐํจ์ ์ ๊ฑฐํ๊ณ ์ถ์ฒ ํ๋ ์ฌ๋ถ ๋ ์ด๋ธ ๋ฑ์ ๊ธ์์ ํ๋์ฝ๋ฉ(`color: white`) ์ค๋ฅ๋ ์์ธ์ฑ ์ข๊ฒ ์ฐ๋ ๊ต์ ํ์ต๋๋ค.
- ๋ฒ์ ์บ์ฑ ๋ฌดํจํ ๊ฐฑ์ : ์ด๋๋ฏผ ํธ์ถ ์คํ ๋ฒ์ ์ `admin.css?v=1.2.4` ๋ฐ `admin.js?v=1.2.4`๋ก ๊ฒฉ์ํ์ฌ ์ต์ ๋ก์ปฌ ์บ์ ๊ฐฑ์ ์ ๊ฐ์ ํ์ต๋๋ค.
๐ข v1.2.3 ํซํฝ์ค ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (2026-06-25)
- ๊ตฌ๋ /์๊ธ์ Drawer ๊ฐ๋ ์ฑ ํจ์น (index.html): `#user-subscription-drawer` ๋ฐ `#subscription-plan-drawer` ์ ํ๋์ฝ๋ฉ๋์ด ์๋ ์ธ๋ผ์ธ ๋คํฌ ๋ฐฐ๊ฒฝ(`#11131c`)์ ํ ๋ง ์ฐ๋ํ CSS ๋ณ์(`var(--bg-secondary)`)๋ก ๋ณ๊ฒฝํ๊ณ , Drawer ํค๋ ๊ธ์์์ `var(--text-primary)`๋ก ๋์นํ์ฌ ๋ผ์ดํธ ๋ชจ๋ ๋ช ์๋น๋ฅผ ํ๋ณตํ์ต๋๋ค.
- ๋จ์ฒด ๊ด๋ฆฌ ํญ ๋ฆฌ๋์์ธ (index.html): ๋จ์ฒด ๊ด๋ฆฌ ์๋จ์ ํต๊ณ ์นด๋ ๋ ์ด์์์ ๋์๋ณด๋์ ๋์ผํ `.metrics-grid` ๋ฐ `.metric-card` ๋์์ธ ์์คํ ์นด๋(GNB ์นจ๋ฒ ๋ฐฉ์ง ๋ง์ง ํฌํจ)๋ก ๊ฐํธํ์ฌ ์๊ฐ์ ์ผ๊ด์ฑ๊ณผ ์์ด์ฝ ๋ ๋๋ง์ ์ ์ฉํ์ต๋๋ค.
- ์ด๋๋ฏผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฐ๋ ์ฑ ํจ์น (admin.css): ๋ผ์ดํธ ๋ชจ๋ ์ ํ ์ ๊ฒ์ ๋ฐฐ๊ฒฝ์ ์๋ฎฌ๋ ์ดํฐ ์์ญ ๋ด๋ถ ํ ์คํธ๋ค์ด ๊ธ๋ก๋ฒ ์์ ์ค๋ฒ๋ผ์ด๋๋ก ์ธํด ๋คํฌ ๊ทธ๋ ์ด๋ก ๋ณ๊ฒฝ๋๋ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด, ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ทฐํฌํธ ์์ญ ์ ์ฒด๋ฅผ ๋คํฌ ๋ชจ๋ ๋ณ์ ๊ฐ์ ์์์ผ๋ก ์ฒ๋ฆฌํ์ฌ ์๋ฒฝํ ์์ธ์ฑ์ ๋ณด์ฅํ์ต๋๋ค.
- ๋ฒ์ ์บ์ฑ ๋ฌดํจํ ๊ฐฑ์ : ์ด๋๋ฏผ ํธ์ถ ์คํ์ `admin.css?v=1.2.3` ๋ฐ `admin.js?v=1.2.3`๋ก ๊ฐฑ์ ํ์ฌ ํด๋ผ์ด์ธํธ ์บ์ ๋ฌดํจํ๋ฅผ ์ฒ๋ฆฌํ์ต๋๋ค.
๐ข v1.2.2 ํซํฝ์ค ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (2026-06-25)
- ์ด๋๋ฏผ ์๋ฎฌ๋ ์ดํฐ ๊ฐ๋ ์ฑ ํจ์น: ๋ผ์ดํธ ๋ชจ๋ ํ์ฑํ ์ ์ด๋์ด ์๋ฎฌ๋ ์ดํฐ ์์ญ(์ด๋ฒคํธ ํ๋ธ ๋ฑ) ๋ด๋ถ์ ํ ์คํธ๊ฐ ๊ธ๋ก๋ฒ ์ค๋ฒ๋ผ์ด๋๋ก ์ธํด ๊ฐ๋ ์ฑ์ด ๊นจ์ง๋ ํ์์ ๋ฐฉ์งํ๊ธฐ ์ํด, ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ทฐํฌํธ ๋ด๋ถ ๋ณ์๋ฅผ ๊ฐ์ ๋ก ๋คํฌ ๋ชจ๋ ๊ท๊ฒฉ์ผ๋ก ๊ณ ์ ํ์ต๋๋ค.
- ๋จ์ฒด ๊ด๋ฆฌ ํญ ๋ฆฌ๋์์ธ: ๋จ์ฒด ๊ด๋ฆฌ ์๋จ์ ํต๊ณ ์์ญ์ด ํฐ์ ๋ฐฐ๊ฒฝ์์ ๊ตฌ๋ถ๋์ง ์๊ณ ์๋ฆฌ๋ ๊ฒฐํจ์ ์์ ํ์ฌ ๋์๋ณด๋์ ๋์ผํ ์ธ๋ จ๋ ์นด๋ ๋ ์ด์์๊ณผ ์์ด์ฝ ์์คํ ์ผ๋ก ๊ฐํธํ์ต๋๋ค.
- ๋ฒ์ ์บ์ฑ ๋ฌดํจํ ๊ฐฑ์ : CSS์ JS ์คํฌ๋ฆฝํธ ์ฐธ์กฐ๋ฅผ `v=1.2.2`๋ก ๊ฐฑ์ ํ์ฌ ํด๋ผ์ด์ธํธ ๋จ์ ๋ก์ปฌ ์บ์๋ฅผ ์๋ก๊ณ ์นจํ์ต๋๋ค.
๐ข v1.2.1 ํซํฝ์ค ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (2026-06-25)
- ์ด๋๋ฏผ ์ฝ์ ์บ์ ๋ฌดํจํ (Cache-busting): index.html ๋ด์ ์คํฌ๋ฆฝํธ ์ฐธ์กฐ๋ฅผ `admin.js?v=1.2.1`๋ก ๊ฐฑ์ ํ์ฌ ๊ธฐ์กด ๋ก์ปฌ ์บ์๋ก ์ธํ ์ค๋์(์๊ธ์ ํฌ๋์ ๋ฐ ์ค์๊ฐ ๋ก๊ทธ ํญ ๋ฏธ์๋)์ ์์ ํ ์ ์ํํ์ต๋๋ค.
- ๊ณ ๊ฐ์ง์ ์ ๊ณ ์์ธ Drawer ๊ฐ๋ ์ฑ ๋ฐ ์ฌ๋ฐฑ ํจ์น: ์ ๊ณ /๋ฌธ์ ์์ธ Drawer ๋ด๋ถ์ ํ๋์ฝ๋ฉ `white` ์์์ `var(--text-primary)` ํ ๋ง ๋ฐ์ํ ๋ณ์๋ก ์ ํํ์ฌ ๋ผ์ดํธ ๋ชจ๋ ๊ฐ๋ ์ฑ์ ๋ณด์ฅํ๊ณ , ์์ชฝ ํจ๋ฉ์ 16px๋ก ๋ํ์ต๋๋ค.
- ์๊ธ์ ๋ผ๋ฒจ ๋ฐ ๋ฆฌ์ ๋ฌธ๊ตฌ ๊ธฐํ ์ฌ์ ์ผ์น: ์๊ธ์ ํญ ๋ฉ์ธ ์ ๋ชฉ์ '๊ตฌ๋ ๋ฐ ์๊ธ์ ๊ด๋ฆฌ'๋ก ํ์คํํ๊ณ , ํ ์คํธ ๋ฐ์ดํฐ ์ด๊ธฐํ ์๋ฃ ์๋ด์ฐฝ ๋ฌธ๊ตฌ๋ฅผ ๊ธฐํ ์กฐ๊ฑด๊ณผ ์์ ํ ์ผ์น์์ผฐ์ต๋๋ค.
๐ข v1.2.0 ๋ง์ด๋ ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (2026-06-24)
- ์ ์๋ฒํธ ์ฒด๊ณ ๋ํญ ๊ฐํธ: ๊ธฐ์กด ๋ฌด์์ GUID ๋์ ๋ํ ๊ณ ์ ์๋ณ์ฝ๋(event_code)์ ODF ์คํฌ์ธ ์ฝ๋(sport_code)๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ฐจ์ ์๋ฒ(Sequence)์ ์ฑ๋ฒํ๋ ๊ฐ๋ ์ฑ ๋์ ์ฒด๊ณ ๋์ .
- ์ด๋ฉ์ผ ๋ฐ์ก Fail-Safe ์ด์คํ: Resend API ๋ฉ์ผ ๋ฐ์ก ์ฅ์ ์ํฉ์์๋ DB ๋ฌธ์ ๋ฐ์ดํฐ ์ ์ฅ ์ ์ค์ด ์๊ธฐ์ง ์๋๋ก ์์ธ ์ฐจ๋จ ๋ํผ ์ถ๊ฐ.
- ์๋๋ก์ด๋ ๋ชจ๋ฐ์ผ APK ๊ฒ์ฆ ์๋ฃ: ์ ๊ท UI ์ด์ ๋ฒ์ ๋๋ฒ๊ทธ APK ์ฑ๊ณต์ ์ผ๋ก ๋น๋ฉ ์๋ฃ (vivasport-v1.2.0-release-2205.apk).
๐ ๋ณด์ ๊ฐ์ด๋๋ผ์ธ (Security Notes)
- Firebase JWT ๊ฒ์ฆ: ๋ชจ๋ ์ฌ์ฉ์ API ์์ฒญ์ Firebase ์ธ์ฆ ํค๋ ๊ฒ์ฆ ๋ฏธ๋ค์จ์ด๋ฅผ ํต๊ณผํด์ผ๋ง D1 ํธ๋์ญ์ ์ ํ์ฉํฉ๋๋ค.
- Turnstile ๋ด ๋ฐฉ์ง: ๋นํ์ ์ ์๊ฐ ๊ฐ๋ฅํ ๋๊ธ ๋ฐ ๋ฌธ์ ์ ๊ณ ํผ์ ํด๋ผ์ด์ธํธ์์ Turnstile ์์ ฏ ํ ํฐ ๊ฒ์ฆ์ ํ์๋ก ํต๊ณผํด์ผ ํฉ๋๋ค.
๐ค Agent Playbook & Handoff Summary
๐ค ์ต์ ์ธ์ ํธ๋์คํ ์์ฝ (Handoff Summary)
* [์๋ฃ] ์ค์๊ฐ ์์คํ ๋ฐ API ๋ก๊ทธ ๋ชจ๋ํฐ ํฐ๋ฏธ๋ ๋ทฐํฌํธ ๋์ด 550px ํ์ฅ (v1.2.5)
* [์๋ฃ] ์ด๋๋ฏผ CSS ์ ํ์ ๋ฌธ๋ฒ ํ์คํ ๋ฐ ๋ธ๋ผ์ฐ์ ํ์ฑ ํธํ์ฑ ๋ณต๊ตฌ (v1.2.4)
* [์๋ฃ] ๊ตฌ๋ ๋ฐ ์๊ธ์ ์ค์ Drawer ๋ฐฐ๊ฒฝ์ `!important` ๋ณด๊ฐ์ผ๋ก ๋ผ์ดํธ ๋ชจ๋ ์์ธ์ฑ ์์ (v1.2.4)
* [์๋ฃ] ์ ๋ ฅ ํ๋/๋ ์ด๋ธ์ ๋คํฌ ๊ณ์ด ํ๋์ฝ๋ฉ ์คํ์ผ์ ํ ๋ง ๋ณ์ ์ฐ๋ ๊ธฐ๋ฐ์ผ๋ก ์ ํ (v1.2.4)
* [์๋ฃ] ์ด๋๋ฏผ ๊ตฌ๋ ๋ฐ ์๊ธ์ Drawer ๊ฐ๋ ์ฑ ๋ฐ ํ ๋ง ๋ณ์ ์ฐ๋ ํจ์น (v1.2.3)
* [์๋ฃ] ๋จ์ฒด ๊ด๋ฆฌ ํญ ๋์๋ณด๋ ํต๊ณ ์นด๋ ๋ ์ด์์ ์ ๋ ฌ ๋ฐ ์ผ๊ด์ฑ ๊ฐํธ (v1.2.3)
* [์๋ฃ] ์ด๋๋ฏผ ์๋ฎฌ๋ ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์ญ ๋ผ์ดํธ ๋ชจ๋ ๊ฐ๋ ์ฑ ๋ถ๊ดด ํจ์น (v1.2.2)
* [์๋ฃ] ๋จ์ฒด ๊ด๋ฆฌ ํญ ํต๊ณ ์์ญ ๋์๋ณด๋ํ ๋ฆฌ๋์์ธ ๋ฐ ๋ ์ด์์ ์ ๋ ฌ ์์ (v1.2.2)
* [์๋ฃ] ์ด๋๋ฏผ ์ฝ์ ์บ์ ๋ฌดํจํ ๋ฐ ๊ธฐ๋ฅ ๋ณต๊ตฌ (Cache-busting v1.2.1)
* [์๋ฃ] ๊ณ ๊ฐ์ง์(Abuse) ์์ธ Drawer ๋ผ์ดํธ ๋ชจ๋ ๊ฐ๋ ์ฑ ๋ฐ 16px ์ฌ๋ฐฑ ํจ์น
* [์๋ฃ] React Web Certificate.tsx ์ปดํ์ผ ์๋ฌ(dynamic_form_data ์์ฑ ๋๋ฝ) ์์ ์๋ฃ
* [์๋ฃ] ์ด๋๋ฏผ ์ฐธ๊ฐ์ ์๋ฅ ๊ฒ์ ๋ชจ๋ฌ ํ์ ๋ฐ D1 DB PATCH status API E2E ์น์ธ ํ๋ก์ธ์ค ์ฐ๋
* [์๋ฃ] UI ๋ผ๋ฒจ ํ์คํ ๋ฐ ๋ฆฌ์ ์ฑ๊ณต ์ ์ผ๋ฟ ๋ํ์์ ๋ฌธ๊ตฌ ๊ธฐํ ์ฌ์ ์ฑํฌ ์๋ฃ
* [์๋ฃ] D1 events ํ ์ด๋ธ event_code ์ปฌ๋ผ ๋ถ์ฌ ์์ (DEF-D1-001)
* [์๋ฃ] Workers POST /api/events API event_code ๋ฐ์ธ๋ฉ ์ถ๊ฐ ๋ฐ ์ ์๋ฒํธ ์ฑ๋ฒ ๊ณ ๋ํ
* [์๋ฃ] React Web EventCreate.tsx event_code ์ ๋ ฅ UI ๊ฒํ ๋ฐ ์ ์ ๋น๋ ์๋ฃ
* [์๋ฃ] ๋ง์ดํ์ด์ง FAQ ๋์ ์ฐ๋ ๋ฐ localStorage ๋๊ธฐํ ๋๋ฝ ๊ฒฐํจ ์์ ์๋ฃ (DEF-UX-002)
* [์๋ฃ] ์ด๋๋ฏผ ๋จ์ฒด/ํฌ๋ฃจ ๊ด๋ฆฌ ๊ธฐ๋ฅ D1 DB ์ค๋ฐ์ดํฐ ๋งคํ ๋ฐ 8์ปฌ๋ผ ์ ๋ ฌ, GUI ์ ์ด ์ฐ๋ ์๋ฃ (DEF-ADMIN-003)
* [์๋ฃ] ๋ชจ๋ฐ์ผ ์ฑ 2์์ ํ๋ฉด(๋ง์ผ๋ฆฌ์ง ์ฌ์ฉ, ๋ญํน, ๋ด ์ฉํ ๊ด๋ฆฌ) ๋ค์ดํฐ๋ธ ์ด์ ๋ฐ ํฌ์ค ์ปค๋ฅํธ ์ฐ๋ ์๋ฃ
* [์๋ฃ] ํ๋ซํผ ์ ๋ฐ(React Web, Admin, Event Hub) ํ๋จ ์์ญ์ [DEV MODE] ๊ฐ๋ฐ์ ๋ชจ๋ ํ๋ฉด ID/๋ฒ์ ์ ๋ณด ์ผ๊ด ์ ์ฉ ์๋ฃ
* [์๋ฃ] ์๋ธ๋ชจ๋ ๋ฐฐํฌ ์ค๋ฅ ํด๊ฒฐ์ ์ํ .gitmodules ์ค์ ์์ฑ ๋ฐ vivasport-react ๋งคํ ์๋ฃ (๋ฐฐํฌ ์์ ํ) (DEF-DEP-004)
* [์๋ฃ] ํ ํ๋ฉด ์๋จ ๋ค๋น๊ฒ์ด์ ๋ธ๋๋ ํ์ดํ ๋ณต๊ตฌ ๋ฐ ์ค๋ณต ๋ ธ์ถ ๊ต์ ์๋ฃ (Header.tsx)
๐ Antigravity Agent Playbook
- commander: ์์ ์์ ๋ฐ ์ข ๋ฃ ์ ์ธ์์ธ๊ณ ๋ฐ Git Commit/Push ํ์ธ.
- docs-architect: vivasport-docs/index.html ํฌํธ ๋ฌธ์๋ฅผ ๋จ์ผ ์ง์ค ๊ณต๊ธ์(Single Source of Truth)์ผ๋ก ์ ์ง ๊ด๋ฆฌ.
- regression-guard: Firebase/Admin ๋ก๊ทธ์ธ, CORS ํค๋ ๋ฐ Turnstile ๋ฑ ๊ธฐ์กด์ ๊ฒฌ๊ณ ํ ์ธ์ฆ ํ๋ฆ ํผ์ ๋ฐฉ์ง.
๐ก ๋์๋ง & ๋ฌธ์ ํด๊ฒฐ
plan-free์ธ ๊ฒฝ์ฐ, React ์ฑ ๋ํ ๊ฐ์ค ํผ์์ ์ฐธ๊ฐ๋น ์
๋ ฅ ์นธ์ด ๋นํ์ฑํ๋๋ฉฐ ๋ฌด์กฐ๊ฑด 0์์ผ๋ก ๊ณ ์ ๋ฉ๋๋ค. ๊ฐ์ ๋ก API๋ฅผ ์กฐ์ํ์ฌ 0์ ์ด๊ณผ์ ๊ฒฐ์ ๊ธ์ก์ ๋ณด๋ด๋๋ผ๋ Workers ๋ฐฑ์๋ ๋ฏธ๋ค์จ์ด ๋จ์์ 403 Forbidden์ ๋ฐํํ์ฌ ์ฐจ๋จํฉ๋๋ค.
๋ฑ๊ธ๋ณ ์๊ฐ ํ๋(Basic: 1๊ฑด, Coach: 5๊ฑด, Organizer: ๋ฌด์ ํ) ์ญ์ ๋ฐฑ์๋ ํธ๋์ญ์ ์๋ ๊ฒ์ฆ์ ํตํด ์๊ฒฉํ๊ฒ ์ฐจ๋จ๋ฉ๋๋ค.
JAVA_HOME)๋ฅผ ์ค์ ํ ์ํ์์ cd android && ./gradlew assembleDebug ๋ช
๋ น์ ์คํํ๋ฉด ์ฆ๊ฐ app-debug.apk ํ์ผ์ด ์์ฑ๋์ด ์ค์ ํฐ์ด๋ ์๋ฎฌ๋ ์ดํฐ์์ ์ฆ์ ํ
์คํธํ ์ ์์ต๋๋ค.