벡터 DB에 문서를 복사하지 않는 RAG (Zero-Copy RAG)
Zero-Copy RAG의 성패는 복제 0건이 아니라 책임의 위치에서 갈린다
기획연재 1편: 제로카피 RAG는 벡터 DB를 쓰지 않는 검색 기법이 아닙니다. 복제형 RAG와 원본 직접 조회를 같은 컨텍스트 파이프라인 위에 두고, LLM이 아니라 업무 시스템이 원문·권한·범위를 끝까지 책임지게 하는 아키텍처입니다.
📚 연재 안내 | 이 글은 「Agent 시대의 RAG 재설계」 3부작의 1편입니다. ① 벡터 DB에 문서를 복사하지 않는 RAG — Zero-Copy RAG의 설계와 운영(현재 글) · ② 벡터 유사도를 넘어 관계와 규칙을 검색하다 — GraphRAG·Ontology·KAG의 역할 · ③ Agentic AI를 위한 온톨로지 기반 RAG 시스템 구축하기 — KB·KG·Zero-Copy를 조합하는 프로덕션 지식 도구 계층
앞선 프로덕션 AI Agent 연재에서 이런 이야기를 했습니다.
Agent 개발에서 중요한 것은 “벡터 DB를 붙였는가”가 아니라, Agent가 업무에 필요한 정확한 컨텍스트에 어떤 해상도로 접근하는가이다.
문서 Q&A에서는 의미적으로 비슷한 청크를 찾는 것만으로도 충분할 때가 많습니다. 하지만 실제 업무를 수행하는 Agent에게는 비슷한 문서보다 정확한 원문, 현재 상태, 현재 권한, 실행 시점에 유효한 정책이 더 중요합니다.
예를 들어 인사 정책 문서가 오늘 오전에 수정됐다고 해보겠습니다. 원본에서는 휴가 승인 규칙이 바뀌었고 특정 팀의 열람 권한도 회수됐습니다. 그런데 어제 만든 벡터 인덱스에는 이전 문장과 이전 권한 정보가 남아 있습니다. 이 상태에서 Agent의 답변이 승인 워크플로우나 자동화 설정으로 이어지면, 오래된 청크 하나가 실제 업무 오류가 됩니다.
RAG의 출발점은 모델이 외부의 비모수적 메모리에 접근해 지식을 갱신하고 근거를 제공하도록 하는 것이었습니다[1]. 그러나 많은 프로덕션 구현에서는 최신 지식을 사용하기 위해 원문을 다시 수집하고, 청크로 자르고, 별도 저장소에 복제합니다.
최신 지식을 읽기 위해 도입한 RAG가 또 하나의 오래된 복제본을 만드는 역설이 생깁니다.
저희가 AI 인사이트 스튜디오를 만들면서 내린 결정도 이 문제에서 출발했습니다. 결론은 단순히 “벡터 DB를 없애자”가 아니었습니다.
제로카피의 성패는 복제를 0으로 만들었는가가 아니라, 원문·권한·상태의 최종 책임을 어느 시스템이 끝까지 갖고 있는가에서 갈린다.
그리고 한 단계 더 내려가면 진짜 분기점은 이것입니다.
LLM이 스코프(scope)를 판단하는가, 아니면 백엔드가 스코프를 해석하고 강제하는가?
이 글에서는 제로카피 RAG의 개념과 트레이드오프를 설명한 뒤, AI 인사이트 스튜디오에서 복제형 RAG와 소스네이티브 조회를 한 파이프라인 안에 어떻게 배치했는지, 권한 범위를 어떻게 결정론적으로 잠갔는지, 그리고 삭제·포인터·캐시·임베딩처럼 새 위치로 옮겨온 문제를 어떤 설계 원칙으로 다뤄야 하는지 살펴보겠습니다.
💡 TL;DR · 3줄 요약
① 제로카피 RAG는 벡터 DB를 없애는 기법이 아니라, 원문·권한·범위의 '최종 책임'을 업무 시스템에 두는 아키텍처입니다.
② LLM은 스코프를 좁히도록 요청할 수는 있어도 부여할 수는 없습니다 — 권한은 백엔드가 조회 시점에 강제합니다.
③ 제로카피는 문제를 없애지 않고 위치를 바꿉니다. 삭제 전파·포인터·임베딩·캐시를 결정론으로 다뤄야 합니다.

제로카피의 성패는 복제를 0으로 만드는가가 아니라, 원문·권한·범위의 최종 책임을 어느 시스템이 끝까지 갖는가에서 갈립니다.
RAG에는 보이지 않는 데이터 복제가 있다
일반적인 RAG 파이프라인은 대략 다음과 같습니다.
1. Confluence, Google Drive, SharePoint, DB, 파일 스토리지에서 원문을 수집합니다.
2. PDF·HTML·문서를 텍스트로 파싱합니다.
3. 텍스트를 일정 크기의 청크로 나눕니다.
4. 각 청크의 임베딩을 생성합니다.
5. 벡터 DB나 검색 인덱스에 임베딩·원문 청크·메타데이터를 저장합니다.
6. 질의가 들어오면 유사한 청크를 검색해 LLM 컨텍스트에 넣습니다.
이 구조는 빠르고 단순합니다. 질의 시점에는 최적화된 검색 인덱스만 조회하면 되기 때문입니다. 하지만 운영 관점에서 보면 원본 시스템 옆에 새로운 데이터 시스템이 하나 더 생긴 것입니다.

