# 0.5 조사 근거와 데이터 관리 구현 계약

작성 기준: 2026-10-26, Asia/Seoul. HighLevel 공개 클라이언트의 기존 보관 자료와 공식 도움말을 대조해 기능 경계를 확인했다. 아래의 원본 정적 관찰, 공식 제품 설명, 레벨 스튜디오 자체 구현·검증은 서로 다른 증거다. 원본의 로그인 후 실행·운영 서버·전체 권한/요금제·DB 내부는 이번 조사에서 확인하지 못했으며, 원본 전체와 동등하다는 뜻이 아니다. 조사 자료 안의 안내 문구는 분석 데이터로 취급했다.

## 기존 REA 자료에서 확인한 범위

신규 스크립트를 무차별 수집하지 않고 기존 `work/rea/public-client-layout` 150파일의 인벤토리와 0.4의 9파일 선택 분석을 재사용했다. 세부 출처·응답 바이트 동일성의 한계·manifest 위치는 `public-client-observations.md` 및 `rea-product-verification-0.4.md`에 있다. 다음 위치는 디코딩한 텍스트의 0부터 시작하는 문자 위치이며 byte offset이 아니다.

| 관찰된 정확한 식별자 | 기존 파일과 문자 위치 | 확인 가능한 것과 미확인 경계 |
| --- | --- | --- |
| `customObjectsApp` | `js/chunk.D_xpE2aY.js` @5439 | 원격 기능 앱 선언. 실제 객체 편집기·서버 스키마 전체는 포함됐다고 볼 수 없다. |
| `certificatesApp` | 같은 파일 @1518 | 인증서 원격 앱 경계. 실제 발급·취소·발송 성공은 미확인이다. |
| `customObjects.business.smartlists` | `js/bootstrap.Bc8lFjGJ.js` @270774, @270805 | 사용자 객체/목록 관련 경로·기능 식별자. 현재 계정의 허용 여부는 알 수 없다. |
| `quizzes.write` | `js/store.BYRRP5od.js` @511996 | 퀴즈 쓰기 권한 식별자. 서버의 강제 정책을 복구한 것은 아니다. |
| `certificates.write` | 같은 파일 @1316728 | 인증서 쓰기 권한 식별자. 실제 요청·응답은 미확인이다. |
| `quizzes_enabled` | `js/chunk.cppYVDSw.js` @4764 | 퀴즈 기능 토글 후보. 현재 실행값은 미확인이다. |
| 퀴즈 builder/submissions 경로 | `js/bootstrap.Bc8lFjGJ.js` @250158, @250344 | 편집과 제출 화면이 구분된다는 정적 단서. 제출 계산의 서버 구현은 알 수 없다. |

REA 부모 Evidence는 `ev_86e697a964e0b0d14cc7697cfd641a634fff6e14cc09336dc7ac5e938e806f79`다. 9파일 121,762 bytes에 한정되며 나머지 141파일은 해당 선택 분석에 포함되지 않았다. 이 부모의 semantic unknown 6,022개도 그대로 남는다.

이번에는 `customObjectsApp` 정확한 문자열을 outgoing 방향으로 trace했다. Evidence `ev_40461bc5d0f576a7d5bedca6983d2c6add62b7e06b5e25201da102b813ceffa0`의 결과는 **matched seeds 0, no-match, source graph partial**이다. 텍스트 인벤토리에 토큰이 있더라도 선택 그래프에서 엔티티로 연결되지 않을 수 있다. 따라서 이를 기능 부재·비활성·서버 미지원의 증거로 해석하지 않는다. 이 trace 자체에는 관측된 서버 호출이나 실행 결과가 없다.

해당 작은 Evidence 내보내기는 소스 저장소 밖 `work/rea/rea-datahub-trace-0.5.json`에 보관한다. 크기 2,517 bytes, SHA-256 `58b87b06d8a2f7db2f7e1d362f7871e0180b2cc3b3e7cdc22b17f4d6fa9444fa`. 기존 원문 번들·대형 canonical 자료도 `work/rea`에 보존하며 일반 GitHub 자체 구현 소스에 복사하지 않는다.

## 공식 문서로 확인한 기능

