# 0.5 제품 계약과 남은 경계

2026-10-26 (Asia/Seoul). 레벨 스튜디오의 자체 구현 명세다. HighLevel 공식 도움말·공개 클라이언트 정적 근거는 `REA-0.5.md`에 구분하며 아래 데이터 모델/검증/운영 정책이 원본 비공개 서버와 동일하다는 뜻은 아니다. 0.4의 CRM·일정·파일·청구·캠페인·평판·자동화 계약은 이 문서가 변경한 항목을 제외하고 유지한다.

## 다중 페이지와 퍼널 단계

사이트는 안정적인 page ID, 고유 slug, 제목, 블록 배열을 가진 페이지 1~50개를 가진다. 운영자는 추가·선택·수정·정렬·제거하고 페이지별 블록을 편집한다. 중복 ID/slug, 빈 제목, 잘못된 주소를 거절한다. 마지막 페이지를 제거해 유효하지 않은 사이트를 만들지 않는다.

초안과 게시 revision을 구분한다. 게시 시 전체 페이지 구조를 보존하며 그 뒤 초안을 바꿔도 기존 발행본은 유지한다. `/site/<게시-site-slug>/<페이지-slug>`와 사이트 첫 페이지 경로, 게시 페이지의 내비게이션·이전/다음 단계 링크를 렌더한다. `/preview/site/<site-id>?page=<slug>`는 현재 초안을 미리 본다. 원본 임의 HTML/JavaScript를 실행하는 builder가 아니라 지원 블록을 안전하게 렌더하는 구조다.

예전 단일 페이지 schemaVersion 1 자료는 안정적인 첫 page ID/slug로 보강한다. 이전 sections 데이터와 게시 이력을 손실시키지 않는다. 이 퍼널 단계는 내부 페이지 이동이며 광고/방문자 계측·결제 전환·외부 도메인 연결을 자동 제공하지 않는다.

## 조건부 폼과 실제 접수

필드는 기존 타입·필수·선택 항목·CRM 매핑에 기본값과 `visibleWhen`을 가질 수 있다. 조건은 다른 필드 ID의 `equals`, `not_equals`, checkbox `checked`다. 대상 필드 부재·자기 참조·순환·잘못된 타입·유효하지 않은 기본값을 거절한다. 종속 필드의 기준 필드 자체가 숨겨져 있으면 종속 필드도 숨긴다.

폼 표시만 믿지 않고 실제 POST 때 게시 revision의 조건과 값으로 가시성을 다시 계산한다. 숨긴 필드의 입력은 응답 데이터/CRM 매핑에서 제외하고 숨긴 필수 필드는 필수 검사하지 않는다. 표시된 필드는 기존 타입·필수 검증을 적용한다. 초안의 조건 변경은 발행된 폼 접수 계약을 몰래 바꾸지 않는다.

접수 ID와 내용이 같은 재전송은 기존 응답이며 같은 ID의 다른 입력은 충돌이다. 검증 후 접수 기록·고객 연결·내부 자동화 이벤트를 저장한다. 오류를 accepted 성공으로 만들지 않는다. 임의 조건 JavaScript, 원본의 모든 설문 분기·결과 메시지·외부 리디렉션/분석을 완전히 복제한 것은 아니다.

## 강의 퀴즈·진도·수료증

과정/모듈/차시·수강 등록·미디어·진도 관리 위에 차시별 단일 선택 퀴즈를 저장한다. 퀴즈는 1~100문제이며 보기 2개 이상·유일한 문제 ID·유효한 정답 인덱스·0~100 통과 기준을 검사한다. 점수는 정답 문항 비율이며 모든 문제에 보기 하나씩 답해야 한다. 원본의 카테고리별/가중/복수 선택 점수까지 제공한다는 의미는 아니다.

응시는 활성 고객과 수강 등록에 연결된다. 고유 attempt ID, 당시 퀴즈·답안·점수·통과 여부·제출 시각을 보관한다. 같은 ID/답안 재요청은 중복 생성하지 않고 답안을 바꾼 재요청은 거절한다. 통과 시 해당 차시 진도를 반영한다. 퀴즈가 변경되면 기존 합격 기록을 새 문제의 합격으로 간주하지 않고 현재 퀴즈 통과를 다시 요구한다. 기존 시도 원본은 유지한다.

수료증은 모든 차시와 현재 필요한 퀴즈 완료 후 발급한다. 번호, 고객/과정/수강 등록, 차시 요구사항, 완료 ID, 퀴즈 증빙, 발급 시각을 스냅샷으로 고정한다. 같은 certificate ID의 재요청은 멱등이며 다른 과정/고객으로 재사용하지 않는다. 스냅샷과 실제 연결/점수가 어긋난 백업은 거절한다. `/certificate/<id>`를 열어 인쇄 가능한 내부 학습 기록을 렌더한다. 발행 당시 이름/과정/퀴즈 증빙은 후속 고객 수정·병합 때문에 덮어쓰지 않는다.

이 player는 **소유자 작업공간의 관리자용 비공개 응시 화면**이다. 별도 학생 로그인·권한 격리된 독립 학습자 포털·공인 자격·외부 인증기관 검증·이메일 자동 발송을 제공한다고 표현하지 않는다.

## 타입 필드·사업 객체·동적 목록

