@god-beom/fsot-vite
v0.1.2
Published
Overlay collaboration tool for React frontends — per-page kanban & real-time chat
Readme
fsot - Frontend Source Overlay Tool
fwagger — 화면 데이터 명세 (VM Swagger)
프론트엔드 라우트별로 사용하는 데이터 타입과 훅을 자동으로 수집해서 Swagger처럼 보여주는 기능입니다.
🚀 빠른 시작
라우트에 매핑된 컴포넌트 옆에 *.VM.ts 파일을 만들면 끝입니다.
src/pages/cost/
CostDashboardPage.tsx ← 라우터에 등록된 컴포넌트
CostDashboardPage.VM.ts ← 이 파일만 만들면 fsot가 알아서 수집# 스펙 생성
cd apps/your-app
npx fsot swagger
# → fsot.swagger.json 생성됨개발 서버를 켜면 오버레이에서 실시간으로 확인할 수 있습니다.
VM 파일 작성 규칙
서버와 주고받는 데이터 타입과 React Query 훅만 작성합니다. UI 전용 타입(탭 구조, 정렬 상태 등)은 포함하지 않습니다.
// CostDashboardPage.VM.ts
import { useQuery } from '@tanstack/react-query';
import { fetchMonthlyCost, type MonthlyCostResponse } from '../../api/costApi';
// ─── Types ───────────────────────────────────
export interface CloudCostVM {
cloud: 'aws' | 'azure' | 'gcp';
amount: number;
currency: string;
}
export interface MonthlyCostVM {
periodLabel: string;
total: number;
clouds: CloudCostVM[];
}
// ─── Mapper ──────────────────────────────────
function toMonthlyCostVM(dto: MonthlyCostResponse): MonthlyCostVM {
return dto;
}
// ─── Hook ────────────────────────────────────
export function useMonthlyCost() {
return useQuery({
queryKey: ['cost', 'monthly'],
queryFn: fetchMonthlyCost,
select: toMonthlyCostVM,
});
}파일명 태그 자동 추론:
| 파일명 패턴 | 태그 |
|-------------|------|
| *Page.VM.ts | PAGE |
| *Tab.VM.ts | TAB |
| *Modal.VM.ts | MODAL |
| 그 외 | COMPONENT |
fsot가 VM을 수집하는 방법
1단계 — 라우터 파일 파싱
vite.config.ts에 지정한 라우터 파일(src/routes/index.tsx)을 읽어서 경로-컴포넌트 매핑을 추출합니다.
// 이런 라우터 정의에서
{ path: '/cost/dashboard', element: <CostDashboardPage /> }
// 이 매핑을 만듭니다
'/cost/dashboard' → CostDashboardPage.tsx2단계 — import 트리 재귀 추적
각 페이지 컴포넌트에서 시작해 import된 파일들을 따라가며 *.VM.ts 파일을 찾습니다.
CostDashboardPage.tsx
└─ import CostSummaryCard → CostSummaryCard.VM.ts ✅ 수집
└─ import BudgetSettingsModal → BudgetSettingsModal.VM.ts ✅ 수집
└─ import SomeChart → VM 없음 → skip단, 다른 라우트 컴포넌트를 만나면 탐색을 멈춥니다. 탭 레이아웃처럼 자식 라우트 컴포넌트를 import하는 경우 VM이 중복 수집되는 것을 방지합니다.
3단계 — VM 파일 AST 파싱
수집된 *.VM.ts 파일에서 interface와 export function useXxx() 를 파싱해 스펙을 생성합니다.
4단계 — LayoutPage 자동 제외
자식 라우트가 있는 LayoutPage는 항상 첫 번째 탭으로 리다이렉트되므로 manifest에서 자동으로 제외됩니다.
/asset/scheduler ← LayoutPage → 자동 제외
/asset/scheduler/schedule ← TabPage → 수집 ✅
/asset/scheduler/history ← TabPage → 수집 ✅따라서 LayoutPage에는 VM 파일이 필요 없습니다. 탭 전환 로직은 LayoutPage 컴포넌트 안에 직접 작성합니다.
탭 구조 권장 패턴
asset/scheduler/
SchedulerLayoutPage.tsx ← 탭 UI + 전환 로직 (VM 없음)
SchedulePageTab.tsx ← 라우트: /asset/scheduler/schedule
SchedulePageTab.VM.ts ← Types + Mapper + Hook ✅
ScheduleHistoryPageTab.tsx ← 라우트: /asset/scheduler/history
ScheduleHistoryPageTab.VM.ts ← Types + Mapper + Hook ✅한 파일에 모두: TabPage.VM.ts = Types + Mapper + Hook (완결)
vite.config.ts 설정
import { fsot } from '@prime/fsot/vite';
export default defineConfig({
plugins: [
fsot({
projectId: 'my-project',
serverUrl: 'https://...',
vm: {
router: 'react-router',
routerFile: 'src/routes/index.tsx',
include: 'src/**/*.VM.ts',
},
}),
],
});인증 구조
Supabase Auth를 사용하지 않고, fsot_users 테이블 + bcrypt + JWT로 직접 인증합니다.
클라이언트 → POST /api/auth/setup, /api/auth/login → 서버(bcrypt 검증) → JWT 반환
클라이언트 → Authorization: Bearer <JWT> → 서버(JWT 검증 + fsot_users 조회)- 비밀번호 해싱: 서버에서만 (bcrypt)
- 세션: JWT (localStorage
fsot_token), 유효기간 7일 - DB:
fsot_users테이블 (RLS OFF) - Supabase Auth 완전 미사용 → bit/hito와 충돌 없음
서버 API
| 엔드포인트 | 설명 | |---|---| | GET /api/auth/setup-status | 초기 설정 여부 확인 | | POST /api/auth/setup | 관리자 계정 생성 (첫 사용 시) | | POST /api/auth/login | 로그인 → JWT 반환 | | GET /api/auth/me | 현재 사용자 정보 (JWT 필요) | | GET /api/admin/users | 사용자 목록 (admin+) | | POST /api/admin/users | 사용자 생성 (admin+) | | PUT /api/admin/users | 사용자 수정 (admin+) | | DELETE /api/admin/users | 사용자 삭제 (admin+) |
역할 (role)
| role | 설명 | |---|---| | god | 최고 관리자, 프로젝트 간 로그인 가능 | | hostAdmin | 호스트앱 관리자 | | admin | 일반 관리자 | | user | 일반 사용자 |
보안 TODO (미해결)
fsot 전용 테이블 RLS OFF 상태 — 보안 취약
현재 아래 테이블들의 RLS가 OFF되어 있습니다:
kanban_cardskanban_columnschat_messagespage_completions
원인: Supabase Auth 제거 후 기존 RLS 정책(auth.uid() 기반)이 동작하지 않아 임시로 RLS를 비활성화.
위험: 클라이언트(useKanban.ts, useChat.ts)가 Supabase anon key로 직접 DB에 접근하고 있어, anon key를 아는 누구나 데이터를 조작할 수 있음.
해결 방법: 클라이언트의 Supabase 직접 접근을 제거하고, 모든 CRUD를 fsot 서버 API를 경유하도록 변경.
현재 (위험): 클라이언트 → (anon key) → Supabase (RLS OFF)
목표 (안전): 클라이언트 → (JWT) → fsot 서버 → (service_role) → Supabase수정 대상 파일:
src/hooks/useKanban.ts— 칸반 CRUDsrc/hooks/useChat.ts— 채팅 메시지 CRUDsrc/overlay/KanbanPanel.tsx— page_completions 직접 접근- 서버에 대응하는 API 라우트 추가 필요
실행
# 서버 (포트 4000)
cd apps/fsot/server && npx tsx src/index.ts
# 환경변수: server/.env
SUPABASE_URL=https://kgejfsxantavmzwdukmq.supabase.co
SUPABASE_SECRET_KEY=<service_role_key>
JWT_SECRET=<optional, 기본값 있음>
PORT=4000Supabase 프로젝트
sotfront (kgejfsxantavmzwdukmq) — bit, hito와 공유. fsot 전용 테이블만 사용.