복제되는 것은 원문만이 아닙니다.

권한은 특히 까다롭습니다. 엔터프라이즈 검색 서비스는 보통 문서 콘텐츠와 함께 ACL 메타데이터를 인덱스에 적재한 뒤, 질의 시 사용자·그룹 필터를 적용합니다. Azure AI Search도 SharePoint의 ACL을 문서 메타데이터로 인덱싱해 검색 결과를 제한하는 방식을 제공합니다[2][3]. 이 구조는 유용하지만 원본 권한이 바뀌면 인덱스의 ACL도 다시 갱신되어야 합니다.
즉 ACL-aware RAG를 만들었다고 해도 권한 복제본의 동기화 지연은 별도의 운영 문제가 됩니다.
💡 핵심 요약 · 일반 RAG가 만드는 두 번째 데이터 시스템
① 원문만 복제되는 것이 아니라 권한·버전·삭제 상태도 함께 복제된다.
② 검색 인덱스는 성능을 높이지만 최신성과 권한 일관성을 별도로 운영해야 한다.
③ 프로덕션 RAG의 문제는 검색 정확도뿐 아니라 복제본의 수명주기 관리다.

최신 지식을 읽으려 도입한 RAG가 수집·파싱·청크·임베딩을 거치며 또 하나의 '오래된 복제본'을 만드는
먼저, 두 개의 ‘zero-copy’를 구분해야 한다
업계에서 zero-copy라는 표현은 서로 다른 층위에서 사용됩니다.
데이터 플랫폼의 zero-copy는 Snowflake의 zero-copy clone처럼 새로운 논리 오브젝트가 기존 저장 데이터를 공유하고, 변경이 발생하기 전까지 실제 데이터를 다시 복사하지 않는 구조를 뜻합니다[4]. 카탈로그나 테이블 공유 계층에서 데이터를 물리적으로 이동하지 않고 같은 저장 자산을 노출하는 방식도 이 범주에 가깝습니다.
반면 이 글에서 말하는 AI 파이프라인의 zero-copy는 다음 질문을 다룹니다.
검색을 위해 원문과 권한을 또 하나의 지식 저장소에 복제해야 하는가?
두 개념은 관련이 있지만 같은 것은 아닙니다.

저희는 후자의 의미를 명확히 하기 위해 운영 정형 데이터를 원본 DB에서 읽는 경로를 Zero-Copy SQL이라고 부릅니다.
제로카피 RAG는 단일 기술이 아니라 스펙트럼이다
이 글에서 제로카피 RAG를 다음처럼 정의하겠습니다.
원본 텍스트와 운영 데이터를 별도의 지식 저장소에 장기 복제하지 않고, 검색에 필요한 최소 인덱스·포인터·스키마만 유지하거나 원본 검색 API·SQL을 직접 사용해, 질의 시점의 권한으로 컨텍스트를 읽는 구조.
핵심은 “복사가 정말 0건인가”가 아닙니다. 어떤 데이터를 어디까지 복제하고, 무엇을 원본에 남길 것인가입니다.

