Skip to content

Repository files navigation

🚀 INFI-Neet 우주정거장 방문자 등록 및 실시간 관제 시스템

니트컴퍼니 용산점 3기 청년들의 전시를 위한 INFI-Neet 우주정거장 방문자 등록 및 실시간 관제 시스템입니다. 현장 방문자가 모바일/태블릿을 통해 방명록(이름)을 등록하면, 대형 프로젝터 스크린에 2D 물리 엔진(Matter.js) 기반의 인터랙티브 타이포그래피(부유하는 이름 태그)로 실시간 송출 및 관제하는 기능을 수행합니다.

Co-authored with Antigravity
본 프로젝트는 Google DeepMind의 AI 코딩 어시스턴트 Antigravity와의 협동 페어 프로그래밍을 통해 기획안 구체화, 데이터베이스 스키마 및 RLS 설계, Matter.js 물리 연산 커스터마이징, 어드민 세션 보안 로직 설계 등의 전 과정을 함께 고민하고 구현하였습니다.


🌌 주요 화면 및 핵심 기능 (Key Features)

1. 메인 디스플레이 (+page.svelte) - 실시간 관제 스크린

대형 프로젝터 전시용 스크린으로, 반응형 캔버스를 통해 무중력 우주 정거장을 시각화합니다.

  • 실시간 데이터 동기화: 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을 적용합니다.

2. 여행자 등록 페이지 (+page.svelte) - 현장 접수

현장에 배치된 태블릿 또는 모바일 기기를 통해 접속하는 탑승 신청 폼입니다.

  • 인터페이스: 깔끔한 환영 메시지와 1글자 이상의 이름 입력 필드, 그리고 [ 탑승 수속 ] 버튼을 제공합니다.
  • 등록 로직: 이름 제출 시 즉시 Supabase DB에 INSERT 연산을 수행하며, 완료 시 우주 여행 환영 메시지를 띄우고 10초 후 미조작 시에도 메인 등록 화면으로 자동 초기화(리다이렉트)됩니다.

3. 관리자 관제 대시보드 (+page.svelte) - 백오피스 컨트롤 패널

관리자가 실시간으로 스크린 명부 및 설정을 제어하는 안전한 관제 콘솔입니다.

  • 보안 강화 로그인:
    • 타이밍 공격(Timing Attack) 방어: crypto.timingSafeEqual을 도입하여 비밀번호 검증 소요 시간을 동일하게 제어합니다. (+page.server.ts)
    • 브루트포스 억제: 로그인 실패/시도 시 서버사이드에서 1~2초의 의도적인 지연(Sleep)을 발생시킵니다.
    • 보안 세션 쿠키: httpOnly, secure (개발 환경에서는 유연하게 해제), sameSite: 'strict'를 통해 쿠키 기반 보안 세션을 확립합니다.
  • 데이터 조회 및 CSV Export: 시간순으로 정렬된 방문자 목록 테이블을 실시간으로 확인하고, Date Picker 범위 지정을 통해 CSV 파일로 명부를 다운로드할 수 있습니다.
  • 메인 화면 실시간 필터링:
    • 날짜 지정: 특정 시작/종료 일시 범위(TIMESTAMPTZ)에 등록된 태그만 노출하도록 제어합니다.
    • 시간 기준 가림: 등록 후 N시간 경과한 이름 태그를 스크린에서 숨깁니다.
    • 모든 설정 변경 사항은 admin_config 테이블에 단일 행(id=1)으로 기록되며, 메인 디스플레이가 이를 실시간으로 반영합니다.
  • 선택 삭제 및 2차 보안 (Soft Delete):
    • 리스트에서 오입력 또는 테스트용 데이터를 체크하여 선택 삭제할 수 있습니다.
    • 삭제 실행 시 2차 보안 비밀번호(ADMIN_DELETE_PASSWORD) 검증을 거치며, 데이터는 실제 물리 삭제되지 않고 deleted_at 필드를 채워 논리 삭제(Soft Delete) 처리됩니다.
  • 통계 시각화: 10분 단위로 세분화된 방문자 유입량 추이를 네온 민트 라인/에어리어 꺾은선 SVG 그래프로 한눈에 제공합니다.

🛠 기술 스택 (Tech Stack)

구분 기술 스택 비고
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 빠르고 효율적인 의존성 관리

📐 아키텍처 및 데이터 흐름 (Architecture & Data Flow)

자세한 사양은 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
Loading

디렉토리 구조 (Directory Structure)

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                 # 글로벌 메타 정보 및 웹 폰트 사전 로드

💾 데이터베이스 스키마 및 보안 (Database & Security)

자세한 사양은 03_schema.md 파일을 참조해 주세요.

테이블 구조

1. travelers (여행자 정보)

  • id : UUID (PL/pgSQL 기반의 커스텀 UUID v7 사용으로 시간순 정렬 및 인덱싱 최적화)
  • name : VARCHAR(50) (여행자 이름)
  • metadata : JSONB (향후 연락처, 소속, 탑승 소감 등 유연한 스키마 확장을 위한 JSON 필드)
  • created_at : TIMESTAMPTZ (등록 시간)
  • deleted_at : TIMESTAMPTZ (Soft Delete 플래그)

