740 lines
19 KiB
Markdown
740 lines
19 KiB
Markdown
# 템플릿 개발 워크플로우
|
|
|
|
> **위치**: `docs/extension/template-workflow.md`
|
|
> **관련 문서**: [template-basics.md](./template-basics.md) | [template-commands.md](./template-commands.md) | [template-development.md](../frontend/template-development.md)
|
|
|
|
---
|
|
|
|
## TL;DR (5초 요약)
|
|
|
|
```text
|
|
1. 필수 파일: template.json, routes.json, _base.json, errors/{404,403,500}.json
|
|
2. 빌드 설정: package.json, vite.config.ts (IIFE 빌드, terser 대신 esbuild)
|
|
3. 검증 순서: JSON 파싱 → layout_name 필수 → error_config → 에러 레이아웃 파일 존재
|
|
4. 컴포넌트: 템플릿에 실제 존재하는 것만 import (없는 컴포넌트 import 시 빌드 실패)
|
|
5. 설치: php artisan template:build → php artisan template:install → php artisan template:activate
|
|
```
|
|
|
|
---
|
|
|
|
## 목차
|
|
|
|
1. [개요](#개요)
|
|
2. [템플릿 생성 체크리스트](#템플릿-생성-체크리스트)
|
|
3. [Phase 1: 필수 메타데이터 파일](#phase-1-필수-메타데이터-파일)
|
|
4. [Phase 2: 레이아웃 파일](#phase-2-레이아웃-파일)
|
|
5. [Phase 3: 빌드 설정](#phase-3-빌드-설정)
|
|
6. [Phase 4: 컴포넌트 설정](#phase-4-컴포넌트-설정)
|
|
7. [Phase 5: 다국어 파일](#phase-5-다국어-파일)
|
|
8. [Phase 6: 빌드 및 설치](#phase-6-빌드-및-설치)
|
|
9. [검증 로직 이해](#검증-로직-이해)
|
|
10. [자주 발생하는 오류와 해결](#자주-발생하는-오류와-해결)
|
|
11. [템플릿 개발 Artisan 명령어](#템플릿-개발-artisan-명령어)
|
|
|
|
---
|
|
|
|
## 개요
|
|
|
|
이 문서는 그누보드7 템플릿을 처음부터 만들 때 필요한 **실전 워크플로우**를 정리합니다.
|
|
실제 `sirsoft-user_sample` 템플릿 개발 과정에서 겪은 시행착오를 바탕으로 작성되었습니다.
|
|
|
|
---
|
|
|
|
## 템플릿 생성 체크리스트
|
|
|
|
새 템플릿 생성 시 아래 체크리스트를 순서대로 완료합니다:
|
|
|
|
```
|
|
Phase 1: 필수 메타데이터
|
|
□ template.json 생성 (error_config 포함)
|
|
□ template.json externals 점검 (icon 컴포넌트 사용 시 Font Awesome 항목 필수)
|
|
□ routes.json 생성 (layout_name 필수)
|
|
|
|
Phase 2: 레이아웃 파일
|
|
□ layouts/_user_base.json 또는 layouts/_admin_base.json 생성
|
|
□ layouts/home.json (또는 dashboard.json) 생성
|
|
□ layouts/errors/404.json 생성
|
|
□ layouts/errors/403.json 생성
|
|
□ layouts/errors/500.json 생성
|
|
|
|
Phase 3: 빌드 설정
|
|
□ package.json 생성 (build 스크립트 포함)
|
|
□ vite.config.ts 생성 (IIFE 빌드, esbuild minify)
|
|
□ tsconfig.json 생성
|
|
□ tsconfig.node.json 생성
|
|
□ tailwind.config.js 생성
|
|
□ postcss.config.js 생성
|
|
|
|
Phase 4: 컴포넌트 설정
|
|
□ src/index.ts 생성 (실제 존재하는 컴포넌트만 import)
|
|
□ src/handlers/index.ts 생성
|
|
□ src/styles/main.css 생성
|
|
|
|
Phase 5: 다국어 파일
|
|
□ lang/ko.json 생성 (errors 섹션 포함)
|
|
□ lang/en.json 생성 (errors 섹션 포함)
|
|
|
|
Phase 6: 빌드 및 설치
|
|
□ php artisan template:build [identifier]
|
|
□ php artisan template:install [identifier]
|
|
□ php artisan template:activate [identifier]
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 1: 필수 메타데이터 파일
|
|
|
|
> `g7_version` / `dependencies` 등 버전 제약 작성 규칙은 [changelog-rules.md](changelog-rules.md#8-코어-버전-제약-정책) 참조.
|
|
|
|
### 1.1 template.json
|
|
|
|
```json
|
|
{
|
|
"identifier": "vendor-template_name",
|
|
"vendor": "vendor",
|
|
"name": {
|
|
"ko": "템플릿 한국어 이름",
|
|
"en": "Template English Name"
|
|
},
|
|
"version": "1.0.0",
|
|
"description": {
|
|
"ko": "템플릿 설명 (한국어)",
|
|
"en": "Template description (English)"
|
|
},
|
|
"type": "user",
|
|
"locales": ["ko", "en"],
|
|
"author": {
|
|
"name": "vendor",
|
|
"email": "contact@vendor.com"
|
|
},
|
|
"g7_version": ">=1.0.0",
|
|
"dependencies": {
|
|
"modules": {},
|
|
"plugins": {}
|
|
},
|
|
"assets": {
|
|
"css": ["assets/css/main.css"],
|
|
"js": []
|
|
},
|
|
"components": {
|
|
"basic": ["button", "div", "span", "h1", "h2", "p", "icon"],
|
|
"composite": ["header", "footer", "card"],
|
|
"layout": ["container", "flex", "grid"]
|
|
},
|
|
"error_config": {
|
|
"layouts": {
|
|
"404": "404",
|
|
"403": "403",
|
|
"500": "500"
|
|
}
|
|
},
|
|
"externals": [
|
|
{
|
|
"id": "fontawesome",
|
|
"type": "style",
|
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css",
|
|
"preconnect": "https://cdnjs.cloudflare.com"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**필수 항목**:
|
|
- `identifier`: 고유 식별자 (vendor-name 형식)
|
|
- `type`: "admin" 또는 "user"
|
|
- `locales`: 지원 언어 배열
|
|
- `error_config.layouts`: 404, 403, 500 매핑
|
|
|
|
**`externals` (선택, 단 icon 사용 시 사실상 필수)**:
|
|
|
|
- 최초 HTML 문서에 정적으로 필요한 외부 스타일·웹폰트·스크립트를 선언한다.
|
|
- `components.basic` 에 `icon` 을 등록하면 Icon 컴포넌트가 생성하는
|
|
`<i class="fas fa-...">` 를 렌더링할 Font Awesome CSS 가 반드시 필요하므로
|
|
위 `fontawesome` 항목을 함께 둔다. 누락 시 활성 템플릿에서 아이콘이 표시되지
|
|
않는다. icon 을 쓰지 않으면 `"externals": []` 로 두거나 필드를 생략한다.
|
|
- 속성/타입 상세: [template-basics.md "외부 리소스 (externals)"](template-basics.md)
|
|
|
|
### 1.1.1 hidden 필드 (선택)
|
|
|
|
`template.json` 에 `"hidden": true` 를 설정하면 관리자 UI 의 템플릿 목록에서 기본 제외됩니다. 학습용 샘플 템플릿, 내부 운영 전용 템플릿을 일반 사용자에게 감출 때 사용합니다.
|
|
|
|
- 제외 대상: 관리자 UI (`GET /api/admin/templates` 기본 응답)
|
|
- 제외 대상 아님: artisan CLI (`template:list`, `template:install`, `template:activate` 등), 설치/제거/업데이트 감지
|
|
- 슈퍼관리자는 "숨김 포함" 토글로 일시 조회 가능 (`?include_hidden=1`)
|
|
- artisan CLI 에서도 기본 목록에서는 숨기고, `php artisan template:list --hidden` 으로 숨김 포함 목록을 조회할 수 있습니다
|
|
- 사용 사례: 학습용 샘플 템플릿(예: `gnuboard7-hello_admin_template`, `gnuboard7-hello_user_template`), 내부 시연용 템플릿
|
|
|
|
### 1.2 routes.json
|
|
|
|
```
|
|
주의: layout_name 필드가 없으면 설치 실패!
|
|
```
|
|
|
|
```json
|
|
{
|
|
"version": "1.0.0",
|
|
"layout_name": "routes",
|
|
"meta": {
|
|
"title": "Routes",
|
|
"description": "Route definitions for template"
|
|
},
|
|
"data_sources": [],
|
|
"components": [],
|
|
"routes": [
|
|
{
|
|
"path": "/",
|
|
"layout": "home",
|
|
"auth": false
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**필수 필드**:
|
|
- `layout_name`: DB 저장용 레이아웃 이름 (필수!)
|
|
- `routes`: 라우트 정의 배열
|
|
|
|
---
|
|
|
|
## Phase 2: 레이아웃 파일
|
|
|
|
### 2.1 디렉토리 구조
|
|
|
|
```
|
|
templates/_bundled/vendor-template/
|
|
└── layouts/
|
|
├── _user_base.json # 베이스 레이아웃 (user 템플릿)
|
|
├── _admin_base.json # 베이스 레이아웃 (admin 템플릿)
|
|
├── home.json # 홈 레이아웃
|
|
├── routes.json # 라우트 정의
|
|
├── errors/ # 필수 디렉토리
|
|
│ ├── 404.json # 필수
|
|
│ ├── 403.json # 필수
|
|
│ └── 500.json # 필수
|
|
└── partials/ # partial 파일 (DB 저장 안됨)
|
|
```
|
|
|
|
### 2.2 베이스 레이아웃 (_user_base.json)
|
|
|
|
```json
|
|
{
|
|
"version": "1.0.0",
|
|
"layout_name": "_user_base",
|
|
"meta": {
|
|
"is_base": true
|
|
},
|
|
"data_sources": [],
|
|
"slots": {
|
|
"header": [],
|
|
"content": [],
|
|
"footer": []
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2.3 에러 레이아웃
|
|
|
|
**404.json 예시**:
|
|
```json
|
|
{
|
|
"version": "1.0.0",
|
|
"layout_name": "404",
|
|
"extends": "_user_base",
|
|
"meta": {
|
|
"title": "$t:errors.404.title",
|
|
"is_error_layout": true,
|
|
"error_code": 404
|
|
},
|
|
"data_sources": [],
|
|
"slots": {
|
|
"content": [
|
|
{
|
|
"id": "error_container",
|
|
"type": "basic",
|
|
"name": "Div",
|
|
"props": {
|
|
"className": "flex flex-col items-center justify-center min-h-[60vh] text-center p-8"
|
|
},
|
|
"children": [
|
|
{
|
|
"id": "error_code",
|
|
"type": "basic",
|
|
"name": "H1",
|
|
"props": {
|
|
"className": "text-6xl font-bold text-gray-900 dark:text-white mb-4"
|
|
},
|
|
"text": "404"
|
|
},
|
|
{
|
|
"id": "error_title",
|
|
"type": "basic",
|
|
"name": "H2",
|
|
"props": {
|
|
"className": "text-2xl font-semibold text-gray-700 dark:text-gray-300 mb-2"
|
|
},
|
|
"text": "$t:errors.404.title"
|
|
},
|
|
{
|
|
"id": "error_message",
|
|
"type": "basic",
|
|
"name": "P",
|
|
"props": {
|
|
"className": "text-gray-500 dark:text-gray-400 mb-8"
|
|
},
|
|
"text": "$t:errors.404.message"
|
|
},
|
|
{
|
|
"id": "back_button",
|
|
"type": "basic",
|
|
"name": "Button",
|
|
"props": {
|
|
"className": "px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg"
|
|
},
|
|
"text": "$t:errors.back_home",
|
|
"actions": [
|
|
{
|
|
"type": "click",
|
|
"handler": "navigate",
|
|
"params": { "path": "/" }
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
**403.json, 500.json**도 동일한 구조로 생성 (error_code와 아이콘만 다름)
|
|
|
|
---
|
|
|
|
## Phase 3: 빌드 설정
|
|
|
|
### 3.1 package.json
|
|
|
|
```json
|
|
{
|
|
"name": "vendor-template",
|
|
"version": "1.0.0",
|
|
"type": "module",
|
|
"scripts": {
|
|
"build": "vite build",
|
|
"dev": "vite build --watch"
|
|
},
|
|
"dependencies": {
|
|
"react": "^18.2.0",
|
|
"react-dom": "^18.2.0"
|
|
},
|
|
"devDependencies": {
|
|
"@types/react": "^18.2.0",
|
|
"@types/react-dom": "^18.2.0",
|
|
"@vitejs/plugin-react": "^4.0.0",
|
|
"autoprefixer": "^10.4.14",
|
|
"postcss": "^8.4.24",
|
|
"tailwindcss": "^3.3.2",
|
|
"typescript": "^5.0.0",
|
|
"vite": "^5.0.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3.2 vite.config.ts
|
|
|
|
```
|
|
주의: minify는 'esbuild' 사용 (terser는 별도 설치 필요)
|
|
필수: process.env.NODE_ENV 정의 (외부 라이브러리 호환성)
|
|
```
|
|
|
|
```typescript
|
|
import { defineConfig } from 'vite';
|
|
import react from '@vitejs/plugin-react';
|
|
import path from 'path';
|
|
|
|
export default defineConfig({
|
|
plugins: [react()],
|
|
define: {
|
|
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
},
|
|
build: {
|
|
lib: {
|
|
entry: path.resolve(__dirname, 'src/index.ts'),
|
|
name: 'VendorTemplate',
|
|
formats: ['iife'],
|
|
fileName: () => 'components.iife.js',
|
|
},
|
|
outDir: 'dist',
|
|
emptyDirBeforeWrite: true,
|
|
minify: 'esbuild', // terser 대신 esbuild 사용!
|
|
rollupOptions: {
|
|
external: ['react', 'react-dom'],
|
|
output: {
|
|
globals: {
|
|
react: 'React',
|
|
'react-dom': 'ReactDOM',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
### 3.3 tsconfig.json
|
|
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"target": "ES2020",
|
|
"useDefineForClassFields": true,
|
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
"module": "ESNext",
|
|
"skipLibCheck": true,
|
|
"moduleResolution": "bundler",
|
|
"allowImportingTsExtensions": true,
|
|
"resolveJsonModule": true,
|
|
"isolatedModules": true,
|
|
"noEmit": true,
|
|
"jsx": "react-jsx",
|
|
"strict": true,
|
|
"noUnusedLocals": true,
|
|
"noUnusedParameters": true,
|
|
"noFallthroughCasesInSwitch": true
|
|
},
|
|
"include": ["src"]
|
|
}
|
|
```
|
|
|
|
### 3.4 tailwind.config.js
|
|
|
|
```javascript
|
|
/** @type {import('tailwindcss').Config} */
|
|
export default {
|
|
content: ['./src/**/*.{js,ts,jsx,tsx}', './layouts/**/*.json'],
|
|
darkMode: 'class',
|
|
theme: { extend: {} },
|
|
plugins: [],
|
|
};
|
|
```
|
|
|
|
### 3.5 postcss.config.js
|
|
|
|
```javascript
|
|
export default {
|
|
plugins: {
|
|
tailwindcss: {},
|
|
autoprefixer: {},
|
|
},
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 4: 컴포넌트 설정
|
|
|
|
### 4.1 src/index.ts
|
|
|
|
```
|
|
필수: 실제 존재하는 컴포넌트만 import!
|
|
존재하지 않는 컴포넌트를 import하면 빌드 실패
|
|
```
|
|
|
|
```typescript
|
|
// 실제 존재하는 컴포넌트만 import
|
|
import * as basicComponents from './components/basic';
|
|
import * as compositeComponents from './components/composite';
|
|
import * as layoutComponents from './components/layout';
|
|
import { handlerMap } from './handlers';
|
|
|
|
// 전역 등록
|
|
declare global {
|
|
interface Window {
|
|
G7Core: {
|
|
registerComponents: (components: Record<string, unknown>) => void;
|
|
registerHandlers: (handlers: Record<string, unknown>) => void;
|
|
};
|
|
}
|
|
}
|
|
|
|
// 컴포넌트 등록
|
|
const allComponents = {
|
|
...basicComponents,
|
|
...compositeComponents,
|
|
...layoutComponents,
|
|
};
|
|
|
|
if (window.G7Core) {
|
|
window.G7Core.registerComponents(allComponents);
|
|
window.G7Core.registerHandlers(handlerMap);
|
|
}
|
|
|
|
export { allComponents, handlerMap };
|
|
```
|
|
|
|
### 4.2 src/handlers/index.ts
|
|
|
|
```typescript
|
|
export const handlerMap = {
|
|
// 커스텀 핸들러 등록
|
|
} as const;
|
|
|
|
export type HandlerName = keyof typeof handlerMap;
|
|
```
|
|
|
|
### 4.3 src/styles/main.css
|
|
|
|
```css
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
```
|
|
|
|
### 4.4 레이아웃 편집기 확장점 등록 (선택)
|
|
|
|
템플릿이 자기 컴포넌트의 커스텀 속성 위젯(예 아이콘 피커)·노드 에디터·캔버스 인플레이스 오버레이를 레이아웃 편집기에 추가하려면 `G7Core.layoutEditor` 확장점에 등록한다. 등록은 `src/index.ts` **최상위(module-load)에서 호출**한다 — `initTemplate`/`window.load` 게이트 안에 두면 편집기 직접 진입 시 위젯이 누락되는 회귀가 발생한다(코어 stub 예약 접수함이 큐로 보존하므로 module-load 시점 등록이 안전).
|
|
|
|
```typescript
|
|
import { registerMyTemplateEditorWidgets } from './layout-editor/registerEditorWidgets';
|
|
|
|
// index.ts 최상위 — initTemplate/window.load 밖
|
|
registerMyTemplateEditorWidgets();
|
|
```
|
|
|
|
```typescript
|
|
// layout-editor/registerEditorWidgets.ts
|
|
export function registerMyTemplateEditorWidgets(): void {
|
|
if (typeof window === 'undefined') return;
|
|
const le = (window as any).G7Core?.layoutEditor;
|
|
if (!le?.registerWidget) return; // stub 부재 → 큐가 보존
|
|
le.registerWidget('icon-picker', IconPickerWidget);
|
|
le.registerCanvasOverlay?.('tabnav', TabNavInplaceOverlay);
|
|
}
|
|
```
|
|
|
|
확장점 API·등록 시점·작성 규약 상세: [editor-spec.md "편집기 확장점"](editor-spec.md). identity launcher 등록(`G7Core.identity.setLauncher`)과 동일한 옵셔널 체이닝 가드 패턴이다.
|
|
|
|
---
|
|
|
|
## Phase 5: 다국어 파일
|
|
|
|
### 5.1 lang/ko.json
|
|
|
|
```
|
|
필수: errors 섹션 필수! (에러 레이아웃에서 참조)
|
|
```
|
|
|
|
```json
|
|
{
|
|
"site": {
|
|
"title": "사이트 제목"
|
|
},
|
|
"errors": {
|
|
"404": {
|
|
"title": "페이지를 찾을 수 없습니다",
|
|
"message": "요청하신 페이지가 존재하지 않거나 이동되었습니다."
|
|
},
|
|
"403": {
|
|
"title": "접근이 거부되었습니다",
|
|
"message": "이 페이지에 접근할 권한이 없습니다."
|
|
},
|
|
"500": {
|
|
"title": "서버 오류",
|
|
"message": "서버에 문제가 발생했습니다. 잠시 후 다시 시도해 주세요."
|
|
},
|
|
"back_home": "홈으로 돌아가기"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.2 lang/en.json
|
|
|
|
```json
|
|
{
|
|
"site": {
|
|
"title": "Site Title"
|
|
},
|
|
"errors": {
|
|
"404": {
|
|
"title": "Page Not Found",
|
|
"message": "The page you requested does not exist or has been moved."
|
|
},
|
|
"403": {
|
|
"title": "Access Denied",
|
|
"message": "You do not have permission to access this page."
|
|
},
|
|
"500": {
|
|
"title": "Server Error",
|
|
"message": "Something went wrong on our end. Please try again later."
|
|
},
|
|
"back_home": "Back to Home"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 6: 빌드 및 설치
|
|
|
|
### 6.1 빌드
|
|
|
|
```bash
|
|
php artisan template:build vendor-template
|
|
```
|
|
|
|
### 6.2 설치
|
|
|
|
```bash
|
|
php artisan template:install vendor-template
|
|
```
|
|
|
|
### 6.3 활성화
|
|
|
|
```bash
|
|
php artisan template:activate vendor-template
|
|
```
|
|
|
|
---
|
|
|
|
## 검증 로직 이해
|
|
|
|
### 설치 시 자동 검증 순서
|
|
|
|
1. **JSON 파싱 검증**: 모든 레이아웃 파일의 JSON 유효성
|
|
2. **layout_name 필수**: 템플릿 레이아웃은 반드시 `layout_name` 필드 필요
|
|
3. **error_config 검증**: `template.json`에 `error_config.layouts` 섹션 필수
|
|
4. **에러 레이아웃 검증**: 404, 403, 500 레이아웃 파일 존재 확인
|
|
5. **partial 병합 검증**: `partial` 참조 파일 존재 및 순환 참조 확인
|
|
|
|
### ValidatesLayoutFiles 트레이트
|
|
|
|
```php
|
|
// 스캔 대상 디렉토리
|
|
layouts/*.json // ✅ 스캔 (DB 저장)
|
|
layouts/errors/*.json // ✅ 스캔 (DB 저장)
|
|
layouts/partials/*.json // ❌ 스캔 안 함 (extends로만 참조)
|
|
```
|
|
|
|
### TemplateManager::validateErrorLayouts()
|
|
|
|
```php
|
|
// 필수 에러 코드
|
|
$requiredErrorCodes = [404, 403, 500];
|
|
|
|
// 검증 항목
|
|
1. error_config.layouts 섹션 존재
|
|
2. 404, 403, 500 키 존재
|
|
3. layouts/errors/{code}.json 파일 존재
|
|
```
|
|
|
|
---
|
|
|
|
## 자주 발생하는 오류와 해결
|
|
|
|
### 오류 1: layout_name 필드 누락
|
|
|
|
```
|
|
❌ 오류: layout_name 필드가 누락되었습니다
|
|
```
|
|
|
|
**원인**: routes.json 또는 레이아웃 파일에 `layout_name` 필드 없음
|
|
|
|
**해결**:
|
|
```json
|
|
{
|
|
"layout_name": "routes", // 추가
|
|
// ... 나머지 내용
|
|
}
|
|
```
|
|
|
|
### 오류 2: 에러 레이아웃 파일 없음
|
|
|
|
```
|
|
❌ 오류: 404 에러 레이아웃 파일을 찾을 수 없습니다
|
|
```
|
|
|
|
**원인**: `layouts/errors/404.json` 파일 없음
|
|
|
|
**해결**:
|
|
1. `layouts/errors/` 디렉토리 생성
|
|
2. `404.json`, `403.json`, `500.json` 파일 생성
|
|
|
|
### 오류 3: terser not found
|
|
|
|
```
|
|
❌ 오류: Cannot find module 'terser'
|
|
```
|
|
|
|
**원인**: vite.config.ts에서 `minify: 'terser'` 설정했지만 terser 미설치
|
|
|
|
**해결**:
|
|
```typescript
|
|
// vite.config.ts
|
|
build: {
|
|
minify: 'esbuild', // terser 대신 esbuild 사용
|
|
}
|
|
```
|
|
|
|
### 오류 4: 컴포넌트 import 실패
|
|
|
|
```
|
|
❌ 오류: Module not found: './components/basic/H5'
|
|
```
|
|
|
|
**원인**: 존재하지 않는 컴포넌트를 import
|
|
|
|
**해결**: 실제 존재하는 컴포넌트만 import
|
|
```typescript
|
|
// ❌ 잘못된 예
|
|
import { H5, H6, Box } from './components/basic'; // H5, H6, Box가 없으면 에러
|
|
|
|
// ✅ 올바른 예
|
|
import { H1, H2, H3, Div, Span } from './components/basic'; // 실제 존재하는 것만
|
|
```
|
|
|
|
### 오류 5: error_config 누락
|
|
|
|
```
|
|
❌ 오류: 에러 페이지 설정(error_config)이 누락되었습니다
|
|
```
|
|
|
|
**원인**: `template.json`에 `error_config` 섹션 없음
|
|
|
|
**해결**:
|
|
```json
|
|
{
|
|
"error_config": {
|
|
"layouts": {
|
|
"404": "404",
|
|
"403": "403",
|
|
"500": "500"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 템플릿 개발 Artisan 명령어
|
|
|
|
| 명령어 | 설명 |
|
|
|--------|------|
|
|
| `php artisan template:list` | 템플릿 목록 조회 |
|
|
| `php artisan template:build [id]` | 템플릿 빌드 |
|
|
| `php artisan template:build --all` | 모든 템플릿 빌드 |
|
|
| `php artisan template:install [id]` | 템플릿 설치 |
|
|
| `php artisan template:activate [id]` | 템플릿 활성화 |
|
|
| `php artisan template:deactivate [id]` | 템플릿 비활성화 |
|
|
| `php artisan template:uninstall [id]` | 템플릿 삭제 |
|
|
| `php artisan template:refresh-layout [id]` | 레이아웃 갱신 (빌드 없이) |
|
|
| `php artisan template:cache-clear` | 템플릿 캐시 초기화 |
|
|
|
|
---
|
|
|
|
## 관련 문서
|
|
|
|
- [템플릿 기초](./template-basics.md) - 템플릿 타입, 메타데이터
|
|
- [템플릿 커맨드](./template-commands.md) - Artisan 커맨드 상세
|
|
- [템플릿 개발 가이드](../frontend/template-development.md) - 컴포넌트 개발
|
|
- [레이아웃 JSON 스키마](../frontend/layout-json.md) - 레이아웃 작성법
|
|
- [컴포넌트 개발 규칙](../frontend/components.md) - 컴포넌트 작성법
|