Frontend Migration

NiceGUI 서버사이드 UI를 Next.js 클라이언트사이드로 점진 전환하는 프로젝트입니다.
기존 NiceGUI(18080)와 FastAPI(8600)는 그대로 유지하며, 페이지 단위로 하나씩 Next.js(18081)로 이전합니다.

lightbulb 왜 전환하는가

NiceGUI의 한계 — 서버사이드 UI 상태 + 싱글 프로세스 + WebSocket 상시 연결로 수평 확장 불가
항목NiceGUI (현재)Next.js (전환)
렌더링 서버사이드 (Python → Quasar) 클라이언트사이드 (React)
상태 관리 서버 메모리 (세션별) 브라우저 (localStorage + React state)
연결 방식 WebSocket 상시 연결 HTTP (REST API 호출)
확장성 싱글 프로세스, 수직만 무상태, 수평 확장 가능
빌드/배포 Python 프로세스 정적 빌드 + CDN 가능
개발 생산성 Python UI 코드 React 생태계 + TypeScript

route 전환 전략

점진적 전환 (Strangler Fig 패턴)

NiceGUI를 한 번에 끄지 않고, 페이지 단위로 Next.js에 구현한 뒤 트래픽을 이전합니다. 모든 페이지가 전환 완료되면 NiceGUI를 종료합니다. FastAPI 백엔드(8600)는 변경 없이 그대로 공유합니다.

1

셋업 + 인증 + 대시보드

프로젝트 셋업, 로그인, 레이아웃(사이드바+헤더), 대시보드 페이지. 나머지는 스텁.

2

핵심 Studio 페이지

이상 목록, 규칙 관리, 감지 로그, 플레이북/진단/액션/검증 Studio.

3

AI Chat + 도구

AI Chat (SSE 스트리밍), Tool Studio, 지식베이스, 질문 흐름, 매크로.

4

관리 + Agent 페이지

사용자/설정/활동로그/LLM사용량 + 4 AI Agent 채팅 페이지.

5

파이프라인 + 나머지

파이프라인 모니터, 테이블 이관, 채팅 이력, 도구 요청.

6

NiceGUI 종료

전체 전환 완료 확인 후 NiceGUI 프로세스 제거. 18080 포트 해제.

architecture 시스템 구조

