# YOUELBLOCKS 개발자 노트

## 📋 **프로젝트 개요**

YOUELBLOCKS는 WordPress 블록 에디터(Gutenberg)를 활용한 현대적인 폼 빌더 플러그인입니다.
PHP 7.4+와 WordPress 5.0+를 기반으로 설계되어 최신 웹 기술을 완전히 활용합니다.

**현재 버전**: 6.4.5 (2025-01-27)
**마지막 업데이트**: 코드 모듈화 완료 및 중복 블록 등록 문제 해결

## 🚨 **버전 6.4.5 - 코드 모듈화 완료 및 중복 블록 등록 문제 해결**

### **주요 변경사항**
- **완전한 코드 모듈화**: 기존 `block-editor.js` (2,168줄)를 5개의 모듈로 분리
- **중복 블록 등록 문제 해결**: `block-editor.js` 완전 제거로 중복 등록 방지
- **기능 누락 없는 완벽한 분리**: 모든 기능이 원본과 동일하게 작동

### **새로운 파일 구조**
```
assets/js/
├── block-editor-core.js          # 핵심 설정 및 초기화
├── utils/
│   └── field-utils.js           # 필드 유틸리티 (FieldNameUtils, getIcon 등)
└── blocks/
    ├── integrated-form.js        # 통합폼 블록
    ├── field-blocks.js          # 기본 필드 블록들
    └── field-special.js         # 특수 필드 블록들 (email, select, multiselect 등)
```

### **분리된 기능들**
1. **`FieldNameUtils`** → `field-utils.js`
2. **`getAllowedBlocks`** → `integrated-form.js`  
3. **`openPostcodeService`** → `field-utils.js`
4. **자동 필드명 생성 시스템** → `block-editor-core.js`
5. **페이지 ID 설정 시스템** → `block-editor-core.js`
6. **`getIcon`** → `field-utils.js`
7. **`presets`** → `integrated-form.js`
8. **`fieldTypes`** → `field-utils.js`
9. **`userCanManageOptions`** → `integrated-form.js`
10. **CSS 스타일 로드** → `block-editor-core.js`

### **해결된 문제들**
- ✅ **중복 블록 등록**: `block-editor.js` 완전 제거
- ✅ **필드 타입 중복**: `select`를 `fieldTypes`에서 제거
- ✅ **presets 중복**: `"form-full"` 중복 정의 제거
- ✅ **FieldNameUtils 형식**: 원본과 동일한 `p${pageId}d${dateStr}c${columnStr}` 형식 복원
- ✅ **통합폼이 없는 경우 처리**: 전체 페이지 검사 로직 추가

### **검증 결과**
- ✅ 모든 블록이 정상적으로 등록됨
- ✅ 필드명 자동 생성 시스템 정상 작동
- ✅ 우편번호 서비스 정상 작동
- ✅ 통합폼 블록 검증 오류 해결
- ✅ WordPress 6.4+ 호환성 유지

---

## 🚨 **버전 6.4.4 - 이메일 및 다중선택 필드 검증 오류 해결**

### **문제 상황**
WordPress 6.4+ 블록 검증 시스템에서 이메일 필드와 다중선택 필드가 지속적으로 검증 오류를 발생시켰습니다.

#### **이메일 필드 문제:**
```
Block validation failed for `youelblocks/field-email`
Content generated by `save` function: <select name="p689d0725c004_domain">
Content retrieved from post body: <select name="p689d0725c004_domain" defaultvalue="gmail.com">
```

#### **다중선택 필드 문제:**
```
Block validation failed for `youelblocks/field-multiselect`
Content generated by `save` function: <input type="multiselect">
Content retrieved from post body: <select name="p689d0725c010[]" multiple>
```

### **근본 원인 분석**

#### **1. JavaScript 클로저 문제**
```javascript
// 문제가 있던 코드
specialFieldTypes.forEach(function (fieldConfig) {
  registerBlockType(`youelblocks/field-${fieldConfig.type}`, {
    save: function (props) {
      // fieldConfig.type이 항상 마지막 값(주소)을 참조
      const currentFieldType = fieldConfig.type; // ❌ 클로저 문제
    }
  });
});
```

