실무 RAG 가이드: Drizzle ORM과 pgvector로 구축하는 지능형 검색
들어가며
2025년의 AI 서비스 개발은 단순히 API를 호출하는 단계를 넘어, 서비스가 가진 고유 데이터를 어떻게 AI가 이해하게 하느냐의 싸움이 되었습니다. 그 중심에 있는 기술이 바로 **RAG(Retrieval-Augmented Generation)**입니다.
오늘은 제가 블로그 인프라로 애용하는 Drizzle ORM과 PostgreSQL의 확장 모듈인 pgvector를 사용하여, 복잡한 인프라 없이도 강력한 벡터 검색 시스템을 구축하는 실무적인 방법을 정리해 보았습니다.
1. RAG와 벡터 검색의 이해
RAG는 사용자의 질문을 받으면 관련된 문서를 먼저 찾아내고(Retrieval), 그 내용을 프롬프트에 추가하여(Augmentation) 답변을 생성(Generation)하는 기술입니다. 여기서 ‘관련된 문서’를 찾기 위해 필요한 것이 바로 벡터 유사도 검색입니다.
왜 pgvector인가?
- 데이터 통합: 기존 사용자 정보와 게시글 데이터가 있는 PostgreSQL 안에서 벡터 데이터를 함께 관리할 수 있습니다.
- 비용 절감: 별도의 Vector DB(Pinecone 등)를 구독할 필요가 없습니다.
- 안정성: 수십 년간 검증된 PostgreSQL의 트랜잭션과 보안 기능을 그대로 누릴 수 있습니다.
2. 데이터베이스 스키마 설계
Drizzle ORM을 사용하여 벡터 데이터를 담을 테이블을 정의해 보겠습니다. OpenAI의 text-embedding-3-small 모델은 1536차원의 벡터를 생성하므로 이에 맞춰 설정합니다.
import { pgTable, uuid, text, vector } from 'drizzle-orm/pg-core';
export const blogEmbeddings = pgTable('blog_embeddings', {
id: uuid('id').primaryKey().defaultRandom(),
content: text('content').notNull(),
metadata: text('metadata'), // 소스 URL이나 작성자 정보 등
embedding: vector('embedding', { dimensions: 1536 }), // 벡터 필드 정의
});
3. 데이터 임베딩 및 저장하기
텍스트 데이터를 벡터값으로 변환하여 저장하는 ‘인제스천(Ingestion)’ 과정입니다. Vercel AI SDK를 사용하면 코드가 매우 간결해집니다.
import { embed } from 'ai';
import { openai } from '@ai-sdk/openai';
import { db } from '@/db';
import { blogEmbeddings } from '@/db/schema';
async function ingestPost(text: string) {
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: text,
});
await db.insert(blogEmbeddings).values({
content: text,
embedding: embedding,
});
}
4. 유사도 검색 쿼리 작성
사용자의 질문과 가장 유사한 맥락을 찾는 핵심 단계입니다. pgvector의 <=> 연산자(Cosine Distance)를 사용합니다.
import { sql } from 'drizzle-orm';
async function findContext(userQuery: string) {
// 1. 사용자 질문을 벡터로 변환
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: userQuery,
});
// 2. 유사도 기반 상위 5개 검색
const results = await db
.select({
content: blogEmbeddings.content,
similarity: sql<number>`1 - (${blogEmbeddings.embedding} <=> ${embedding})`,
})
.from(blogEmbeddings)
.orderBy(sql`${blogEmbeddings.embedding} <=> ${embedding}`)
.limit(5);
return results;
}
마치며
Drizzle ORM과 pgvector의 조합은 기술 블로그나 중소규모 서비스에서 AI 기능을 도입할 때 가장 효율적인 선택지입니다. 타입 안전성을 챙기면서도 SQL의 강력한 기능을 그대로 쓸 수 있기 때문이죠.
여러분도 이제 단순히 모델에만 의존하지 말고, 여러분만의 데이터를 AI에게 주입해 보세요. 훨씬 더 똑똑하고 정확한 답변을 경험하게 될 것입니다. 궁금한 점은 언제든 댓글로 남겨주세요!