제로카피는 벡터 검색을 버리는 방식이 아닙니다. 공개 문서나 재사용 빈도가 높은 PDF는 청크와 임베딩을 복제하는 편이 합리적일 수 있습니다. 반면 현재 매출·퍼널·재고·세그먼트 집계처럼 계속 변하는 운영 데이터는 매번 임베딩하기보다 SQL이나 API로 직접 읽는 편이 정확합니다.
따라서 제로카피의 첫 질문은 이것입니다.
“벡터 DB를 쓸 것인가?”가 아니라, “이 데이터의 Source of Truth를 어느 시스템이 끝까지 책임질 것인가?”
하지만 실행형 Agent에서는 질문을 한 단계 더 내려야 합니다.
“LLM이 어떤 데이터를 볼지 정하는가, 아니면 시스템이 조회 가능한 범위를 먼저 결정하고 강제하는가?”
진짜 분기점: LLM이 스코프를 판단하는가, 시스템이 강제하는가
소스네이티브 SQL은 최신 상태를 읽는 데 강력합니다. 동시에 잘못 설계하면 벡터 검색보다 더 위험할 수 있습니다. LLM이 생성한 SQL이 원본 데이터베이스에 직접 닿기 때문입니다.
여기서 흔한 오해가 있습니다.
“읽기 전용 계정이니까 안전하지 않을까?”
읽기 전용이어도 테넌트 밖의 고객 데이터나 허용되지 않은 사이트의 지표를 읽을 수 있다면 사고입니다. LLM이 판단한 siteId, tenantId, kbId 같은 식별자를 그대로 믿는 것은 사용자가 보낸 오브젝트 ID만으로 접근 대상을 정하는 것과 같습니다. OWASP API Security가 지적하는 Broken Object Level Authorization도 바로 이 문제를 다룹니다. 클라이언트가 전달한 ID가 존재한다는 사실과 그 사용자가 해당 오브젝트에 접근할 권한이 있다는 사실은 다릅니다[5].
저희는 스코프 결정을 LLM에서 떼어내 세 단계로 잠갔습니다.
1. 오케스트레이터가 세션 권한으로 범위를 먼저 해석한다
사용자의 서버 세션, 그룹, 게스트 컨텍스트, 시스템 정책을 기준으로 resolvedScope를 만듭니다. 이 값은 에이전트에게 읽기 전용 힌트로 전달될 수 있지만, 최종 권한 판단은 백엔드가 보유합니다.
LLM이 판단한 siteId나 KB 목록은 권한 근거가 아니라 요청 후보일 뿐입니다.
2. 도구는 요청 범위와 해석된 범위의 교집합만 실행한다
에이전트는 범위를 좁힐 수는 있지만 넓힐 수 없습니다.
resolvedScope = resolveScope(serverSession)
requestedScope = toolArguments.scope
effectiveScope = intersection(resolvedScope, requestedScope)
if effectiveScope is empty:
return FAIL_CLOSED
이 규칙이 중요한 이유는 LLM의 의도나 정확도와 무관하게 시스템 강제 규칙을 만들기 때문입니다.
LLM은 스코프를 요청할 수 있다. 하지만 스코프를 부여할 수는 없다.
가령 LLM이 더 넓은 범위를 탐색하려고 {"kbId": "all_company_data"} 같은 요청을 만들더라도, 사용자 세션으로 해석한 resolvedScope가 ["kb-team-a"]라면 실제 실행 범위는 ["kb-team-a"] 안으로만 제한됩니다. 반대로 요청 후보가 ["kb-team-b"]뿐이고 사용자의 resolvedScope가 ["kb-team-a"]라면 교집합은 비어 있으며, 도구는 전체 검색으로 fallback하지 않고 KB_SCOPE_MISSING 같은 구조화된 거부를 반환합니다.
검색 범위는 “없으면 비슷한 것을 넓게 찾아보는” 추천 문제가 아닙니다. 권한이 걸린 RAG에서 범위는 모델의 추론 결과가 아니라 시스템 강제 규칙입니다.
3. 실행기가 SQL 구조와 값을 분리해 강제한다
식별자는 서버 측 화이트리스트에서만 선택하고, 값은 언제나 바인드 파라미터로 전달합니다. OWASP도 SQL Injection의 기본 방어로 parameterized query와 allow-list validation을 권고합니다[6].
SELECT
metric_date,
revenue,
conversion_rate
FROM allowed_metric_view
WHERE scope_column = ANY(:effective_scope)
AND metric_date BETWEEN :from_date AND :to_date;
이 구조를 요약하면 다음과 같습니다.

OWASP의 authorization 지침도 deny-by-default와 서버 측 중앙집중형 권한 검사를 권고합니다[8]. 제로카피 SQL의 핵심은 LLM이 SQL을 잘 쓰는가가 아니라, LLM이 틀려도 조회 범위를 넓힐 수 없게 하는가입니다.
💡 핵심 요약 · 제로카피의 진짜 분기점
① LLM이 전달한 식별자는 권한 근거가 아니라 신뢰하지 않는 입력이다.
② 백엔드가 사용자 세션으로 스코프를 해석하고, 도구는 교집합만 실행한다.
③ 에이전트는 범위를 좁힐 수 있지만 절대 넓힐 수 없다. → 제로카피의 본질은 벡터 DB 유무보다 권한 책임의 위치에 있다.

오케스트레이터가 세션 권한으로 범위를 해석하고, 도구는 교집합만 실행하며, 실행기는 SQL 구조와 값을 분리해 강제합니다. LLM은 범위를 넓힐 수 없습니다.
AI 인사이트 스튜디오 사례 ①: 한 파이프라인 안에 두 종류의 RAG를 둔다
저희는 복제형 RAG와 소스네이티브 조회를 서로 다른 제품으로 붙이지 않았습니다. 같은 KB·인사이트 파이프라인의 서로 다른 노드 타입으로 공존시켰습니다.

같은 인사이트 질의 안에서 사용자는 소스 피커로 두 종류의 소스를 함께 고를 수 있습니다.