CRM 사용자 필드와 사업 객체 필드는 text/number/date/select/boolean/contact를 지원한다. 객체 정의당 최대 100필드, 중복 필드 ID/이름·예약된 위험 키·중복 옵션·잘못된 날짜/숫자/선택값·없는 고객 관계를 검사한다. `0`과 `false`는 실제 값으로 보존한다. 필수는 기존 데이터에도 적용하므로 기존 값을 깨뜨리는 스키마 변경은 거절한다.

사업 레코드는 이름·객체 ID·실제 필드 값·생성/수정/보관 상태를 가진다. 생성·수정·소프트 보관/복원과 CSV를 제공한다. 보관된 것을 포함해 레코드가 있는 객체, 고객 값/저장 필터가 연결된 CRM 필드의 영구 삭제를 거절한다. 신규 관계는 활성 고객만 선택하며 이미 연결한 고객의 보관은 과거 관계를 삭제하지 않는다.

CSV는 헤더를 읽어 ID/이름/필드를 매핑한다. 10,000행 이내에서 생성·수정·ID upsert를 선택하고 한 행이라도 오류면 전체 변경을 거절한다. 중복 입력 ID·다른 객체의 레코드 ID를 거절한다. 동일 ID CSV의 반복 upsert는 레코드 수를 늘리지 않는다. CSV의 텍스트 수식 접두사·따옴표/개행을 안전하게 처리하며 숫자 음수와 boolean은 보존한다. 원문 전체 백업은 JSON을 사용한다.

동적 목록은 태그 all/any, 유입, 담당자, 이름/회사/연락처 검색, 활성 여부와 복수 사용자 필드 AND 조건을 저장한다. 비교는 eq/ne/contains/gt/gte/lt/lte/is_set/is_empty이며 필드 타입에 맞는 연산만 허용한다. UI에서 조건 행을 추가/제거/수정할 수 있다. 조회 때 실제 고객 데이터로 다시 계산하고 별도 복제 고객 목록을 만들지 않는다. 목록 삭제는 고객을 삭제하지 않는다.

고객 병합은 동적 관계 값을 최종 기준 고객으로 재연결한다. CRM 값 충돌은 기준 고객 값이 우선하고 빈값만 원본에서 채운다. `dataHubMergeHistory`에 원본과 선택 결과를 보존하며 일반 병합 참조 재배선에서 배열 전체를 제외한다. tombstone의 값은 같은 이관 계보에 한 번만 반영하므로 병합 후 사용자가 명시적으로 비운 값을 재검증/후속 병합 때 되살리지 않는다. 발행본·접수본·캠페인 준비본·응시/수료증의 원본 스냅샷은 현재 링크와 구분한다.

관계 타입은 고객 참조이며 임의 객체 간 다대다 association 엔진 전체를 뜻하지 않는다. 표시는 CRM 값 화면 처음 200고객/5열, 동적 목록 처음 500고객이며 동적 목록 CSV에는 전체 결과를 포함한다.

## 저장·호환·검증

`web/`이 정본이다. 순수 명령→전체 상태 검증→기존 소유자별 서버 저장/CAS와 mutation 멱등을 사용한다. D1은 상태/chunk와 파일 metadata, R2는 실제 파일 본문을 저장하고 로컬은 Node 24.22.1의 SQLite/디스크 어댑터를 사용한다. 새로운 source clone도 `npm ci`, `npm test`, `npm run check`, `npm run build`로 재현하고 `Start.cmd` 또는 `npm start`로 loopback API를 실행한다.

schemaVersion 1을 유지한다. 이전 JSON에 새 배열·페이지·기본 필드가 없으면 결정적으로 보강하며 입력/기존 revision·extras·원본 스냅샷을 바꾸지 않는다. JSON은 업로드 파일 bytes를 포함하지 않으므로 E checkpoint의 온라인 DB snapshot과 objects 원본, 클라우드 D1/R2는 별도로 인계한다.

현재 통합 자동 시험 **126개 통과**, 실제 로컬 API/UI의 페이지별 내비게이션·조건부 폼 접수·관리자 퀴즈/수료 조건·수료증 인쇄 경로를 확인했다. DataHub의 10개 시험은 원자 CSV/반복 upsert·병합 삭제값 보존·다중 AND UI까지 포함한다. 최종 생산 빌드·원격 배포·clone/E 복원은 각각 완료 영수증을 따른다.

## 남은 경계

- HighLevel 로그인 후 UI·서버·권한의 동등성은 미검증이다. 정적 REA partial/no-match가 원본 기능 부재를 뜻하지 않는다.
- 실제 SMS/이메일/전화·소셜 발송, 실결제·환불·구독, AI 호출, 외부 리뷰·Google/Outlook 인증/동기화는 미연결이다.
- Sites 연결 적격성 확인 도구가 현재 도구 목록에 없어 ChatGPT Gmail 연결을 배포 사이트의 권한으로 간주할 근거가 없다. 관리자 deny를 확인한 것은 아니다.
- owner-private audience를 유지한다. 익명 고객 폼과 독립 수강생 로그인은 없다.
- 자동화는 접속 중 약 25초 tick·API·다음 접속 복구로 due 작업을 처리한다. 사이트를 닫은 동안 정시 실행할 상주 cron은 없다.
