Developer Manual · 개발 매뉴얼

frontend — 사용자 인터페이스 (SPA)

Nuxt 3 기반 산업용 데이터 히스토리안/제조 분석 플랫폼 Factory Sight의 프론트엔드. 대시보드·시계열 분석·알람·모니터링 에디터·공장 마스터·시스템 관리 화면을 제공하는 데이터 집약형 엔터프라이즈 SPA.

🖥 Nuxt 3.15 · Vue 3.5 🟦 TypeScript 5.7 📦 pnpm 9.15 🍍 Pinia 🗓 작성일 2026-06-25

1개요

package.json name은 "nuxt-app", README는 기본 "Nuxt Minimal Starter" 템플릿 그대로라 실질 미작성. 정체성은 코드/CI에서 드러난다 — 배포 경로 /opt/factorysight/frontend/webroot, 산출물 historian-frontend-*.zip, 모든 API baseURL /historian/api.

ℹ️ SPA 구조
ssr: false. CI는 pnpm generate로 정적 산출물(.output/public)을 만들어 zip 배포. 즉 정적 호스팅 + 별도 백엔드(/historian/api) 구조. InfluxDB 기반 시계열 질의가 핵심 데이터 소스.

2빠른 시작 · 실행

pnpm install            # (--frozen-lockfile 로 CI 재현성)
pnpm dev                # 개발 서버 (vite proxy: /historian/api → :8080)
pnpm generate           # 정적 산출물 생성 (.output/public)
pnpm preview            # 산출물 미리보기
패키지 매니저
pnpm 9.15.2
API baseURL
/historian/api
dev 프록시 타깃
localhost:8080
렌더링
SPA (ssr:false)
⚠️ 백엔드 필요
개발 시 /historian/apilocalhost:8080(backend)으로 프록시된다. 백엔드가 떠 있어야 로그인·데이터 조회가 동작. 운영은 동일 origin 백엔드 가정.

3디렉터리 · 라우팅

