Search 설정 프로필
공통 Search 선택값과 Typesense 전용 연결값을 구분하고 database·Typesense 배포 프로필의 차이를 설명합니다.
Search 설정 프로필
일반 App 개발은 아래 database 프로필로 진행할 수 있습니다. Index 동작이 필요할 때 로컬 Typesense 프로필을 켜고 재색인과 drift 확인을 수행하세요. Engine credential은 배포 환경이 소유하며 App은 동일한 Resource 계약을 사용합니다. 건수 일치는 index 상태의 근거이고 행위자 접근 권한의 근거는 아닙니다.
Database 프로필로 시작
Database 프로필은 외부 검색 서비스를 요구하지 않습니다.
SCOUT_DRIVER=database
SCOUT_QUEUE=false
SCOUT_AFTER_COMMIT=true
KNOWLEDGE_SEARCH_SEMANTIC_ENABLED=false
KNOWLEDGE_SEARCH_EMBEDDING_PROFILE=none
SEARCH_CANDIDATE_GATEWAY가 없으므로 Nexia는 database gateway를 고릅니다.
로컬이나 자체 완결형 on-premise에 적합하지만 Typesense의 score, typo
tolerance, facet, highlight 기능은 제공하지 않습니다.
무엇인가
Nexia Search 설정은 공통 선택값과 engine별 연결값으로 나뉩니다. 공통 설정은 어떤 index 연동과 후보 gateway를 쓸지 Core에 알려 줍니다. Typesense 연결값은 해당 배포가 Typesense를 선택했을 때만 필요합니다. Database-only 환경에서 의미 없는 Typesense credential을 임시 문자열로 채울 이유는 없습니다.
각 key의 역할은 다음과 같습니다.
| 설정 | 범위 | 의미 |
|---|---|---|
SCOUT_DRIVER | 공통 | Laravel Scout index engine 선택 |
SEARCH_CANDIDATE_GATEWAY | 공통, 선택 | 후보 supplier override. 생략하면 Scout가 typesense일 때 Typesense, 나머지는 database를 선택 |
SCOUT_QUEUE | 공통 | Scout 동기화를 queue에서 처리할지 선택 |
SCOUT_AFTER_COMMIT | 공통 | DB transaction commit 이후에 index를 동기화 |
KNOWLEDGE_SEARCH_SEMANTIC_ENABLED | Knowledge 전용 | 통제된 Knowledge retrieval에 semantic 후보를 추가. 기본 false |
KNOWLEDGE_SEARCH_EMBEDDING_PROFILE | Knowledge 전용 | provider·model/version·dimension·storage generation 전체를 선택. 기본 none |
후보 선택 규칙은 config/search.php에 있습니다.
'gateway' => env(
'SEARCH_CANDIDATE_GATEWAY',
env('SCOUT_DRIVER') === 'typesense' ? 'typesense' : 'database',
),일반적인 배포에서는 override를 생략합니다. Index와 후보 검색 adapter를 일부러 다르게 운용하는 단계적 전환이나 진단 상황에서만 명시적으로 지정합니다.
배포 프로필에서의 위치
Typesense 프로필은 공통 선택값을 바꾸고, 아래 engine 전용 값은 배포 설정이나 secret 관리 체계에서 공급합니다.
| Typesense 설정 | 필요한 역할 |
|---|---|
TYPESENSE_HOST | Engine host. config/scout.php 기본값은 Compose service 이름 typesense |
TYPESENSE_PORT | Engine port. 기본 8108 |
TYPESENSE_PATH | 선택 URL path. 기본 empty |
TYPESENSE_PROTOCOL | 배포에 따른 http 또는 https |
TYPESENSE_API_KEY | Scout와 index lifecycle이 쓰는 서버 전용 관리 key |
TYPESENSE_SCOPED_KEY_PARENT | Typesense에 실제 등록된 search-only key. 만료되는 tenant child key 서명에 사용 |
두 key는 바꿔 쓸 수 없습니다. TYPESENSE_API_KEY는 index 관리가 가능하며
browser로 보내지 않습니다. TYPESENSE_SCOPED_KEY_PARENT는 Typesense에 search
action만 허용된 key로 실제 존재해야 합니다. 같은 이름의 환경변수에 임의 문자열을
넣는다고 유효한 parent가 되지 않습니다. Nexia 서버 adapter는 이 parent에서
tenant filter와 만료 시간이 포함된 child key를 만들어 후보 query에 사용합니다.
Index lifecycle
Typesense에는 allowlist로 허용된 Resource Key만 들어갑니다. 나머지 Resource는
SCOUT_DRIVER=typesense에서도 목록 ?search=를 Scout database engine으로 계속
처리합니다. 즉 allowlist에 추가하는 것이 lane을 옮기는 유일한 단계이며, 빼두어도
그 Resource의 목록 검색이 깨지지 않습니다. 하나의 collection에
모든 tenant의 document가 tenant_key 필드로 구분되어 함께 들어가므로, 재구축은
tenant 단위가 아니라 Resource 단위입니다. nexia-search:reindex는 새 물리
collection을 만들고 ready 상태인 모든 tenant를 backfill한 뒤에야 stable alias를
원자적으로 옮깁니다. 한 tenant의 backfill이라도 실패하면 alias는 이전 대상에
그대로 두고 명령은 실패로 끝납니다. 이전 collection은 rollback을 위해 남습니다.
--tenant=<key>는 "이 tenant를 live collection 안에서 갱신한다"는 뜻입니다.
alias가 가리키는 collection에서 그 tenant의 document를 지우고 다시 넣을 뿐,
alias는 절대 옮기지 않습니다. 옮기면 공유 collection이 tenant 하나의 데이터로
바뀝니다. alias가 아직 없으면 보존할 대상도 없으므로 모든 ready tenant를 대상으로
하는 전체 재구축으로 넘어가며, 명령이 그 사실을 출력합니다. nexia-demo:reset이
이 동작에 의존합니다. 데모 초기화는 다른 tenant의 live document를 건드리지 않고
데모 tenant만 갱신합니다.
읽기 전용 nexia-search:check-drift는 tenant·Resource별 database와 engine의
count 차이를 보고하며 복구하지 않습니다. 두 명령 모두 database 프로필에서는
필요하지 않습니다.
Knowledge semantic generation
Knowledge semantic chunk는 공유 Typesense collection과 별도 lifecycle을 사용합니다. Profile은 provider, model/version, dimension, extraction 계약, storage 모양을 포함한 vector generation 전체의 identity입니다. Model 이름만 바꿔 기존 vector를 제자리에서 덮는 방식은 지원하지 않습니다.
유효한 profile을 활성화한 뒤 명시적으로 재구축을 queue합니다.
task artisan -- nexia-search:reconcile-knowledge-search-index --semantic-reindex --limit=500명령은 ready tenant를 순회하며 현재 eligible revision을 active profile 대상으로
queue하고, 이번 실행에서는 tenant마다 --limit개까지만 일반 reconciler로
처리합니다. 나머지는 예약된 reconcile 실행이 이어서 drain합니다. Indexer는
embedding batch 전체를 검증한 다음 generation을 교체하며, 제거 경로와 같은
revision lock 안에서 publication eligibility를 마지막으로 다시 확인합니다.
정확히 일치하는 live generation은 다시 쓰지 않습니다.
현재 구현된 프로필은 로컬·테스트 전용 deterministic-local-test-vector-16-v1뿐입니다. 운영용 프로필이 제공되기 전까지 운영에서는 semantic 검색을 끄세요. Startup과
운영 환경 검증이 해당 profile을 거부합니다. 향후 운영 provider를 추가하려면
먼저 data-export 정책을 승인하고 dimension에 맞는 병렬 storage를 준비해야 합니다.
로컬 Typesense 프로필
로컬 스택은 Compose service이며 Typesense Cloud가 아닙니다. 로컬 배포가 Cloud를 가리키면 reindex가 운영 collection을 덮어씁니다.
SCOUT_DRIVER=typesense
SEARCH_CANDIDATE_GATEWAY=typesense
SCOUT_QUEUE=false
TYPESENSE_HOST=typesense
TYPESENSE_PORT=8108
TYPESENSE_PROTOCOL=http
task dev:up:typesense로 시작합니다. 기본 실행 서비스는 app, vite,
typesense입니다. BROADCAST_CONNECTION=reverb이거나
REVERB_APP_KEY / VITE_REVERB_APP_KEY 중 하나가 비어 있지 않으면 같은
수렴 과정에서 reverb도 실행 상태로 유지합니다. Horizon은 실행하지 않으므로
로컬에서는 SCOUT_QUEUE를 false로 유지해야 합니다. 그러지 않으면 index
쓰기가 queue에 쌓인 채 처리되지 않습니다.
TYPESENSE_SCOPED_KEY_PARENT는 engine에 실제로 존재하는 key여야 합니다.
search-only parent는 task dev:up:typesense가 자동으로 등록하며,
TYPESENSE_PROTOCOL이 http가 아닌 배포는 건너뜁니다. 아래 명령은 로컬 admin
key로 직접 등록하는 fallback입니다.
curl -X POST 'http://localhost:8108/keys' \
-H 'X-TYPESENSE-API-KEY: local-typesense-search-key' \
-H 'Content-Type: application/json' \
-d '{"description":"local search-only parent","actions":["documents:search"],"collections":["*"],"value":"local-typesense-scoped-key-parent"}'응답의 value가 TYPESENSE_SCOPED_KEY_PARENT에 들어갈 값입니다. Admin key는
parent가 될 수 없습니다. Admin key로 서명한 child key는 관리 action을 함께
갖게 됩니다.
경계
Typesense key를 VITE_* 변수로 만들면 안 됩니다. Laravel은 선택된 public
frontend config만 공개하며 Search credential은 public 정보가 아닙니다.
저장소에 있는 개발용 secret을 복사하거나 Docker bootstrap key를 운영 credential로 쓰지 않습니다. 운영 secret store와 Typesense key 관리 절차가 실제 값을 소유합니다.
SEARCH_CANDIDATE_GATEWAY는 권한 우회 설정이 아닙니다. 신뢰하지 않는 ID를
가져올 supplier만 선택합니다. CoreSearchService는 여전히 tenant DB를 읽고
현재 permission과 record scope를 적용합니다.
동작 예시
Typesense 연결 정보와 관리 key, search-only scoped-key parent를 이미 준비한 배포는 다음처럼 engine을 선택할 수 있습니다.
SCOUT_DRIVER=typesense
SEARCH_CANDIDATE_GATEWAY=typesense
SCOUT_QUEUE=true
SCOUT_AFTER_COMMIT=true
KNOWLEDGE_SEARCH_SEMANTIC_ENABLED=false
KNOWLEDGE_SEARCH_EMBEDDING_PROFILE=none
Config cache를 다시 만들고 장기 실행 worker를 재시작한 뒤, 운영자는 allowlisted Resource 하나를 모든 ready tenant에서 새 collection으로 backfill합니다.
task artisan -- nexia-search:reindex shell.workspace
task artisan -- nexia-search:check-drift --tenant=acme성공하면 ready tenant별 backfilled into 행과 Resource당 한 번의 stable alias
swap이 나오고, database와 engine count가 같은 OK 행이 이어집니다.
--tenant=acme를 reindex에 붙이면 live collection 안에서 해당 tenant만
갱신하고 alias는 옮기지 않습니다. 이것은 index 상태 검증이지 사용자 권한
검증이 아닙니다. API Search는 반환할 레코드를 매번 다시 확인합니다.
함께 읽기
- 환경변수 — Search·embedding 배포값 전체를 owner별로 조회합니다.
- 통제된 Search Runtime — 후보 ID가 Core 권한 검사를 통과하는 과정을 따라갑니다.
- 설정의 소유권 — 값을 배포·플랫폼·테넌트·App 중 어디에 둘지 판단합니다.
- Octane·FrankenPHP 런타임 — cache된 config 변경 뒤 worker 재시작이 필요한 이유입니다.
- Artisan 명령어 — 다른 지원 명령의 정확한 signature를 찾습니다.