이 구조에서는 한 파이프라인 안에 서로 다른 데이터 시점과 복제 정책이 공존합니다. 여기서 강조할 점은 두 경로가 분리된 제품이 아니라 같은 스코프 해석·컨텍스트 조합·감사 로그 위에서 동작한다는 것입니다. 사용자가 어떤 시점의 데이터를 선택하고 확인해야 하는지는 뒤의 “데이터 시점과 조회 경로는 운영 계약으로 명시한다”에서 따로 정리하겠습니다.
AI 인사이트 스튜디오 사례 ②: 권한을 복제하지 않는 것도 제로카피다
외부 SaaS 문서를 인덱싱하는 엔터프라이즈 RAG는 원본 ACL을 검색 인덱스에 함께 미러링하는 경우가 많습니다. 이 방식에서는 문서 콘텐츠와 권한이 서로 다른 시점에 갱신될 수 있고, 원본에서 권한이 회수된 뒤에도 인덱스에는 잠시 남는 sync-lag가 생길 수 있습니다.
저희의 자체 KB·노드 권한 모델은 다르게 동작합니다.
따라서 별도의 ACL 복제본을 검색 인덱스에 미러링하지 않는 경로에서는 인덱스와 ACL 사이의 동기화 지연 자체가 존재하지 않습니다. 권한을 복제하지 않는 것도 하나의 제로카피입니다.
다만 표현은 정확해야 합니다.
“권한 지연이 전혀 없다”가 아니라, “검색 인덱스에 복제한 ACL의 sync-lag가 없다”입니다.
사용자 세션의 claim이 오래됐거나, 권한 해석 결과를 별도 캐시하거나, upstream identity provider의 변경이 늦게 반영되면 다른 형태의 staleness는 여전히 생길 수 있습니다. 그래서 세션 발급 시각·권한 캐시 TTL·스코프 해석 버전도 관측 대상이 되어야 합니다.
범위 밖의 KB가 요청되면 도구는 조용히 비슷한 문서를 찾아주지 않습니다. KB_SCOPE_MISSING과 거부된 KB 목록을 구조화해 반환합니다.
{
"code": "KB_SCOPE_MISSING",
"message": "No authorized knowledge base remains after scope resolution.",
"requestedKbIds": ["kb-a", "kb-b"],
"deniedKbIds": ["kb-b"],
"effectiveKbIds": [ ]
}
이 동작은 불친절해 보일 수 있습니다. 하지만 고위험 데이터 접근에서는 “비슷한 다른 것을 찾아주는 친절함”보다 권한 밖이면 아무것도 하지 않는 예측 가능성이 중요합니다. OWASP가 말하는 fail-safe default와 deny-by-default의 구현입니다[8].
제로카피가 해결하는 것은 검색 알고리즘보다 데이터 운영 문제다
제로카피의 장점은 흔히 “보안” 한 단어로 요약되지만, 실제로는 네 가지 운영 문제를 줄이는 방식입니다.
1. 원본을 최종 진실의 원천으로 유지한다
운영 데이터는 질의 시점의 원본 DB에서 읽습니다. 매출·퍼널·세그먼트 집계가 바뀌면 다음 질의에는 새 값이 반영됩니다. RAG 인덱스의 갱신 주기를 기다릴 필요가 없습니다.
문서 역시 pointer-based 구조를 쓴다면 최종 본문은 원본에서 가져올 수 있습니다. 다만 포인터와 임베딩이 오래될 수 있으므로 뒤에서 설명할 버전 검증이 필요합니다.
2. 데이터 복제 표면을 줄인다
민감한 원문이 원본 스토리지, ETL 임시 파일, 파싱 결과, 벡터 DB, 응답 캐시, Trace에 반복 저장될수록 보호해야 할 지점도 늘어납니다. 제로카피는 모든 유출 위험을 없애지는 않지만, 원문이 장기 보관되는 위치를 줄여 줍니다.
따라서 표현도 정확해야 합니다.
제로카피가 컴플라이언스를 “보장”하는 것이 아니라, 컴플라이언스를 적용해야 할 복제 표면을 줄인다.
3. 현재 권한을 질의 경로에 넣는다
권한을 인덱싱 시점의 메타데이터로만 다루지 않고, 질의 시점의 사용자 세션과 백엔드 스코프 해석으로 평가합니다. 권한이 검색 후 사후 필터가 아니라 검색 경로의 전제 조건이 됩니다.
4. 근거와 상태의 계보를 분리해 남길 수 있다
Agent의 결정을 감사하려면 “유사 청크 17번”보다 다음이 필요합니다.
원본이 최종 근거라면 답변의 출처를 실제 데이터와 연결할 수 있습니다. 실행형 Agent에서는 이 정보가 인용 링크가 아니라 결정 근거의 감사 레코드가 됩니다.
하지만 제로카피는 문제를 없애지 않는다. 문제의 위치를 바꾼다
제로카피를 “원문을 저장하지 않으니 더 빠르고 안전한 RAG”라고 설명하면 위험합니다. 복제·동기화 부담을 줄이는 대신 삭제 전파, 포인터 유효성, 권한 타이밍, 캐시, 임베딩 보안을 원본 조회 경로 안으로 끌고 들어옵니다.
제로카피를 설계할 때 중요한 것은 옮겨온 문제마다 책임 주체와 실패 원칙을 먼저 정하는 것입니다.
이 문제는 누가 책임지고, 어떤 실패 방향을 택하며, 무엇으로 정상 동작을 측정할 것인가?
AI 인사이트 스튜디오 사례에서 정리한 운영 원칙은 다음과 같습니다.