2. admin_config (전역 스크린 설정 - 단일 행 id = 1 보장)

  • id : INT PRIMARY KEY CHECK (id = 1)
  • show_date_from : TIMESTAMPTZ (노출 날짜 범위 시작)
  • show_date_to : TIMESTAMPTZ (노출 날짜 범위 종료)
  • hide_after_hours : INT (경과 시간 숨김 기준)

RLS (Row Level Security) 정책

  1. travelers:
    • INSERT (Anon): 전면 허용하여 현장 등록 디바이스에서 다이렉트 저장 가능.
    • SELECT (Anon): 메인 화면에서 노출 데이터를 호출하고 실시간 WebSocket 복제를 수신할 수 있도록 전면 허용 (true).
    • UPDATE/DELETE: 기본 완전 차단. 오직 SUPABASE_SERVICE_ROLE_KEY를 가진 SvelteKit 서버사이드 API(+page.server.ts)를 통해서만 논리적 삭제 및 수정 가능.
  2. admin_config:
    • SELECT (Anon): 메인 화면에서 설정을 읽을 수 있도록 허용.
    • UPDATE: 일반 유저 차단. 관리자 로그인을 마친 서버 세션을 통해서만 변경 가능.

🎨 디자인 시스템 (Design System)

자세한 사양은 04_design_system.md 파일을 참조해 주세요.

  • 배경색: #000000 (순수 검정 테마로 우주 공간 느낌 강화 및 눈 피로도 감소)
  • 주 색상: #FFFFFF (기본 텍스트 및 태그 텍스트)
  • 포인트 색상:
    • Neon Mint (#00FFCC) : 탑승 스캔 진행바, 액티브 인디케이터
    • Neon Pink (#FF3366) : 대시보드 삭제 및 경고 액션
  • 텍스트 태그 비주얼: 반투명 흰색 테두리(rgba(255, 255, 255, 0.2)), 둥근 알약형(border-radius: 9999px), 투명한 배경 처리로 가볍게 날아다니는 시각 연출.
  • 레이어 계층 (Z-Index):
    • 0: Matter.js 물리 엔진 Canvas
    • 10: Svelte DOM 이름 태그 (Canvas 매핑 요소)
    • 20: 탑승 상태바 및 메인 알림 UI 카드
    • 50: 관리자 모달 및 드롭다운 팝업

⚙️ 개발 환경 및 구축 프로세스 (Environment & Implementation Setup)

자세한 로드맵 및 개발 마일스톤은 05_roadmap.md 파일을 참조해 주세요.

1. 패키지 및 런타임 환경

  • 패키지 매니저: 빠르고 효율적인 의존성 캐싱을 위해 pnpm을 기본 패키지 관리자로 설정하여 운영 중입니다.
  • 런타임: Node.js v18 이상 및 TypeScript 6.0.3 사양에서 빌드 안정성을 검증했습니다.

2. 환경 변수 설계 및 보안 격리

서버사이드 전용 키와 프론트엔드 공개 환경 변수를 명확히 분리하여 .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 수행 시 검증하기 위한 투-팩터 비밀번호 장치.

3. 데이터베이스 초기화 및 실시간 복제(Replication) 설정

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;

4. 로컬 개발 및 프로덕션 빌드 파이프라인

  • 로컬 개발 모드: HMR(Hot Module Replacement)이 통합된 Vite 개발 서버를 구동하여 개발 및 실시간 디버깅을 진행했습니다 (pnpm dev).
  • 프로덕션 빌드: SvelteKit 어댑터를 활용하여 최적화된 프로덕션 빌드를 생성하고 Vercel에 배포를 완료했습니다 (pnpm build).

🔧 주요 해결된 이슈 및 최적화 내역 (Troubleshooting)

전체 트러블슈팅 및 개선 내역은 06_issue_tracking.md 파일을 참고하세요.

  1. 물리 엔진 메모리 누수 방지: 컴포넌트 파괴 시 Matter.js Engine.clear(), World.clear()를 안전하게 호출하여 브라우저 좀비 태스크 생성 차단.
  2. 동시 탑승 꼬임 해결: 등록 성공 이벤트가 연달아 터질 때 화면 모달이 깜빡이거나 스킵되는 현상을 탑승 큐(boardingQueue Stack)를 구축하여 FIFO 기반 순차 렌더링으로 개선.
  3. Soft Delete 실시간 연동 해소: RLS SELECT 조건이 deleted_at IS NULL로 제약되어 있으면 WebSocket이 소프트 딜리트 UPDATE 전파를 받지 못하는 현상에 대해 RLS USING(true)로 개방 후 프론트엔드 $derived 룬에서 필터링하도록 우회 설계.
  4. 터널링 및 이탈 객체 GC: 브라우저 창 크기 변화나 프레임 저하로 화면 밖(경계면)을 뚫고 끝없이 추락하는 오브젝트를 감지하여 1초마다 가비지 컬렉터(GC) 스크립트가 물리 월드 및 배열 상태에서 강제 제거하도록 안전장치 확보.
  5. 날짜/시간 범위 필터링 타임존 정밀 보정: 일(Date) 단위 입력 대신 datetime-local로 분 단위 정밀도를 확보하고, 선후 일시가 역전될 때(From이 To보다 뒤가 되는 경우 등) 양방향 인풋이 유기적으로 자동 조율되는 선행 핸들러 연계 및 ISO 8601 시차 보정.

About

니트컴퍼니 용산점 3기 청년들의 전시 INFI-Neet 방명록 시각화 및 대시보드 서비스

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages