graph TD
subgraph CLIENT["클라이언트 (웹 브라우저)"]
UI["블록 에디터 UI\n(드래그앤드롭 / 인라인 편집)"]
IDB["로컬 대용량 스토리지\nIndexedDB\n(오프라인 드래프트 캐시)"]
WF["워크플로우 엔진\nWorkflowEngine\n(DRAFT ↔ LOCKED)"]
CE["기여도 엔진\nContributionEngine\n(해시·가중치 산출)"]
AI_MOD["AI 청크 교정 모듈\n(HTML 보존 / 중단·재개)"]
end
subgraph SERVER["서버"]
RT["실시간 채널\nPresence · Broadcast\n(편집모드 선점 / 동기화)"]
DB["데이터베이스\nProjects · Parts · Chapters\nBlocks · Histories"]
AI_SRV["생성형 AI 서버\n(문체 교정 / 도서 생성)"]
end
UI -->|100ms 디바운스 캐싱| IDB
IDB -->|네트워크 복구 시 동기화| DB
UI -->|상태 전이 요청| WF
WF -->|상태 업데이트| DB
UI -->|블록 저장 시 해시 비교| CE
CE -->|이력 기록 weight| DB
UI -->|교정 요청| AI_MOD
AI_MOD -->|청크 JSON 전송| AI_SRV
AI_SRV -->|교정 결과 반환| AI_MOD
AI_MOD -->|병합 확정| DB
UI <-->|Presence / 편집모드| RT
RT <--> DBstateDiagram-v2
direction LR
[*] --> DRAFT : 블록 생성
DRAFT --> LOCKED : 승인 처리\n(편집자 권한)
LOCKED --> DRAFT : 승인 취소\n(잠금 해제)
state DRAFT {
direction TB
d1: 직접 수정 가능
d2: AI 교정 대상 포함
d3: 기여도 이력 기록 중
}
state LOCKED {
direction TB
l1: 직접 수정·삭제 차단
l2: 수정 요청 기록 가능\n(pending → reviewing → approved)
l3: 최종 기여도 산출 대상
}
LOCKED --> [*] : 출판 내보내기\n(기여도 최종 산출)flowchart TD
A([교정 요청]) --> B[텍스트 블록 필터링\ntype === 'text']
B --> C[JSON 직렬화\n블록ID + Raw HTML Text 배열]
C --> D[청크 분할\n고정 크기 N개 단위]
D --> E{중단 신호?}
E -- "Yes (AbortController)" --> Z([처리 중단\n완료 청크 결과 유지])
E -- No --> F[동적 메타 프롬프트 생성\n① HTML 구조 보존 강제 명령\n② 프로젝트 문체 지침 결합]
F --> G[AI 서버 전송\n현재 청크]
G --> H[응답 수신\n{ blockId, suggested, reason } 배열]
H --> I[마크다운 래퍼 제거\nJSON 파싱]
I --> J{다음 청크 존재?}
J -- Yes --> E
J -- No --> K[Two-pane Diff UI 렌더링\n원본 / 제안 좌우 대조]
K --> L{사용자 선택}
L -- "블록별 선택 수락" --> M[선택 블록만 병합\nDOM 구조 유지]
L -- "전체 일괄 수락" --> M
L -- "재검사 요청" --> G
M --> N([기여도 이력 기록\n교정 수락 작업 반영])flowchart TD
A([블록 저장 이벤트]) --> B[콘텐츠 해시 생성\nContent Hash]
B --> C{이전 버전\n해시와 동일?}
C -- "동일 (무변동)" --> Z([이력 기록 생략])
C -- "다름 (실질 변동)" --> D[블록 유형 분류]
D --> D1{블록 유형}
D1 -- 텍스트 --> E1["가중치 = 순수 글자 수 × 단위 가중치\n(최소값 보장)"]
D1 -- 이미지 --> E2[가중치 = 고정값 High]
D1 -- 표/Table --> E3[가중치 = 고정값 Mid]
D1 -- 코드 --> E4[가중치 = 고정값 Low]
E1 & E2 & E3 & E4 --> F["이력 저장\n{ blockId, authorId, contentHash,\naction, weight, timestamp }"]
F --> G([다음 저장 이벤트 대기])
subgraph EXPORT["출판 내보내기 시점"]
H[최종 블록 목록 순회] --> I[저자별 누적 가중치 합산\nauthor_weight 가산]
I --> J[전체 합계 계산\ntotal_weight]
J --> K["지분율 정규화\nroyalty = author_weight ÷ total_weight"]
K --> L[기여도 리포트 출력\n히트맵 + 지분율 차트]
end블록 유형 | 기본 가중치 산출 방식 |
텍스트 | HTML 태그 제거 후 순수 글자 수 × 단위 가중치 (최소값 보장) |
이미지 | 비텍스트 고가치 콘텐츠를 반영한 고정 가중치 |
표(Table) | 중간 고정 가중치 |
코드 | 하위 고정 가중치 |
출판 내보내기 시점:
1. 최종 블록 목록 순회 → 저자별 누적 가중치 합산
author_weight[authorId] += Σ history.weight (해당 저자 기여분)
2. total_weight = Σ author_weight (전체 저자 합계)
3. royalty[authorId] = author_weight[authorId] / total_weight
→ 결과: { 저자A: 0.40, 저자B: 0.35, 저자C: 0.25 }DRAFT(초안 작성 중) ←→ LOCKED(승인 완료 / 출판 확정, 직접 수정 불가)텍스트 블록:
HTML 태그 제거 후 순수 글자 수 ÷ 판형 기준 글자 수
이미지 블록:
판형 기준 글자 수 × 0.4
(이미지 1개가 해당 판형 페이지의 40%를 점유한다고 가정)
표(Table) 블록:
각 셀 텍스트 합산 + 행(Row) 수 × 50자 (구조적 오버헤드 반영)
프로젝트 합산:
각 챕터는 새 페이지에서 시작 (올림 처리)
totalPages = Σ ceil(챕터 환산 글자 수 ÷ 판형 기준 글자 수)단계 | 내용 |
0. 자료 주입 | 사용자 업로드 참고 파일을 AI Files API로 전송, 생성 프롬프트에 컨텍스트로 주입 |
1. 목차 생성 | 제목·설명·분량 설정 기반으로 { parts[{ title, chapters[{ title, question, summary }] }] } JSON 생성 |
2. 구조 저장 | 생성된 목차를 계층 구조(파트→챕터→블록)로 데이터베이스에 순차 삽입 |
3. 콘텐츠 생성 | 분량 설정(brief 500자 / standard 1,500자 / detailed 3,000자 / comprehensive 5,000자) 및 문체 설정(격식체/친근체/학술체/구어체) 조합 프롬프트로 블록 배열 [{ type, content }] 생성 및 저장 |