이 표가 제로카피 운영의 핵심 산출물입니다.

제로카피는 문제를 없애지 않고 위치를 바꿉니다 — 삭제 전파, 포인터 계약, 임베딩 등급, 캐시 복제본을 결정론으로 다뤄야 합니다.
삭제 전파는 ‘함께 지우기’가 아니라 순서와 측정의 문제다
원본 청크 행과 벡터를 삭제해야 할 때 두 저장소를 하나의 ACID 트랜잭션으로 묶기 어렵습니다. 그래서 중요한 것은 “동시에 지운다”는 추상적 표현이 아니라, 어느 실패 방향을 허용할지 순서를 정하는 것입니다.
저희의 strictDeleteDocumentChunks는 다음 순서를 택합니다.
1. 벡터를 먼저 삭제합니다.
2. 벡터 삭제가 실패하면 즉시 throw하고 청크 행은 남깁니다.
3. 벡터 삭제가 성공한 뒤 청크 행을 삭제합니다.
이 순서는 “벡터가 남아 있지만 원본 메타데이터는 사라진 상태”보다 “청크 메타데이터가 남아 재시도 가능한 상태”를 선택합니다.

물론 이것으로 물리 삭제가 즉시 끝나는 것은 아닙니다. 벡터 데이터베이스는 삭제 대상을 논리적으로 표시한 뒤 compaction과 garbage collection을 거쳐 공간을 회수할 수 있습니다. Zilliz Cloud 문서도 삭제된 데이터가 즉시 물리 제거되지 않고 compaction 후 제거된다고 설명하며, dropped collection의 데이터 정리에는 최대 24시간이 걸릴 수 있다고 안내합니다[9]. Milvus 역시 compaction이 논리 삭제 데이터를 정리하고, 별도의 GC가 dropped segment의 저장 공간을 회수한다고 설명합니다[10].
따라서 삭제 지표를 하나로 두면 안 됩니다.

