2026-03-12
OpenAI vs Gemini Embedding 2 — 임베딩 모델 비교 분석
들어가며
RAG(Retrieval-Augmented Generation) 파이프라인을 구축하거나, 시맨틱 검색을 구현할 때 가장 중요한 선택 중 하나가 바로 임베딩 모델이다. 임베딩 모델은 텍스트(혹은 이미지, 오디오 등)를 고차원 벡터 공간에 매핑하여, 의미적으로 유사한 콘텐츠를 수학적으로 가까운 위치에 배치한다. 이 벡터들 사이의 거리를 계산하면 "이 문서가 저 질문과 얼마나 관련 있는가"를 정량화할 수 있다.
현재 가장 널리 쓰이는 임베딩 모델은 OpenAI의 text-embedding-3-large와 Google의 Gemini Embedding 2다. 두 모델 모두 상용 API로 제공되며, 각각의 강점과 한계가 뚜렷하다. 이 글에서는 두 모델을 다양한 관점에서 비교하고, 실제 프로덕션 환경에서 어떤 모델을 선택해야 하는지에 대한 실질적인 가이드를 제공한다.
주요 스펙 비교
먼저 두 모델의 핵심 스펙을 표로 정리해보자.
Featuretext-embedding-3-largeGemini Embedding 2가격 (1M 토큰당)$0.13$0.20벡터 차원256 ~ 3,072128 ~ 3,072최대 입력 토큰8,1918,192멀티모달 지원텍스트만텍스트, 이미지, 오디오, 비디오, PDFTask Type 지정미지원지원 (RETRIEVAL_DOCUMENT, RETRIEVAL_QUERY 등)MRL (Matryoshka) 지원지원지원리전 제한없음us-central1만 지원
표만 봐도 두 모델의 성격 차이가 드러난다. OpenAI는 텍스트 임베딩에 집중하면서 가격 경쟁력을 갖추고 있고, Gemini는 멀티모달이라는 독보적인 기능을 제공하되 가격이 다소 높다.
하위 모델도 알아두자
두 회사 모두 메인 모델 외에 경량 모델을 제공한다. 용도에 따라 이쪽이 더 적합할 수 있다.
OpenAI text-embedding-3-small
가격: $0.02 / 1M 토큰 — 놀라울 정도로 저렴하다
벡터 차원: 512 ~ 1,536
최대 입력: 8,191 토큰
멀티모달: 텍스트만
대량의 텍스트를 임베딩해야 하지만 최고 수준의 정확도가 필수가 아닌 경우에 적합하다. 예를 들어 사내 문서 검색이나 FAQ 매칭 같은 용도라면 이것만으로도 충분할 수 있다.
Gemini Embedding 001 (text-embedding-004)
가격: $0.15 / 1M 토큰
벡터 차원: 최대 768
최대 입력: 2,048 토큰
멀티모달: 텍스트만
Task Type 지원: 지원
Gemini Embedding 2의 전작이다. 텍스트만 지원하고 입력 길이도 2,048로 짧지만, Task Type을 지원한다는 점은 동일하다. 긴 문서를 다루지 않는다면 여전히 쓸 만하지만, 최대 입력이 2,048 토큰이라는 점이 실무에서는 꽤 큰 제약이다.
Task Type — Gemini만의 차별점
Gemini 임베딩 모델의 독특한 기능 중 하나가 Task Type이다. 임베딩을 생성할 때 이 벡터가 어떤 용도로 쓰일지를 명시적으로 알려줄 수 있다.
RETRIEVAL_DOCUMENT— 검색될 대상 문서를 임베딩할 때RETRIEVAL_QUERY— 검색 쿼리를 임베딩할 때SEMANTIC_SIMILARITY— 두 텍스트의 유사도를 비교할 때CLASSIFICATION— 분류 작업에 사용할 때CLUSTERING— 클러스터링 작업에 사용할 때
이것이 왜 중요한가? 검색 시나리오를 생각해보자. 사용자가 "Next.js에서 SSR 구현하는 방법"이라고 검색하면, 이 짧은 쿼리와 긴 기술 문서는 본질적으로 다른 성격의 텍스트다. Task Type을 지정하면 모델이 이 차이를 인식하고, 쿼리용 벡터와 문서용 벡터를 각각의 목적에 최적화된 방식으로 생성한다. 이론적으로는 검색 정확도가 향상된다.
OpenAI의 모델은 이런 구분 없이 모든 텍스트를 동일하게 임베딩한다. 실제 벤치마크에서 이 차이가 얼마나 유의미한지는 데이터셋과 도메인에 따라 다르지만, 개념적으로는 Gemini의 접근이 더 정교하다.
// Gemini Embedding 2 — Task Type 사용 예시
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
// 문서 임베딩 시
const docEmbedding = await ai.models.embedContent({
model: "gemini-embedding-exp-03-07",
contents: "Next.js 14에서 App Router를 사용한 SSR 구현 가이드...",
config: {
taskType: "RETRIEVAL_DOCUMENT",
outputDimensionality: 768,
},
});
// 검색 쿼리 임베딩 시
const queryEmbedding = await ai.models.embedContent({
model: "gemini-embedding-exp-03-07",
contents: "Next.js SSR 구현 방법",
config: {
taskType: "RETRIEVAL_QUERY",
outputDimensionality: 768,
},
});멀티모달 임베딩 — 게임 체인저
Gemini Embedding 2의 가장 큰 차별화 요소는 단연 멀티모달 임베딩이다. 텍스트뿐만 아니라 이미지, 오디오, 비디오, PDF까지 동일한 벡터 공간에 임베딩할 수 있다. 이것이 의미하는 바는 크다.
예를 들어, 기술 블로그에 코드 스크린샷이나 아키텍처 다이어그램 이미지가 포함되어 있다고 하자. 기존 텍스트 전용 임베딩 모델로는 이미지의 내용을 검색할 방법이 없다. 이미지에 대한 alt 텍스트나 캡션을 수동으로 작성해서 그걸 임베딩하는 우회 방법은 있지만, 이미지 자체의 시각적 정보를 벡터화할 수는 없다.
Gemini Embedding 2는 이미지를 직접 벡터로 변환한다. 그리고 이 벡터는 텍스트 벡터와 같은 공간에 존재하므로, 텍스트 쿼리로 이미지를 검색하거나, 이미지로 유사한 텍스트를 검색하는 크로스모달 검색이 가능해진다.
// Gemini Embedding 2 — 이미지 임베딩 예시
import { GoogleGenAI } from "@google/genai";
import * as fs from "fs";
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
// 로컬 이미지를 base64로 읽어서 임베딩
const imageBuffer = fs.readFileSync("./architecture-diagram.png");
const base64Image = imageBuffer.toString("base64");
const imageEmbedding = await ai.models.embedContent({
model: "gemini-embedding-exp-03-07",
contents: {
parts: [
{
inlineData: {
mimeType: "image/png",
data: base64Image,
},
},
],
},
config: {
taskType: "RETRIEVAL_DOCUMENT",
outputDimensionality: 768,
},
});다만 실제 프로덕션에서 이 기능을 쓰려면 고려해야 할 점이 있다. 블로그나 웹 서비스에서 이미지를 보통 Cloudinary 같은 CDN에 호스팅한다. Gemini API에 이미지를 전달하려면 해당 이미지를 다운로드해서 MIME 타입과 함께 base64 인코딩된 데이터로 변환해야 한다. URL을 직접 전달하는 방식이 아니라, 실제 바이너리 데이터를 보내야 하는 것이다.
// CDN 이미지를 다운로드하여 Gemini에 전달하는 예시
async function embedCdnImage(imageUrl: string) {
// 1. 이미지 다운로드
const response = await fetch(imageUrl);
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const base64 = buffer.toString("base64");
// 2. Content-Type에서 MIME 타입 추출
const contentType = response.headers.get("content-type") || "image/jpeg";
// 3. Gemini API에 전달
const embedding = await ai.models.embedContent({
model: "gemini-embedding-exp-03-07",
contents: {
parts: [
{
inlineData: {
mimeType: contentType,
data: base64,
},
},
],
},
config: {
taskType: "RETRIEVAL_DOCUMENT",
outputDimensionality: 768,
},
});
return embedding;
}이 과정에서 네트워크 비용과 처리 시간이 추가로 발생한다. 이미지가 많은 서비스라면 임베딩 생성 파이프라인의 복잡도가 상당히 올라간다.
리전 제한 — 무시할 수 없는 현실
Gemini Embedding 모델은 현재 us-central1 리전만 지원한다. 이는 Vertex AI를 통해 사용할 때 특히 체감된다. 한국에서 서비스를 운영한다면 임베딩 API 호출 시 미국 중부까지 네트워크 왕복이 발생한다는 뜻이다.
실시간 검색에서 이 지연이 문제가 될 수 있다. 물론 임베딩 생성은 대부분 비동기 배치 작업으로 처리하고, 검색 시에는 쿼리 임베딩만 실시간으로 생성하면 되므로 실제 영향은 쿼리당 수십 ms 정도의 추가 지연일 것이다. 하지만 아시아 리전을 지원하는 OpenAI에 비하면 분명한 단점이다.
Matryoshka Representation Learning (MRL)
두 모델 모두 MRL(Matryoshka Representation Learning)을 지원한다. MRL은 벡터의 앞부분 N개 차원만 잘라서 사용해도 의미 있는 표현이 유지되도록 학습하는 기법이다. 마트료시카 인형처럼 큰 벡터 안에 작은 벡터가 내포되어 있는 구조다.
예를 들어, text-embedding-3-large의 전체 차원은 3,072이지만, 상위 256개 차원만 사용해도 어느 정도의 검색 품질을 유지할 수 있다. 벡터 DB 저장 비용과 검색 속도를 최적화하고 싶을 때 유용하다.
Gemini Embedding 2는 128차원부터 3,072차원까지 지원하므로, 더 공격적인 차원 축소가 가능하다. 물론 차원을 줄이면 정보 손실이 발생하므로, 실제 서비스에서는 자신의 데이터셋으로 테스트해보고 적절한 차원을 결정해야 한다.
비용 분석
단순히 토큰당 가격만 비교하면 OpenAI가 저렴하다. 하지만 실제 비용은 여러 요소를 고려해야 한다.
텍스트 전용 RAG
블로그 포스트 100개를 임베딩한다고 가정하자. 포스트당 평균 2,000 토큰이라면 총 200,000 토큰이다.
OpenAI
text-embedding-3-large: 200K / 1M × $0.13 = $0.026Gemini Embedding 2: 200K / 1M × $0.20 = $0.040
OpenAI
text-embedding-3-small: 200K / 1M × $0.02 = $0.004
절대 금액 자체는 미미하다. 수백만 토큰 규모가 아니라면 가격 차이는 사실상 무의미하다. 하지만 대규모 서비스에서는 이 차이가 누적된다. 텍스트만 다루는 RAG 파이프라인이라면 OpenAI가 비용 면에서 확실히 유리하고, 극단적으로 비용을 줄이고 싶다면 text-embedding-3-small이 압도적이다.
멀티모달을 고려한다면
이미지 검색이 필요한 경우, Gemini Embedding 2가 사실상 유일한 선택지다. OpenAI에는 이에 대응하는 상용 멀티모달 임베딩 모델이 없다. CLIP 같은 오픈소스 모델을 자체 호스팅하는 방법이 있지만, 인프라 관리 비용과 품질을 고려하면 Gemini API를 쓰는 것이 현실적일 수 있다.
모델 전환의 함정
임베딩 모델을 선택할 때 반드시 인지해야 할 점이 하나 있다. 임베딩 모델을 바꾸면 기존의 모든 임베딩을 다시 생성해야 한다.
서로 다른 모델이 생성한 벡터는 동일한 벡터 공간에 있지 않다. OpenAI 모델로 생성한 문서 벡터와 Gemini 모델로 생성한 쿼리 벡터를 비교하면 의미 없는 결과가 나온다. 따라서 모델을 전환하려면:
기존에 저장된 모든 벡터를 새 모델로 재생성해야 한다
데이터가 많을수록 전환 비용과 시간이 증가한다
전환 중 검색 서비스의 일관성을 어떻게 유지할지 고민해야 한다
벡터 차원이 달라지면 벡터 DB의 인덱스도 재구성해야 할 수 있다
이 때문에 임베딩 모델은 한번 정하면 쉽게 바꾸기 어렵다. 초기에 신중하게 선택하는 것이 중요하다. 그리고 혹시 나중에 전환해야 할 상황을 대비해서, 임베딩 생성 로직을 추상화 계층 뒤에 두는 것이 좋다.
// 임베딩 모델 추상화 예시
interface EmbeddingProvider {
embedText(text: string): Promise<number[]>;
embedImage?(imageData: Buffer, mimeType: string): Promise<number[]>;
dimensions: number;
modelName: string;
}
class OpenAIEmbedding implements EmbeddingProvider {
dimensions = 3072;
modelName = "text-embedding-3-large";
async embedText(text: string): Promise<number[]> {
const response = await openai.embeddings.create({
model: this.modelName,
input: text,
});
return response.data[0].embedding;
}
}
class GeminiEmbedding implements EmbeddingProvider {
dimensions = 768;
modelName = "gemini-embedding-exp-03-07";
async embedText(text: string): Promise<number[]> {
const response = await ai.models.embedContent({
model: this.modelName,
contents: text,
config: {
taskType: "RETRIEVAL_DOCUMENT",
outputDimensionality: this.dimensions,
},
});
return response.embeddings?.[0]?.values ?? [];
}
async embedImage(imageData: Buffer, mimeType: string): Promise<number[]> {
const response = await ai.models.embedContent({
model: this.modelName,
contents: {
parts: [{
inlineData: {
mimeType,
data: imageData.toString("base64"),
},
}],
},
config: {
taskType: "RETRIEVAL_DOCUMENT",
outputDimensionality: this.dimensions,
},
});
return response.embeddings?.[0]?.values ?? [];
}
}실제 선택 기준 정리
결국 어떤 모델을 선택할지는 요구사항에 따라 달라진다. 정리하면 이렇다.
OpenAI text-embedding-3-large를 선택해야 할 때
텍스트 전용 RAG 파이프라인을 구축할 때
비용을 최소화하고 싶을 때 (더 저렴하게 가려면
text-embedding-3-small)리전 제약 없이 글로벌하게 서비스해야 할 때
이미 OpenAI 생태계(GPT-4, Whisper 등)를 사용 중일 때
안정적이고 검증된 모델이 필요할 때
Gemini Embedding 2를 선택해야 할 때
이미지, 오디오, 비디오 등 멀티모달 검색이 핵심 기능일 때
Task Type을 활용한 정밀한 임베딩이 필요할 때
Google Cloud 생태계를 이미 사용 중일 때
us-central1 리전 제약이 문제되지 않을 때
이 블로그의 선택
이 블로그는 현재 text-embedding-3-large를 사용하고 있다. 블로그 포스트와 노트를 벡터화해서 시맨틱 검색에 활용하는 구조인데, 현재로서는 텍스트 기반 검색만으로 충분하다.
Gemini Embedding 2의 멀티모달 기능은 분명 매력적이다. 블로그 글에 포함된 아키텍처 다이어그램이나 스크린샷을 벡터 검색으로 찾을 수 있다면 사용자 경험이 한 단계 올라갈 것이다. 하지만 현실적으로 따져보면:
현재 이미지는 Cloudinary에 호스팅되어 있어, 임베딩 생성 시마다 이미지를 다운로드하고 변환하는 파이프라인을 추가로 구축해야 한다
모델을 전환하면 기존의 모든 임베딩을 재생성해야 한다
텍스트 검색만으로도 현재 블로그 규모에서는 충분히 잘 작동한다
가격도 OpenAI 쪽이 저렴하다
결론적으로, 지금은 text-embedding-3-large를 유지하기로 했다. 멀티모달 검색이 반드시 필요한 시점이 오면 그때 전환을 고려할 것이다. 다만 그때를 대비해서 임베딩 생성 로직은 프로바이더 패턴으로 추상화해두었다. 모델 전환 시 구현체만 교체하면 되도록.
마치며
임베딩 모델 시장은 빠르게 진화하고 있다. OpenAI도 조만간 멀티모달 임베딩을 내놓을 가능성이 높고, Google도 리전 확장과 가격 인하를 진행할 것이다. 중요한 것은 현재 요구사항에 맞는 모델을 선택하되, 미래의 전환 가능성을 열어두는 설계를 하는 것이다.
텍스트만 다루는 대부분의 RAG 시스템이라면 OpenAI의 text-embedding-3-large(혹은 비용에 민감하다면 text-embedding-3-small)가 여전히 가장 무난한 선택이다. 하지만 멀티모달 검색이라는 새로운 가능성을 탐색하고 싶다면, Gemini Embedding 2는 현재 시점에서 거의 유일한 상용 옵션이다. 각자의 프로젝트 요구사항에 맞게 현명한 선택을 하길 바란다.
관련 글
벡터 유사도 기반pgvector로 블로그 관련 글 추천 구현하기
PostgreSQL의 pgvector 확장과 OpenAI 임베딩을 활용해 블로그에 관련 글 추천 기능을 구현한 과정을 정리합니다.
87% 일치블로그 검색에 임베딩 기반 벡터 유사도를 도입한 이야기
LIKE 검색의 한계를 넘어 OpenAI 임베딩과 pgvector를 활용한 하이브리드 검색을 구현한 과정. 청킹 전략, 스코어링 방식, 점수 보정까지.
80% 일치개인 블로그에 RAG 기반 AI 챗봇 구축하기
pgvector + OpenAI 임베딩 + Groq LLM으로 블로그 콘텐츠 기반 RAG 챗봇을 만든 과정을 정리했습니다.