frontend/ ├─ app.vue 최상위 셸: <NuxtLayout><NuxtPage keepalive /></NuxtLayout> ├─ pages/ 파일 기반 라우팅 (도메인별, 약 42개 .vue) ├─ components/ 도메인/공통 컴포넌트 (213개, autoImport pathPrefix:false) ├─ layouts/ default / empty / login ├─ middleware/ auth.global.ts (전역 인증), routing.global.ts (빈 껍데기) ├─ plugins/ client 전용 (apiHistorian, code, highcharts, plotly …) ├─ stores/ Pinia (auth, historian, tag, common, dstts …) ├─ repository/ API 통신 (FetchFactory + modules/* 도메인별) ├─ composables/ useAnalyticsValidation, useIntervalManager ├─ utils/ types/ 순수 함수·타입 (autoImport) └─ docs/ embedded-guide (OpenAPI yaml + redoc html)

주요 라우트 그룹

그룹페이지(예)
인증/엔트리index, login, changePassword, error/401
analytics(핵심)TagTrend, HistogramBoxPlot, MultiHistogram, CorrelationRegression, ParallelCoordinate, RawData
alarm / alertTagAlarm(Log), MultiAlarm(Log), SopAlarm / AlertAlarm, Channel
monitoringMonitoringMgmt/[monitoringNo], MonitoringViewer/[monitoringNo] (동적 라우트, 에디터/뷰어)
factory / systemMachineMgmt·ProcessMgmt·TagMgmt / UserMgmt·AuthMgmt·MenuMgmt·CodeMgmt
customcustom/gnp/*, custom/scr/* — GNP·SCR 고객사 전용 화면이 코어에 직접 포함

메뉴는 정적 라우트가 아니라 백엔드에서 동적으로 받은 메뉴 트리(arrayToTree) + 탭 네비게이션으로 구성. <NuxtPage keepalive />로 페이지가 메모리 상주.

4상태관리 (Pinia)

모두 setup-store(컴포지션) 스타일 + HMR. storesDirs로 자동 등록, persistedstate로 localStorage 영속화.

  • authStore (영속 authState): access/refresh 토큰을 쿠키 + 스토어 동시 저장, login/loginByXApiKey(임베드)/logout/checkAuth. 비밀번호 초기화 강제 이동 로직 보유.
  • historianStore (영속 tabs): 메뉴 트리, 탭 네비게이션 전체 로직, isLoading(전역 로더), snackbar/drawer/toasts. MONITORING 타입 메뉴는 URI를 /monitoring/MonitoringViewer/{no}로 재작성.
  • 그 외 tagStore, tag/tagGridStore, common/commonStore(집계함수 목록 등), dstts/histogramStore.
⚠️ API-전역상태 강결합
FetchFactory.call()이 요청 시 historianStore.isLoading을 직접 토글한다 → API 계층이 전역 로딩 상태에 강결합(테스트/재사용 시 스토어 의존).

5API 통신 계층

3중 구조로 체계적이다.

  1. repository/FetchFactory.ts — 기반 클래스. call(method,url,data,opts) 표준 호출(GET body 생략, isLoading 플래그로 전역 로더 토글), useAsyncFetch(), excelExportStream()(견고한 Blob 다운로드 — Content-Disposition 파싱, RFC 5987 filename*, sanitize).
  2. repository/modules/** (60+) — 각 extends FetchFactory, PREFIX_URI(대개 /api/v1) + 엔드포인트 메서드. 응답 CommonResponse {result,code,message,data}.
  3. plugins/apiHistorian.ts (client) — DI 컨테이너. 모든 모듈을 $api 도메인 트리(api.factory.tag, api.analytics.tagTrend …)로 provide. $fetch.createonRequest(Bearer 토큰 자동 주입, 없으면 /login) / onResponseError(401→/login).
  4. plugins/code.ts (client) — 부팅 시 공통코드 전량 로드 후 Map 메모이즈 → $code(id) 헬퍼(매번 API 호출 없이 코드 조회).
ℹ️ 프록시
개발 시 vite proxy로 /historian/apilocalhost:8080. 운영은 정적 배포라 동일 origin 백엔드 가정.

6인증 · 미들웨어

핵심은 middleware/auth.global.ts (전역, client-only — if(import.meta.server) return).

  • 임베드 모드(?embed=true): 레이아웃 empty 강제, ?token= 있으면 loginByXApiKey로 X-API-KEY 로그인 → 성공 시 URL에서 token 제거, 실패 시 /error/401. (외부 iframe 임베드 인증, docs/embedded-guide와 대응)
  • 일반 모드: /login 진입 시 clearAuth(); 비밀번호 초기화 대상 → /changePassword; accessToken 쿠키 있으면 통과; refreshToken만 있으면 /auth/refresh-token 재발급; 둘 다 없으면 /login.
  • 토큰 주입은 미들웨어가 아닌 apiHistorian onRequest에서, 401은 onResponseError에서 이중 방어.
⚠️ 죽은 코드
middleware/routing.global.ts는 빈 함수(defineNuxtRouteMiddleware(()=>{})) — 미사용.

레이아웃: default(AppBar/Navigation/TabBar/Loader/SnackBar), login, empty(임베드).

7UI · 컴포넌트

213개 .vue 컴포넌트, 자동 import(pathPrefix:false)로 전역 사용. 도메인별: system 33, editor 32, analytics 24, custom 24, form 19 …

  • components/form/ (19) — Kendo 입력을 사내 표준으로 래핑(FormInput·FormDropDownList·FormDateTimePicker …).
  • components/editor/ (32) — 모니터링 대시보드 비주얼 에디터(도형/레이어 조건/뷰어). 제품의 가장 복잡한 영역.
  • components/grid/ — Kendo Grid 커스텀 셀/필터.
⚠️ UI 스택 과중
UI 3종(Kendo 주력 + Vuetify 셸 + Tailwind 유틸) + 차트 4종(Highcharts / Plotly / ECharts / Kendo) 혼재 → 번들·일관성·학습비용 부담. layouts/default.vue의 Kendo 팝업 위치 보정 MutationObserver는 라이브러리 충돌 회피용 DOM 핫픽스(코드 스멜).

8설정

  • nuxt.config.ts: ssr:false, compatibilityDate 2024-11-01, viewport maximum-scale=1(줌 차단, 키오스크 지향). modules 12종, plugins 5종 전부 mode:"client". vite proxy + usePolling(파일 변경 폴링).
  • tsconfig.json: strict:false · noImplicitAny:false · strictNullChecks:false · skipLibCheck:true타입 안정성 의도적 완화(빌드 통과 우선).
  • 환경변수: runtimeConfig 미사용, baseURL·프록시·서버 IP가 코드/설정에 하드코딩 → 멀티 환경 대응 취약.

9빌드 · 배포

  • pnpm 고정(packageManager: pnpm@9.15.2), pnpm.overrides로 일부 버전 핀.
  • CI(.gitlab-ci.yml) 2-stage: build(pnpm install --frozen-lockfilepnpm generatehistorian-frontend-{VERSION}.zip, develop/tags) → deploy(수동, 웹루트 비우고 unzip, fsuser 권한, 백업).
  • 전통적 온프레미스 정적 zip 배포.

10기술 부채 · 개선 과제

#항목심각도
1TypeScript strict:false 등 → 잠재 타입 버그(apiHistorian의 존재하지 않는 타입 GnpCommonModules, authStore.isCheckedPassword 타입 누락). 점진적 strict 전환HIGH
2README 미작성(기본 템플릿) → 온보딩 곤란. 실제 정보는 CI/docs에 산재MEDIUM
3죽은 코드/스텁(routing.global.ts, utils/workers/*, pages/test/*) 정리MEDIUM
4검증기 미완성(주석 처리·빈 반환), zod와 함수형 validator 혼용 정리MEDIUM
5UI 스택 과중(3 UI + 4 차트) → 번들·일관성. 표준화/경량화 검토MEDIUM
6환경설정 하드코딩(runtimeConfig/.env 미활용) → 멀티 환경 대응MEDIUM
7토큰 localStorage+쿠키 저장 → XSS 노출면 검토. console.log 프로덕션 잔존MEDIUM
8고객사 커스텀(custom/gnp·scr) 코어 혼입 → 멀티테넌트/빌드 분리 고려MEDIUM

11신규 개발자 가이드

읽는 순서

  1. nuxt.config.ts — 모듈·프록시·플러그인 전반
  2. app.vue + layouts/default.vue — 셸·네비게이션 구조
  3. middleware/auth.global.ts — 인증·임베드 흐름
  4. repository/FetchFactory.ts + plugins/apiHistorian.tsAPI 계층·$api DI(핵심)
  5. stores/authStore.ts, stores/historianStore.ts — 전역 상태·탭
  6. 대표 페이지 pages/analytics/TagTrend.vue — 검색바+차트+다이얼로그 패턴
  7. components/form/*, components/editor/* — 표준 폼·에디터
✅ 강점
repository 계층(FetchFactory 상속 + $api 도메인 트리 DI)이 일관·확장성 있게 설계됨. 공통코드 캐싱, 견고한 Blob 다운로드, 임베드 인증, 분석 검증 composable 등 실무 디테일이 잘 잡혀 있고, 폼/그리드/차트를 사내 표준으로 래핑해 재사용성 확보.