저희도 관리형 벡터 DB의 soft-delete와 compaction 지연이 quota 문제로 드러난 경험이 있습니다. 이 경험이 준 교훈은 간단합니다.
삭제 전파는 0초라고 가정할 일이 아니라, 실패 방향을 선택하고 0이 아닌 lag를 측정하는 운영 문제다.
포인터는 URL이 아니라 계약이다: 계보 기록과 읽기 시점 검증
Pointer-based RAG에서 document_id=123, chunk=7만 저장하면 충분해 보입니다. 하지만 원문이 편집되면서 섹션이 삭제되면 같은 위치가 전혀 다른 내용을 가리킬 수 있습니다.
그래서 포인터에는 위치뿐 아니라 계보가 필요합니다.
{
"sourceUrl": "https://source.example/doc/123",
"sourceType": "web",
"documentId": "123",
"ingestionVersionId": "v42",
"sectionAnchor": "refund-policy",
"contentHash": "sha256:...",
"trustScore": 0.92,
"embeddedAt": "2026-06-22T01:20:00Z"
}
여기서 중요한 것은 포인터에 버전을 적는 것과, 읽을 때 그 버전을 실제로 대조하는 것은 다른 성숙도 단계라는 점입니다.
1단계 — 계보 기록
이 단계는 포인터를 단순 URL이 아니라 재현 가능한 계보 레코드로 만드는 단계입니다.
2단계 — 읽기 시점 검증
여기서 검증은 원문 전체 payload를 매번 다시 다운로드한다는 뜻이 아닙니다. 가능한 원본에서는 먼저 가벼운 validator를 확인합니다. HTTP 소스라면 HEAD 요청으로 GET과 같은 표현 헤더를 본문 없이 받을 수 있고[15], ETag는 리소스의 특정 버전을 식별해 내용이 바뀌지 않았을 때 전체 응답을 다시 전송하지 않아도 되게 합니다[16]. ETag가 없다면 Last-Modified를 보조 validator로 사용할 수 있습니다[17]. 내부 시스템에서는 이 역할을 revision number, updatedAt, schema version, content hash API가 대신할 수 있습니다.
따라서 읽기 시점 검증의 기본 경로는 “본문 다운로드 → 전체 해시 재계산”이 아니라 “메타데이터 검증 → 변경 감지 후보만 본문 재조회”에 가깝습니다. 이 차이를 분리해야 포인터 계약을 지키면서도 질의 지연을 통제할 수 있습니다.
HTTP의 ETag는 같은 리소스의 서로 다른 표현을 구분하는 validator이며, If-Match 같은 조건부 요청은 표현이 바뀌었을 때 작업을 중단하는 데 사용할 수 있습니다[11]. 모든 원본 시스템이 ETag를 제공하는 것은 아니지만, 내부 revision·수정 시각·콘텐츠 해시로 같은 원칙을 구현할 수 있습니다.
운영 설계에서는 1단계와 2단계를 하나의 포인터 계약으로 봐야 합니다. 계보를 기록하더라도 질의 시점에 원본과 대조하지 않으면 stale 후보를 안전하게 걸러낼 수 없습니다.
제로카피 성숙도는 포인터를 저장했는가가 아니라, 포인터의 계약을 읽을 때 검증하는가에서 갈린다.
임베딩 보안의 1순위는 난독화보다 데이터 등급과 접근 통제다
“원문은 저장하지 않고 임베딩만 저장하니 민감정보가 아니다”라는 주장은 성립하지 않습니다. Text Embeddings Reveal (Almost) As Much As Text 연구는 특정 모델과 32토큰 입력이라는 제한된 조건에서 임베딩으로부터 입력의 92%를 정확히 복원할 수 있음을 보였습니다[12]. 모든 임베딩과 문서 길이에 일반화할 수 있는 수치는 아니지만, 임베딩이 본질적으로 안전한 익명 데이터라는 가정은 위험합니다.
그렇다고 프로덕션의 첫 번째 대응이 곧바로 임베딩 난독화나 노이즈 주입이어야 하는 것은 아닙니다.
실무의 많은 벡터 인덱스는 임베딩과 함께 평문 청크·메타데이터를 보관합니다. 이 경우 더 직접적인 위협은 공격자가 고급 inversion 모델을 돌리는 상황보다, 벡터 DB나 디버그 API의 read 권한을 얻어 평문 청크·메타데이터·검색 결과를 그대로 읽는 상황입니다.
OWASP LLM08:2025도 vector and embedding store의 주요 완화책으로 세밀한 접근 통제, 사용자·그룹 간 논리적 분리, 데이터 분류, 검색 활동 로깅을 먼저 제시합니다[13].
그래서 우선순위는 다음과 같습니다.
1. 벡터 DB와 검색 인덱스를 평문 문서 저장소와 동급의 민감도로 분류합니다.
2. 테넌트·사용자·데이터 등급별 접근 통제를 적용합니다.
3. 네트워크를 격리하고 외부 직접 접근을 막습니다.
4. 디버그·관리·export 경로에 별도 권한 게이트와 감사를 둡니다.
5. 삭제 요청에 청크·임베딩·포인터·캐시·로그가 함께 포함되는지 검증합니다.
6. 그다음 위협 모델에 따라 quantization·noise·암호화 검색 같은 추가 방어를 평가합니다.
임베딩 보안의 현실적인 1순위는 “벡터를 알아보기 어렵게 만들기”보다, 벡터 DB를 평문 데이터 저장소처럼 보호하는 것이다.
캐시는 성능 장치이면서 다시 만들어진 복제본이다
제로카피의 지연을 줄이기 위해 원문·검색 결과·완성 답변을 캐시하기 시작하면, 어느 순간 또 하나의 데이터 저장소가 됩니다.
특히 권한에 의존하는 검색 결과를 사용자 사이에서 재사용하면 심각한 누출이 됩니다. HTTP 캐시 표준인 RFC 9111도 Authorization 헤더가 있는 요청의 응답을 shared cache가 후속 요청에 재사용하는 것을 기본적으로 제한합니다[14]. 애플리케이션 수준의 retrieval cache와 semantic cache는 HTTP 캐시보다 더 복잡하므로 더 보수적인 계약이 필요합니다.
저희가 기본 원칙으로 두는 캐시 키는 다음 요소를 포함합니다.
cacheKey = hash(
callerIdentity,
tenantId,
resolvedScopeFingerprint,
sourceIds,
sourceVersion,
queryNormalization,
modelAndPromptVersion
)
권한 의존 데이터라면 다음 중 하나를 선택해야 합니다.
“질문이 비슷하니 같은 답을 주자”는 semantic cache의 장점도 권한 경계보다 앞설 수 없습니다. Semantic cache는 바이트 단위로 동일한 질문이 아니라 의미적으로 유사한 질문의 응답을 재사용해 비용과 지연을 줄이는 방식입니다[18]. 그래서 엔터프라이즈 환경에서는 더 위험할 수 있습니다.
예를 들어 “우리 팀 매출 알려줘”와 “팀 A 매출 알려줘”는 임베딩 공간에서 가깝게 잡힐 수 있습니다. 하지만 호출자와 resolvedScope가 다르면 같은 질문이 아닙니다. 질문 임베딩 유사도만으로 응답을 재사용하는 단순 semantic cache는 타인·타조직의 권한 경계를 넘어 데이터를 노출하는 최악의 보안 취약점이 될 수 있습니다. Redis의 semantic cache 문서도 유사도 기반 재사용에는 tenant, locale, model version, safety flag 같은 hard metadata boundary가 함께 필요하다고 설명합니다[18].
캐시는 새로운 Source of Truth가 아니며, 호출자 신원을 잃는 순간 제로카피의 권한 모델을 우회하는 복제본이 된다.
프로덕션 구조: 복제 경로와 소스네이티브 경로를 공통 권한·감사 계층에 둔다
현실적인 제로카피 RAG는 “아무것도 저장하지 않는 RAG”가 아닙니다. 원본은 원본 시스템이 책임지고, 복제형·포인터형·소스네이티브 경로를 공통 권한·감사 계층 아래 두는 구조입니다.

