크라우니캔버스 F5 — 검색.한선 (전문검색 엔진)
개요
crowny-canvas 앱 계약(F5 모듈)의 검색.한선을 독립 컴파일 라이브러리로 구현.
페이지 제목+블록 텍스트를 2그램 인덱스로 색인, 1000페이지 합성 데이터에서
검색_질의() 단독 소요 51ms(300ms 기준 대비 6배 마진)로 셀프테스트 PASS.
무엇을 했는지
검색_색인갱신(id, 제목, 본문텍스트) — 페이지 저장 시 호출할 색인 갱신 함수 노출
검색_질의(q) → [{id,제목,발췌},...] JSON, 제목 정확매칭 +1000 가중
- 2그램 인덱스: 문자 2그램(글자()/부분(), 색인 원문 200자 상한으로 O(n²) 비용 억제) +
ASCII US(0x1F) 내부 구분자로 그램 경계 매칭(공백 포함 그램도 안전)
- PSV 샤딩: 문자열 65535B 하드 캡(crownyc.c STR_MAX_LEN) 때문에 append-log
단일 파일로는 1000페이지(≈400KB)를 못 담음 → 매니페스트(
검색샤드.psv) +
샤드 파일(
검색인덱스_N.psv, 문자수 12000 상한 롤오버)로 분할
- 조회 성능 최적화: 초기 버전은 바이트 단위 스캔(
버퍼읽기 2인자 우회용 __내장__(392,..,1)
1바이트씩)으로 1000페이지 질의 775ms~1.2s —
버퍼찾기(BUF_FIND)+버퍼잘라 "찾아서
자르기" 패턴으로 전환해 51ms까지 단축(15배)
VM 함정 신규 발견 (가드레일 템플릿에 반영 완료)
버퍼읽기(buf,i) 2인자 호출 = 쓰레기값: hanseonc_high 테이블 arity=2, VM opcode 392
실제는 3인자(len,off,handle) 요구 → 우회
__내장__(392,buf,i,1) (1바이트 문자열 반환,
정수 아님, ASCII 등가비교만)
- 문자열 65535B 캡은 파일 읽기에도 적용 — append-log PSV가 자연 성장하면 조용히
뒷부분 유실(에러 없음). 샤딩 설계 필수
버퍼찾기(arity 일치, C memcmp)로 통째 탐색 후 자르기가 바이트 단위 스캔보다
압도적으로 빠름(1바이트 스캔은 매 위치 STR_NEW 문자열 핸들 소모)
관련 파일
/Users/ef/crowny-canvas/한선씨/검색.한선 — 최종 구현(독립 컴파일, 다른 신규 라이브러리 미가져오기)
/Users/ef/.claude/templates/한선씨-가드레일.md — 신규 VM 함정 3건 반영(버퍼읽기 2인자, 65535B 샤딩, 찾아서자르기)
- 학습DB:
한선씨PSV버퍼구분자찾아자르기, 한선씨대용량PSV버퍼찾아자르기2그램색인 (crownycode-learn add)
컴파일/실행 실측
cd /Users/ef/CrownyOS/crownyc
CROWNY_STRICT=1 ./hanseonc_high /Users/ef/crowny-canvas/한선씨/검색.한선 > /tmp/검색_final.toau
# exit=0, 경고 0 (STRICT 포함)
CANVAS_SEARCH_SELFTEST=1 ./crownyc run /tmp/검색_final.toau
# [검색.한선] 질의('마케팅') 소요 51ms
# PASS: 300ms 이내(51ms)
# PASS: 결과에 질의어(제목) 포함 확인
# PASS: 빈 질의 → []
# PASS: 삭제 표식 후 미검색 확인
잔여 이슈
- 결선(저장소.한선/서버.한선이
검색_색인갱신 호출, /api/검색?q= 라우팅)은 별도 결선 단계 담당(계약상 이 임무 범위 밖)
libs/CIF변환.한선 등 기존 라이브러리도 2인자 버퍼읽기 사용 — 동일 결함 의심(미검증, 별도 감사 필요)
- 그램은 바이트 아닌 문자 2그램이라 의미상 자연스러우나, 근접-오탐(희귀 질의도 흔한 부분열과 겹치면 낮은 점수로 노출)은 설계상 트레이드오프 — 필요 시 최소점수 임계값 도입 여지