LangENKO

Guard

불변식(invariant) 계층 — Mandu 정적 분석기가 강제하는 모든 규칙, 각 규칙의 의미, 위반 해결 방법.

since v0.22
On this page

Guard

Guard는 에이전트가 당신의 레포에 커밋하면서도 불태워먹지 않을 수 있는 이유 입니다. 빌드 타임(그리고 dev에서는 매 저장마다)에 실행되는 정적 분석기로, import를 훑고 아키텍처 계약을 위반하는 것은 거부합니다.

이 페이지는 불변식 카탈로그입니다. Guard가 소리를 지른다면, 해결책은 여기 있습니다.

Guard가 실제로 하는 일

감시 대상 파일(.ts, .tsx, .js, .jsx, .mjs, .cjs)마다 Guard는 모든 import, import(), require() 문을 추출하고(출처: guard/analyzer.ts:64-132), 각각을 레이어로 해석한 뒤 활성화된 프리셋의 레이어 그래프와 대조합니다.

여섯 가지 위반 패밀리가 발생할 수 있습니다 (출처: guard/types.ts:191-197):

타입 의미
layer-violation import가 금지된 레이어 경계를 넘음
circular-dependency A → B → A 감지
cross-slice 같은 레이어, 다른 슬라이스를 직접 import
deep-nesting 슬라이스의 public API를 우회해 깊숙이 접근
file-type 스캔 대상 레이어 안에 .js 또는 .jsx 파일 존재
invalid-shared-segment src/shared/ 하위 경로가 허용 목록에 없음

그 위에 FS-routes, contract/slot 규칙이 추가로 겹칩니다 (아래 참고).

레이어 모델

각 레이어는 { name, pattern, canImport, description } 튜플입니다 (출처: guard/types.ts:116-128). Mandu 프리셋은 client/widgets, client/features, server/infra, shared/contracts, shared/utils/client, shared/utils/server 같은 레이어를 정의합니다. 파일은 각 레이어의 glob 패턴과 경로가 매칭되어 레이어에 할당됩니다 (출처: guard/analyzer.ts:152-166).

import는 다음 조건에서만 허용됩니다:

importedLayer === importingLayer
  OR
importedLayer ∈ importingLayer.canImport

이 검사 전에 두 가지 예외가 먼저 적용됩니다:

  1. shared/env는 서버 전용. 레이어가 client/로 시작하는 파일에서 shared/env를 import하면 심각도 error로 거부됩니다 (출처: guard/validator.ts:277-287).
  2. 같은 레이어, 다른 슬라이스 — 레이어의 public API로 해석되는 import는 허용; 직접 깊이 들어가는 import는 cross-slice 발동.

불변식 카탈로그

layer-violation

발동 조건: importing 레이어의 canImport 리스트에 imported 레이어가 없음.

// src/client/widgets/chart/Chart.tsx
import { db } from "@/server/infra/db"; // ✗ client/widgets는 server/infra import 불가

해결: 서버 사이드 작업을 API 라우트 뒤로 옮기거나, src/shared/contracts 아래에 공유 contract/schema를 노출하세요.

circular-dependency

발동 조건: A.tsB.ts를 import하고 B.tsA.ts를 import (직접 사이클 감지, guard/validator.ts:624-685).

해결: 공유되는 부분을 제3의 모듈로 추출하거나, 의존성 역전으로 사이클을 끊으세요.

cross-slice

발동 조건: 같은 레이어, 다른 슬라이스, 직접 import. 예: 슬라이스 chart/의 위젯이 슬라이스 table/의 내부를 import.

해결: 슬라이스의 public API(대개 index.ts)를 통해 import하거나, 공유 조각을 두 슬라이스 모두 import 가능한 레이어로 옮기세요.

deep-nesting

발동 조건: import가 슬라이스의 public API를 건너뛰어 깊숙이 접근 (예: @/client/widgets/chart/internal/helpers).

해결: 필요한 심볼을 슬라이스의 index.ts에서 export하세요.

file-type

발동 조건: 파일 확장자가 .js 또는 .jsx (출처: guard/validator.ts:233-237).

Guard  FAIL  src/client/utils/legacy.js:1
  Rule: TypeScript Only
  Fix: .ts 또는 .tsx로 리네임하고 타입을 추가하세요.

해결: 리네임하고 타이핑. 예외 없음.

invalid-shared-segment

발동 조건: src/shared/ 아래 파일이 허용 목록에 없는 세그먼트에 있음 (출처: guard/validator.ts:155-188).

허용 구조:

경로 용도
src/shared/contracts/ 또는 src/shared/contracts.ts 서버/클라이언트 공유 API contract
src/shared/schema/ 또는 src/shared/schema.ts Zod 스키마, DB 스키마
src/shared/types/ 또는 src/shared/types.ts 순수 TypeScript 타입
src/shared/utils/client/ 클라이언트에서 안전한 유틸
src/shared/utils/server/ Node/Bun API가 필요한 유틸
src/shared/env/ 또는 src/shared/env.ts 환경 설정 (서버 전용 판독기)

그 외 — src/shared/components/, src/shared/lib/, src/shared/helpers/ — 는 거부됩니다.

해결: 올바른 버킷을 고르세요. 맞는 게 없다면 shared에 있을 파일이 아닙니다.

FS-routes 규칙

app/** 하위 파일에만 적용되는 추가 불변식 (출처: guard/validator.ts:392-498):

파일 import 가능 레이어 (fsRoutes 설정이 있을 때)
app/**/page.tsx fsRoutes.pageCanImport 레이어만
app/**/layout.tsx fsRoutes.layoutCanImport 레이어만
app/**/route.ts fsRoutes.routeCanImport 레이어만

