docs:E-Sign 기술 설계 문서 v1.1 업데이트 - 필드 템플릿 & 복사 기능

- DB 스키마: esign_field_templates, esign_field_template_items 추가
- API 명세: 5.3 필드 템플릿 API 섹션 추가 (5개 엔드포인트)
- 화면 목록: ES_FIELDS 화면에 템플릿 기능 설명 추가
- 구현 파일: 신규 마이그레이션 2개, 모델 2개 추가
- 모델 관계도: EsignFieldTemplate, EsignFieldTemplateItem 추가
- 로드맵: Phase 3.5 필드 템플릿(구현 완료) 추가
- 사용자 가이드: 16장 필드 템플릿 사용법 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-12 18:19:16 +09:00
parent 40ce95fa55
commit d88ee4b67e

View File

@@ -2,7 +2,7 @@
> **프로젝트명**: SAM E-Sign (가칭)
> **작성일**: 2026-02-12
> **버전**: v1.0 (구현 완료)
> **버전**: v1.1 (필드 템플릿 & 복사 기능 추가)
> **작성자**: DX 추진팀
---
@@ -243,6 +243,8 @@ esign_contracts (1) ──── (N) esign_signers
│ │
(N) (N)
esign_sign_fields esign_audit_logs
esign_field_templates (1) ──── (N) esign_field_template_items
```
### 4.2 esign_contracts (계약서)
@@ -406,6 +408,56 @@ CREATE TABLE esign_audit_logs (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
### 4.6 esign_field_templates (필드 배치 템플릿)
```sql
CREATE TABLE esign_field_templates (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL, -- 템플릿 이름
description TEXT NULL, -- 템플릿 설명
signer_count TINYINT UNSIGNED DEFAULT 2, -- 서명자 수
is_active BOOLEAN DEFAULT TRUE, -- 활성 여부 (삭제 시 false)
created_by BIGINT UNSIGNED NULL, -- 생성자 user_id
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_esign_field_templates_tenant (tenant_id),
INDEX idx_esign_field_templates_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
### 4.7 esign_field_template_items (템플릿 필드 항목)
```sql
CREATE TABLE esign_field_template_items (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
template_id BIGINT UNSIGNED NOT NULL,
signer_order TINYINT UNSIGNED NOT NULL, -- 서명자 순서 (1, 2, ...)
page_number SMALLINT UNSIGNED NOT NULL, -- 페이지 번호
position_x DECIMAL(8,2) NOT NULL, -- X 좌표 (%)
position_y DECIMAL(8,2) NOT NULL, -- Y 좌표 (%)
width DECIMAL(8,2) NOT NULL, -- 너비 (%)
height DECIMAL(8,2) NOT NULL, -- 높이 (%)
field_type ENUM('signature','stamp','text','date','checkbox') DEFAULT 'signature',
field_label VARCHAR(100) NULL, -- 필드 라벨
is_required BOOLEAN DEFAULT TRUE,
sort_order SMALLINT UNSIGNED DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (template_id) REFERENCES esign_field_templates(id) ON DELETE CASCADE,
INDEX idx_esign_field_template_items_template (template_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
> **핵심 설계**: `signer_id` 대신 `signer_order`(1, 2)를 저장합니다.
> 템플릿 적용 시 현재 계약 서명자의 `sign_order`와 매핑하여 `signer_id`를 결정합니다.
---
## 5. API 명세
@@ -563,7 +615,91 @@ Content-Type: application/json
GET /api/v1/esign/contracts/{id}/fields
```
### 5.3 서명 요청 API
### 5.3 필드 템플릿 API
#### 템플릿 목록 조회
```
GET /esign/contracts/templates?signer_count=2
```
**Response 200:**
```json
{
"success": true,
"data": [
{
"id": 1,
"name": "기본 2인 서명 배치",
"description": "마지막 페이지 좌우 서명란",
"signer_count": 2,
"items": [
{
"signer_order": 1, "page_number": 3,
"position_x": 15.5, "position_y": 82.0,
"width": 20.0, "height": 8.0,
"field_type": "signature", "field_label": "갑 서명"
}
]
}
]
}
```
#### 템플릿 저장 (현재 필드를 템플릿으로)
```
POST /esign/contracts/templates
Content-Type: application/json
```
```json
{
"name": "기본 2인 서명 배치",
"description": "마지막 페이지 좌우 서명란",
"items": [
{
"signer_order": 1, "page_number": 3,
"position_x": 15.5, "position_y": 82.0,
"width": 20.0, "height": 8.0,
"field_type": "signature", "field_label": "갑 서명",
"is_required": true
}
]
}
```
> `signer_order`는 프론트엔드에서 `signer_id` → 해당 서명자의 `sign_order`로 변환하여 전송합니다.
#### 템플릿 삭제 (soft delete)
```
DELETE /esign/contracts/templates/{id}
```
> `is_active`를 `false`로 변경합니다.
#### 템플릿을 계약에 적용
```
POST /esign/contracts/{id}/apply-template
Content-Type: application/json
```
```json
{
"template_id": 1
}
```
> 기존 필드를 삭제하고 템플릿의 필드를 적용합니다.
> `signer_order` → 현재 계약의 `sign_order`에 해당하는 `signer_id`로 매핑합니다.
> 템플릿의 `signer_count`가 계약의 서명자 수보다 크면 422 에러를 반환합니다.
#### 다른 계약에서 필드 복사
```
POST /esign/contracts/{id}/copy-fields/{sourceId}
```
> 소스 계약의 필드를 대상 계약으로 복사합니다.
> 소스 서명자의 `sign_order` → 대상 서명자의 `sign_order`로 매핑합니다.
### 5.4 서명 요청 API
#### 서명 요청 발송
```
@@ -576,7 +712,7 @@ POST /api/v1/esign/contracts/{id}/send
POST /api/v1/esign/contracts/{id}/remind
```
### 5.4 서명 수행 API (토큰 기반, 비로그인)
### 5.5 서명 수행 API (토큰 기반, 비로그인)
#### 서명 페이지 접속
```
@@ -663,7 +799,7 @@ POST /api/v1/esign/sign/{access_token}/reject
}
```
### 5.5 문서 API
### 5.6 문서 API
#### 원본 PDF 조회 (인증 후)
```
@@ -694,7 +830,7 @@ GET /api/v1/esign/contracts/{id}/verify
}
```
### 5.6 감사 로그 API
### 5.7 감사 로그 API
#### 감사 로그 조회
```
@@ -858,7 +994,7 @@ storage/app/esign/
|---|--------|--------|------|------|
| 1 | ES_DASH | 대시보드 | /esign | 계약 현황 통계 + 목록 |
| 2 | ES_CREATE | 계약 생성 | /esign/create | PDF 업로드 + 정보 입력 |
| 3 | ES_FIELDS | 서명 위치 지정 | /esign/{id}/fields | PDF 위에 서명란 배치 |
| 3 | ES_FIELDS | 서명 위치 지정 | /esign/{id}/fields | PDF 위에 서명란 배치 + 템플릿 저장/불러오기/복사 |
| 4 | ES_SEND | 서명 요청 발송 | /esign/{id}/send | 상대방 정보 입력 + 발송 |
| 5 | ES_DETAIL | 계약 상세 | /esign/{id} | 진행 상태 + 감사 로그 |
@@ -908,6 +1044,17 @@ storage/app/esign/
| 계약 목록 필터/정렬/검색 | MNG + API |
| 서명 거절 + 사유 입력 | MNG + API |
### Phase 3.5: 필드 템플릿 & 복사 (구현 완료)
| 작업 | 담당 | 상태 |
|------|------|------|
| esign_field_templates / esign_field_template_items 테이블 생성 | API | 완료 |
| EsignFieldTemplate, EsignFieldTemplateItem 모델 | MNG | 완료 |
| 템플릿 CRUD API (목록/저장/삭제) | MNG | 완료 |
| 템플릿 적용 API (signer_order 매핑) | MNG | 완료 |
| 다른 계약에서 필드 복사 API | MNG | 완료 |
| 서명 위치 설정 화면에 템플릿 드롭다운 + 모달 3개 | MNG | 완료 |
### Phase 4: 확장 기능 (v2, 추후)
| 기능 | 설명 |
@@ -915,7 +1062,6 @@ storage/app/esign/
| SMS 인증 | Coolsms/NHN Cloud 연동 |
| 카카오 알림톡 | 카카오 비즈메시지 연동 |
| 다자간 서명 (3인 이상) | signers 테이블 확장 |
| 템플릿 관리 | 자주 쓰는 계약서 양식 저장 |
| 외부 API 제공 | 타 시스템에서 전자계약 호출 |
| 블록체인 공증 | 계약 해시를 블록체인에 기록 |
@@ -954,7 +1100,9 @@ database/migrations/
├── 2026_02_12_100000_create_esign_contracts_table.php
├── 2026_02_12_110000_create_esign_signers_table.php
├── 2026_02_12_120000_create_esign_sign_fields_table.php
── 2026_02_12_130000_create_esign_audit_logs_table.php
── 2026_02_12_130000_create_esign_audit_logs_table.php
├── 2026_02_12_140000_create_esign_field_templates_table.php
└── 2026_02_12_140100_create_esign_field_template_items_table.php
app/Models/ESign/
├── EsignContract.php
@@ -988,8 +1136,17 @@ routes/api/v1/
### 10.2 MNG 프로젝트 (`/home/aweso/sam/mng`)
```
app/Models/ESign/
├── EsignContract.php
├── EsignSigner.php
├── EsignSignField.php
├── EsignAuditLog.php
├── EsignFieldTemplate.php # 필드 템플릿
└── EsignFieldTemplateItem.php # 템플릿 필드 항목
app/Http/Controllers/ESign/
├── EsignController.php # 인증 필요 (5 화면)
├── EsignApiController.php # 내부 API (9 메서드 + 템플릿 5 메서드)
└── EsignPublicController.php # 비인증 (3 화면)
resources/views/esign/
@@ -1089,6 +1246,13 @@ EsignSignField
EsignAuditLog
├── contract() → BelongsTo → EsignContract
└── signer() → BelongsTo → EsignSigner
EsignFieldTemplate
├── items() → HasMany → EsignFieldTemplateItem
└── creator() → BelongsTo → User
EsignFieldTemplateItem
└── template() → BelongsTo → EsignFieldTemplate
```
---
@@ -1312,7 +1476,89 @@ public function dashboard(Request $request): View|Response
---
## 16. 미구현 기능 (v1.1 이후)
## 16. 필드 템플릿 사용법 (사용자 가이드)
### 16.1 개요
서명 필드를 매 계약마다 수동 배치하는 반복 작업을 줄이기 위한 기능입니다.
자주 쓰는 필드 배치를 **템플릿으로 저장**하거나, **기존 계약에서 복사**할 수 있습니다.
### 16.2 진입 경로
```
사이드바: 전자계약(E-Sign) → 대시보드 (또는 보관함)
→ 계약 클릭 → 상세 페이지
→ [서명 위치 설정] 버튼 → 필드 편집기 진입
```
### 16.3 Toolbar 메뉴
필드 편집기 상단 Toolbar 우측에 **[템플릿 ▾]** 드롭다운 버튼이 있습니다.
```
[← 뒤로] [] 100% [+] [▦] [↩ ↪] [템플릿 ▾] [저장]
├─ 📁 템플릿으로 저장
├─ 📂 템플릿 불러오기
└─ 📋 다른 계약에서 복사
```
### 16.4 시나리오별 사용법
#### A. 템플릿으로 저장 (반복 사용할 배치 저장)
1. 계약의 서명 위치 설정 화면에서 필드를 원하는 대로 배치합니다
2. **[템플릿 ▾]** → **📁 템플릿으로 저장** 클릭
3. 모달에서 **이름**과 **설명**(선택)을 입력합니다
4. **[저장]** 클릭 → 현재 필드 배치가 템플릿으로 저장됩니다
> 저장 시 각 필드의 `signer_id`는 자동으로 `signer_order`(1, 2)로 변환됩니다.
> 따라서 다른 계약에 적용해도 서명자 순서에 맞게 자동 매핑됩니다.
#### B. 템플릿 불러오기 (저장된 배치를 새 계약에 적용)
1. 새 계약의 서명 위치 설정 화면 진입
2. **[템플릿 ▾]** → **📂 템플릿 불러오기** 클릭
3. 모달에 저장된 템플릿 목록이 표시됩니다 (현재 계약의 서명자 수에 맞는 것만)
4. 원하는 템플릿 선택 → **[적용]** 클릭
5. 확인 대화상자에서 **확인** → 기존 필드가 삭제되고 템플릿 필드가 적용됩니다
> 템플릿의 서명자 수가 현재 계약보다 많으면 에러 메시지가 표시됩니다.
> 불필요한 템플릿은 목록에서 **[×]** 버튼으로 삭제할 수 있습니다.
#### C. 다른 계약에서 복사 (템플릿 없이 직접 복사)
1. 새 계약의 서명 위치 설정 화면 진입
2. **[템플릿 ▾]** → **📋 다른 계약에서 복사** 클릭
3. 모달에서 계약 **제목 또는 코드로 검색**
4. 복사할 계약 선택 → **[복사]** 클릭
5. 확인 대화상자에서 **확인** → 필드가 복사됩니다
> 소스 계약 서명자의 `sign_order`를 기준으로 대상 계약 서명자에 매핑됩니다.
> 현재 계약 자신은 목록에서 제외됩니다.
### 16.5 서명자 매핑 로직
```
[템플릿/복사 적용 시]
signer_order = 1 → 현재 계약에서 sign_order = 1인 서명자의 signer_id
signer_order = 2 → 현재 계약에서 sign_order = 2인 서명자의 signer_id
[예시]
템플릿: signer_order=1 (갑 서명란), signer_order=2 (을 서명란)
계약 A: 김갑순(sign_order=1, id=10), 박을동(sign_order=2, id=11)
결과: signer_order=1 → signer_id=10, signer_order=2 → signer_id=11
```
### 16.6 주의사항
- 템플릿/복사 적용 시 **기존 필드가 모두 삭제**됩니다 (확인 대화상자 표시)
- 적용 후 **[저장] 버튼을 눌러야** DB에 최종 반영됩니다
- 적용 후 필드 위치를 추가 조정할 수 있습니다
- Undo(Ctrl+Z)로 적용 전 상태로 되돌릴 수 없습니다 (서버에서 직접 적용되므로)
---
## 17. 미구현 기능 (v1.1 이후)
| 기능 | 현재 상태 | 구현 방안 |
|------|----------|----------|
@@ -1326,4 +1572,4 @@ public function dashboard(Request $request): View|Response
---
*이 문서는 SAM E-Sign v1.0 구현 기준 기술 설계서입니다. 최종 업데이트: 2026-02-12*
*이 문서는 SAM E-Sign v1.1 구현 기준 기술 설계서입니다. 최종 업데이트: 2026-02-12*