#### **2. 필드 타입 중복 등록**
```javascript
// field-utils.js에서 중복 정의
CTMFieldUtils.fieldTypes = [
  { type: "multiselect", label: "다중선택" }, // ❌ 중복
  // ...
];

CTMFieldUtils.specialFieldTypes = [
  { type: "multiselect", label: "다중선택" }, // ❌ 중복
  // ...
];
```

#### **3. HTML 구조 불일치**
- **edit 함수**: `select[multiple]` 출력
- **save 함수**: `input[type="multiselect"]` 출력
- **WordPress 기대값**: `select[multiple]`

#### **4. React 속성 변환 문제**
```javascript
// 문제가 있던 코드
el("option", {
  selected: optionValue === defaultDomain && { selected: true } // ❌ React 속성
})

// WordPress가 기대하는 HTML
<option selected="selected">gmail.com</option>
```

### **해결 과정**

#### **1단계: 파일 모듈화 완료**
```
기존: assets/js/block-editor.js (74KB, 2168줄)
변경: 
├── assets/js/utils/field-utils.js
├── assets/js/block-editor-core.js
├── assets/js/blocks/integrated-form.js
├── assets/js/blocks/field-blocks.js
└── assets/js/blocks/field-special.js
```

#### **2단계: 필드 타입 중복 제거**
```javascript
// field-utils.js 수정
CTMFieldUtils.fieldTypes = [
  { type: "text", label: "텍스트 필드" },
  { type: "textarea", label: "텍스트 영역" },
  // multiselect, radio, file, address 제거 - specialFieldTypes에서만 처리
];

CTMFieldUtils.specialFieldTypes = [
  { type: "email", label: "이메일" },
  { type: "select", label: "드롭다운" },
  { type: "multiselect", label: "다중선택" },
  { type: "radio", label: "라디오 버튼" },
  { type: "file", label: "파일 업로드" },
  { type: "address", label: "주소" },
];
```

#### **3단계: 클로저 문제 해결**
```javascript
// field-special.js 수정
specialFieldTypes.forEach(function (fieldConfig) {
  const currentFieldType = fieldConfig.type; // ✅ 루프 내에서 캡처
  
  registerBlockType(`youelblocks/field-${currentFieldType}`, {
    attributes: {
      fieldType: {
        type: "string",
        default: currentFieldType, // ✅ attributes에 저장
      },
    },
    save: function (props) {
      const currentFieldType = attributes.fieldType; // ✅ attributes에서 가져오기
    }
  });
});
```

#### **4단계: HTML 속성 정규화**
```javascript
// 이메일 필드 수정
el("select", {
  name: fieldName + "_domain",
  defaultValue: "gmail.com", // ✅ React 속성
  // defaultvalue 제거
})

// 다중선택 필드 수정
el("select", {
  name: fieldName + "[]", // ✅ HTML 표준
  multiple: true,
  // 불필요한 hidden input 제거
})
```

#### **5단계: React 속성 변환 수정**
```javascript
// selected 속성 수정
el("option", {
  value: optionValue,
  selected: defaultValues.includes(optionValue) ? "selected" : undefined, // ✅ HTML 속성
})

// defaultChecked 속성 수정
el("input", {
  type: "radio",
  defaultChecked: optionValue === defaultValue ? "checked" : undefined, // ✅ HTML 속성
})
```

### **최종 해결 코드**

#### **이메일 필드 (field-special.js)**
```javascript
if (currentFieldType === "email") {
  return el(
    "div", blockProps,
    el("div", { className: `youelblocks-field youelblocks-field-${currentFieldType}` },
      attributes.showLabel && el("label", { htmlFor: fieldName }, attributes.fieldLabel),
      el("div", { className: "youelblocks-email-field-container" },
        el("input", { type: "text", name: fieldName + "_address" }),
        el("span", {}, "@"),
        el("select", { 
          name: fieldName + "_domain", 
          defaultValue: "gmail.com" // ✅ 올바른 React 속성
        },
          options.map((option, index) => 
            el("option", { key: index, value: option }, option)
          )
        )
      )
    )
  );
}
```

