FAISS로 버티다 Pinecone으로 넘어간 진짜 이유
RAG 챗봇을 로컬 FAISS로 운영하다가 한 가지 문제가 생겼다. 문서가 5만 건을 넘어가자 인덱스 파일이 2GB를 넘겼고, 서버를 재시작할 때마다 이 파일을 통째로 메모리에 올리는 데 40초가 걸렸다. 응답 속도가 버텨줄 수 있는 수준이 아니었다.
클라우드 벡터 DB인 Pinecone을 찾아봤다. 무료 플랜이 있었다. 인덱스 1개, 저장 용량 2GB — 개인 프로젝트나 프로토타입 수준에서는 충분했다. OpenAI 임베딩과 연동하는 데 코드 30줄이면 됐다. 셋업부터 실제 유사도 검색까지 직접 검증한 전 과정을 공개한다.
1. 벡터 DB가 필요한 이유와 Pinecone 무료 플랜 조건
일반 데이터베이스는 “정확히 일치하는 값”을 찾는 데 최적화돼 있다. 벡터 DB는 다르다. “의미적으로 가까운 것”을 찾는다. “강아지 사료 추천”이라고 검색했을 때 “반려견 먹이 종류”라는 문서를 찾아주는 게 벡터 검색이다. 텍스트를 숫자 벡터(임베딩)로 변환해 저장하고, 질문 벡터와 가장 가까운 벡터를 수학적으로 계산해 꺼내는 구조다.
Pinecone 무료 플랜(Starter)의 조건은 아래와 같다. 프로토타입과 개인 프로젝트에는 충분한 수준이다.
- 인덱스 수: 1개
- 저장 용량: 약 100만 벡터 (1536차원 기준 약 2GB)
- 월 비용: 완전 무료 (신용카드 불필요)
- 리전: AWS us-east-1 고정 (무료 플랜)
2. Pinecone 계정 생성 및 인덱스 셋업
계정 생성 및 API 키 발급
Pinecone 공식 사이트(pinecone.io)에서 구글 계정으로 가입하면 바로 대시보드로 진입한다. 왼쪽 메뉴 [API Keys]에서 기본 생성된 키를 복사한다. 프로젝트 폴더의 .env 파일에 아래처럼 저장한다.
PINECONE_API_KEY=여기에_발급받은_키
OPENAI_API_KEY=여기에_OpenAI_키
인덱스 생성 — 차원 수가 핵심
인덱스를 만들 때 가장 중요한 설정이 차원 수(dimension)다. OpenAI의 text-embedding-3-small 모델은 1536차원 벡터를 생성한다. 인덱스의 차원 수와 임베딩 모델의 출력 차원이 반드시 일치해야 한다. 한 번 만든 인덱스의 차원은 변경할 수 없으므로 처음에 정확히 맞춰야 한다.
pip install pinecone openai python-dotenv
import os
from pinecone import Pinecone, ServerlessSpec
from dotenv import load_dotenv
load_dotenv()
pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))
INDEX_NAME = "my-rag-index"
# 인덱스가 없을 때만 생성
if INDEX_NAME not in pc.list_indexes().names():
pc.create_index(
name=INDEX_NAME,
dimension=1536, # text-embedding-3-small 출력 차원
metric="cosine", # 텍스트 유사도는 코사인 유사도가 적합
spec=ServerlessSpec(
cloud="aws",
region="us-east-1" # 무료 플랜은 이 리전만 지원
)
)
print(f"인덱스 '{INDEX_NAME}' 생성 완료")
else:
print(f"인덱스 '{INDEX_NAME}' 이미 존재")
index = pc.Index(INDEX_NAME)
3. OpenAI 임베딩 생성 및 Pinecone 저장·검색 전체 코드
1단계: 텍스트를 벡터로 변환해 Pinecone에 저장
문서 리스트를 임베딩으로 변환하고 Pinecone에 업서트(upsert)한다. 한 번에 100개씩 배치로 올리는 것이 API 효율 면에서 훨씬 낫다.
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def get_embedding(text: str) -> list:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
# 저장할 문서 샘플
documents = [
{"id": "doc1", "text": "파이썬은 데이터 분석과 AI 개발에 널리 쓰이는 프로그래밍 언어다."},
{"id": "doc2", "text": "텔레그램 봇은 파이썬으로 30분 만에 만들 수 있다."},
{"id": "doc3", "text": "Pinecone은 클라우드 기반 벡터 데이터베이스 서비스다."},
{"id": "doc4", "text": "OpenAI Whisper API는 음성을 텍스트로 변환해주는 STT 모델이다."},
{"id": "doc5", "text": "GitHub Actions를 활용하면 파이썬 스크립트를 무료로 자동 실행할 수 있다."},
]
# 임베딩 생성 후 Pinecone에 배치 저장
vectors = []
for doc in documents:
embedding = get_embedding(doc["text"])
vectors.append({
"id": doc["id"],
"values": embedding,
"metadata": {"text": doc["text"]} # 원본 텍스트도 함께 저장
})
index.upsert(vectors=vectors)
print(f"{len(vectors)}개 문서 저장 완료")
2단계: 질문으로 유사 문서 검색
질문을 임베딩으로 변환하고 Pinecone에서 가장 유사한 문서를 꺼내온다. top_k는 반환할 결과 수이고, include_metadata=True를 설정해야 저장해둔 원본 텍스트도 함께 받을 수 있다.
def search_similar(query: str, top_k: int = 3) -> list:
query_embedding = get_embedding(query)
results = index.query(
vector=query_embedding,
top_k=top_k,
include_metadata=True
)
return results["matches"]
# 검색 테스트
query = "자동화 스크립트를 서버 없이 실행하는 방법"
matches = search_similar(query)
print(f"\n['{query}'] 검색 결과:")
for match in matches:
score = round(match["score"], 4)
text = match["metadata"]["text"]
print(f" 유사도 {score} — {text}")
4. 직접 맞은 에러 2가지와 해결법
에러 ① dimension mismatch — 차원 불일치
인덱스를 처음에 dimension=1536으로 만들었다가 나중에 임베딩 모델을 text-embedding-3-large(3072차원)로 바꿨더니 Vector dimension mismatch 에러가 났다. 이미 만들어진 인덱스의 차원은 바꿀 수 없다. 기존 인덱스를 삭제하고 새로 만들거나, 처음부터 쓸 모델을 확정한 뒤 그에 맞는 차원으로 인덱스를 생성해야 한다. 비용 효율을 생각하면 text-embedding-3-small(1536차원)이 대부분의 프로젝트에 충분하다.
에러 ② upsert 후 바로 query했을 때 결과가 0건
문서를 저장하고 곧바로 검색했는데 결과가 비어 있었다. Pinecone은 upsert 후 인덱싱이 완료되기까지 수 초의 지연이 있다. 저장 직후 바로 쿼리하면 아직 인덱싱이 안 된 벡터는 검색에 잡히지 않는다. time.sleep(3)을 upsert와 query 사이에 넣거나, index.describe_index_stats()로 벡터 카운트가 올라가는 것을 확인한 뒤 검색하면 해결된다.
5. FAISS vs Pinecone 실전 비교
두 가지를 모두 써본 뒤 정리한 기준이다. 프로젝트 규모와 목적에 따라 선택이 달라진다.
| 비교 항목 | FAISS (로컬) | Pinecone (클라우드) |
|---|---|---|
| 비용 | 완전 무료 | 무료 플랜 제공 (100만 벡터) |
| 데이터 규모 | 수만 건까지 쾌적, 그 이상은 메모리 부담 | 수백만 건도 안정적 처리 |
| 서버 재시작 시 | 인덱스 파일 전체 메모리 로딩 필요 | 클라우드 상시 유지, 즉시 접근 |
| 적합한 용도 | 로컬 프로토타입, 소규모 RAG | 클라우드 배포, 중대규모 RAG 서비스 |
로컬에서 빠르게 프로토타입을 만들 때는 FAISS가 낫다. 설치도 없고 API 키도 필요 없다. 하지만 서비스를 클라우드에 올리거나 데이터가 수십만 건을 넘어가면 Pinecone으로 넘어가는 게 맞다. 두 가지 모두 써본 지금은 개발 초기에는 FAISS, 배포 단계에서 Pinecone으로 마이그레이션하는 흐름을 기본 패턴으로 쓰고 있다.