master-project
CHAPTER 31 / 50
읽기 약 2분
FUNCTION
검색 API: 전문 검색 + 필터링
핵심 개념
PostgreSQL FTS·tsvector·GIN 인덱스·trigram·하이라이트 — 빠르고 정확한 검색 구현.
본문
tsvector 컬럼 추가
-- 마이그레이션
alter table generations
add column search_vec tsvector
generated always as (
to_tsvector('simple',
coalesce(prompt, '') || ' ' ||
coalesce(result, '')
)
) stored;
-- GIN 인덱스 (검색 빠름)
create index idx_gens_fts on generations using gin(search_vec);검색 쿼리
-- to_tsquery (정확한 매치)
select * from generations
where search_vec @@ to_tsquery('simple', '카피');
-- websearch_to_tsquery (사용자 친화적)
select * from generations
where search_vec @@ websearch_to_tsquery('simple', 'AI 카피 OR 광고');
-- 부분 매치 (prefix)
select * from generations
where search_vec @@ to_tsquery('simple', '카피:*');Supabase Client에서 사용
const { data } = await supabase
.from('generations')
.select('*')
.textSearch('search_vec', `${query}:*`, { type: 'websearch' })
.order('created_at', { ascending: false })
.limit(20)한국어 검색 (trigram)
-- pg_trgm 확장 (PostgreSQL 부분 매치)
create extension if not exists pg_trgm;
-- 한국어는 형태소 분석기 부재 → trigram이 차선
create index idx_gens_prompt_trgm on generations using gin(prompt gin_trgm_ops);
-- 검색
select * from generations
where prompt % 'AI 카피' -- 유사도 매치
order by similarity(prompt, 'AI 카피') desc
limit 20;하이라이트
select
id,
ts_headline('simple', prompt, to_tsquery('simple', 'AI'),
'StartSel=<mark>, StopSel=</mark>') as highlighted_prompt
from generations
where search_vec @@ to_tsquery('simple', 'AI');// 클라이언트
<div dangerouslySetInnerHTML={{ __html: gen.highlighted_prompt }} />멀티 필터 (검색 + 카테고리 + 날짜)
// /api/generations/search
import { z } from 'zod'
const schema = z.object({
q: z.string().optional(),
category: z.enum(['blog', 'sns', 'email']).optional(),
date_from: z.string().optional(),
date_to: z.string().optional(),
cursor: z.string().optional(),
})
export async function GET(request: Request) {
const params = schema.parse(Object.fromEntries(new URL(request.url).searchParams))
const supabase = await createClient()
let q = supabase.from('generations').select('*')
if (params.q) q = q.textSearch('search_vec', `${params.q}:*`)
if (params.category) q = q.eq('category', params.category)
if (params.date_from) q = q.gte('created_at', params.date_from)
if (params.date_to) q = q.lte('created_at', params.date_to)
if (params.cursor) q = q.lt('created_at', params.cursor)
q = q.order('created_at', { ascending: false }).limit(20)
const { data } = await q
return Response.json({ data })
}외부 검색엔진 (큰 서비스)
[Algolia] — UI 컴포넌트·인스턴트 검색·$1/1K req
[MeiliSearch] — 오픈소스 self-host·자동 typo 보정
[Elasticsearch] — 강력하지만 운영 부담
[Typesense] — Algolia 오픈소스 대안
→ 1만+ 사용자 도달 전: PostgreSQL FTS로 충분검색 성능 측정
EXPLAIN ANALYZE
SELECT * FROM generations
WHERE search_vec @@ to_tsquery('simple', '카피:*')
ORDER BY created_at DESC
LIMIT 20;
-- "Bitmap Heap Scan" + "Bitmap Index Scan on idx_gens_fts" → 빠름
-- "Seq Scan" → 인덱스 안 탐 → 점검다음 챕터
CH.32 "웹훅: Stripe + 외부 서비스 연동".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 마스터 프로젝트의 검색 API 부분을 분석해서 실전 적용 + 개선 우선순위 3가지를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
검색 API 관련 모범 사례·안티패턴 5개를 비교 분석해서 실전 적용를 위한 추천 방안을 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 검색 API 최적화 가능 위치와 리스크를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 1인 개발자 시장의 검색 API 트렌드와 차별화 포인트를 정리해줘.
⭐ 이것만 기억하세요
검색 API: 전문 검색 + 필터링은 이 3가지만 확실히 잡으세요
1.PostgreSQL FTS = 무료·내장·1만 행까지 충분
2.한국어는 trigram 보조 + ILIKE
3.다음 챕터에서 웹훅 처리
공유하기
진행도 31 / 50