#### **다중선택 필드 (field-special.js)**
```javascript
if (currentFieldType === "multiselect") {
  return el(
    "div", blockProps,
    el("div", { className: `youelblocks-field youelblocks-field-${currentFieldType}` },
      attributes.showLabel && el("label", { htmlFor: fieldName }, attributes.fieldLabel),
      el("select", { 
        name: fieldName + "[]", // ✅ HTML 표준
        multiple: true,
        required: attributes.required,
        className: "youelblocks-field-input"
      },
        options.map((option, index) => {
          const optionValue = option.trim();
          const defaultValues = attributes.defaultValue ? 
            attributes.defaultValue.split(",").map(v => v.trim()) : [];
          return el("option", {
            key: index,
            value: optionValue,
            selected: defaultValues.includes(optionValue) ? "selected" : undefined // ✅ HTML 속성
          }, optionValue);
        })
      )
    )
  );
}
```

### **검증 결과**
- ✅ 이메일 필드: `defaultvalue` vs `defaultValue` 불일치 해결
- ✅ 다중선택 필드: `input[type="multiselect"]` vs `select[multiple]` 불일치 해결
- ✅ React 속성: `selected: true` vs `selected="selected"` 변환 문제 해결
- ✅ 필드 타입 중복: 각 파일이 고유한 필드만 처리
- ✅ 클로저 문제: `currentFieldType` 올바른 캡처

### **성능 개선**
- **파일 크기**: 74KB → 15-20KB (각 파일)
- **로딩 속도**: 모듈화로 인한 선택적 로딩
- **유지보수성**: 기능별 파일 분리로 코드 관리 용이
- **확장성**: 새로운 필드 타입 추가 시 해당 파일만 수정

### **호환성 확인**
- ✅ WordPress 6.4+ 블록 검증 시스템 완전 호환
- ✅ PHP 7.4+ 호환성 유지
- ✅ 기존 데이터 마이그레이션 불필요
- ✅ 하위 호환성 보장

## 🏗️ **아키텍처 개요**

### **핵심 클래스 구조**
```
YOUELBLOCKS_Manager (싱글톤)
├── YOUELBLOCKS_Editor (블록 에디터 관리)
├── YOUELBLOCKS_Public (프론트엔드 관리)
├── YOUELBLOCKS_Admin (관리자 페이지)
├── YOUELBLOCKS_Admin_Pages (관리자 페이지 콘텐츠)
├── YOUELBLOCKS_Admin_Menu (관리자 메뉴 등록)
├── YOUELBLOCKS_Utils (유틸리티 함수)
├── YOUELBLOCKS_Compatibility (호환성 체크)
└── YOUELBLOCKS_Admin_Ajax (AJAX 핸들러)
```

### **JavaScript 모듈 구조**
```
assets/js/
├── utils/
│   └── field-utils.js (공통 유틸리티)
├── block-editor-core.js (핵심 에디터 로직)
└── blocks/
    ├── integrated-form.js (통합폼 블록)
    ├── field-blocks.js (기본 필드들)
    └── field-special.js (특수 필드들)
```

### **데이터 흐름**
1. **블록 등록**: WordPress 블록 에디터에 커스텀 블록 등록
2. **폼 구성**: 사용자가 블록 에디터에서 통합폼 블록 구성
3. **필드 추가**: 통합폼 블록 내부에 다양한 필드 블록 추가
4. **블록 허용 설정**: 필드 블록, 일반 블록, 고급 블록 허용 범위 설정
5. **데이터 제출**: 프론트엔드에서 AJAX를 통해 데이터 제출
6. **데이터 저장**: WordPress 데이터베이스에 안전하게 저장
7. **데이터 관리**: 관리자 페이지에서 데이터 확인/수정/삭제

## 🔧 **기술 스택**

### **백엔드**
- **PHP**: 8.0+ (타입 힌트, 반환 타입, 최신 기능 활용)
- **WordPress**: 5.0+ (블록 에디터, REST API, 최신 기능)
- **MySQL**: WordPress 표준 데이터베이스 API 활용

### **프론트엔드**
- **JavaScript**: ES6+ (모듈, 클래스, 비동기 처리)
- **CSS**: 현대적인 CSS (Grid, Flexbox, CSS Variables)
- **React**: WordPress 블록 에디터 컴포넌트

### **개발 도구**
- **Composer**: 의존성 관리 (향후 계획)
- **Webpack**: 자산 번들링 (향후 계획)
- **ESLint**: 코드 품질 관리 (향후 계획)

## 🚀 **주요 기능 구현**

### **1. 블록 에디터 통합**

#### **블록 등록**
```php
register_block_type('youelblocks/form', [
    'editor_script' => 'youelblocks-block-editor',
    'editor_style' => 'youelblocks-block-editor',
    'style' => 'youelblocks-block',
    'render_callback' => [$this, 'render_form_block'],
    'attributes' => [
        'formTitle' => ['type' => 'string', 'default' => ''],
        'submitButtonText' => ['type' => 'string', 'default' => ''],
        'successMessage' => ['type' => 'string', 'default' => ''],
        'fields' => ['type' => 'array', 'default' => []],
    ],
]);
```

#### **필드 타입 지원**
- 텍스트 필드
- 이메일 필드 (✅ 검증 오류 해결됨)
- 숫자 필드
- 텍스트 영역
- 체크박스
- 라디오 버튼
- 드롭다운
- 다중선택 (✅ 검증 오류 해결됨)
- 파일 업로드
- 날짜/시간
- 색상 선택
- 범위 선택
- 별점 평가
- 주소 입력
- 전화번호
- URL

### **2. 데이터 관리 시스템**

#### **데이터베이스 구조**
```sql
CREATE TABLE wp_ctm_form_data_{page_id} (
    id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    form_data longtext NOT NULL,
    submission_time datetime DEFAULT CURRENT_TIMESTAMP,
    ip_address varchar(45) DEFAULT NULL,
    user_agent text DEFAULT NULL,
    user_id bigint(20) unsigned DEFAULT NULL,
    status varchar(20) DEFAULT 'active',
    created_at datetime DEFAULT CURRENT_TIMESTAMP,
    updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY submission_time (submission_time),
    KEY status (status),
    KEY user_id (user_id)
);
```

#### **데이터 처리**
```php
// 데이터 검증
$validation_rules = [
    'email' => [
        'type' => 'email',
        'required' => true,
        'label' => '이메일'
    ]
];

$errors = YOUELBLOCKS_Utils::validate_data($form_data, $validation_rules);

// 데이터 저장
$result = $wpdb->insert($table_name, [
    'form_data' => wp_json_encode($form_data, JSON_UNESCAPED_UNICODE),
    'ip_address' => YOUELBLOCKS_Utils::get_client_ip(),
    'user_agent' => YOUELBLOCKS_Utils::get_user_agent(),
    'user_id' => YOUELBLOCKS_Utils::get_current_user_id(),
]);
```

### **3. 보안 시스템**

#### **Nonce 검증**
```php
// Nonce 생성
$nonce = YOUELBLOCKS_Utils::create_nonce('form_submit');

// Nonce 검증
if (!YOUELBLOCKS_Utils::verify_nonce($_POST['nonce'], 'form_submit')) {
    YOUELBLOCKS_Utils::send_error_response('보안 검증에 실패했습니다.', [], 403);
}
```

#### **데이터 Sanitization**
```php
// 입력 데이터 정리
$sanitized_data = YOUELBLOCKS_Utils::sanitize_data($_POST['form_data'], 'text');

// 파일 업로드 보안
$upload_result = YOUELBLOCKS_Utils::handle_file_upload($_FILES['file'], [
    'max_size' => 5 * 1024 * 1024, // 5MB
    'allowed_types' => ['jpg', 'jpeg', 'png', 'gif', 'pdf'],
]);
```

### **4. 호환성 시스템**

#### **시스템 요구사항 체크**
```php
class YOUELBLOCKS_Compatibility {
    public static function check_requirements(): bool {
        $errors = self::get_compatibility_errors();
        
        if (!empty($errors)) {
            add_action('admin_notices', function() use ($errors) {
                self::display_compatibility_errors($errors);
            });
            return false;
        }
        
        return true;
    }
    
    private static function get_compatibility_errors(): array {
        $errors = [];
        
        // PHP 버전 체크
        if (version_compare(PHP_VERSION, '7.4', '<')) {
            $errors[] = sprintf('PHP 7.4 이상이 필요합니다. 현재 버전: %s', PHP_VERSION);
        }
        
        // WordPress 버전 체크
        if (version_compare(get_bloginfo('version'), '5.0', '<')) {
            $errors[] = sprintf('WordPress 5.0 이상이 필요합니다. 현재 버전: %s', get_bloginfo('version'));
        }
        
        return $errors;
    }
}
```

### **5. 다국어 지원**

#### **텍스트 도메인 설정**
```php
// 플러그인 헤더
Text Domain: youelblocks
Domain Path: /languages

// 텍스트 도메인 로드
function youelblocks_load_textdomain() {
    load_plugin_textdomain('youelblocks', false, dirname(plugin_basename(__FILE__)) . '/languages');
}

// 번역 함수 사용
__('텍스트', 'youelblocks')
_e('텍스트', 'youelblocks')
_n('단수', '복수', $number, 'youelblocks')
```

#### **POT 파일 생성**
```bash
# POT 파일 생성 (향후 자동화 예정)
xgettext --from-code=UTF-8 --keyword=__ --keyword=_e --keyword=_n \
    -o languages/youelblocks.pot *.php includes/*.php assets/js/*.js
```

## 🔄 **업데이트 시스템**

### **버전 관리**
```php
// 버전 체크 및 업데이트
function youelblocks_check_version(): void {
    $current_version = get_option('youelblocks_version', '0.0.0');
    $plugin_version = YOUELBLOCKS_VERSION;
    
    if (version_compare($current_version, $plugin_version, '<')) {
        youelblocks_handle_version_update($current_version, $plugin_version);
        update_option('youelblocks_version', $plugin_version);
    }
}

// 마이그레이션 처리
function youelblocks_handle_version_update(string $old_version, string $new_version): void {
    if (version_compare($old_version, '6.0.0', '<')) {
        youelblocks_migrate_to_6_0_0();
    }
    if (version_compare($old_version, '6.0.16', '<')) {
        youelblocks_migrate_to_6_0_16();
    }
    if (version_compare($old_version, '6.4.4', '<')) {
        youelblocks_migrate_to_6_4_4(); // 새로운 마이그레이션
    }
}
```

## 🎯 **성능 최적화**

### **메모리 관리**
- 최소 128MB 메모리 요구사항
- 대용량 데이터 처리 시 분할 처리
- 불필요한 데이터 로딩 방지

### **데이터베이스 최적화**
- 인덱스 활용으로 쿼리 성능 향상
- 페이지네이션으로 대용량 데이터 처리
- 캐싱 시스템 (향후 구현 예정)

### **자산 최적화**
- CSS/JS 파일 압축
- 이미지 최적화
- CDN 지원 (향후 구현 예정)

## 🔧 **개발 환경 설정**

### **로컬 개발 환경**
```bash
# WordPress 개발 환경 설정
wp-env start

# 플러그인 활성화
wp plugin activate youelblocks

# 디버그 모드 활성화
define('YOUELBLOCKS_DEBUG', true);
```

### **테스트 환경**
```php
// 단위 테스트 (향후 구현 예정)
class YOUELBLOCKS_Utils_Test extends WP_UnitTestCase {
    public function test_sanitize_data() {
        $result = YOUELBLOCKS_Utils::sanitize_data('<script>alert("xss")</script>', 'text');
        $this->assertEquals('alert("xss")', $result);
    }
}
```

## 📚 **API 문서**

### **REST API 엔드포인트**
```php
// 폼 데이터 조회
GET /wp-json/youelblocks/v1/forms/{page_id}/data

// 폼 데이터 생성
POST /wp-json/youelblocks/v1/forms/{page_id}/data

// 폼 데이터 수정
PUT /wp-json/youelblocks/v1/forms/{page_id}/data/{id}

// 폼 데이터 삭제
DELETE /wp-json/youelblocks/v1/forms/{page_id}/data/{id}
```

### **훅과 필터**
```php
// 폼 데이터 처리 전
add_filter('youelblocks_before_save_form_data', function($data, $page_id) {
    // 데이터 수정 로직
    return $data;
}, 10, 2);

// 폼 데이터 처리 후
add_action('youelblocks_after_save_form_data', function($data, $page_id, $result) {
    // 후처리 로직
}, 10, 3);

// 필드 렌더링 커스터마이징
add_filter('youelblocks_field_render', function($html, $attributes) {
    // 커스텀 렌더링 로직
    return $html;
}, 10, 2);
```

## 🚀 **향후 개발 계획**

### **단기 계획 (6.4.x)**
- [x] 이메일 필드 검증 오류 완전 해결 ✅
- [x] 다중선택 필드 검증 오류 완전 해결 ✅
- [x] 파일 모듈화 완료 ✅
- [x] JavaScript 클로저 문제 해결 ✅
- [x] React 속성 변환 문제 해결 ✅
- [ ] 블록 허용 설정 UI 개선
- [ ] 필드 블록 드래그 앤 드롭 정렬
- [ ] 실시간 미리보기 기능

### **중기 계획 (6.5.x)**
- [ ] REST API 완전 구현
- [ ] 웹훅 시스템 추가
- [ ] 분석 및 통계 기능 강화
- [ ] 캐싱 시스템 구현
- [ ] 조건부 로직 구현
- [ ] 이메일 템플릿 시스템

### **장기 계획 (7.0.x)**
- [ ] React 기반 관리자 인터페이스
- [ ] 실시간 협업 기능
- [ ] AI 기반 폼 최적화
- [ ] 모바일 앱 지원
- [ ] 다중 사이트 지원

## 🐛 **디버깅 가이드**

### **블록 검증 오류 디버깅**
```javascript
// 브라우저 콘솔에서 확인
console.log('Save function output:', saveOutput);
console.log('Edit function output:', editOutput);

// WordPress 개발자 도구에서 확인
// Network 탭 → blocks.min.js → Response 확인
```

### **로그 시스템**
```php
// 디버그 로그
YOUELBLOCKS_Utils::safe_debug_log('디버그 메시지', $data);

// 정보 로그
YOUELBLOCKS_Utils::safe_info_log('정보 메시지');

// 오류 로그
YOUELBLOCKS_Utils::safe_error_log('오류 메시지', $exception);
```

### **디버그 모드**
```php
// 디버그 모드 활성화
define('YOUELBLOCKS_DEBUG', true);

// 상세한 오류 정보 표시
if (YOUELBLOCKS_DEBUG) {
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
}
```

## 📞 **지원 및 기여**

### **개발자 지원**
- **GitHub Issues**: https://github.com/youelblocks/youelblocks/issues
- **개발자 문서**: https://youelblocks.co.kr/docs/developer
- **API 문서**: https://youelblocks.co.kr/docs/api

### **기여 가이드**
1. GitHub 저장소 포크
2. 기능 브랜치 생성
3. 코드 작성 및 테스트
4. Pull Request 제출

### **코딩 표준**
- PSR-12 코딩 표준 준수
- WordPress 코딩 표준 준수
- 타입 힌트 및 반환 타입 명시
- 완전한 문서화

---

**최종 업데이트**: 2025-01-27
**버전**: 6.4.4
**개발자**: YOUELBLOCKS Team