니트컴퍼니 용산점 3기 청년들의 전시를 위한 INFI-Neet 우주정거장 방문자 등록 및 실시간 관제 시스템입니다. 현장 방문자가 모바일/태블릿을 통해 방명록(이름)을 등록하면, 대형 프로젝터 스크린에 2D 물리 엔진(Matter.js) 기반의 인터랙티브 타이포그래피(부유하는 이름 태그)로 실시간 송출 및 관제하는 기능을 수행합니다.
Co-authored with Antigravity
본 프로젝트는 Google DeepMind의 AI 코딩 어시스턴트 Antigravity와의 협동 페어 프로그래밍을 통해 기획안 구체화, 데이터베이스 스키마 및 RLS 설계, Matter.js 물리 연산 커스터마이징, 어드민 세션 보안 로직 설계 등의 전 과정을 함께 고민하고 구현하였습니다.
대형 프로젝터 전시용 스크린으로, 반응형 캔버스를 통해 무중력 우주 정거장을 시각화합니다.
- 실시간 데이터 동기화: Supabase Realtime (WebSocket) 채널을 구독하여 신규 탑승자 등록 이벤트를 실시간으로 수신합니다.
- 탑승 연출 시퀀스 (Boarding Sequence): 신규 탑승자 발생 시 화면 중앙에 스캔 진행바 및
[ {여행자이름} 님 탑승 준비... ]->[ 탑승 완료! ]카드를 노출하고(4.5초), 완료 후 최상단에서 해당 이름 태그가 낙하합니다. - 동시 탑승 대기 큐 (Boarding Queue): 여러 명이 동시에 등록하더라도 연출이 꼬이지 않도록 클라이언트 측에 대기 큐를 구축하여 순차적으로 탑승 카드를 적층 연출합니다.
- 무중력 부유 연출 (Matter.js):
- 이름 태그들이 화면 하단에 무겁게 적층되거나 뒤엉키지 않도록 미세한 하향 중력과 하단부(Y축 68% 이상)의 부력(Buoyancy) 필드 효과를 결합하여 상시 부유하게 만듭니다.
- 개체 간의 명확한 충돌 경계 및 미세한 **상호 척력(Repulsion Force)**과 반발력(Restitution)을 세밀하게 조율하여, 서로 겹치지 않고 가볍게 튕기며 끊임없이 유기적으로 상호작용합니다.
- 상단 부유 형광 우주인 (관람객 10명 당 1명 스폰):
- 스크린에 표시되는 관람객 수에 맞춰 관람객 10명 당 1명 비율로 화면 상단 영역(Y축 8% ~ 40%)을 조용히 부유하는 형광 우주인 오브젝트를 동적으로 생성 및 관리합니다.
- 상단 반중력 부력 필드, 일체형 형광 2D 벡터 렌더링, 펄싱 제트 파티클 및 마우스 드래그 앤 드롭 물리 상호작용을 지원합니다.
- 해상도 독립적 스케일링: 대형 스크린부터 태블릿까지
vw,vh기반 스케일링을 통해 약 400명의 이름 태그가 최적의 밀도로 배치되도록 동적 Scale-to-fit을 적용합니다.
현장에 배치된 태블릿 또는 모바일 기기를 통해 접속하는 탑승 신청 폼입니다.
- 인터페이스: 깔끔한 환영 메시지와 1글자 이상의 이름 입력 필드, 그리고
[ 탑승 수속 ]버튼을 제공합니다. - 등록 로직: 이름 제출 시 즉시 Supabase DB에
INSERT연산을 수행하며, 완료 시 우주 여행 환영 메시지를 띄우고 10초 후 미조작 시에도 메인 등록 화면으로 자동 초기화(리다이렉트)됩니다.
관리자가 실시간으로 스크린 명부 및 설정을 제어하는 안전한 관제 콘솔입니다.
- 보안 강화 로그인:
- 타이밍 공격(Timing Attack) 방어:
crypto.timingSafeEqual을 도입하여 비밀번호 검증 소요 시간을 동일하게 제어합니다. (+page.server.ts) - 브루트포스 억제: 로그인 실패/시도 시 서버사이드에서 1~2초의 의도적인 지연(Sleep)을 발생시킵니다.
- 보안 세션 쿠키:
httpOnly,secure(개발 환경에서는 유연하게 해제),sameSite: 'strict'를 통해 쿠키 기반 보안 세션을 확립합니다.
- 타이밍 공격(Timing Attack) 방어:
- 데이터 조회 및 CSV Export: 시간순으로 정렬된 방문자 목록 테이블을 실시간으로 확인하고, Date Picker 범위 지정을 통해 CSV 파일로 명부를 다운로드할 수 있습니다.
- 메인 화면 실시간 필터링:
- 날짜 지정: 특정 시작/종료 일시 범위(
TIMESTAMPTZ)에 등록된 태그만 노출하도록 제어합니다. - 시간 기준 가림: 등록 후 N시간 경과한 이름 태그를 스크린에서 숨깁니다.
- 모든 설정 변경 사항은
admin_config테이블에 단일 행(id=1)으로 기록되며, 메인 디스플레이가 이를 실시간으로 반영합니다.
- 날짜 지정: 특정 시작/종료 일시 범위(
- 선택 삭제 및 2차 보안 (Soft Delete):
- 리스트에서 오입력 또는 테스트용 데이터를 체크하여 선택 삭제할 수 있습니다.
- 삭제 실행 시 2차 보안 비밀번호(
ADMIN_DELETE_PASSWORD) 검증을 거치며, 데이터는 실제 물리 삭제되지 않고deleted_at필드를 채워 논리 삭제(Soft Delete) 처리됩니다.
- 통계 시각화: 10분 단위로 세분화된 방문자 유입량 추이를 네온 민트 라인/에어리어 꺾은선 SVG 그래프로 한눈에 제공합니다.
| 구분 | 기술 스택 | 비고 |
|---|---|---|
| Frontend | SvelteKit (Svelte 5 Runes) | $state, $derived, $effect 반응성 모델 활용 |
| Language | TypeScript | 정적 타입 안정성 보장 |
| DB & Realtime | Supabase (PostgreSQL 15+) | WebSocket 기반 Realtime CDC 구독 |
| Physics | Matter.js | 2D 물리 연산 (부유, 중력, 충돌, 반발력) |
| Styling | Vanilla CSS | :root 변수 기반 중앙 통제 및 완전 블랙 테마 |
| Package | pnpm | 빠르고 효율적인 의존성 관리 |
자세한 사양은 02_architecture.md 파일을 참조해 주세요.
graph TD
User[여행자 현장 등록 /register] -->|1. INSERT| DB[(Supabase Database)]
DB -->|2. Realtime CDC Broadcast| Main[메인 관제 스크린 /]
Main -->|3. Physics Object Spawn| Matter[Matter.js 물리 엔진 연산]
Admin[관리자 페이지 /admin] -->|4. HTTP POST Server Actions| SvelteServer[SvelteKit 서버 API]
SvelteServer -->|5. Verify with Timing-Safe / Delay| SvelteServer
SvelteServer -->|6. UPDATE Config/Soft Delete| DB
DB -->|7. Realtime Config / Update Sync| Main
src/
├── lib/
│ ├── assets/ # 이미지, 아이콘 및 로고 에셋
│ ├── components/ # 공통 UI 아톰 및 레이아웃 컴포넌트
│ ├── db/
│ │ └── supabaseClient.ts # Supabase Anon 클라이언트 초기화
│ ├── physics/
│ │ └── matterEngine.ts # Matter.js 물리 연산 및 부유 메커니즘 캡슐화
│ └── index.ts
├── routes/
│ ├── +layout.svelte # 전역 스타일 및 Pretendard 폰트 바인딩
│ ├── +page.svelte # 메인 관제 스크린 (Matter.js Canvas & Boarding Card)
│ ├── register/
│ │ └── +page.svelte # 방문객 탑승 수속 등록 폼
│ └── admin/
│ ├── +page.svelte # 대시보드 리스트, 통계 그래프, 제어 콘솔
│ ├── +page.server.ts # 비밀번호 검증(접속/삭제), CSV 다운로드, Soft Delete API
│ └── export/ # CSV 데이터 다운로드 API 엔드포인트
├── app.css # `:root` CSS 커스텀 변수 및 리셋 스타일
└── app.html # 글로벌 메타 정보 및 웹 폰트 사전 로드
자세한 사양은 03_schema.md 파일을 참조해 주세요.
id:UUID(PL/pgSQL 기반의 커스텀 UUID v7 사용으로 시간순 정렬 및 인덱싱 최적화)name:VARCHAR(50)(여행자 이름)metadata:JSONB(향후 연락처, 소속, 탑승 소감 등 유연한 스키마 확장을 위한 JSON 필드)created_at:TIMESTAMPTZ(등록 시간)deleted_at:TIMESTAMPTZ(Soft Delete 플래그)
id:INT PRIMARY KEY CHECK (id = 1)show_date_from:TIMESTAMPTZ(노출 날짜 범위 시작)show_date_to:TIMESTAMPTZ(노출 날짜 범위 종료)hide_after_hours:INT(경과 시간 숨김 기준)
travelers:INSERT(Anon): 전면 허용하여 현장 등록 디바이스에서 다이렉트 저장 가능.SELECT(Anon): 메인 화면에서 노출 데이터를 호출하고 실시간 WebSocket 복제를 수신할 수 있도록 전면 허용 (true).UPDATE/DELETE: 기본 완전 차단. 오직SUPABASE_SERVICE_ROLE_KEY를 가진 SvelteKit 서버사이드 API(+page.server.ts)를 통해서만 논리적 삭제 및 수정 가능.
admin_config:SELECT(Anon): 메인 화면에서 설정을 읽을 수 있도록 허용.UPDATE: 일반 유저 차단. 관리자 로그인을 마친 서버 세션을 통해서만 변경 가능.
자세한 사양은 04_design_system.md 파일을 참조해 주세요.
- 배경색:
#000000(순수 검정 테마로 우주 공간 느낌 강화 및 눈 피로도 감소) - 주 색상:
#FFFFFF(기본 텍스트 및 태그 텍스트) - 포인트 색상:
- Neon Mint (
#00FFCC) : 탑승 스캔 진행바, 액티브 인디케이터 - Neon Pink (
#FF3366) : 대시보드 삭제 및 경고 액션
- Neon Mint (
- 텍스트 태그 비주얼: 반투명 흰색 테두리(
rgba(255, 255, 255, 0.2)), 둥근 알약형(border-radius: 9999px), 투명한 배경 처리로 가볍게 날아다니는 시각 연출. - 레이어 계층 (Z-Index):
0: Matter.js 물리 엔진 Canvas10: Svelte DOM 이름 태그 (Canvas 매핑 요소)20: 탑승 상태바 및 메인 알림 UI 카드50: 관리자 모달 및 드롭다운 팝업
자세한 로드맵 및 개발 마일스톤은 05_roadmap.md 파일을 참조해 주세요.
- 패키지 매니저: 빠르고 효율적인 의존성 캐싱을 위해
pnpm을 기본 패키지 관리자로 설정하여 운영 중입니다. - 런타임: Node.js v18 이상 및 TypeScript 6.0.3 사양에서 빌드 안정성을 검증했습니다.
서버사이드 전용 키와 프론트엔드 공개 환경 변수를 명확히 분리하여 .env 파일로 로컬 및 배포 환경에서 적용하고 있습니다.
PUBLIC_SUPABASE_URL/PUBLIC_SUPABASE_ANON_KEY: 프론트엔드 단의 실시간 WebSocket 구독 및 Insert 데이터 송신에 활용.SUPABASE_SERVICE_ROLE_KEY: 일반 클라이언트가 접근할 수 없는 어드민 API(+page.server.ts) 전용 DB 제어에 활용.ADMIN_ACCESS_PASSWORD/ADMIN_DELETE_PASSWORD: 대시보드 진입 및 데이터 Soft Delete 수행 시 검증하기 위한 투-팩터 비밀번호 장치.
Supabase(PostgreSQL) 상에 데이터 정렬 최적화를 위한 UUID v7 생성 함수 및 논리 삭제(Soft Delete) 지원 테이블을 구축했으며, 실시간 WebSocket 감지를 활성화하기 위해 실시간 Publication 채널을 구성했습니다.
- 마이그레이션: 03_schema.md에 기술된 초기화 스크립트를 통해 스키마 정의 및 주석, 제약 조건을 세팅했습니다.
- Replication 활성화:
ALTER PUBLICATION supabase_realtime ADD TABLE travelers; ALTER PUBLICATION supabase_realtime ADD TABLE admin_config;
- 로컬 개발 모드: HMR(Hot Module Replacement)이 통합된 Vite 개발 서버를 구동하여 개발 및 실시간 디버깅을 진행했습니다 (
pnpm dev). - 프로덕션 빌드: SvelteKit 어댑터를 활용하여 최적화된 프로덕션 빌드를 생성하고 Vercel에 배포를 완료했습니다 (
pnpm build).
전체 트러블슈팅 및 개선 내역은 06_issue_tracking.md 파일을 참고하세요.
- 물리 엔진 메모리 누수 방지: 컴포넌트 파괴 시 Matter.js
Engine.clear(),World.clear()를 안전하게 호출하여 브라우저 좀비 태스크 생성 차단. - 동시 탑승 꼬임 해결: 등록 성공 이벤트가 연달아 터질 때 화면 모달이 깜빡이거나 스킵되는 현상을 탑승 큐(
boardingQueueStack)를 구축하여 FIFO 기반 순차 렌더링으로 개선. - Soft Delete 실시간 연동 해소: RLS
SELECT조건이deleted_at IS NULL로 제약되어 있으면 WebSocket이 소프트 딜리트UPDATE전파를 받지 못하는 현상에 대해 RLSUSING(true)로 개방 후 프론트엔드$derived룬에서 필터링하도록 우회 설계. - 터널링 및 이탈 객체 GC: 브라우저 창 크기 변화나 프레임 저하로 화면 밖(경계면)을 뚫고 끝없이 추락하는 오브젝트를 감지하여 1초마다 가비지 컬렉터(GC) 스크립트가 물리 월드 및 배열 상태에서 강제 제거하도록 안전장치 확보.
- 날짜/시간 범위 필터링 타임존 정밀 보정: 일(Date) 단위 입력 대신
datetime-local로 분 단위 정밀도를 확보하고, 선후 일시가 역전될 때(From이 To보다 뒤가 되는 경우 등) 양방향 인풋이 유기적으로 자동 조율되는 선행 핸들러 연계 및 ISO 8601 시차 보정.