┌─────────────────────────────────────────────────────────┐ │ 브라우저 │ │ │ │ Next.js (18081) NiceGUI (18080) │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ React SPA │ │ Quasar UI │ │ │ │ (새 페이지) │ │ (기존 페이지) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ /api/* rewrites │ WebSocket │ └─────────┼────────────────────────────┼───────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────┐ │ FastAPI (:8600) │ │ │ │ /api/users/login 인증 │ │ /api/dashboard/* 대시보드 │ │ /api/anomalies 이상 목록 │ │ /api/system/health 헬스 체크 │ │ ... │ └──────────────┬──────────────────────┘ │ ┌────┴────┐ │ SQLite / │ │ Oracle │ └──────────┘
핵심 — Next.js의 rewrites/api/* 요청을 FastAPI(8600)로 프록시하므로 CORS 설정 불필요. 두 프론트엔드가 동일한 백엔드를 공유합니다.

dns 포트 할당

8600
FastAPI 백엔드
18081
Next.js 프론트엔드 (신규)
18080
NiceGUI 프론트엔드 (기존)

sync_alt API 프록시

next.config.tsrewrites로 모든 API 호출을 FastAPI로 전달합니다.

// next.config.ts
const nextConfig: NextConfig = {
  async rewrites() {
    return [{
      source: "/api/:path*",
      destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
    }];
  },
};

왜 rewrites인가?

브라우저 → Next.js(18081) → FastAPI(8600) 프록시로 동작하므로 CORS 헤더 설정이 필요 없고, 쿠키도 same-origin으로 자연스럽게 전달됩니다. 프로덕션에서는 Nginx/Cloudflare에서 경로 기반 라우팅으로 교체 가능합니다.

lock 인증 흐름

1. 로그인 폼 → POST /api/users/login { username, password } 2. 응답 (FastAPI) { token, user_id, username, display_name, role, pages, nav } 3. localStorage "flopi_auth"에 저장 4. 이후 모든 API 호출 Authorization: Bearer <token> 5. 401 응답 시 localStorage 클리어 → /login 리다이렉트
nav 배열 — 로그인 응답에 포함된 nav 배열을 사이드바가 그대로 렌더링합니다. 역할별 접근 권한 필터링은 백엔드(get_nav_items())에서 완료됩니다.

checklist Phase 1 범위

folder 디렉토리 구조

FLOPI/frontend/ ├── package.json # 의존성 + 스크립트 ├── next.config.ts # rewrites (API 프록시) ├── postcss.config.mjs # Tailwind v4 ├── tsconfig.json ├── .env.local # NEXT_PUBLIC_API_URL ├── app/ │ ├── layout.tsx # Root: Inter 폰트 + Providers │ ├── globals.css # CSS 변수 + 글래스모피즘 │ ├── login/ │ │ └── page.tsx # 로그인 │ └── (dashboard)/ │ ├── layout.tsx # 인증 가드 + Sidebar + Header │ ├── page.tsx # 대시보드 (KPI + 차트) │ ├── anomalies/ # 스텁 페이지들 │ ├── rules/ # ↓ │ ├── ... # (24개 스텁) │ └── migration/ ├── components/ │ ├── Providers.tsx # AuthProvider + ThemeProvider │ ├── Sidebar.tsx # 좌측 네비 (nav 배열 기반) │ ├── Header.tsx # 상단 바 (헬스점 + 테마토글) │ ├── KpiCard.tsx # KPI 카드 │ ├── GlassCard.tsx # 글래스모피즘 래퍼 │ ├── SeverityBadge.tsx # CRITICAL / WARNING │ ├── StatusBadge.tsx # 감지됨 / 처리중 / 해결 │ └── EmptyState.tsx # 빈 상태 표시 └── lib/ ├── api.ts # fetch 래퍼 (Bearer, 401) ├── auth.tsx # AuthProvider + useAuth ├── theme.tsx # ThemeProvider + useTheme ├── types.ts # API 응답 타입 정의 └── constants.ts # 색상, 매핑, 아이콘

code 기술 스택

항목선택이유
프레임워크Next.js 15 (App Router)파일 기반 라우팅, 정적 빌드 지원
UIReact 19생태계 + 인력 풀
언어TypeScript타입 안전성
스타일Tailwind CSS v4유틸리티 우선, CSS 변수 네이티브
차트RechartsReact 네이티브, 도넛/바/라인 지원
아이콘Lucide ReactMaterial Icons 대체, 트리셰이킹
빌드TurbopackHMR 속도 개선

palette 테마 설계

라이트 기본 + 다크 전환

<html> 클래스에 dark 추가/제거로 전환합니다. CSS 변수가 자동으로 전환되어 모든 컴포넌트에 반영됩니다. 사용자 선택은 localStorage "flopi_theme"에 저장됩니다.

/* globals.css */
@custom-variant dark (&:where(.dark, .dark *));

:root {
  --flopi-bg: #f8fafc;
  --flopi-card: rgba(255,255,255,0.85);
  --flopi-primary: #059669;
  --flopi-text: #1f2937;
}
.dark {
  --flopi-bg: #030712;
  --flopi-card: rgba(17,24,39,0.6);
  --flopi-primary: #10b981;
  --flopi-text: #f3f4f6;
}

글래스모피즘

NiceGUI의 .glass-card 스타일을 CSS 변수 기반으로 재구현했습니다.

.glass-card {
  background: var(--flopi-card);
  backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid var(--flopi-border);
  border-radius: 16px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}

dashboard 대시보드

NiceGUI 대시보드와 동일한 구조를 React로 재현합니다.

