# 훅 패턴 검증 (validate-hook)

훅 시스템 및 **작업계획서 내 훅 관련 코드 블록**이 그누보드7 규정을 준수하는지 검증합니다.

## 0단계: 검증 대상 유형 판별 (CRITICAL)

```text
⚠️ CRITICAL: 검증 대상이 소스코드인지 작업계획서인지 먼저 판별
```

### 판별 기준

| 파일 확장자 | 유형             | 검증 방식                                      |
| ----------- | ---------------- | ---------------------------------------------- |
| `*.php`     | 소스코드         | 파일 전체를 훅/리스너 코드로 검증              |
| `*.md`      | **작업계획서**   | 마크다운 내 PHP 코드 블록 추출 후 검증         |

### 작업계획서 검증 시 추가 단계

작업계획서(`.md`) 파일인 경우:

1. **코드 블록 추출**: 마크다운에서 \`\`\`php 코드 블록 추출
2. **훅 관련 코드 식별**: `HookManager`, `doAction`, `applyFilters`, `Listener` 등의 키워드로 식별
3. **규정 적용**: hooks.md의 훅 시스템 규칙 적용

```text
⚠️ 작업계획서 검증 시 주의사항:
- 코드 블록이 예시/설명 목적인지, 실제 구현 계획인지 구분
- "예시:", "Example:", "// 예시" 등이 포함된 코드는 참고용으로 처리
- 실제 구현 계획 코드만 엄격하게 검증
```

## 1단계: 규정 문서 읽기

다음 규정 문서를 읽어 최신 규칙을 확인합니다:

- `docs/extension/hooks.md` - 훅 시스템 규칙
- `docs/backend/service-repository.md` - Service에서의 훅 실행
- `docs/extension/plugin-development.md` - 플러그인 격리 원칙

## 2단계: 검증 대상 파일 읽기

$ARGUMENTS 경로의 파일을 읽습니다.

경로가 지정되지 않은 경우, 다음 파일들을 대상으로 합니다:

- `**/Listeners/**/*.php` - 훅 리스너
- `app/Services/**/*.php` - 훅 실행 서비스
- `modules/_bundled/**/src/Services/**/*.php` - _bundled 모듈 서비스
- `modules/_bundled/**/src/Listeners/**/*.php` - _bundled 모듈 리스너
- `plugins/_bundled/**/src/Listeners/**/*.php` - _bundled 플러그인 리스너

## 3단계: 규정 기반 자동 검증 (CRITICAL)

```text
⚠️ CRITICAL: 1단계에서 읽은 규정 문서의 모든 규칙을 검증 항목으로 사용합니다.
스킬에 하드코딩된 검증 항목이 아닌, 규정 문서가 Single Source of Truth입니다.
```

### 3.1 규정에서 검증 패턴 추출

1단계에서 읽은 모든 규정 문서에서 다음 패턴을 추출합니다:

| 추출 대상 | 검증 유형 |
| --------- | --------- |
| `❌` 또는 `잘못된` 키워드가 포함된 코드 블록 | **금지 패턴** - 발견 시 에러 |
| `✅` 또는 `올바른` 키워드가 포함된 코드 블록 | **필수 패턴** - 누락 시 경고 |
| `⚠️ 절대 금지`, `CRITICAL` 등 강조된 규칙 | **필수 검증 항목** |
| `TL;DR` 섹션의 핵심 포인트 | **우선 검증 항목** |

### 3.2 검증 방법

**금지 패턴 검증**:

```bash
# 규정 문서의 ❌ 예시에서 추출한 패턴을 grep으로 검사
grep -rn "[금지 패턴]" [대상 파일/디렉토리]
```

발견 시: 에러로 보고하고 규정 문서의 해당 섹션 참조 안내

**필수 패턴 검증**:

- 해당 컨텍스트에서 필수 패턴이 사용되어야 하는 경우, 누락 여부 확인
- 누락 시: 경고로 보고하고 규정 문서의 올바른 예시 안내

### 3.3 훅 특화 검증

규정 문서의 훅 관련 섹션에서 추출한 규칙으로 다음을 검증합니다:

1. **훅 네이밍**: `[vendor-module].[entity].[action]_[timing]` 패턴 준수 여부
2. **훅 실행 순서 (Service)**: before → filter → action → after 순서
3. **Listener 인터페이스**: `HookListenerInterface` 구현 여부
4. **Filter 훅 type 필드**: `'type' => 'filter'` 명시 여부
5. **Filter 훅 리턴 값**: 값 반환 여부

### 3.4 파일 유형별 규정 매핑

검증 대상 파일의 경로에 따라 해당하는 규정 문서를 우선적으로 적용합니다:

| 파일 경로 패턴 | 우선 적용 규정 |
| -------------- | -------------- |
| `**/Listeners/**` | hooks.md |
| `**/Services/**` | service-repository.md, hooks.md |

### 3.5 핵심 규칙 검증

규정 문서에 명시된 절대 규칙을 검증합니다.

#### 3.5.1 _bundled 디렉토리 작업 원칙

```text
⚠️ CRITICAL: 확장 수정/개발은 반드시 _bundled 디렉토리에서만 작업
→ 활성 디렉토리 직접 수정 절대 금지
```

**검증 항목**:

- 수정 대상 파일이 `_bundled/` 하위에 있는지 확인
- 활성 디렉토리(`modules/vendor-xxx/`, `plugins/vendor-xxx/`)의 파일 수정 시 경고

#### 3.5.2 플러그인 격리 원칙

```text
⚠️ CRITICAL: 플러그인 간 직접 의존 금지 → 모듈에만 의존, 플러그인끼리는 API로만 통신
```

| ❌ 금지 | ✅ 올바른 사용 |
| -------- | --------------- |
| 플러그인 A → 플러그인 B 직접 참조 | 플러그인 A → 모듈 API → 플러그인 B |
| 플러그인에서 다른 플러그인 클래스 import | 훅/이벤트 또는 API 통신 |

**검증 항목**:

- 플러그인 코드에서 다른 플러그인의 네임스페이스(`Plugins\`) 참조 여부 확인
- 플러그인에서 모듈(`Modules\`) 의존만 허용

## 4단계: 결과 보고

검증 결과를 다음 형식으로 보고합니다:

```text
## 훅 패턴 검증 결과

### 검증 파일
- [파일 경로]

### [규정 문서명] - [섹션명] 검증 결과

#### 금지 패턴 검사
- ❌ 금지 패턴 발견: [패턴] (파일:라인)
  - 규정: [규정 문서명] > [섹션명]
  - 수정 방법: [올바른 패턴으로 변경]
- ✅ 금지 패턴 없음

#### 필수 패턴 검사
- ❌ 필수 패턴 누락: [컨텍스트]에서 [패턴] 필요
  - 규정: [규정 문서명] > [섹션명]
- ✅ 필수 패턴 준수

#### 훅 네이밍 검증
- ✅ 올바름: [훅 이름]
- ❌ 잘못됨: [훅 이름] → [수정 제안]

#### Filter 훅 type 필드 검증
- ✅ type 명시됨: [훅 이름]
- ❌ type 누락: [훅 이름] → 'type' => 'filter' 추가 필요
```

---

## 5단계: 작업계획서 전용 검증 (마크다운 파일인 경우)

검증 대상이 작업계획서(`.md`)인 경우, 다음 추가 검증을 수행합니다.

### 5.1 훅 관련 코드 블록 식별

다음 키워드가 포함된 코드 블록을 훅 관련 코드로 식별:

| 키워드                          | 용도                    |
| ------------------------------- | ----------------------- |
| `HookManager::doAction`         | Action Hook 실행        |
| `HookManager::applyFilters`     | Filter Hook 실행        |
| `implements HookListenerInterface` | 리스너 클래스 정의   |
| `getHooks()`                    | 훅 등록 정의            |
| `'type' => 'filter'`            | Filter Hook 타입 명시   |

### 5.2 훅 네이밍 검증

```php
// ✅ 올바른 패턴
HookManager::doAction('sirsoft-ecommerce.product.before_create', $data);
HookManager::doAction('sirsoft-ecommerce.product.after_create', $result);
$data = HookManager::applyFilters('sirsoft-ecommerce.product.filter_create_data', $data);

// ❌ 잘못된 패턴 - 네이밍 규칙 위반
HookManager::doAction('product.create', $data);  // ❌ vendor-module 누락
HookManager::doAction('beforeCreateProduct', $data);  // ❌ 잘못된 형식
```

**네이밍 규칙**: `[vendor-module].[entity].[action]_[timing]`

**검증 항목**:

- ❌ vendor-module 접두사 누락
- ❌ entity 누락
- ❌ action_timing 형식 위반
- ✅ 올바른 네이밍 패턴

### 5.3 훅 실행 순서 검증 (Service 코드)

```php
// ✅ 올바른 순서: before → filter → action → after
public function create(array $data): Product
{
    // 1. before
    HookManager::doAction('sirsoft-ecommerce.product.before_create', $data);

    // 2. filter
    $data = HookManager::applyFilters('sirsoft-ecommerce.product.filter_create_data', $data);

    // 3. action
    $result = $this->productRepository->create($data);

    // 4. after
    HookManager::doAction('sirsoft-ecommerce.product.after_create', $result);

    return $result;
}

// ❌ 잘못된 순서
public function create(array $data): Product
{
    $result = $this->productRepository->create($data);  // ❌ 훅 없이 바로 실행
    HookManager::doAction('sirsoft-ecommerce.product.before_create', $data);  // ❌ 순서 잘못됨
    return $result;
}
```

### 5.4 Listener 정의 검증

```php
// ✅ 올바른 패턴
class ProductCacheListener implements HookListenerInterface
{
    public function getHooks(): array
    {
        return [
            [
                'hook' => 'sirsoft-ecommerce.product.after_create',
                'method' => 'clearProductCache',
                'priority' => 10,
            ],
            [
                'hook' => 'sirsoft-ecommerce.product.filter_list_data',
                'method' => 'addCategoryInfo',
                'priority' => 10,
                'type' => 'filter',  // ✅ Filter Hook은 type 명시 필수
            ],
        ];
    }

    public function addCategoryInfo(array $data): array
    {
        // ... 데이터 변형
        return $data;  // ✅ Filter Hook은 반드시 값 반환
    }
}

// ❌ 잘못된 패턴 - Filter Hook인데 type 누락
[
    'hook' => 'sirsoft-ecommerce.product.filter_list_data',
    'method' => 'addCategoryInfo',
    'priority' => 10,
    // ❌ 'type' => 'filter' 누락
],

// ❌ 잘못된 패턴 - Filter Hook인데 반환값 없음
public function addCategoryInfo(array $data): void  // ❌ void 반환
{
    // ...
}
```

**검증 항목**:

- ❌ HookListenerInterface 미구현
- ❌ Filter Hook에 `'type' => 'filter'` 누락
- ❌ Filter Hook 메서드가 값을 반환하지 않음
- ✅ 올바른 리스너 구조

### 5.5 Filter Hook 반환값 검증

```php
// ✅ 올바른 패턴
public function filterProductData(array $data): array
{
    $data['calculated_price'] = $data['price'] * 1.1;
    return $data;  // ✅ 반드시 반환
}

// ❌ 잘못된 패턴
public function filterProductData(array $data): void  // ❌ void
{
    $data['calculated_price'] = $data['price'] * 1.1;
    // 반환값 없음
}
```

### 5.6 작업계획서 검증 결과 보고 형식

```text
## 작업계획서 훅 패턴 검증 결과

### 검증 파일
- [마크다운 파일 경로]

### 훅 코드 블록 요약
- 훅 실행 코드 블록: X개
- 리스너 정의 블록: Y개
- 총 검증 대상: Z개

### 훅 네이밍 검증
- ✅ 올바른 네이밍: [훅 이름]
- ❌ 잘못된 네이밍: [훅 이름] (코드 블록 위치)
  - 규칙: [vendor-module].[entity].[action]_[timing]
  - 수정 제안: [올바른 이름]

### 훅 실행 순서 검증
- ✅ before → filter → action → after 순서 준수
- ❌ 순서 위반: [위치] - [문제 설명]

### Listener 정의 검증
- ✅ HookListenerInterface 구현
- ❌ Interface 미구현: [위치]

### Filter Hook type 필드 검증
- ✅ type 명시됨: [훅 이름]
- ❌ type 누락: [훅 이름] (코드 블록 위치)
  - 수정: 'type' => 'filter' 추가 필요

### Filter Hook 반환값 검증
- ✅ 반환값 있음: [메서드명]
- ❌ 반환값 없음: [메서드명] (코드 블록 위치)
  - void 대신 array/mixed 반환 필요
```

---

## 핵심 원칙

```text
⚠️ CRITICAL:
- 규정 문서가 Single Source of Truth - 스킬에 검증 항목 하드코딩 금지
- 규정 문서의 ❌/✅ 예시를 자동으로 검증 패턴으로 사용
- 규정이 변경되면 검증도 자동으로 변경됨
- 파일 유형에 따라 해당 규정 문서 우선 적용
- 작업계획서(.md) 검증 시 코드 블록 추출 후 동일한 규정 적용
- 모든 확장 작업은 _bundled 디렉토리에서만 수행 (활성 디렉토리 직접 수정 금지)
- 플러그인 간 직접 의존 금지 → 모듈에만 의존
```
