종합 감사 결과 발견된 블로커 5건:
1. AddColumnModal / CreateTableModal prop 미스매치 — 호출부 tableName/sourceTableName(camelCase)
vs 인터페이스 table_name/source_table_name(snake_case). 결과적으로 컬럼 추가 기능이 실제로
동작하지 않았음 (/api/ddl/tables/undefined/columns). 호출부를 인터페이스에 맞춰 통일.
2. IS_NULLABLE 강제 'Y' 덮어쓰기 (backend mapper) — upsertColumnSettings 의 VALUES 절이
모든 케이스에서 'Y' 리터럴을 박음. NN 토글 후 "컬럼 설정 저장" 누르면 필수 입력 제약이
조용히 사라짐. COALESCE(#{is_nullable}, 'Y') 로 변경 + ON CONFLICT 절에 IS_NULLABLE
COALESCE 추가. Service 도 settings 에서 is_nullable 을 읽어 'Y'/'N' 으로 정규화 후 전달.
3. 저장 안 한 편집 침묵 손실 — 우측 패널에서 편집 후 저장 안 누르고 다른 테이블로 이동 시
변경 사항 소실. hasUnsavedChanges memo 추가 (columns vs originalColumns JSON 비교).
handleTableSelect 에서 dirty 면 confirm 다이얼로그.
4. PK / NN / IDX / UQ 위치별 비대칭 저장 — 그리드 행 칩은 즉시 저장하는데 우측 상세 패널
토글은 메모리만 변경하고 저장 버튼 필요. 두 위치 모두 즉시 저장으로 통일 (상세 패널의
onColumnChange 가 is_nullable / is_unique 필드를 받으면 handleNullableToggle /
handleUniqueToggle 호출하도록).
5. SUPER_ADMIN role 검증 누락 (backend 보안) — DdlController 의 isSuperAdmin 이 company_code
== "*" 만 보고 role 클레임 무시. 토큰 변조 또는 "*" 회사 소속만으로 운영 DB DROP / ALTER
가능. company_code "*" AND role "SUPER_ADMIN" 둘 다 충족 시에만 통과로 변경. 11개 엔드포인트
에 @RequestAttribute("role") 추가.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
INVION — Low-Code ERP/PLM Platform
프로젝트 개요
INVION은 제조업 특화 Low-Code ERP/PLM(Product Lifecycle Management) 플랫폼입니다. 사용자가 런타임에 화면·테이블·워크플로우를 정의할 수 있는 메타데이터 기반 설계로, 코드 수정 없이 업무 화면을 구성합니다.
Spring Boot + Next.js 풀스택 구조이며, 멀티테넌시(company_code 기반)로 복수 기업을 단일 인스턴스에서 운영합니다.
주요 특징
- Low-Code 화면 디자이너: 드래그앤드롭으로 업무 화면 구성, V2 컴포넌트 시스템
- Spring Boot 백엔드: Java 21 + MyBatis (SqlSessionTemplate) + PostgreSQL
- v5 Cosmic Glassmorphism UI: 코스믹 배경 + 글래스 블러 기반 모던 디자인 시스템
- 멀티테넌시: company_code 기반 데이터 격리, Super Admin 전사 접근
- 반응형 디자인: 데스크톱 / 태블릿 / 모바일 완전 대응
- 3D / 차트 / 지도: React Three Fiber, Recharts, Leaflet 통합
- GitOps 배포: Jenkins + Kaniko + Helm + Traefik (Kubernetes)
기술 스택
Frontend
| 영역 | 기술 | 비고 |
|---|---|---|
| 프레임워크 | Next.js 15 (App Router, Turbopack) | React 19 |
| 언어 | TypeScript 5 | strict mode |
| 스타일링 | Tailwind CSS v4 + v5 커스텀 CSS (--v5- prefix) |
글래스모피즘 테마 |
| UI 라이브러리 | shadcn/ui + Radix UI (15개 컴포넌트) | 커스텀 오버라이드 |
| 상태 관리 | Zustand 5 (글로벌) + TanStack Query 5 (서버) | |
| 테이블 | TanStack Table + TanStack Virtual | 가상 스크롤 |
| HTTP 클라이언트 | Axios | 70개 API 모듈 (lib/api/) |
| 플로우 디자이너 | XY Flow (@xyflow/react) | 데이터플로우 |
| 3D | React Three Fiber + Drei | Digital Twin |
| 차트 | Recharts + D3 | 대시보드 / 분석 |
| 지도 | Leaflet + React Leaflet | 위치 기반 시각화 |
| 리치 텍스트 | Tiptap | 에디터 |
| 드래그앤드롭 | DnD Kit | 화면 디자이너 / 탭 정렬 |
| 폼 | React Hook Form + Zod | 유효성 검증 |
| 문서 생성 | jsPDF + exceljs + mammoth | PDF / Excel / Word |
| 바코드 / QR | jsbarcode + qrcode + @zxing | 스캔 / 생성 |
| 아이콘 | Lucide React |
Backend — Spring Boot
| 영역 | 기술 | 비고 |
|---|---|---|
| 언어 | Java 21 | LTS |
| 프레임워크 | Spring Boot 3.3.5 | |
| 빌드 | Gradle (Groovy DSL) | |
| SQL Mapper | MyBatis 3 (SqlSessionTemplate 직접 사용) | Mapper Interface 미사용 |
| 데이터베이스 | PostgreSQL + HikariCP | |
| 보안 | Spring Security + JWT (jjwt 0.12.3) | |
| JSON | Jackson (숫자→문자열 직렬화, null 키 포함) | |
| 메일 | Spring Boot Starter Mail | IMAP 수신 + SMTP 발신 |
| 로깅 | SLF4J + Logback | Spring Boot 기본 |
아키텍처 핵심:
Map<String, Object>기반 — Low-Code 플랫폼 특성상 테이블/컬럼이 런타임에 결정되므로 DTO 클래스를 사용하지 않습니다. Service에서sqlSession으로 XML을 직접 호출하는 3레이어 구조입니다.Controller → Service (extends BaseService) → XML Mapper
개발 도구
| 영역 | 기술 |
|---|---|
| 컨테이너화 | Docker + Docker Compose |
| 백엔드 핫리로드 | Spring Boot DevTools |
| 코드 품질 | ESLint + Prettier |
| CI/CD | Jenkins + Kaniko (이미지 빌드) |
| 오케스트레이션 | Kubernetes + Helm (GitOps) |
| 리버스 프록시 | Traefik (HTTPS, Let's Encrypt) |
| 컨테이너 레지스트리 | registry.kpslp.kr (프라이빗) |
프로젝트 구조
INVION/
├── frontend/ # Next.js 15 프론트엔드
│ ├── app/ # App Router 페이지
│ │ ├── (main)/ # 메인 레이아웃 (인증 후)
│ │ ├── (auth)/ # 로그인/인증
│ │ ├── (admin)/ # 관리자 페이지
│ │ └── (pop)/ # 팝업 페이지
│ ├── components/ # React 컴포넌트 (~27개 도메인)
│ │ ├── ui/ # shadcn/ui 기본 컴포넌트
│ │ ├── layout/ # AppLayout, TabBar, Sidebar, Header
│ │ ├── screen/ # 화면 디자이너 (~57개 파일)
│ │ ├── dataflow/ # 데이터플로우 디자이너
│ │ ├── v2/ # V2 동적 컴포넌트 시스템
│ │ ├── admin/ # 관리자 컴포넌트
│ │ ├── approval/ # 전자결재
│ │ ├── flow/ # 워크플로우
│ │ ├── barcode/ # 바코드/라벨
│ │ ├── report/ # 리포트
│ │ ├── mail/ # 메일
│ │ ├── multilang/ # 다국어
│ │ ├── animations/ # 애니메이션 컴포넌트
│ │ └── ... # dashboard, vehicle, tax-invoice 등
│ ├── lib/ # 유틸리티, API 클라이언트, 서비스
│ │ ├── api/ # Axios 기반 API 클라이언트 (70개 모듈)
│ │ ├── stores/ # Zustand 스토어
│ │ ├── schemas/ # Zod 스키마
│ │ ├── services/ # 프론트 비즈니스 로직
│ │ └── types/ # TypeScript 타입 정의
│ ├── stores/ # 글로벌 Zustand 스토어
│ ├── hooks/ # Custom React Hooks
│ └── styles/ # v5-layout.css (글래스모피즘)
│
├── backend-spring/ # Spring Boot 백엔드 (95 컨트롤러, 97 서비스, 96 XML)
│ └── src/main/
│ ├── java/com/erp/
│ │ ├── common/ # BaseService (sqlSession 주입)
│ │ ├── config/ # Security, MyBatis, Jackson, Exception
│ │ ├── controller/ # @RestController
│ │ ├── service/ # @Service extends BaseService
│ │ ├── security/ # JWT 인증
│ │ └── util/ # 유틸리티
│ └── resources/
│ ├── application.yml
│ └── mapper/ # MyBatis XML (소문자 camelCase)
│
├── db/ # 데이터베이스
│ └── migrations/ # 순차 마이그레이션 SQL
├── docker/ # Docker 설정 (dev/prod/deploy)
├── scripts/ # 개발/배포 스크립트 (dev/prod)
├── Dockerfile # 프로덕션 멀티스테이지 빌드 (Spring + Next.js)
└── Jenkinsfile # CI/CD 파이프라인 (Kaniko + Helm)
빠른 시작
1. 필수 요구사항
- Java: 21
- Node.js: 22 LTS+ (
frontend/package.json의engines.node에>=22.0.0강제. 프로젝트 루트에.nvmrc박혀있어nvm use로 자동 전환됨) - PostgreSQL: 데이터베이스 서버
- npm: 10.0+
2. 개발 환경 실행
# 프론트엔드 (Turbopack, port 9771)
cd frontend && npm install && npm run dev
# 백엔드 — Spring Boot (port 8081)
cd backend-spring && ./gradlew bootRun
3. Docker 환경 실행 (dev)
frontend + backend 한 컴포즈 파일로 띄움. 코드 변경은 volume mount 로 컨테이너 안에 즉시 반영됨 (turbopack 자동 리로드).
# 개발 환경 한 번에 띄우기
docker compose -f docker/dev/docker-compose.invyone.yml up -d
# 내리기 / 재시작 / 로그
docker compose -f docker/dev/docker-compose.invyone.yml down
docker compose -f docker/dev/docker-compose.invyone.yml restart
docker compose -f docker/dev/docker-compose.invyone.yml logs -f
# 프로덕션 배포 (별도)
docker compose -f docker/deploy/docker-compose.yml up -d
4. 서비스 접속
| 환경 | 서비스 | URL | 설명 |
|---|---|---|---|
로컬 dev (npm run dev / gradlew bootRun) |
프론트엔드 | http://localhost:9771 | Next.js (turbopack) |
| 백엔드 API | http://localhost:8081 | Spring Boot | |
| 도커 dev (위 컴포즈) | 프론트엔드 | http://localhost:9772 | 컨테이너 내부 3000 → 호스트 9772 |
| 백엔드 API | http://localhost:8083 | 컨테이너 내부 8081 → 호스트 8083 |
프론트엔드는
next.config.mjs의 rewrites 설정으로/api/*요청을 백엔드로 프록시합니다 (도커 컴포즈에서는 컨테이너 네트워크 내부 이름invyone-backend-spring:8081로 프록시).
주요 기능
1. 화면 디자이너 (Screen Designer)
- 드래그앤드롭 기반 업무 화면 구성
- 그리드 레이아웃 빌더 + 레이어 관리
- V2 동적 컴포넌트 시스템 (Input, Select, Date, List, Hierarchy, Media 등)
- 화면 복사 / 메뉴 할당 / 임베딩
2. 데이터플로우 디자이너 (Data Flow)
- XY Flow 기반 비주얼 데이터 흐름 설계
- 테이블 노드 + 관계 에지 + 외부 호출 노드
- 조건부 로직 / 연결 설정
3. 사용자 및 권한 관리
- 역할 기반 접근 제어 (RBAC)
- 부서/조직 계층 관리
- 멀티테넌시 (company_code 기반 데이터 격리)
4. 전자결재 (Approval)
- 결재 요청/승인/반려 워크플로우
- 글로벌 리스너 기반 실시간 알림
5. 제품/BOM 관리
- BOM 구성 및 버전 관리
- 제품 카테고리 트리
6. Digital Twin / 3D 시각화
- React Three Fiber 기반 3D 뷰
- Yard Layout 시각화
- Digital Twin 템플릿 관리
7. 기타 기능
- 메일 연동 (IMAP 수신 + SMTP 발신)
- 바코드/QR 생성 및 스캔 (jsbarcode + qrcode + @zxing)
- 대시보드 차트 (Recharts + D3)
- 지도 시각화 (Leaflet)
- 리포트 / 세금계산서
- 문서 생성 (PDF, Excel, Word)
- 다국어 지원
- 번호 채번 규칙
- 배치 스케줄링
- 파일/문서 관리
디자인 시스템 — v5 Cosmic Glassmorphism
INVION v5는 코스믹 글래스모피즘 디자인 언어를 사용합니다.
- 코스믹 배경: 별/파티클/성운 애니메이션 (
CosmicBackground.tsx) - 글래스 UI:
backdrop-filter: blur()기반 반투명 패널 - CSS 변수:
--v5-prefix로 shadcn/Tailwind 변수와 충돌 방지 - 다크/라이트 테마: 크로스페이드 전환 애니메이션
- 모션 디테일: 모든 전환/호버/진입에 애니메이션 적용 (CSS 기반, framer-motion 미사용)
주요 스타일 파일:
frontend/styles/v5-layout.css— v5 전체 CSSfrontend/app/(auth)/login/login.css— 로그인 전용
환경 변수
# backend-spring/src/main/resources/application.yml
server:
port: 8081
spring:
datasource:
url: jdbc:postgresql://host:port/dbname
username: postgres
password: ****
jwt:
secret: your-jwt-secret
expiration: 86400000
file:
upload-dir: ./uploads
# frontend/.env.local — 클라이언트(브라우저) 가 사용할 API base URL
# 반드시 상대경로 "/api" 로 둘 것. 절대 URL (예: http://localhost:8083/api) 을 박으면
# 다른 머신/도메인에서 접속할 때 클라이언트가 자기 자신을 찌르며 connection refused 발생함.
# next dev server 의 rewrites 가 "/api/*" 를 컨테이너 내부 backend 로 프록시한다.
NEXT_PUBLIC_API_URL=/api
배포
프로덕션 빌드
# 멀티스테이지 Docker 빌드 (Spring Boot + Next.js → 단일 컨테이너)
docker build -t invion .
멀티스테이지 빌드 과정:
- Stage 1 — Spring Boot 빌드 (
eclipse-temurin:21-jdk-alpine, Gradle → bootJar) - Stage 2 — Next.js 빌드 (
node:22-alpine, npm → standalone) - Stage 3 — 런타임 (
eclipse-temurin:21-jre-alpine+ Node.js, 두 서비스 병렬 실행)
CI/CD 파이프라인
Git Push → Jenkins → Kaniko 이미지 빌드 → 프라이빗 레지스트리 푸시
→ Helm 차트 태그 업데이트 → Kubernetes 자동 배포 (GitOps)
- 이미지 빌드: Kaniko (Docker-in-Docker 불필요)
- 레지스트리:
registry.kpslp.kr(프라이빗) - 배포: Helm 차트 + GitOps (이미지 태그 자동 업데이트)
- 프로덕션 도메인: Traefik 리버스 프록시 + Let's Encrypt HTTPS
알려진 설정 정책
후임자/협업자가 "이거 왜 이렇게 짜놨지?" 헷갈리지 않도록 의도가 있는 비명시적 결정만 정리.
frontend/next.config.mjs — isDev 분기
output: "standalone" 과 experimental.webpackMemoryOptimizations 는 prod build 에서만 켜진다 (NODE_ENV !== "production" 이면 비활성화). dev 모드에서 같이 켜면 다음 두 가지 부작용이 동시에 발생함:
output: standalone이 dev 청크 manifest 경로 처리를 깨트림 → 라우트 그룹(main)/(auth)의 layout/page 청크가_next/static/chunks/...에 못 만들어짐webpackMemoryOptimizations가 컴파일된 SSR 청크를 GC 해버려서 첫 visit 후 ENOENT 발생 → ChunkLoadError 영구화
→ 두 옵션은 절대 dev 에서 켜지 말 것. prod build 에서만 의미 있음.
docker/dev/frontend.Dockerfile — turbopack 강제
dev:docker 스크립트에 --turbopack 플래그가 박혀있다. 도커에서 webpack 으로 돌리면 next 15 + app router + 라우트 그룹 (main)/(auth) 조합에서 layout/page 청크가 disk 에 flush 되지 않는 케이스가 발생함. 로컬 dev (npm run dev) 도 동일하게 turbopack 사용. 베이스 이미지는 node:22-alpine.
Frontend → Backend API 호출 — 반드시 상대경로
위 "환경 변수" 섹션 참고. NEXT_PUBLIC_API_URL 에 절대 URL 을 박으면 안 된다. /api 로 두고 next.config.mjs 의 rewrites 가 처리하도록 위임.
코드 컨벤션
네이밍 규칙
| 레이어 | 케이스 | 예시 |
|---|---|---|
| DB 컬럼/테이블 | UPPER_SNAKE_CASE | SCREEN_ID, COMPANY_CODE |
| MyBatis XML SQL | UPPER_SNAKE_CASE | SELECT SCREEN_ID FROM SCREEN_DEFINITIONS |
| MyBatis #{파라미터} | camelCase | #{screenId}, #{companyCode} |
| API 응답 키 (Map) | lower_snake_case | screen_id, company_code |
| 프론트 타입 필드 | lower_snake_case | screen_id, menu_name_kor |
| JS 변수/함수/props | camelCase | screenId, handleClick |
공통 규칙
- TypeScript: strict mode 활성화
- ESLint + Prettier: 일관된 코드 스타일
- shadcn/ui: UI 컴포넌트 표준
- API 클라이언트:
frontend/lib/api/Axios 기반 전용 클라이언트 사용 (fetch 직접 사용 금지) - 멀티테넌시: 모든 쿼리에 company_code 필터링 필수
- Map 기반: Spring Boot 백엔드는 DTO 대신
Map<String, Object>사용 - snake→camel 변환 금지:
toCamelCaseKeys()등 변환 함수 사용 불가 - MyBatis 설정:
map-underscore-to-camel-case: false유지