그리고 설정과 무관하게 항상 적용되는 강제 규칙:

  • noPageToPage: page.tsx는 다른 page.tsx를 import할 수 없음 — 상대 경로, 별칭, app/ 경유 모두 불가 (출처: guard/validator.ts:548-583).

심각도와 설정

각 위반 타입에는 설정 가능한 심각도가 있습니다 (출처: guard/types.ts:34-47):

// mandu.config.ts
export default {
  guard: {
    severity: {
      layerViolation: "error",        // 기본값
      circularDependency: "warn",     // 기본값
      deepNesting: "info",            // 기본값
      crossSliceDependency: "warn",   // 기본값
      fileType: "error",              // 기본값
      invalidSharedSegment: "error",  // 기본값
    },
  },
};

"error"는 빌드 실패. "warn"은 리포터에 노출되지만 실패하지 않음. "info"는 로그만 남김.

에러 코드 → 해결 매핑

규칙 ID 패밀리 표준 해결
layer-violation imports 타겟 레이어를 소스 레이어의 canImport에 추가하거나 허용된 레이어 경유
circular-dependency imports 공유 코드를 제3의 모듈로 추출
cross-slice imports 슬라이스의 public API를 통해 import
deep-nesting imports 심볼을 index.ts에서 export
file-type file .js/.jsx.ts/.tsx로 리네임
invalid-shared-segment file 허용된 src/shared/* 버킷으로 이동
FORBIDDEN_IMPORT_IN_GENERATED codegen 생성된 파일에서 금지된 fs/child_process 등 import 제거
SLOT_MISSING_DEFAULT_EXPORT slots export default Mandu.filling(...) 추가
SLOT_MISSING_FILLING_PATTERN slots 핸들러를 Mandu.filling()으로 래핑
SLOT_ZOD_DIRECT_IMPORT slots 스키마를 src/shared/schema에서 import (zod 직접 X)
ISLAND_FIRST_INTEGRITY island page 옆에 .island.tsx 생성; page.tsxisland() import 금지
CLIENT_MODULE_NOT_FOUND island 라우트의 clientModule 경로가 없음 — spec 재생성 또는 파일 추가
CONTRACT_NOT_FOUND contract spec이 가리키는 contract 파일 생성
CONTRACT_METHOD_NOT_IMPLEMENTED contract 누락된 메서드를 slot에 구현

안티패턴

이건 하지 마세요.

  1. 빌드를 통과시키려고 Guard 비활성화. 위반이 곧 신호입니다. import를 고치거나 파일을 옮기세요 — 심각도를 낮추지 마세요.
  2. 허용 목록에 없는 새 src/shared/utils/ 버킷 만들기. 허용 목록은 의도적으로 짧습니다. 다른 카테고리가 필요하다면 아키텍처 대화를 먼저 하세요.
  3. island 관련 에러를 덮으려고 "use client" 추가. island 계약은 파일명 AND 지시어를 봅니다. 어느 한쪽이 다른 쪽을 대신하지 못합니다.

🤖 에이전트 프롬프트

🤖 Agent Prompt — Guard
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/architect/guard.ko to my project.

Summary of the page:
Guard는 빌드 타임 정적 분석기. 여섯 가지 불변식 패밀리를 강제한다: layer-violation, circular-dependency, cross-slice, deep-nesting, file-type, invalid-shared-segment. 또한 FS-routes 규칙(page/layout/route import whitelist)과 contract 규칙(slot/contract export)도 실행한다. 심각도(severity)는 설정 가능하며 'error'는 빌드를 실패시킨다. 각 위반은 filePath, line, column, ruleName, ruleDescription, suggestions를 포함한다.

Required invariants — must hold after your changes:
- 스캔 대상 레이어 내의 .js 또는 .jsx 파일은 file-type 위반이다
- src/shared는 {contracts, schema, types, utils/client, utils/server, env}로 제한된다. 다른 세그먼트는 invalid-shared-segment
- 클라이언트 레이어 파일은 shared/env를 import할 수 없다 (서버 전용)
- page.tsx는 다른 page.tsx를 import할 수 없다 (noPageToPage 규칙)
- 레이어 import는 importing 레이어의 canImport 리스트에 있어야 한다
- 같은 레이어 내 다른 슬라이스 간 직접 import는 public API 경유가 아닌 한 거부된다

Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
   in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.

For Agents

AI hint

Guard는 빌드 타임 정적 분석기. 여섯 가지 불변식 패밀리를 강제한다: layer-violation, circular-dependency, cross-slice, deep-nesting, file-type, invalid-shared-segment. 또한 FS-routes 규칙(page/layout/route import whitelist)과 contract 규칙(slot/contract export)도 실행한다. 심각도(severity)는 설정 가능하며 'error'는 빌드를 실패시킨다. 각 위반은 filePath, line, column, ruleName, ruleDescription, suggestions를 포함한다.

Invariants
  • 스캔 대상 레이어 내의 .js 또는 .jsx 파일은 file-type 위반이다
  • src/shared는 {contracts, schema, types, utils/client, utils/server, env}로 제한된다. 다른 세그먼트는 invalid-shared-segment
  • 클라이언트 레이어 파일은 shared/env를 import할 수 없다 (서버 전용)
  • page.tsx는 다른 page.tsx를 import할 수 없다 (noPageToPage 규칙)
  • 레이어 import는 importing 레이어의 canImport 리스트에 있어야 한다
  • 같은 레이어 내 다른 슬라이스 간 직접 import는 public API 경유가 아닌 한 거부된다
Guard scope
guard