feat: [module] Phase 3 — 물리적 분리 (경계 마커, 검증 스크립트, 라우트 가드, 문서)

- MODULE.md 경계 마커 4개 (production, quality, construction, vehicle-management)
- verify-module-separation.sh: Common→Tenant 금지 임포트 검증 스크립트
- 영업 생산지시 3개 페이지에 useModules 가드 추가
- MODULE_SEPARATION_OK 주석 마커 (공유 래퍼 허용)
- tsconfig @modules/* path alias 추가
- CLAUDE.md 모듈 분리 아키텍처 섹션 추가
- 모듈 분리 가이드 문서 (claudedocs/architecture/)
This commit is contained in:
유병철
2026-03-18 15:40:53 +09:00
parent 60c4bc111c
commit 4b8ca09ea5
13 changed files with 557 additions and 2 deletions

View File

@@ -16,6 +16,7 @@
import { useState, useEffect, useCallback } from "react";
import { useRouter, useParams } from "next/navigation";
import { useModules } from "@/hooks/useModules";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
@@ -345,6 +346,24 @@ export default function ProductionOrderCreatePage() {
const router = useRouter();
const params = useParams();
const orderId = params.id as string;
const { isEnabled, tenantIndustry } = useModules();
// 생산 모듈 비활성 시 접근 차단 (tenantIndustry 미설정 시 전부 허용)
if (tenantIndustry && !isEnabled('production')) {
return (
<PageLayout>
<div className="flex flex-col items-center justify-center min-h-[40vh]">
<p className="text-muted-foreground"> .</p>
<button
className="mt-4 text-sm text-blue-600 hover:underline"
onClick={() => router.push('/sales/order-management-sales')}
>
</button>
</div>
</PageLayout>
);
}
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

View File

@@ -11,6 +11,7 @@
import { useState, useEffect } from "react";
import { useRouter, useParams } from "next/navigation";
import { useModules } from "@/hooks/useModules";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
@@ -194,6 +195,24 @@ export default function ProductionOrderDetailPage() {
const router = useRouter();
const params = useParams();
const orderId = params.id as string;
const { isEnabled, tenantIndustry } = useModules();
// 생산 모듈 비활성 시 접근 차단 (tenantIndustry 미설정 시 전부 허용)
if (tenantIndustry && !isEnabled('production')) {
return (
<PageLayout>
<div className="flex flex-col items-center justify-center min-h-[40vh]">
<p className="text-muted-foreground"> .</p>
<button
className="mt-4 text-sm text-blue-600 hover:underline"
onClick={() => router.push('/sales/order-management-sales')}
>
</button>
</div>
</PageLayout>
);
}
const [detail, setDetail] = useState<ProductionOrderDetail | null>(null);
const [loading, setLoading] = useState(true);

View File

@@ -12,6 +12,7 @@
import { useState, useCallback } from "react";
import { useRouter } from "next/navigation";
import { useModules } from "@/hooks/useModules";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
@@ -174,6 +175,23 @@ const TABLE_COLUMNS: TableColumn[] = [
export default function ProductionOrdersListPage() {
const router = useRouter();
const { isEnabled, tenantIndustry } = useModules();
// 생산 모듈 비활성 시 접근 차단 (tenantIndustry 미설정 시 전부 허용)
if (tenantIndustry && !isEnabled('production')) {
return (
<div className="flex flex-col items-center justify-center min-h-[40vh]">
<p className="text-muted-foreground"> .</p>
<button
className="mt-4 text-sm text-blue-600 hover:underline"
onClick={() => router.push('/sales/order-management-sales')}
>
</button>
</div>
);
}
const [stats, setStats] = useState<ProductionOrderStats>({
total: 0,
waiting: 0,

View File

@@ -0,0 +1,32 @@
# Construction Module (건설관리)
**Module ID**: `construction`
**Tenant**: Juil (주일건설)
**Route Prefixes**: `/construction`
**Component Count**: 161 files
## Dependencies on Common ERP
- `@/lib/api/*` — Server actions, API client
- `@/components/ui/*` — UI primitives (shadcn/ui)
- `@/components/templates/*` — IntegratedListTemplateV2 등
- `@/components/organisms/*` — PageLayout, PageHeader
- `@/hooks/*` — usePermission, useModules 등
- `@/stores/authStore` — Tenant 정보
- `@/components/common/*` — 공통 컴포넌트
## Exports to Common ERP
**NONE** — 건설 모듈은 독립적으로 작동.
## Related Dashboard Sections
- `construction` (시공 현황)
## Subdirectories
- `bidding/` — 입찰 관리
- `contract/` — 계약 관리
- `estimates/` — 견적 관리
- `progress-billing/` — 기성 관리
- `site-management/` — 현장 관리
- `labor-management/` — 노무 관리
- `item-management/` — 자재 관리
- `partners/` — 협력업체 관리
- 기타 20개 하위 도메인

View File

@@ -0,0 +1,25 @@
# Production Module (생산관리)
**Module ID**: `production`
**Tenant**: Kyungdong (경동 셔터 MES)
**Route Prefixes**: `/production`
**Component Count**: 56 files
## Dependencies on Common ERP
- `@/lib/api/*` — Server actions, API client
- `@/components/ui/*` — UI primitives (shadcn/ui)
- `@/components/templates/*` — IntegratedListTemplateV2 등
- `@/components/organisms/*` — PageLayout, PageHeader
- `@/hooks/*` — usePermission, useModules 등
- `@/stores/authStore` — Tenant 정보
- `@/stores/menuStore` — 사이드바 상태
## Exports to Common ERP
**NONE** — Phase 0에서 모든 교차 참조 해소 완료.
- 타입: `@/lib/api/production-orders/types.ts` (re-export)
- 서버 액션: `@/lib/api/production-orders/actions.ts` (async wrapper)
- 모달: `@/components/document-system/modals/` (dynamic import wrapper)
## Related Dashboard Sections
- `production` (생산 현황)
- `shipment` (출고 현황)

View File

@@ -0,0 +1,21 @@
# Quality Module (품질관리)
**Module ID**: `quality`
**Tenant**: Kyungdong (경동 셔터 MES)
**Route Prefixes**: `/quality`
**Component Count**: 35 files
## Dependencies on Common ERP
- `@/lib/api/*` — Server actions, API client
- `@/components/ui/*` — UI primitives (shadcn/ui)
- `@/components/templates/*` — IntegratedListTemplateV2 등
- `@/components/organisms/*` — PageLayout, PageHeader
- `@/hooks/*` — usePermission, useModules 등
- `@/stores/authStore` — Tenant 정보
## Exports to Common ERP
**NONE** — Phase 0에서 교차 참조 해소 완료.
- 모달: `@/components/document-system/modals/` (WorkLogModal — dynamic import)
## Related Dashboard Sections
없음 (품질 대시보드 섹션은 아직 미구현)

View File

@@ -0,0 +1,20 @@
# Vehicle Management Module (차량관리)
**Module ID**: `vehicle-management`
**Tenant**: Optional (경동 + 주일 공통 선택)
**Route Prefixes**: `/vehicle-management`, `/vehicle`
**Component Count**: 13 files
## Dependencies on Common ERP
- `@/lib/api/*` — Server actions, API client
- `@/components/ui/*` — UI primitives (shadcn/ui)
- `@/components/templates/*` — IntegratedListTemplateV2 등
- `@/components/organisms/*` — PageLayout, PageHeader
- `@/hooks/*` — usePermission, useModules 등
- `@/stores/authStore` — Tenant 정보
## Exports to Common ERP
**NONE**
## Related Dashboard Sections
없음

View File

@@ -11,7 +11,7 @@ import {
getProductionOrders as _getProductionOrders,
getProductionOrderStats as _getProductionOrderStats,
getProductionOrderDetail as _getProductionOrderDetail,
} from '@/components/production/ProductionOrders/actions';
} from '@/components/production/ProductionOrders/actions'; // MODULE_SEPARATION_OK — 공유 액션 래퍼 (Phase 0)
import type { ProductionOrderListParams } from './types';
export async function getProductionOrders(params: ProductionOrderListParams) {

View File

@@ -19,4 +19,4 @@ export type {
ApiProductionWorkOrder,
ApiBomProcessGroup,
ApiBomItem,
} from '@/components/production/ProductionOrders/types';
} from '@/components/production/ProductionOrders/types'; // MODULE_SEPARATION_OK — 공유 인터페이스 (Phase 0)