섹션API컴포넌트
시스템 개요/api/dashboard/overviewKpiCard × 4
플랫폼 활동/api/dashboard/platform-statsKpiCard × 5
AI 엔진/api/dashboard/platform-statsKpiCard × 3
도구 요청/api/tool-requests/statsKpiCard × 4
인기 도구/api/dashboard/platform-stats리스트
카테고리별 이상/api/dashboard/platform-stats리스트
상태 분포/api/dashboard/overview도넛 차트 (Recharts)
최근 이상/api/anomalies?limit=5GlassCard + Badge

list 전체 페이지 목록 (25개)

경로페이지그룹상태
/대시보드-완료
/pipeline-monitor파이프라인-스텁
/ai-chatAI Chat-스텁
/tool-studio도구-스텁
/knowledge-base지식베이스-스텁
/question-flows질문 흐름-스텁
/chat-scenarios매크로-스텁
/anomalies이상 목록이상감지스텁
/logs감지 로그이상감지스텁
/rules규칙 관리이상감지스텁
/agent/sentinelSentinel이상감지스텁
/playbook-studio플레이북원인분석스텁
/diagnosis-studio진단 케이스원인분석스텁
/agent/diagnosticianDiagnostician원인분석스텁
/action-studio액션조치 제안스텁
/agent/advisorAdvisor조치 제안스텁
/validation-studio검증대안 검증스텁
/agent/validatorValidator대안 검증스텁
/chat-history채팅 이력관리스텁
/tool-requests도구 요청관리스텁
/llm-usageLLM 사용량관리스텁
/activity-log활동 로그관리스텁
/users사용자 관리관리스텁
/system-settings설정관리스텁
/migration테이블 이관관리스텁
제외 항목 — 워크플로우 빌더, 암묵지(RCA), 대응방안(구버전)은 마이그레이션 대상에서 제외되었습니다.

add_circle 페이지 추가 방법

스텁 페이지를 실제 구현으로 전환하는 절차입니다.

페이지 코드 템플릿

"use client";

import { useEffect, useState } from "react";
import GlassCard from "@/components/GlassCard";
import EmptyState from "@/components/EmptyState";

export default function MyPage() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("/api/my-endpoint")
      .then(res => res.json())
      .then(setData)
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <Spinner />;

  return (
    <div className="space-y-6 animate-in">
      <h1 className="text-xl font-bold"
          style={{ color: "var(--flopi-text)" }}>
        페이지 제목
      </h1>
      <GlassCard>
        {data ? <Content /> : <EmptyState message="데이터 없음" />}
      </GlassCard>
    </div>
  );
}

gavel 코딩 규칙

항목규칙
컴포넌트PascalCase, "use client" 디렉티브 필수
스타일Tailwind 유틸리티 + CSS 변수 (var(--flopi-*))
색상하드코딩 금지 — 반드시 CSS 변수 또는 constants.ts 사용
API 호출lib/api.ts의 래퍼 함수 사용 (직접 fetch 금지)
인증useAuth() 훅으로 접근, 직접 localStorage 접근 금지
테마useTheme() 훅, .dark variant 사용
아이콘Lucide React, constants.tsICON_MAP 참조
타입lib/types.ts에 정의, any 금지
���러 처리API 실패 시 빈 상태 표시 (EmptyState), 페이지 크래시 방지

play_arrow 개발 서버

# 의존성 설치
cd FLOPI/frontend && npm install

# 개발 서버 (포트 18081, Turbopack)
npm run dev

# 빌드 검증
npm run build

# 프로덕션 실행
npm run start
전제 조건 — FastAPI 백엔드가 localhost:8600에서 실행 중이어야 합니다. API 프록시가 동작하지 않으면 로그인 및 데이터 로딩이 실패합니다.

검증 체크리스트

#항목확인
1/login → 로그인 폼 표시admin / fab-admin
2로그인 성공 → / 대시보드 리다이렉트
3사이드바 25개 네비게이션 표시
4헤더 헬스 점 녹색 (30초 폴링)
5다크/라이트 토글 동작
6대시보드 KPI + 도넛차트 표시
7로그아웃 → /login 리다이렉트
8NiceGUI(18080) 정상 동작 확인