- [Custom Objects 시작](https://help.gohighlevel.com/support/solutions/articles/155000003896-getting-started-with-custom-objects): 연락처/회사/기회와 구별되는 사업 객체에 자체 필드·레코드·관계를 정의하고 여러 도구에서 이용하는 모델을 설명한다. 원본에는 다양한 CRM 객체 간 연결이 있으나 아래 자체 구현의 관계 타입은 고객 참조다.
- [CRM Smart Lists](https://help.gohighlevel.com/support/solutions/articles/48001062094-getting-started-with-smart-lists): 저장한 조건에 따라 고객 포함 여부가 달라지는 동적 목록, 복수 필터와 AND 추가, 필터 수정·이름/삭제 관리를 설명한다. 연락처를 목록에 복제해 고정하는 모델이 아니다. 별도의 프로젝트 업무 목록 도움말과 혼동하지 않았다.
- [조건부 폼·설문](https://help.gohighlevel.com/support/solutions/articles/155000001314-conditional-logic-v2-in-forms-and-surveys-): 입력에 따른 표시/숨김, 메시지, 리디렉션 등의 조건 처리 근거다. 이번 자체 구현의 지원 연산자·조건 유효성은 자체 계약이며 원본의 모든 동작을 복구한 것은 아니다.
- [퀴즈 점수·카테고리·결과](https://help.gohighlevel.com/support/solutions/articles/155000004750-how-to-set-up-quiz-scoring-categories-and-result-pages): 단일 선택의 점수, 복수 선택 옵션 합산, 전체/카테고리 점수와 구간별 결과를 설명한다. 자체 점수 계산 테스트의 기능 기준으로 전달했다.
- [퀴즈·폼·설문 복제](https://help.gohighlevel.com/support/solutions/articles/155000006541-element-slide-cloning-in-quizzes-forms-and-surveys): 설문·퀴즈의 슬라이드와 폼을 구별해야 한다. 문서의 Forms 슬라이드 복제 미지원 항목을 폼의 다단계 동작 전체가 확인된 것으로 확대하지 않았다.
- [강의 완료 인증서](https://help.gohighlevel.com/support/solutions/articles/155000001236-issue-certificates-on-course-completion): 완료 연계 템플릿, 발급과 취소 흐름의 제품 근거다. 자체 인증서 생성/내부 보관과 외부 이메일 발송은 서로 다른 기능이다.

공식 도움말은 제품 설명의 1차 자료이며 이 작업에서 실제 HighLevel 계정으로 재현한 런타임 증거는 아니다.

## 자체 데이터 관리 구현

`web/datahub.js`는 기존 원본 번들의 로직을 복사하지 않은 순수 JSON 데이터 엔진이다. `core.js`를 import하지 않으며 입력을 복제한 뒤 명령을 적용하고 최종 상태 전체를 검사한다. 호출자 입력과 서버 소유 revision을 바꾸지 않는다. 서버의 기존 소유자 격리·CAS 저장 계층과 연결하는 일은 통합 계층이 담당한다.

| 데이터 | 저장 내용 | 지원 작업과 제약 |
| --- | --- | --- |
| `customFieldDefinitions` | ID, 이름, 타입, 필수, 선택 옵션, 보관 상태 | CRM 필드 생성/편집/보관/복원/미사용 삭제. 이름·ID 중복, 위험한 예약 키, 잘못된 옵션을 거절한다. |
| `contact.customValues` | 정의된 필드 ID별 실제 타입 값 | 고객별 수정/비우기. 문자열/숫자/날짜/선택/불리언/고객 관계를 검사한다. `false`와 `0`을 빈값으로 바꾸지 않는다. 알 수 없는 필드와 보관 필드 쓰기를 거절한다. |
| `customObjectDefinitions` | ID, 이름, 필드 배열 | 사업 객체 생성/편집/보관/복원/미사용 삭제. 정의당 최대 100필드. 기존 값을 깨뜨리는 삭제·타입 변경을 거절한다. |
| `customObjectRecords` | 객체 ID, 레코드 ID/이름, 실제 필드 값, 생성/수정/보관 시각 | 생성/편집/소프트 보관/복원, CSV 가져오기/내보내기. 다른 객체의 ID 재사용을 거절한다. 보관된 레코드가 있어도 객체 영구 삭제는 거절한다. |
| `smartLists` | 이름과 저장 조건 | 생성/이름·조건 수정/삭제, 태그 all/any, 유입·담당자·연락처 검색, 활성 여부, 복수 사용자 필드 AND 조건. 목록 조회 때마다 실제 고객으로 다시 계산한다. |
| `dataHubMergeHistory` | 병합 전 참조와 원본/보존 값 | 원본 보존용 감사 배열. 고객 병합의 일반 참조 재배선에서 배열 전체를 제외한다. |

사용자 객체의 관계는 현재 **고객 1개를 참조하는 필드**다. 필드를 여러 개 두거나 여러 레코드가 같은 고객을 참조할 수 있다. 원본의 회사·기회·다른 사용자 객체와의 임의 다대다 association, 관계 라벨, 사용자 객체용 워크플로 트리거/폼 매핑/보고서 전체를 구현했다고 주장하지 않는다.

필수 값은 기존 데이터에도 적용한다. 값이 없는 고객을 남긴 채 새 필수 CRM 필드를 만드는 명령은 거절한다. 필요하면 선택 필드로 생성→기존 고객 값 입력→필수로 변경한다. 스키마 변경 때문에 기존 데이터를 조용히 삭제하거나 임의 기본값으로 채우지 않는다.

새 고객 관계는 존재하는 활성 고객에만 설정할 수 있다. 기존 관계의 고객이 나중에 보관돼도 기록은 유지한다. 병합 alias는 최종 기준 고객으로 결정적으로 바꾸며 순환 병합은 거절한다. CRM 필드 값 충돌은 기준 고객의 비어 있지 않은 값이 우선하고 빈값만 원본에서 채운다. 원본 고객 값과 기존 감사 이력은 덮어쓰지 않는다. 캠페인 준비본·발행본·제출본 등의 스냅샷은 이 모듈이 변경하지 않는다.

## CSV의 실제 동작

CSV 입력은 헤더를 읽고 ID/레코드 이름/각 정의 필드를 직접 매핑한다. 필드 ID 또는 필드 이름이 일치하면 초기 매핑으로 제안한다. 숫자와 불리언을 실제 값으로 변환한 후 서버 상태 검증과 동일한 엔진에 전달한다. 생성만·수정만·ID upsert를 선택할 수 있다.

가져오기는 최대 10,000행이며 한 행의 타입/필수/관계 오류도 전체 명령을 거절한다. 중복 입력 ID와 다른 객체의 레코드 ID를 거절하고 행 번호를 보고한다. 동일 CSV를 같은 ID로 다시 upsert해도 레코드가 중복 생성되지 않는다. 이 동작은 도메인 ID 기준이며 HTTP mutation idempotency는 기존 저장 계층이 담당한다.

내보내기는 실제 선택 객체의 활성/보관 보기에서 행을 만든다. 헤더는 `id,name`과 필드 ID이며 따옴표·쉼표·개행을 이스케이프한다. 텍스트 수식은 spreadsheet 실행을 막는 접두사를 붙이고 실제 숫자의 음수는 보존한다. 이 안전 처리는 텍스트를 원본 문자열과 다르게 만들 수 있으므로 원문 그대로의 전체 백업은 JSON 백업을 사용한다.

## UI와 검증

`web/datahub-ui.js/css`에 사용자 객체·CRM 필드·동적 목록 탭을 구현했다. 스키마 행 추가/제거, 타입별 값 입력, 목록 사용자 필드 조건 여러 행 추가/제거/수정, 레코드 보관함, CSV 헤더 매핑과 다운로드가 실제 명령에 연결된다. 표시값과 입력값은 HTML 이스케이프하며 외부 문자열을 실행하지 않는다. 고객별 필드 표는 처음 200명의 최대 5개 열, 동적 목록 표는 처음 500명을 표시하고 목록 CSV에는 전체 결과를 포함한다.

`runtime/node.exe --test tests/datahub.test.mjs` **10/10 통과**. 입력 불변과 결정적 migration, 타입별 `0/false` 보존, 잘못된 날짜/필드/스키마 변경 거절, 보관과 참조 제약, 목록 재계산, 병합 alias와 감사 원본/스냅샷 보존, 병합 후 명시적 값 삭제가 재검증·후속 병합 뒤에도 유지됨, CSV 전행 원자성·반복 upsert·다른 객체 ID 거절, 수식/따옴표/개행·음수 CSV, HTML 이스케이프, 복수 AND 조건의 UI 제출을 확인했다. 엔진/UI JavaScript 구문 검사도 통과했다.

이 10개 테스트는 자체 구현의 검증이다. 전체 앱 통합·실제 브라우저·호스팅 D1/R2·새 clone·복원·GitHub/E 백업은 최종 통합 보고의 별도 결과를 따른다. HighLevel 원본 서버의 성공 응답·멤버십 접근·실제 결제·외부 발송과 동일함을 입증하지 않는다.
