JK
>로딩 중 // 로딩 중
뷰 초기화 중WebGL 기반의 고성능 3D 행성 렌더링 엔진과 지리적 위치 마커 시스템.
저사양 기기에서도 프레임 저하 없이 사실적인 대기 조명과 행성 자전을 브라우저에 구현하는 것.
| 기술 | 버전 | 목적 | 선택 이유 |
|---|---|---|---|
| React | ^19.0.1 | UI runtime for both entries | Component model; canvas work stays outside React and is driven through a ref |
| TypeScript | ^7.0.2 | Types for the whole project | Only static gate in CI (lint = tsc --noEmit) with strict mode on |
| Vite | ^8.3.0 | Build tool and dev server | Fast builds with dual entry via rollupOptions.input |
| Tailwind CSS | ^4.3.3 | Utility styling | No config file; @theme tokens in index.css |
| d3-geo | ^3.1.1 | Projection maths only | geoOrthographic/geoPath/graticule without committing to a renderer |
| topojson-client + world-atlas | ^3.1.0 / ^2.0.2 | Country polygons | Free, offline, no API key; India's entry is replaced by official vectors |
| lucide-react | ^0.546.0 | Icons | Tree-shakeable icon set |
| @vercel/analytics | ^2.0.1 | Pageview analytics | Zero-config on Vercel |
| Vitest | ^5.0.4 | Unit tests | Fast, node environment - everything under test is pure logic |
| pnpm | 10.33.2 (pinned) | Package manager | Deterministic lockfile in CI |
| GitHub Actions | ubuntu-latest, Node 22 | CI | Native to the repo host; lint -> test -> build gates |
| Vercel | — | Static hosting and CDN | Static assets + headers + rewrites with no server |
| OSM Nominatim | public API | Geocoding search | Free and keyless |
| 메서드 | 경로 | 목적 |
|---|---|---|
| GET | / | Main app SPA - customizer, search, embed generator (rewritten to /index.html) |
| GET | /embed | Chrome-free iframe target; rewritten to /embed.html and served with CSP frame-ancestors * |
| GET | /embed.html | Same embed entry, served directly; configured by URL query params |
| 메서드 | 경로 | 목적 |
|---|---|---|
| GET | /search | Resolve a place name to lat/lng for the search pin; debounced, abortable, ~1 req/s budget with a 30 s cooldown on 429 |
결정: index.html and embed.html are built from one config and both render the same EarthGlobe component
이유: The embed must never diverge visually or behaviourally from the app; a second renderer would double the surface area of every fix
이점: One renderer, every fix reaches both surfaces
트레이드오프: The embed downloads saved-settings branches it never runs (~3.5 kB, ~0.3 kB gzipped)
결정: Hand-written Canvas 2D painter with d3-geo used only for projection maths
이유: Zero backend, zero keys, static-only hosting - a hand-painted orthographic globe at 110m resolution does not need a GPU stack
이점: Negligible library weight, no shader or context-loss failure modes, full control of the drawing passes
트레이드오프: Per-frame CPU scales with geometry (177 country + 37 state polygons every frame); no LOD, culling or instancing
결정: Filter ISO 356 out of world-atlas and draw India from india-official.json (37 features incl. J&K and Ladakh)
이유: Natural Earth's India does not match official boundaries, and cartographic accuracy should not require a tile server
이점: Authoritative borders, fully offline, no map service at runtime
트레이드오프: ~+210 KB payload; TopoJSON quantisation was rejected because it changes cartographic fidelity
결정: Fixed parameter order in buildEmbedUrl, plus a round-trip test that parses generated URLs back through parseGlobeConfig
이유: A pasted embed snippet is a public API surface that cannot change
이점: A silent behavioural break becomes a red build; the CI workflow flags the round-trip test as load-bearing
트레이드오프: Genuine awkwardness is preserved to keep the byte format stable (e.g. DEFAULT_APP_ZOOM exists only to make a non-behaviour visible)
결정: Coerce-then-clamp instead of strict typing; id optional, numeric strings accepted, longitude wraps, zoom clamps
이유: Hosts send string coordinates, out-of-range zoom or a marker without id - a typeof check would reject those and silently stop the globe moving
이점: Existing embeds keep working; only genuinely unusable payloads are rejected, with a commandRejected event
트레이드오프: Hand-written and only partly uniform; 3 of 20 commands still fail silently
결정: Require event.source === window.parent, with EMBED_ALLOWED_PARENT_ORIGINS shipping empty and outbound events targeting the referrer origin
이유: Embedding is a public, unauthenticated capability - the realistic threat is a third party messaging an embed already on screen
이점: Blocks pop-up/opener/sibling-frame attacks at zero cost to embedders
트레이드오프: Any site that iframes the globe has full script control; acceptable because nothing secret is transmitted
결정: Diff writes field-by-field against a last-known ref, merge them over a fresh disk read, skip unchanged writes, and reconcile on the storage event
이유: Whole-blob last-write-wins destroyed a sibling tab's unrelated fields and fired spurious events
이점: Correct multi-tab behaviour; a slider drag collapses into a single serialisation
트레이드오프: A ~45-value hook return, three parallel write effects, no schema version for forward migration
결정: background=false substitutes pure white/black instead of clearing to transparent; every frame is fully painted
이유: Removes clearRect, avoids compositing cost and letterboxing, and makes the theme halo legible
이점: Simpler and cheaper frames, predictable look across hosts
트레이드오프: The globe cannot be composited over a host page background - a real limitation for embeds
결정: Inertia 0.93^(dt*60), auto-rotation speed*dt*10, wheel zoom 1.08 in / 1/1.08 out
이유: Flat velocity decay behaves differently at 60 vs 120 Hz and asymmetric zoom steps are not reversible
이점: Identical feel on 60 Hz, 120 Hz and ProMotion displays
트레이드오프: Constants are scattered across three sites rather than shared