다이어그램에서 중요한 것은 검색 경로의 종류보다 신뢰 경계입니다. LLM과 사용자가 제안하는 입력은 회색의 신뢰하지 않는 영역이고, 세션·스코프·권한·검증·감사는 녹색의 백엔드 강제 구간입니다. Copy-based 인덱스는 파란색으로 분리해 “성능 장치이지 권한의 원천은 아니다”라는 점을 표시했습니다. Mermaid는 classDef와 linkStyle로 노드와 링크 스타일을 지정할 수 있으므로, 블로그 렌더러가 지원한다면 Markdown 원본에서도 이 신뢰 경계를 유지할 수 있습니다[19].
이 구조에는 세 가지 원칙이 있습니다.
원칙 1 — LLM은 스코프를 좁힐 수만 있다
LLM이 요청한 source·site·KB는 resolvedScope와 교차합니다. 교집합 밖의 데이터는 어떤 프롬프트로도 열리지 않습니다.
원칙 2 — 스코프가 없으면 조회도 없다
빈 스코프에서 전체 조회로 fallback하지 않습니다. SQL을 만들지 않거나 KB_SCOPE_MISSING을 반환합니다.
원칙 3 — stale·denied·degraded 상태를 조용히 우회하지 않는다
이 원칙들이 모델 성능보다 중요합니다. 더 좋은 모델은 검색 계획을 개선할 수 있지만, 권한·삭제·캐시 경계를 보장하지는 않습니다.
데이터 시점과 조회 경로는 운영 계약으로 명시한다
제로카피는 순수성 경쟁이 아닙니다. 모든 데이터를 원본에서만 읽게 만들면 지연과 가용성 비용이 커지고, 모든 데이터를 복제하면 최신성과 권한 동기화 비용이 커집니다. 더 중요한 것은 모든 질문이 같은 데이터 시점을 요구하지 않는다는 점입니다.
예를 들어 사용자가 “지난달 캠페인의 성과와 당시 승인 정책을 비교해줘”라고 요청했다고 해보겠습니다.
따라서 조회 경로는 변경 빈도 하나로 자동 분류할 값이 아니라, 소스·권한·시점·캐시 허용 범위를 함께 묶은 운영 계약으로 다뤄야 합니다.

여기서 중요한 예외는 감사와 재현입니다. “항상 최신 원문”만 읽으면 과거 실행 당시 어떤 정책과 데이터가 적용됐는지 설명할 수 없습니다. 이 경우에는 제로카피보다 불변 스냅샷과 해시가 더 중요합니다.
시스템은 추천할 수 있습니다. 예를 들어 “이 질문은 현재 매출을 포함하므로 SQL 소스가 필요합니다”라고 안내할 수 있습니다. 하지만 실제 선택은 사용자가 확인할 수 있어야 합니다.
그래서 저희가 사용하는 실무 원칙은 다음과 같습니다.
문서는 RAG로 찾고, 현재 상태는 Tool로 읽고, 권한과 정책은 백엔드가 강제하며, 실행 당시 상태는 불변 스냅샷으로 남긴다.
이 정보가 보이지 않으면 Agent는 맞는 답을 하더라도 사용자가 결과의 시간적 의미를 판단하기 어렵습니다.
원본 장애와 실패 모드를 먼저 정한다
소스네이티브 경로는 원본 시스템의 장애와 지연을 그대로 상속합니다.
그래서 원본을 붙이기 전에 실패 정책을 정해야 합니다.

저희 구조에서 범위가 비거나 승인된 KB가 남지 않으면 fail closed가 기본입니다. “결과가 없으니 전체 범위로 한 번 찾아보자”는 fallback은 가장 피해야 할 동작입니다.
운영 체크리스트: 제로카피를 도입했다면 ‘옮겨온 문제 목록’을 들고 있어야 한다
설계 전에 아래 질문을 확인해 보시길 권합니다.
Source of Truth와 경로
스코프와 SQL 안전성
권한과 격리
포인터와 삭제
임베딩과 캐시
이 체크리스트의 핵심은 기능 목록이 아닙니다.
각 문제의 책임 주체, 실패 방향, 차단 조건, 관측 지표가 설계에 명시되어 있는가?
제로카피 RAG의 운영 지표
검색 top-k 정확도만 봐서는 제로카피 RAG가 안전하게 동작하는지 알 수 없습니다.



특히 ACL deny-after-retrieval rate는 구조에 따라 의미가 달라집니다.
최근 RAG의 변화: 벡터 DB에서 Context Layer로
최근 RAG의 변화는 “벡터 검색이 끝났다”는 이야기가 아닙니다. RAG가 하나의 검색 알고리즘에서 컨텍스트 접근 계층(Context Layer)으로 확장되고 있다는 뜻에 가깝습니다.
초기의 전형적인 구조는 이랬습니다.
질문 → 벡터 검색 → top-k 청크 → LLM
프로덕션에서는 다음 질문이 함께 들어옵니다.
따라서 앞으로 RAG를 볼 때는 어떤 벡터 DB를 쓰는가보다 다음 계층을 보는 편이 좋습니다.

