티스토리 뷰
실서비스급 NestJS + Next.js 프로젝트에 AI 기능 붙이기
(LLM + RAG + 벡터DB + 이미지 임베딩까지 실제 앱에 통합하는 방법)
이제 우리는 프론트/백엔드/인프라/배포 자동화/Cloudflare/AWS/Terraform/K8s/GitOps까지
풀스택 SaaS 운영 체인을 완성했다.
다음 단계는 무엇일까?
지금부터는 “기능 확장”이다.
그중에서도 2025년 SaaS에서 가장 중요한 건 바로 AI 기능 통합이다.
이번 글에서는 실제 서비스에 적용 가능한 형태로
LLM + RAG + VDB(Vector Database) + 이미지 임베딩 기능을 NestJS + Next.js에 붙이는 방법을 정리한다.
이 글은 “OpenAI API를 단순 호출하는 수준”을 넘는다.
실제 SaaS에서 사용하는 구조 그대로 구성한다.
🧭 이번 목표
구성 목적
| LLM API(OpenAI, HuggingFace, Ollama) | 모델 선택/호출 로직 구축 |
| RAG(Document + VectorDB) | 검색·요약·QA 기능 |
| Vector Database (Pinecone / Qdrant / Milvus) | 문서 임베딩 저장소 |
| 이미지 임베딩(CLIP 계열) | 이미지 태그/검색 기능 |
| NestJS 서비스화 | API 엔드포인트로 제공 |
| Next.js UI 구현 | RAG·챗 UI·이미지 검색 페이지 구현 |
1️⃣ NestJS에 LLM 모듈 추가하기
우선 OpenAI 기반으로 시작하고,
나중에 HuggingFace·Ollama·Llama·Gemini 등 멀티 모델 전략으로 확장할 수 있게 설계한다.
설치
npm install openai
NestJS LLM 서비스
src/ai/ai.service.ts
import { Injectable } from '@nestjs/common';
import OpenAI from 'openai';
@Injectable()
export class AiService {
private client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
async chat(prompt: string) {
const completion = await this.client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
});
return completion.choices[0].message.content;
}
}
컨트롤러
import { Controller, Post, Body } from '@nestjs/common';
import { AiService } from './ai.service';
@Controller('ai')
export class AiController {
constructor(private readonly ai: AiService) {}
@Post('chat')
async chat(@Body('prompt') prompt: string) {
return this.ai.chat(prompt);
}
}
API 호출:
POST /ai/chat
{ "prompt": "오늘 할 일 요약해줘" }
2️⃣ RAG 구성 – 문서 → 임베딩 → VectorDB 저장 → 검색
실제 SaaS에서는 "검색형 AI 기능"이 필수다.
(고객센터 FAQ, 문서 검색, 노트 검색, 유저 매뉴얼 등)
선택 가능한 VectorDB
DB 장점
| Qdrant (무료, Rust 기반 빠름) | 쉬운 도커 설치, NestJS에서 쓰기 편함 |
| Pinecone | 상용, 고성능 |
| Milvus | 대규모 배포용 |
| Weaviate | 서버 없는 클라우드 옵션 |
여기서는 Qdrant 사용.
2.1 Qdrant Docker 설치
docker-compose.yml
services:
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
volumes:
- qdrant_data:/qdrant/storage
2.2 NestJS에서 Qdrant SDK 설치
npm install @qdrant/js-client-rest
2.3 문서 임베딩 → 저장
embedding.service.ts
import { Injectable } from '@nestjs/common';
import OpenAI from 'openai';
import { QdrantClient } from '@qdrant/js-client-rest';
@Injectable()
export class EmbeddingService {
private openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
private qdrant = new QdrantClient({ url: process.env.QDRANT_URL });
async embedDocument(id: string, text: string) {
const embedding = await this.openai.embeddings.create({
model: "text-embedding-3-small",
input: text,
});
await this.qdrant.upsert("documents", {
points: [
{
id,
vector: embedding.data[0].embedding,
payload: { text },
},
],
});
return { id, ok: true };
}
}
2.4 검색 (RAG Retrieval)
async search(query: string) {
const embedding = await this.openai.embeddings.create({
model: "text-embedding-3-small",
input: query,
});
const result = await this.qdrant.search("documents", {
vector: embedding.data[0].embedding,
limit: 5,
});
return result;
}
3️⃣ RAG 기반 LLM 응답 만들기
async ragAnswer(query: string) {
const hits = await this.search(query);
const context = hits
.map((h) => h.payload.text)
.join("\n------------------\n");
const finalPrompt = `
다음 문서들을 참고해서 질문에 답변해줘:
문서:
${context}
질문:
${query}
`;
return this.chat(finalPrompt);
}
NestJS API:
POST /ai/rag
{
"query": "반려식물 관리법 요약해줘"
}
4️⃣ 이미지 임베딩(CLIP 기반) 검색 기능
텍스트 검색만 하면 허전하다.
요즘 서비스들은 거의 다 “이미지 임베딩 검색”을 넣는다.
예:
- 식물 사진 업로드 → 비슷한 식물 찾기
- 상품 사진 업로드 → 유사 아이템 추천
- 여행 사진 업로드 → 장소 자동 태깅
OpenAI Vision Embedding
const res = await openai.embeddings.create({
model: "clip",
input: fileBuffer,
});
Qdrant에 저장:
await qdrant.upsert("images", {
points: [
{
id,
vector: res.data[0].embedding,
payload: { url: imageUrl },
},
],
});
검색:
async searchImage(file: Buffer) {
const embedding = await openai.embeddings.create({
model: "clip",
input: file,
});
return await qdrant.search("images", {
vector: embedding.data[0].embedding,
limit: 10,
});
}
5️⃣ Next.js UI 구현
검색 → 상세 → 챗 → 이미지 업로드까지 하나의 프론트에 통합한다.
문서 검색 UI
'use client';
import useSWR from 'swr';
import axios from 'axios';
import { useState } from 'react';
export default function RagSearch() {
const [query, setQuery] = useState('');
const { data, mutate } = useSWR(
query ? `/api/rag?query=${query}` : null,
url => axios.get(url).then(r => r.data)
);
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="검색어 입력"
/>
<button onClick={() => mutate()}>검색</button>
{data && data.hits && (
<pre>{JSON.stringify(data.hits, null, 2)}</pre>
)}
</div>
);
}
6️⃣ 최종 아키텍처 (AI 포함)
Next.js (Cloudflare CDN SSR)
│
▼
NestJS API (K8s)
│
├── LLM (OpenAI / Local / HuggingFace)
├── VectorDB (Qdrant)
├── R2 이미지 저장
└── Redis 캐싱
7️⃣ 이번 편 정리
기능 구현
| LLM 텍스트 응답 | ✔️ |
| RAG 검색 | ✔️ |
| Vector Database | ✔️ |
| 이미지 임베딩 검색 | ✔️ |
| NestJS API로 통합 | ✔️ |
| Next.js UI 연동 | ✔️ |
이제 완전한 AI SaaS 기능이 붙었다.
🔮 다음 편 예고
이제 마지막 단계다.
다음 글은
👉 “사용자 인증 + 결제(Stripe) + 구독 상태에 따라 AI 기능 제한”
편으로 들어간다.
즉, 실서비스에서 쓰이는
AI 기능 + 유료 구독 모델(Subscription SaaS) 완성편 이다.
NestJS, Next.js, RAG, VectorDB, Qdrant, LLM, 이미지임베딩, AI검색, OpenAI, SaaS, DevOps
'study > 백엔드' 카테고리의 다른 글
| SaaS 운영 자동화 최종편 (0) | 2025.12.08 |
|---|---|
| Stripe 구독(Subscription) + 사용자 인증(Auth) + AI 기능 제한까지 (0) | 2025.12.05 |
| Terraform + GitOps(ArgoCD)로 “전체 인프라를 코드로 관리”하기 (0) | 2025.11.25 |
| Next.js + NestJS + Cloudflare 완전 통합 운영 아키텍처 구축 (0) | 2025.11.21 |
| AWS + Cloudflare 기반 NestJS 운영환경 구축 (0) | 2025.11.19 |
- Total
- Today
- Yesterday
- Prisma
- PostgreSQL
- CI/CD
- Next.js
- 웹개발
- Docker
- nextJS
- ai철학
- 프론트엔드개발
- rag
- Express
- REACT
- JWT
- Python
- 개발블로그
- JAX
- 백엔드개발
- fastapi
- llm
- node.js
- DevOps
- kotlin
- flax
- 딥러닝
- 쿠버네티스
- seo 최적화 10개
- Redis
- SEO최적화
- 압박면접
- NestJS
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |

