diff --git a/INDEX.md b/INDEX.md index 08e3638..6765a1e 100644 --- a/INDEX.md +++ b/INDEX.md @@ -196,6 +196,7 @@ DB 도메인별: | [hr/](features/hr/) | 인사관리 | | [crm/README.md](features/crm/README.md) | CRM | | [esign/README.md](features/esign/README.md) | 전자서명 | +| [esign/handwriting-verification.md](features/esign/handwriting-verification.md) | 전자서명 고도화 — 필기 문구 확인 (HWR 인식, 유사도 검증, 보험업법 준용) | | [equipment/README.md](features/equipment/README.md) | 설비관리 (API Phase 1 완료 + DB 스키마) | | [boards/README.md](features/boards/README.md) | 게시판 | | [ai/README.md](features/ai/README.md) | AI 기능 (리포트, 토큰사용량, 단가설정, R2 비용추적) | diff --git a/features/esign/handwriting-verification.md b/features/esign/handwriting-verification.md new file mode 100644 index 0000000..e30260a --- /dev/null +++ b/features/esign/handwriting-verification.md @@ -0,0 +1,779 @@ +# 전자계약 eSign 고도화 — 필기 문구 확인 (Handwriting Verification) + +> **작성일**: 2026-03-22 +> **상태**: 기획 중 +> **담당**: R&D실 +> **v1 참조**: [features/esign/README.md](README.md), [projects/e-sign/technical-design.md](../../projects/e-sign/technical-design.md) + +--- + +## 1. 개요 + +### 1.1 배경 + +현재 eSign v1은 **서명 캔버스(signature_pad)** 또는 **도장 이미지 업로드**로 본인 확인을 수행한다. 이 방식은 "서명했다"는 증거만 남기며, 서명자가 **계약 내용을 실제로 확인했는지** 증명하기 어렵다. + +보험업계에서는 이미 **자필 문구 확인(Handwriting Verification)** 방식을 표준으로 적용하고 있다: + +- "본인은 위 내용을 충분히 이해하고 동의합니다"를 **직접 손으로 따라 쓰기** +- 필기 인식(HWR)으로 **일치율을 검증** +- 일정 임계값 이상이어야 다음 단계 진행 + +### 1.2 목적 + +기존 서명/도장에 **필기 문구 확인 단계**를 추가하여, 계약 체결의 법적 증거력과 분쟁 방지 능력을 강화한다. + +### 1.3 핵심 가치 + +| 가치 | 설명 | +|------|------| +| **확인 증거** | "읽고 이해했다"를 자필로 증명 → 분쟁 시 강력한 증거 | +| **위변조 방지** | 단순 클릭/터치가 아닌 자필 행위 → 대리 서명 억제 | +| **법적 효력** | 보험업법 시행령 기반 자필확인 → 금감원 권고 사항 준용 | +| **차별화** | 모두싸인/도큐사인에 없는 자필확인 기능 → SAM 전자계약 경쟁력 | + +### 1.4 v1 vs 고도화 비교 + +| 항목 | v1 (현재) | 고도화 (추가) | +|------|----------|-------------| +| 본인확인 | OTP 인증 | OTP + 필기 문구 확인 | +| 서명 방식 | 캔버스 서명 / 도장 업로드 | 기존 유지 + 자필 문구 확인 단계 추가 | +| 확인 행위 | 체크박스 동의 | 지정 문구 자필 작성 + 인식 검증 | +| 증거 | 서명 이미지 + IP/UA | + 자필 이미지 + 인식 결과 + 일치율 | +| 법적 효력 | 전자서명법 준수 | + 보험업법 자필확인 준용 | + +--- + +## 2. 사용자 경험 (UX) 설계 + +### 2.1 서명 프로세스 흐름 (고도화 후) + +``` +[기존 v1 유지] [고도화 추가] +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 링크 접속 + │ + ▼ + 계약 정보 확인 + │ + ▼ + OTP 인증 (이메일/카카오톡) + │ + ▼ + PDF 문서 열람 ─────────────────→ 필기 확인 단계 (NEW) + │ + ├─ Step 1: 문구 표시 + │ "본인은 위 내용을 확인하였습니다" + │ + ├─ Step 2: 자필 작성 (캔버스) + │ 손가락/펜으로 문구를 따라 씀 + │ + ├─ Step 3: 인식 & 검증 + │ HWR 엔진이 필기 인식 + │ 일치율 80% 이상 → 통과 + │ 미달 → 재작성 안내 + │ + ▼ + 서명/도장 (기존 방식) ←──────── 통과 시 + │ + ▼ + 제출 완료 +``` + +### 2.2 필기 확인 화면 구성 + +``` +┌─────────────────────────────────────────────┐ +│ [SAM 전자계약] Step 2/3 │ +├─────────────────────────────────────────────┤ +│ │ +│ 아래 문구를 직접 손으로 써 주세요 │ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ │ │ +│ │ 본인은 위 내용을 확인하였습니다 │ │ ← 가이드 문구 (연한 회색) +│ │ │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ │ │ +│ │ ~~~~~~~~~~~~~~~~~~~~~~~~ │ │ ← 자필 캔버스 (터치/펜) +│ │ ~~~~~~~~~~~~~~~~~~~~~~~~ │ │ +│ │ │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ 인식 결과: "본인은 위 내용을 확인하였습니다"│ +│ 일치율: 92% ✅ │ +│ │ +│ [초기화] [다음 단계 →] │ +│ │ +└─────────────────────────────────────────────┘ +``` + +### 2.3 실패 시 화면 + +``` +┌─────────────────────────────────────────────┐ +│ │ +│ 인식 결과: "본인은 위 을 확인합니다" │ +│ 일치율: 54% ❌ │ +│ │ +│ ⚠️ 문구가 정확히 인식되지 않았습니다. │ +│ 좀 더 또박또박 써 주세요. │ +│ │ +│ 💡 팁: 글자 사이 간격을 넓게 쓰면 │ +│ 인식률이 높아집니다. │ +│ │ +│ [초기화] [다시 쓰기] │ +│ │ +│ 남은 시도 횟수: 4/5 │ +│ │ +└─────────────────────────────────────────────┘ +``` + +### 2.4 디바이스별 최적화 + +| 디바이스 | 캔버스 크기 | 입력 방식 | 특이사항 | +|----------|-----------|----------|---------| +| 모바일 (< 640px) | 전체 너비 × 120px | 손가락 터치 | 가로 모드 권장 안내 | +| 태블릿 (640~1024px) | 전체 너비 × 150px | 스타일러스/터치 | Apple Pencil 최적화 | +| 웹 (> 1024px) | 700px × 180px | 마우스/터치패드 | 서명패드 USB 지원 | + +--- + +## 3. 기술 아키텍처 + +### 3.1 전체 구조 + +``` +┌──────────────────────────────────────────────────────────┐ +│ 클라이언트 (브라우저) │ +│ │ +│ ┌────────────────┐ ┌──────────────────────────────┐ │ +│ │ 필기 캔버스 │ │ 인식 엔진 (선택) │ │ +│ │ (Canvas API) │ │ │ │ +│ │ │ │ A. 클라우드 API (권장) │ │ +│ │ - 터치 이벤트 │ │ Google Cloud Vision │ │ +│ │ - 스트로크 수집│──→│ or Naver Clova OCR │ │ +│ │ - 이미지 생성 │ │ │ │ +│ │ │ │ B. 서버 사이드 │ │ +│ └────────────────┘ │ Tesseract OCR │ │ +│ └──────────────┬───────────────┘ │ +│ │ │ +└──────────────────────────────────────┼───────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ MNG 서버 (Laravel) │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ HandwritingVerificationService │ │ +│ │ │ │ +│ │ - verifyHandwriting(image, expectedText) │ │ +│ │ - calculateSimilarity(recognized, expected) │ │ +│ │ - saveVerificationResult(signerId, result) │ │ +│ │ - getVerificationHistory(contractId) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ HWR 어댑터 (전략 패턴) │ │ +│ │ │ │ +│ │ interface HwrAdapter { │ │ +│ │ recognize(image): RecognitionResult │ │ +│ │ } │ │ +│ │ │ │ +│ │ ├─ GoogleVisionAdapter (권장) │ │ +│ │ ├─ NaverClovaAdapter (국내 대안) │ │ +│ │ └─ TesseractAdapter (무료 폴백) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────┘ +``` + +### 3.2 HWR(Handwriting Recognition) 엔진 비교 + +| 엔진 | 한글 인식 | 필기 인식 | 비용 | 속도 | 적합도 | +|------|:--------:|:--------:|------|------|:------:| +| **Google Cloud Vision** | ✅ 우수 | ✅ 우수 | $1.5/1000건 | ~1초 | ⭐⭐⭐ | +| **Naver Clova OCR** | ✅ 최우수 | ✅ 우수 | 월 300건 무료 / $0.3~ | ~1초 | ⭐⭐⭐ | +| **Kakao OCR** | ✅ 우수 | 🟡 보통 | 월 10,000건 무료 | ~1.5초 | ⭐⭐ | +| **Tesseract 5** | 🟡 보통 | ❌ 약함 | 무료 | ~3초 | ⭐ | +| **MyScript** | ✅ 우수 | ✅ 최우수 | 유료 (협의) | 실시간 | ⭐⭐⭐ | + +**권장 조합**: Naver Clova OCR (1순위) + Google Vision (폴백) + +- Naver Clova는 한국어 필기체 인식에 특화 (보험사 사용 실적) +- 월 300건 무료 → SAM 전자계약 볼륨에 충분 +- Google Vision은 폴백 및 다국어 대응용 + +### 3.3 인식 프로세스 + +``` +[1] 캔버스 입력 + │ + ├─ 스트로크 데이터 수집 (x, y, timestamp, pressure) + ├─ 캔버스 → PNG (base64) + │ + ▼ +[2] 전처리 (서버) + │ + ├─ 이미지 리사이즈 (너비 1200px 고정) + ├─ 이진화 (Otsu threshold) + ├─ 노이즈 제거 (가우시안 블러) + ├─ 기울기 보정 (deskew) + │ + ▼ +[3] HWR 엔진 호출 + │ + ├─ Naver Clova: POST /custom/v1/general + │ body: { images: [{ format: "png", data: base64 }] } + │ + ├─ Google Vision: POST /v1/images:annotate + │ body: { requests: [{ image: { content: base64 }, features: [{ type: "TEXT_DETECTION" }] }] } + │ + ▼ +[4] 텍스트 유사도 계산 + │ + ├─ 정규화: 공백 제거, 특수문자 제거 + ├─ Levenshtein Distance (편집 거리) + ├─ Jaro-Winkler Similarity (부분 일치 가중) + ├─ 최종 일치율 = (Levenshtein 50% + Jaro-Winkler 50%) + │ + ▼ +[5] 판정 + │ + ├─ 80% 이상: ✅ 통과 → 다음 단계 + ├─ 60~79%: ⚠️ 재시도 권유 (힌트 제공) + └─ 60% 미만: ❌ 재작성 필요 +``` + +--- + +## 4. 데이터 모델 + +### 4.1 신규 테이블: `esign_handwriting_verifications` + +```sql +CREATE TABLE esign_handwriting_verifications ( + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + tenant_id BIGINT UNSIGNED NOT NULL, + contract_id BIGINT UNSIGNED NOT NULL, + signer_id BIGINT UNSIGNED NOT NULL, + + -- 확인 항목 정보 + step_order TINYINT UNSIGNED NOT NULL DEFAULT 1, + prompt_text VARCHAR(200) NOT NULL, -- 요구 문구 + + -- 인식 결과 + recognized_text VARCHAR(500) NULL, -- HWR 인식 결과 + similarity_score DECIMAL(5,2) NULL, -- 일치율 (0.00~100.00) + is_passed BOOLEAN NOT NULL DEFAULT FALSE, -- 통과 여부 + + -- 이미지 저장 + handwriting_image VARCHAR(500) NULL, -- 자필 이미지 경로 + + -- 인식 메타데이터 + hwr_engine VARCHAR(50) NULL, -- 사용 엔진 (clova/google/tesseract) + hwr_confidence DECIMAL(5,2) NULL, -- 엔진 자체 신뢰도 + hwr_raw_response JSON NULL, -- 엔진 원본 응답 + + -- 시도 정보 + attempt_number TINYINT UNSIGNED NOT NULL DEFAULT 1, + + -- 기록 + verified_at TIMESTAMP NULL, -- 통과 시각 + ip_address VARCHAR(45) NULL, + user_agent VARCHAR(500) NULL, + + created_at TIMESTAMP NULL, + updated_at TIMESTAMP NULL, + + INDEX idx_contract_signer (contract_id, signer_id), + INDEX idx_tenant (tenant_id), + + FOREIGN KEY (contract_id) REFERENCES esign_contracts(id) ON DELETE CASCADE, + FOREIGN KEY (signer_id) REFERENCES esign_signers(id) ON DELETE CASCADE +); +``` + +### 4.2 신규 테이블: `esign_verification_templates` + +관리자가 계약 유형별로 확인 문구를 설정하는 템플릿이다. + +```sql +CREATE TABLE esign_verification_templates ( + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + tenant_id BIGINT UNSIGNED NOT NULL, + + name VARCHAR(100) NOT NULL, -- 템플릿명 (예: "표준 확인 문구") + category VARCHAR(50) NULL, -- 카테고리 (근로계약, 영업계약 등) + + -- 확인 단계 (JSON 배열) + steps JSON NOT NULL, + -- [ + -- { "order": 1, "text": "본인은 위 내용을 확인하였습니다", "threshold": 80 }, + -- { "order": 2, "text": "이름: {signer_name}", "threshold": 85 } + -- ] + + pass_threshold DECIMAL(5,2) NOT NULL DEFAULT 80.00, -- 기본 통과 임계값 + max_attempts TINYINT UNSIGNED NOT NULL DEFAULT 5, -- 최대 시도 횟수 + + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_by BIGINT UNSIGNED NULL, + created_at TIMESTAMP NULL, + updated_at TIMESTAMP NULL, + + INDEX idx_tenant_active (tenant_id, is_active) +); +``` + +### 4.3 기존 테이블 변경 + +#### `esign_contracts` 추가 컬럼 + +```sql +ALTER TABLE esign_contracts + ADD COLUMN verification_template_id BIGINT UNSIGNED NULL AFTER completion_template_name, + ADD COLUMN verification_required BOOLEAN NOT NULL DEFAULT FALSE AFTER verification_template_id; +``` + +#### `esign_signers` 추가 컬럼 + +```sql +ALTER TABLE esign_signers + ADD COLUMN verification_status ENUM('pending', 'passed', 'failed', 'skipped') + NOT NULL DEFAULT 'pending' AFTER status, + ADD COLUMN verification_passed_at TIMESTAMP NULL AFTER consent_agreed_at; +``` + +### 4.4 ERD 관계 + +``` +esign_contracts (1) ──── (*) esign_signers + │ │ + │ │ + ├── verification_ ├── (*) esign_handwriting_verifications + │ template_id ──→ │ (시도별 1건, 통과까지 반복) + │ │ + │ └── verification_status + │ verification_passed_at + │ + └──→ esign_verification_templates (1) + └── steps: JSON [{ order, text, threshold }] +``` + +--- + +## 5. API 설계 + +### 5.1 관리자 API (인증 필요) + +#### 확인 템플릿 관리 + +| HTTP | URI | 설명 | +|------|-----|------| +| GET | `/esign/verification-templates` | 템플릿 목록 | +| POST | `/esign/verification-templates` | 템플릿 생성 | +| PUT | `/esign/verification-templates/{id}` | 템플릿 수정 | +| DELETE | `/esign/verification-templates/{id}` | 템플릿 삭제 | + +#### 계약 생성 시 (기존 확장) + +```json +// POST /esign/contracts/store (기존 파라미터 + 추가) +{ + "title": "근로계약서", + "verification_required": true, + "verification_template_id": 1 +} +``` + +### 5.2 서명자 API (토큰 기반, 비인증) + +| HTTP | URI | 설명 | +|------|-----|------| +| GET | `/esign/sign/{token}/api/verification` | 확인 단계 정보 조회 | +| POST | `/esign/sign/{token}/api/verification/submit` | 필기 인식 제출 | +| GET | `/esign/sign/{token}/api/verification/status` | 진행 상태 확인 | + +#### 확인 단계 조회 응답 + +```json +// GET /esign/sign/{token}/api/verification +{ + "verification_required": true, + "steps": [ + { + "order": 1, + "prompt_text": "본인은 위 내용을 확인하였습니다", + "status": "pending", + "attempts_remaining": 5 + }, + { + "order": 2, + "prompt_text": "홍길동", + "status": "pending", + "attempts_remaining": 5 + } + ], + "current_step": 1, + "all_passed": false +} +``` + +#### 필기 제출 요청/응답 + +```json +// POST /esign/sign/{token}/api/verification/submit +// Request +{ + "step_order": 1, + "handwriting_image": "data:image/png;base64,iVBOR..." +} + +// Response (성공) +{ + "recognized_text": "본인은 위 내용을 확인하였습니다", + "similarity_score": 92.5, + "is_passed": true, + "next_step": 2, + "message": "확인이 완료되었습니다." +} + +// Response (실패) +{ + "recognized_text": "본인은 위 내을 확합니다", + "similarity_score": 54.3, + "is_passed": false, + "attempts_remaining": 4, + "hints": [ + "글자 사이 간격을 조금 넓혀 보세요.", + "또박또박 쓰면 인식률이 높아집니다." + ] +} +``` + +--- + +## 6. 서비스 계층 설계 + +### 6.1 신규 서비스 + +``` +app/Services/ESign/ +├── HandwritingVerificationService.php ← 메인 서비스 +├── HwrAdapterInterface.php ← 인식 엔진 인터페이스 +├── Adapters/ +│ ├── NaverClovaAdapter.php ← Naver Clova OCR +│ ├── GoogleVisionAdapter.php ← Google Cloud Vision +│ └── TesseractAdapter.php ← 무료 폴백 +└── TextSimilarityService.php ← 문자열 유사도 계산 +``` + +### 6.2 HandwritingVerificationService 주요 메서드 + +```php +class HandwritingVerificationService +{ + // 확인 단계 정보 조회 + public function getVerificationSteps(EsignSigner $signer): array + + // 필기 이미지 제출 → 인식 → 유사도 검증 + public function submitHandwriting( + EsignSigner $signer, + int $stepOrder, + string $base64Image + ): VerificationResult + + // 모든 단계 통과 여부 확인 + public function isAllStepsPassed(EsignSigner $signer): bool + + // 이미지 전처리 (리사이즈, 이진화, 노이즈 제거) + private function preprocessImage(string $base64Image): string + + // HWR 엔진 호출 (전략 패턴, 폴백 포함) + private function recognizeText(string $processedImage): RecognitionResult + + // 유사도 검증 + private function verifySimilarity( + string $recognized, + string $expected, + float $threshold + ): bool +} +``` + +### 6.3 HwrAdapterInterface + +```php +interface HwrAdapterInterface +{ + /** + * 필기 이미지에서 텍스트를 인식한다. + * + * @param string $base64Image base64 인코딩 이미지 + * @return RecognitionResult { text, confidence, rawResponse } + */ + public function recognize(string $base64Image): RecognitionResult; + + /** + * 엔진 사용 가능 여부 확인 + */ + public function isAvailable(): bool; +} +``` + +### 6.4 TextSimilarityService + +```php +class TextSimilarityService +{ + /** + * 두 문자열의 유사도를 계산한다 (0.0 ~ 100.0) + * + * 알고리즘: + * 1. 정규화 (공백 제거, 특수문자 제거) + * 2. Levenshtein Distance → 유사도 (50% 가중) + * 3. Jaro-Winkler Similarity (50% 가중) + * 4. 최종 점수 반환 + */ + public function calculate(string $recognized, string $expected): float + + /** + * 정규화: 공백/특수문자 제거, 유니코드 정규화 + */ + private function normalize(string $text): string +} +``` + +--- + +## 7. 프론트엔드 설계 + +### 7.1 신규 뷰 파일 + +``` +resources/views/esign/sign/ +├── auth.blade.php (기존) +├── sign.blade.php (기존) +├── verification.blade.php (NEW — 필기 확인 페이지) +└── done.blade.php (기존) +``` + +### 7.2 필기 캔버스 구현 + +기존 `signature_pad` 대신 **필기 전용 캔버스**를 구현한다. 차이점: + +| 항목 | 서명 캔버스 (v1) | 필기 캔버스 (고도화) | +|------|----------------|-------------------| +| 입력 영역 | 작은 박스 (서명용) | 넓은 영역 (문장 필기용) | +| 가이드 | 없음 | 연한 가이드 문구 표시 | +| 줄 표시 | 없음 | 필기 줄(lined) 가이드 | +| 결과 | 이미지 저장만 | 인식 → 검증 → 결과 표시 | +| 라이브러리 | signature_pad | Canvas API 직접 구현 | + +### 7.3 JavaScript 모듈 + +```javascript +// HandwritingCanvas — 필기 전용 캔버스 클래스 +class HandwritingCanvas { + constructor(canvasElement, options = {}) { + this.canvas = canvasElement; + this.ctx = canvasElement.getContext('2d'); + this.strokes = []; // 스트로크 데이터 + this.isDrawing = false; + this.lineWidth = options.lineWidth || 3; + this.guideText = options.guideText || ''; + } + + // 가이드 문구를 연한 색으로 표시 + drawGuide() {} + + // 터치/마우스 이벤트 바인딩 + bindEvents() {} + + // 캔버스 → base64 PNG + toDataURL() {} + + // 스트로크 데이터 (인식 보조용) + getStrokeData() {} + + // 초기화 + clear() {} + + // 빈 캔버스 여부 + isEmpty() {} +} +``` + +--- + +## 8. 비즈니스 규칙 + +### 8.1 기본 확인 문구 (Default Prompts) + +| 순번 | 문구 | 용도 | 임계값 | +|------|------|------|:------:| +| 1 | "본인은 위 내용을 확인하였습니다" | 범용 (모든 계약) | 80% | +| 2 | "{서명자 이름}" | 본인 확인 강화 | 85% | +| 3 | "위 계약 내용에 동의합니다" | 동의 확인 | 80% | + +### 8.2 계약 유형별 기본 템플릿 + +| 계약 유형 | 확인 단계 | 문구 | +|----------|:--------:|------| +| 영업파트너 계약서 | 2단계 | ①내용 확인 + ②본인 이름 | +| 근로계약서 | 2단계 | ①내용 확인 + ②본인 이름 | +| 비밀유지 서약서 | 3단계 | ①내용 확인 + ②기밀 유지 확인 + ③본인 이름 | +| 일반 계약서 | 1단계 | ①내용 확인 | + +### 8.3 검증 규칙 + +| 규칙 | 값 | 비고 | +|------|-----|------| +| 기본 통과 임계값 | 80% | 관리자 조정 가능 (60~95%) | +| 최대 시도 횟수 | 5회/단계 | 초과 시 관리자 확인 필요 | +| 캔버스 최소 입력 | 5 스트로크 | 의미 있는 필기 보장 | +| 이미지 최소 크기 | 200×50px | 인식 품질 보장 | +| 인식 제한 시간 | 5초 | 타임아웃 시 재시도 | +| 필기 확인 선택 여부 | 계약별 on/off | 기존 계약은 영향 없음 | + +### 8.4 감사 로그 추가 액션 + +``` +verification_started — 필기 확인 단계 진입 +verification_attempted — 필기 제출 (결과 포함) +verification_passed — 단계 통과 +verification_failed — 시도 횟수 초과 +verification_completed — 모든 단계 통과 +``` + +--- + +## 9. 보안 고려사항 + +| 위협 | 대응 | +|------|------| +| 이미지 조작 (외부 텍스트 이미지 촬영) | 스트로크 데이터 + 이미지 동시 검증, 스트로크 없는 이미지 거부 | +| 동일 이미지 재사용 | 이미지 해시 중복 검사, 스트로크 타임스탬프 검증 | +| 봇/자동화 공격 | 시도 횟수 제한, reCAPTCHA (선택), 필기 속도 패턴 분석 | +| HWR API 장애 | 다중 엔진 폴백, 장애 시 수동 검증 모드 전환 | +| 개인정보 | 자필 이미지 암호화 저장, 보관 기간 후 자동 삭제 | + +--- + +## 10. 설정 (config) + +```php +// config/esign.php (추가) +'handwriting_verification' => [ + 'enabled' => env('ESIGN_HWR_ENABLED', false), + + // HWR 엔진 우선순위 + 'engine' => env('ESIGN_HWR_ENGINE', 'clova'), // clova, google, tesseract + 'fallback_engine' => env('ESIGN_HWR_FALLBACK', 'google'), + + // Naver Clova OCR + 'clova' => [ + 'api_url' => env('CLOVA_OCR_API_URL'), + 'secret_key' => env('CLOVA_OCR_SECRET_KEY'), + ], + + // Google Cloud Vision + 'google' => [ + 'credentials_path' => env('GOOGLE_VISION_CREDENTIALS'), + ], + + // 검증 설정 + 'default_threshold' => 80.0, // 기본 통과 임계값 (%) + 'max_attempts' => 5, // 최대 시도 횟수 + 'min_strokes' => 5, // 최소 스트로크 수 + 'recognition_timeout' => 5, // 인식 타임아웃 (초) + + // 이미지 전처리 + 'image_max_width' => 1200, + 'image_format' => 'png', +], +``` + +--- + +## 11. 개발 로드맵 + +### Phase 1: 핵심 엔진 (2~3주) + +| 작업 | 담당 | 설명 | +|------|------|------| +| DB 마이그레이션 | MNG | 신규 2테이블 + 기존 2테이블 변경 | +| HWR 어댑터 구현 | MNG | Naver Clova + Google Vision 어댑터 | +| TextSimilarityService | MNG | Levenshtein + Jaro-Winkler 유사도 | +| HandwritingVerificationService | MNG | 메인 비즈니스 로직 | +| 이미지 전처리 | MNG | GD/Imagick 기반 전처리 파이프라인 | + +### Phase 2: 프론트엔드 (1~2주) + +| 작업 | 담당 | 설명 | +|------|------|------| +| HandwritingCanvas 클래스 | MNG | 필기 전용 캔버스 (터치/마우스) | +| verification.blade.php | MNG | 필기 확인 페이지 UI | +| 디바이스 최적화 | MNG | 모바일/태블릿/웹 반응형 | +| 인식 결과 UI | MNG | 일치율 표시, 재시도 안내 | + +### Phase 3: 관리자 기능 (1주) + +| 작업 | 담당 | 설명 | +|------|------|------| +| 확인 템플릿 관리 UI | MNG | 문구 설정, 임계값 조정 | +| 계약 생성 마법사 연동 | MNG | 필기 확인 on/off 토글 | +| 감사 로그 연동 | MNG | 시도 이력 조회 | + +### Phase 4: 검증 및 안정화 (1주) + +| 작업 | 담당 | 설명 | +|------|------|------| +| 인식률 테스트 | R&D | 다양한 필체로 인식률 검증 | +| 임계값 튜닝 | R&D | 실 데이터 기반 최적 임계값 | +| 폴백 시나리오 테스트 | MNG | API 장애, 타임아웃 처리 | +| 보안 테스트 | MNG | 이미지 조작, 재사용 방지 | + +### 총 예상 기간: 5~7주 + +--- + +## 12. 비용 추정 + +### HWR API 비용 (월간) + +| 엔진 | 무료 한도 | 초과 단가 | 예상 월 사용량 | 예상 비용 | +|------|----------|----------|:------------:|----------| +| Naver Clova | 300건/월 | ~$0.3/건 | 100~200건 | **무료** | +| Google Vision | 1,000건/월 | $1.5/1,000건 | 폴백 전용 | **무료~$1.5** | + +> **참고**: 전자계약 월 50건 × 평균 2단계 × 평균 2회 시도 = ~200건/월 → 무료 범위 내 + +--- + +## 13. 기존 v1과의 공존 전략 + +| 항목 | 정책 | +|------|------| +| 기존 계약 | 영향 없음 (verification_required = false) | +| 새 계약 | 관리자가 선택 (필기 확인 on/off) | +| 서명 단계 | 필기 확인 통과 후 기존 서명/도장 단계로 진입 | +| 라우팅 | `/esign/sign/{token}` → auth → **verification** (NEW) → sign → done | +| 코드 구조 | 기존 EsignPublicController에 verification 메서드 추가 (별도 서비스 분리) | +| 뷰 파일 | 신규 verification.blade.php 추가 (기존 파일 수정 최소화) | + +--- + +## 관련 문서 + +- [전자서명 기능 개요](README.md) +- [전자서명 기술 설계 (v1)](../../projects/e-sign/technical-design.md) +- [DB 스키마 — 문서/전자서명](../../system/database/documents.md) +- [알림톡 연동 가이드](../barobill-kakaotalk/esign-notification-guide.md) + +--- + +**최종 업데이트**: 2026-03-22