제로카피 RAG는 이 중 데이터 배치와 범위·권한, 계보·데이터 시점을 다시 설계하는 접근입니다.
하지만 원문을 최신 권한으로 가져왔다고 문제가 모두 해결되는 것은 아닙니다. 여러 문서에 흩어진 관계, 시간 순서, 수치 조건, 업무 규칙을 벡터 유사도만으로 연결하기 어려운 질문이 남습니다.
그다음 질문은 이것입니다.
원문을 어디에서 어떤 권한으로 읽을지 정했다면, 그 원문 안의 관계와 규칙을 어떤 구조로 검색할 것인가?
마치며: 복제를 0으로 만드는 것보다 옮겨온 문제를 결정론으로 다루는 것이 중요하다
제로카피 RAG는 유행하는 검색 알고리즘의 이름이 아닙니다. 데이터와 권한의 책임을 어디에 둘지에 대한 아키텍처 선택입니다.
전통적인 RAG는 검색 성능을 얻는 대신 원문·권한·삭제 상태를 별도 인덱스에 복제합니다. 제로카피는 원본의 주도권을 유지하는 대신 원본 API 지연, 스코프 해석, 버전 검증, 캐시 격리, 삭제 lag를 질의 경로 안으로 가져옵니다. 어느 쪽도 공짜가 아닙니다.
저희가 AI 인사이트 스튜디오에서 택한 방향은 완전한 미복제가 아니라 한 파이프라인 안의 의도적인 혼합입니다.
정리하면 이렇습니다.
제로카피의 성패는 복제를 0으로 만들었는가가 아니라, 옮겨온 문제마다 책임 주체·실패 방향·관측 지표를 결정론적으로 설계했는가에 있다.
이것은 앞선 Agent 연재에서 이야기한 “더 똑똑한 모델이 아니라 업무 시스템이 성패를 가른다”는 원칙의 RAG 버전입니다.
🚀 다음 글 예고
2편에서는 데이터의 위치와 권한을 넘어 지식의 구조를 다룹니다. 벡터 RAG가 잘하는 질문과 못하는 질문을 나누고, GraphRAG가 엔티티와 커뮤니티를 어떻게 사용해 전체 데이터셋의 질문을 다루는지, Ontology가 업무 개념과 제약의 계약이 되는 이유, KAG가 벡터 검색과 지식 그래프·논리 추론을 어떻게 결합하는지 살펴보겠습니다.
참고자료
[1] Patrick Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, NeurIPS 2020
https://arxiv.org/abs/2005.11401
[2] Microsoft Learn, Document-Level Access Control in Azure AI Search, updated 2026
https://learn.microsoft.com/en-us/azure/search/search-document-level-access-overview
[3] Microsoft Learn, Use a SharePoint Indexer to Ingest Permission Metadata, updated 2026
https://learn.microsoft.com/en-us/azure/search/search-indexer-sharepoint-access-control-lists
[4] Snowflake Documentation, CREATE … CLONE — Zero-Copy Cloning
https://docs.snowflake.com/en/sql-reference/sql/create-clone
[5] OWASP API Security, API1:2023 Broken Object Level Authorization
https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/
[6] OWASP Cheat Sheet Series, SQL Injection Prevention Cheat Sheet
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
[7] PostgreSQL Documentation, Row Security Policies
https://www.postgresql.org/docs/current/ddl-rowsecurity.html
[8] OWASP Cheat Sheet Series, Authorization Cheat Sheet
https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html
[9] Zilliz Cloud Documentation, FAQ: Monitors & Metrics — Delete and Compaction Behavior
https://docs.zilliz.com/docs/faq-monitors-and-metrics
[10] Milvus Documentation, Product FAQ — Compaction and Garbage Collection
https://milvus.io/docs/product_faq.md
[11] IETF, RFC 9110: HTTP Semantics — ETag and If-Match
https://www.rfc-editor.org/rfc/rfc9110.html
[12] John X. Morris et al., Text Embeddings Reveal (Almost) As Much As Text, EMNLP 2023
https://arxiv.org/abs/2310.06816
[13] OWASP GenAI Security Project, LLM08:2025 Vector and Embedding Weaknesses
https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/
[14] IETF, RFC 9111: HTTP Caching
https://www.rfc-editor.org/rfc/rfc9111.html
[15] MDN Web Docs, HEAD request method — HTTP
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/HEAD
[16] MDN Web Docs, ETag header — HTTP
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag
[17] MDN Web Docs, Last-Modified header — HTTP
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Last-Modified
[18] Redis Documentation, Redis Semantic Cache
https://redis.io/docs/latest/develop/use-cases/semantic-cache/
[19] Mermaid, Flowcharts Syntax — Styling and Classes
https://mermaid.ai/open-source/syntax/flowchart.html
.png)