2026년 디렉토리 및 마켓플레이스 웹사이트 최고의 기술 스택
디렉토리 및 마켓플레이스 웹사이트를 위한 최고의 기술 스택
대부분의 "최고의 기술 스택" 글들은 누군가가 Product Hunt를 하루 오후 훑어보고 글을 쓴 것처럼 읽힙니다. React와 Postgres를 쓰라고 말하고, Stripe를 던져 넣고, 끝이라고 합니다. 하지만 137,000개의 리스팅 페이지를 빠르게 렌더링하고, 50마일 반경 내 지역 검색을 지원하며, 사용자가 자연어 쿼리를 사용하여 결과를 찾을 수 있는 디렉토리 웹사이트를 만들려고 할 때는 도움이 되지 않습니다.
지난 2년간 정확히 이런 종류의 사이트들을 만들었습니다. 장난감 프로젝트가 아니라 수십만 개의 레코드를 처리하는 프로덕션 디렉토리 및 마켓플레이스 플랫폼, 다국가 결제 처리(0소수점 통화 같은 재미있는 엣지 케이스 포함), 그리고 전체 텍스트, 의미론적 AI, 지리 쿼리를 동시에 결합하는 검색 시스템을 말입니다. 이 글은 Social Animal에서 사용하는 스택의 모든 계층, 각 부분을 선택한 이유, 그 결정을 뒷받침하는 프로덕션 데이터를 다룹니다.
목차
- 디렉토리 및 마켓플레이스 사이트가 아키텍처적으로 독특한 이유
- 10계층 스택 개요
- 계층 1: 프론트엔드 -- Next.js 15
- 계층 2: 데이터베이스 -- Supabase PostgreSQL
- 계층 3: 인증 -- Supabase Auth
- 계층 4: 결제 -- Stripe Connect
- 계층 5: 검색 -- 트리플 검색 패턴
- 계층 6: 미디어 -- Supabase Storage + Next.js Image
- 계층 7: 호스팅 -- Vercel
- 계층 8: 이메일 -- Brevo API
- 계층 9: AI -- Claude API
- 계층 10: 모니터링 -- Vercel Analytics + PostHog
- 전체 스택 비교 표
- 프로덕션에서 이 스택의 비용
- FAQ

디렉토리 및 마켓플레이스 사이트가 아키텍처적으로 독특한 이유
디렉토리 및 마켓플레이스 웹사이트는 표면적으로는 간단해 보입니다. 어떤 것들을 나열하고, 사람들이 검색하게 하고, 어쩌면 결제를 처리합니다. 하지만 실제 데이터로 만들기 시작하면 표준 SaaS 아키텍처가 준비하지 않은 문제들을 마주치게 됩니다.
먼저 페이지 수 문제가 있습니다. 100K+ 리스팅이 있는 디렉토리는 100K+ 페이지가 필요합니다. 모든 페이지를 매 요청마다 서버 렌더링할 수 없고, 빌드 시간에 모든 페이지를 정적으로 생성할 수도 없습니다(빌드가 몇 시간 걸릴 것입니다). 더 똑똑한 방법이 필요합니다 -- ISR(Incremental Static Regeneration) 또는 온디맨드 재검증이 필요합니다.
두 번째로 검색은 다차원입니다. 사용자는 텍스트로 검색하고 싶습니다("가족 치료사"), 의미로 검색하고 싶습니다("관계 불안감을 도와주는 사람"), 위치로 검색하고 싶습니다("Austin에서 20마일 이내"). 대부분의 스택은 이 중 하나를 처리합니다. 세 가지를 모두 동시에 처리하려면 특정한 데이터베이스 아키텍처가 필요합니다.
세 번째로 마켓플레이스는 단순한 체크아웃을 훨씬 능가하는 결제 복잡성이 있습니다. 플랫폼 수수료, 구독 계층, 다중 통화 가격 책정, 그리고 국가별 규제 차이를 다루고 있습니다. 이 중 하나라도 잘못하면 돈을 잃거나 법을 어기게 됩니다.
이러한 제약 조건들이 우리 스택의 모든 결정을 형성했습니다. 각 계층을 하나씩 살펴봅시다.
10계층 스택 개요
깊이 들어가기 전에 전체 그림을 보겠습니다:
| 계층 | 도구 | 이유 |
|---|---|---|
| 프론트엔드 | Next.js 15 (App Router) | 100K+ 페이지에 대한 ISR, 서버 컴포넌트 |
| 데이터베이스 | Supabase PostgreSQL | pgvector + PostGIS + 하나의 DB에서 전체 텍스트 |
| 인증 | Supabase Auth | 행 수준 보안, 역할 기반 액세스 |
| 결제 | Stripe Connect | 마켓플레이스 수수료, 다중 통화 |
| 검색 | 트리플 패턴 (tsvector + pgvector + PostGIS) | 텍스트 + 의미론적 + 지역 동시에 |
| 미디어 | Supabase Storage + Next.js Image | 최적화된 배포, 간단한 업로드 |
| 호스팅 | Vercel | 엣지 배포, ISR 지원, 미리보기 URL |
| 이메일 | Brevo API | 거래 + API 경로에서 마케팅 |
| AI | Claude API | 의미론적 검색, 콘텐츠 강화, 챗봇 |
| 모니터링 | Vercel Analytics + PostHog | 트래픽 + 사용자 행동 추적 |
스택의 모든 계층이 여러 프로젝트에서 프로덕션으로 실행 중입니다. 실제로 어떻게 보이는지 보여드리겠습니다.
계층 1: 프론트엔드 -- Next.js 15
우리는 Next.js와 Astro 모두로 디렉토리 사이트를 만들었습니다. 둘 다 훌륭합니다. 하지만 디렉토리와 마켓플레이스 특히, Next.js 15 with App Router가 한 가지 기능 때문에 승리합니다: Incremental Static Regeneration입니다.
실제 시나리오가 있습니다. 우리 디렉토리 프로젝트 중 하나는 137,000개의 리스팅 페이지를 렌더링합니다. 다른 하나는 91,000개를 처리합니다. 빌드 시간에 이 모든 페이지를 정적으로 생성할 수 없습니다 -- 빌드가 영원히 걸리고 Vercel의 함수 실행 제한을 초과할 것입니다. 그리고 매 요청마다 모든 것을 서버 렌더링할 수도 없습니다. 서버 비용이 엄청날 것이고 Time to First Byte가 나빠질 것입니다.
ISR은 둘 다의 장점을 제공합니다. 페이지의 첫 방문자가 서버 렌더링을 트리거하면 엣지에서 캐시됩니다. 이후 방문자들은 정적 버전을 얻습니다. 재검증 간격을 설정하고(리스팅 페이지에는 일반적으로 3600초를 사용합니다), 캐시가 백그라운드에서 새로고쳐집니다.
// app/listings/[slug]/page.tsx
export const revalidate = 3600; // 매 시간 재검증
export async function generateStaticParams() {
// 가장 많이 방문한 상위 1000개 리스팅만 미리 생성
const { data } = await supabase
.from('listings')
.select('slug')
.order('view_count', { ascending: false })
.limit(1000);
return data?.map((listing) => ({ slug: listing.slug })) ?? [];
}
export default async function ListingPage({ params }: { params: { slug: string } }) {
const { data: listing } = await supabase
.from('listings')
.select('*, categories(*), reviews(*)')
.eq('slug', params.slug)
.single();
// 서버 컴포넌트 -- 이에 대해 클라이언트 쪽 JS가 전송되지 않음
return <ListingDetail listing={listing} />;
}
서버 컴포넌트가 다른 큰 승리입니다. 리스팅 상세 페이지는 대부분 정적 콘텐츠입니다 -- 이름, 설명, 사진, 리뷰. 클라이언트에 React의 런타임을 보낼 이유가 없습니다. 서버 컴포넌트는 서버에서 렌더링되고 순수 HTML을 보냅니다. 리스팅 페이지가 빠르게 로드되고 JavaScript 번들이 작게 유지됩니다.
클라이언트 컴포넌트는 아껴서 사용합니다: 검색 바, 인터랙티브 지도, 예약 양식, 그리고 사용자 상호작용이 필요한 무엇이든. 나머지는 모두 서버에 남아있습니다.
Astro는 안 되나요?
Astro는 상호작용이 최소한인 콘텐츠 중심 디렉토리에 훌륭합니다. 우리는 설명서 사이트와 콘텐츠 중심 프로젝트에 사용했습니다. 하지만 마켓플레이스 사이트는 인증된 상태, 실시간 기능, 복잡한 양식이 필요합니다. Next.js가 이러한 것들을 더 자연스럽게 처리합니다. 디렉토리가 대부분 읽기 전용인 경우(정적 비즈니스 디렉토리 생각해보세요), Astro를 고려할 가치가 있습니다 -- Astro 개발 능력을 확인해보세요.

계층 2: 데이터베이스 -- Supabase PostgreSQL
이것이 스택에서 가장 독단적인 선택이며, 가장 확신하는 선택입니다. Supabase는 모든 확장과 함께 PostgreSQL을 제공합니다 -- 디렉토리/마켓플레이스 사이트의 경우 세 가지 확장이 엄청나게 중요합니다: pgvector, PostGIS, 그리고 PostgreSQL의 내장 전체 텍스트 검색입니다.
우리 디렉토리 프로젝트들을 통해 Supabase에서 253,000+ 레코드를 관리하고 있습니다. 여기에는 리스팅, 사용자 프로필, 리뷰, 예약, 구독 데이터가 포함됩니다. PostgreSQL은 이를 거뜬히 처리합니다 -- 이 보다 훨씬 큰 데이터셋을 위해 설계되었습니다.
실제 통찰은 이것입니다: 전체 텍스트 검색, 벡터 임베딩, 지리 데이터를 같은 데이터베이스에 유지함으로써 여러 서비스 간 데이터 동기화의 아키텍처 복잡성을 피합니다. 텍스트 검색을 위해 Elasticsearch가 필요하지 않습니다. 벡터 검색을 위해 Pinecone이 필요하지 않습니다. 별도의 지역 서비스가 필요하지 않습니다. 하나의 데이터베이스. 하나의 진실의 소스입니다.
-- 텍스트 검색, 벡터 유사성, 지역 근접성을 결합하는 단일 쿼리
SELECT
l.id,
l.name,
l.description,
ts_rank(l.search_vector, plainto_tsquery('english', 'family therapist')) AS text_rank,
1 - (l.embedding <=> $1::vector) AS semantic_similarity,
ST_Distance(
l.location::geography,
ST_MakePoint(-97.7431, 30.2672)::geography
) / 1609.34 AS distance_miles
FROM listings l
WHERE
l.search_vector @@ plainto_tsquery('english', 'family therapist')
AND ST_DWithin(
l.location::geography,
ST_MakePoint(-97.7431, 30.2672)::geography,
80467 -- 50마일 (미터)
)
ORDER BY
(text_rank * 0.3) + (semantic_similarity * 0.5) + ((1 - distance_miles/50) * 0.2) DESC
LIMIT 20;
그것이 하나의 쿼리입니다. 전체 텍스트 순위 지정, 의미론적 유사성 점수 지정, 지역 거리 필터링 -- 모두 PostgreSQL에서 일어나고 있습니다. 세 개의 별도 서비스 전체에서 이를 수행하고 결과를 일관되게 유지해보세요.
디렉토리를 위한 데이터베이스 옵션에 대한 더 깊은 이해를 위해, 헤드리스 CMS 및 데이터베이스 비교를 확인하세요.
Supabase 행 수준 보안
RLS는 마켓플레이스 백엔드를 괴롭히는 문제를 해결하기 때문에 자체적으로 언급할 가치가 있습니다: 데이터베이스 수준에서의 데이터 액세스 제어입니다. 모든 API 엔드포인트에 인증 확인을 작성하는 대신, 데이터베이스 자체에 정책을 정의합니다.
-- 치료사들은 자신의 클라이언트 기록만 볼 수 있음
CREATE POLICY "therapists_own_clients" ON client_records
FOR SELECT USING (
auth.uid() = therapist_id
OR auth.jwt() ->> 'role' = 'admin'
);
API 코드에 버그가 있어서 실수로 쿼리를 노출시킨 경우에도, RLS는 무단 데이터 액세스를 방지합니다. 민감한 사용자 데이터를 처리하는 마켓플레이스 사이트의 경우, 이는 협상의 여지가 없습니다.
계층 3: 인증 -- Supabase Auth
우리가 이미 데이터베이스를 위해 Supabase 생태계에 있으므로, Supabase Auth를 사용하는 것이 자연스러운 선택입니다. 하지만 우리가 마켓플레이스에 사용하는 실제 이유는 RLS와 직접 통합되는 역할 기반 액세스입니다.
우리 마켓플레이스 프로젝트 중 하나는 세 가지 구별되는 사용자 유형에 걸쳐 역할 기반 인증을 실행합니다: 관리자, 서비스 제공자, 클라이언트. 각 역할은 다른 데이터를 보고, 다른 권한을 가지며, 다른 기능에 액세스합니다. 다른 프로젝트는 각 계층이 점진적으로 더 많은 기능을 잠금 해제하는 4계층 멤버십 시스템을 실행합니다.
구현은 사용자의 역할을 JWT 메타데이터에 저장합니다. 이것은 RLS 정책이 추가적인 데이터베이스 쿼리 없이 이를 참조할 수 있다는 뜻입니다:
// 가입 중 역할 할당
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
role: 'therapist',
tier: 'professional'
}
}
});
Supabase Auth는 OAuth 제공자(Google, Apple 등), 매직 링크, 이메일/비밀번호 -- 모두 기본으로 지원합니다. B2C 마켓플레이스의 경우, 소셜 로그인은 실질적으로 필수입니다. 우리는 이메일/비밀번호와 함께 Google OAuth가 가능할 때 가입 전환율이 30-40% 증가하는 것을 보았습니다.
계층 4: 결제 -- Stripe Connect
결제 처리는 마켓플레이스 프로젝트가 정말 복잡해지는 지점입니다. "결제를 받으세요"와 "결제를 받고, 플랫폼 수수료를 취하고, 환불을 처리하고, 30개 국가에 걸쳐 구독을 관리하고, 0소수점 통화를 다루세요" 사이에는 큰 차이가 있습니다.
Stripe Connect는 마켓플레이스 결제 흐름 -- 플랫폼과 서비스 제공자 간 분할을 처리합니다. 우리의 한 프로젝트는 모든 거래에 수수료를 처리하고, 자동으로 플랫폼 수수료와 제공자의 몫을 라우팅합니다.
하지만 구독 측이 정말 흥미로운 곳입니다. 우리는 30+ 국가에 걸쳐 지역 가격 책정이 있는 4계층 구독 시스템을 실행합니다. 이것은 다른 통화 지역에 대해 별도의 Stripe Price 객체를 유지하는 것을 의미합니다.
0소수점 통화 버그
이것은 우리가(그리고 우리의 클라이언트가) 실제 돈을 절약한 이야기를 공유하는 이유입니다. Stripe는 대부분의 통화를 최소 단위 -- 그래서 $10.00 USD는 1000(센트)로 처리합니다. 하지만 일본 엔(JPY)과 한국 원(KRW) 같은 일부 통화는 소수 단위가 없습니다. ¥1000은 그냥 1000입니다, 100000이 아니라.
코드가 맹목적으로 100을 곱해서 최소 단위로 변환하면, 일본 사용자들에게 의도한 금액의 100배를 청구할 것입니다. 우리는 테스트에서 이를 잡았습니다, 하지만 프로덕션 마켓플레이스 중에 이를 하지 않은 것들을 보았습니다.
const ZERO_DECIMAL_CURRENCIES = [
'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW',
'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'
];
function formatAmountForStripe(amount: number, currency: string): number {
if (ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase())) {
return Math.round(amount);
}
return Math.round(amount * 100);
}
지역별 체험 판매 제외
또 다른 함정: 우리는 특정 동남아 국가들을 무료 체험 판매 오퍼에서 제외해야 했습니다. 사기 비율이 그 지역에서 경제적으로 실행 불가능하게 만들었기 때문입니다. Stripe의 API는 고객 세금 위치 확인으로 이를 설정하도록 해주지만, 먼저 그것이 문제라는 것을 알아야 합니다. 이것이 프로덕션에서 다국가 마켓플레이스를 실행함으로써만 배우는 종류의 것입니다.
계층 5: 검색 -- 트리플 검색 패턴
이것은 아마도 이 글에서 가장 가치 있는 아키텍처 패턴입니다. 대부분의 디렉토리 사이트는 기본 텍스트 검색을 제공합니다. 좋은 것들은 위치 필터링을 추가합니다. 우리는 세 가지 검색 유형을 모두 동시에 실행하고 결과를 혼합합니다.
전체 텍스트 검색(PostgreSQL tsvector): 정확한 그리고 어근화된 키워드 일치를 처리합니다. 누군가 "plumber"를 검색할 때, "plumbing"도 일치합니다. 빠르고, 잘 이해되고, Postgres에 내장되어 있습니다.
의미론적 검색(pgvector + Claude 임베딩): 의미 기반 쿼리를 처리합니다. "관계에 대해 덜 불안해하는 것을 도와줄 수 있는 누군가"는 "therapist"라는 단어를 포함하지 않지만, 의미론적 검색은 의도를 이해합니다. 우리는 Claude API를 사용하여 각 리스팅에 대한 임베딩을 생성하고 pgvector에 벡터로 저장합니다.
지역 검색(PostGIS): 근접 쿼리를 처리합니다. "다운타운 Chicago에서 25마일 이내"는 인덱싱되고 빠른 공간 쿼리가 됩니다.
혼합은 정말 흥미로운 곳입니다. 우리는 쿼리에 따라 각 검색 유형의 가중치를 다르게 설정합니다:
interface SearchWeights {
textWeight: number;
semanticWeight: number;
geoWeight: number;
}
function calculateWeights(query: string, hasLocation: boolean): SearchWeights {
const isNaturalLanguage = query.split(' ').length > 4;
if (hasLocation && isNaturalLanguage) {
return { textWeight: 0.2, semanticWeight: 0.5, geoWeight: 0.3 };
} else if (hasLocation) {
return { textWeight: 0.4, semanticWeight: 0.2, geoWeight: 0.4 };
} else if (isNaturalLanguage) {
return { textWeight: 0.2, semanticWeight: 0.7, geoWeight: 0.1 };
}
return { textWeight: 0.7, semanticWeight: 0.2, geoWeight: 0.1 };
}
짧은 키워드 쿼리는 전체 텍스트 검색에 의존합니다. 더 긴 자연어 쿼리는 의미론적 검색에 의존합니다. 위치 컴포넌트가 있는 쿼리는 지역 가중치를 증가시킵니다. 우리의 디렉토리 사이트 중 하나는 137K+ 리스팅에 걸쳐 이 트리플 패턴을 실행하고, 검색 결과는 기본 텍스트 일치를 사용하는 경쟁자보다 눈에 띄게 더 좋습니다.
계층 6: 미디어 -- Supabase Storage + Next.js Image
디렉토리 사이트는 이미지가 많습니다. 리스팅 사진, 프로필 사진, 로고 -- 모두 합쳐집니다. 우리는 업로드를 위해 Supabase Storage를 사용하고 최적화된 배포를 위해 Next.js <Image> 컴포넌트를 사용합니다.
핵심 구성은 적절한 액세스 정책(리스팅 사진은 공개, 사용자 문서는 비공개)을 사용하여 Supabase Storage 버킷을 설정하고 올바른 크기로 WebP/AVIF 형식을 제공하기 위해 Next.js Image 최적화를 사용하는 것입니다:
<Image
src={`${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/listings/${listing.image_path}`}
alt={listing.name}
width={800}
height={600}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
loading="lazy"
/>
Vercel에 배포될 때 Next.js는 형식 변환, 크기 조정, 캐싱을 자동으로 처리합니다. 우리는 원본 업로드를 직접 제공하는 것에 비해 60-70%의 이미지 페이로드 감소를 보았습니다.
계층 7: 호스팅 -- Vercel
우리의 모든 프로덕션 디렉토리 및 마켓플레이스 사이트는 Vercel에서 실행됩니다. 이유는 간단합니다: Vercel은 Next.js가 가장 잘 실행되는 곳입니다. ISR, 서버 컴포넌트, 엣지 미들웨어, 미리보기 배포 -- 모든 것이 구성 없이 작동합니다.
디렉토리 사이트 특히, 엣지 네트워크가 중요합니다. Tokyo의 사용자가 디렉토리를 검색할 때, Virginia의 서버가 아닌 근처의 엣지 노드에서 캐시된 리스팅 페이지를 가져와야 합니다. Vercel의 엣지 캐싱은 ISR 페이지에 대해 자동으로 합니다.
미리보기 배포도 여러 이해 관계자가 있는 마켓플레이스 프로젝트에 큽니다. 모든 풀 요청은 자신의 URL을 얻습니다. 클라이언트는 프로덕션에 가기 전에 실제 URL에서 실제 데이터와 함께 새로운 검색 UI를 검토할 수 있습니다.
Vercel의 Pro 플랜은 팀 멤버당 $20/월이고 대부분의 디렉토리 프로젝트를 커버합니다. 더 큰 사이트(100K+ 페이지)는 더 높은 ISR 제한과 전담 지원을 위해 Enterprise 플랜이 필요할 수 있습니다.
계층 8: 이메일 -- Brevo API
마켓플레이스 프로젝트의 이메일은 두 가지 카테고리로 나뉩니다: 거래(예약 확인, 비밀번호 재설정, 결제 영수증) 및 마케팅(뉴스레터, 기능 공지, 재참여).
우리는 Next.js API 경로에서 호출되는 두 가지 모두를 위해 Brevo(이전 Sendinblue)를 사용합니다:
// app/api/send-booking-confirmation/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const { to, bookingDetails } = await request.json();
const response = await fetch('https://api.brevo.com/v3/smtp/email', {
method: 'POST',
headers: {
'api-key': process.env.BREVO_API_KEY!,
'content-type': 'application/json',
},
body: JSON.stringify({
to: [{ email: to }],
templateId: 12, // 예약 확인 템플릿
params: bookingDetails,
}),
});
return NextResponse.json({ success: response.ok });
}
Brevo의 무료 계층은 매일 300개의 이메일을 처리하며, 초기 단계 마켓플레이스에는 충분합니다. 유료 플랜은 5,000개 이메일에 대해 $9/월에서 시작합니다. SendGrid나 Mailgun과 비교하여, 우리는 Brevo의 전달성 비율이 비슷하고 가격이 예측 가능함을 발견했습니다.
계층 9: AI -- Claude API
AI는 우리 디렉토리 스택에서 트릭이 아닙니다 -- 세 가지 구별되는 작업을 처리하는 핵심 인프라 컴포넌트입니다.
의미론적 검색 임베딩: 모든 리스팅은 Claude에 의해 생성된 임베딩을 얻고, 이것은 의미를 캡처합니다. 이것이 위에 설명한 의미론적 검색 계층을 활성화합니다.
콘텐츠 강화: 사용자 제출 리스팅이 있는 디렉토리의 경우, 품질은 크게 다릅니다. 우리는 Claude를 사용하여 설명을 정규화하고, 구조화된 데이터(시간, 전문성, 서비스 지역)를 추출하고, SEO 친화적인 요약을 생성합니다.
인터랙티브 기능: 우리의 한 프로젝트는 우리가 "Oracle Council"이라고 부르는 것을 실행합니다 -- 다섯 가지 구별되는 AI 인물로, 사용자들은 다양한 유형의 지도를 위해 상담할 수 있습니다. 각 인물은 자신의 시스템 프롬프트, 성격, 그리고 전문 분야를 가집니다. 기발해 보이지만, 상당한 참여를 만들고 사이트의 가장 인기 있는 기능 중 하나입니다.
2025-2026 기준 Claude API 가격: Claude 3.5 Sonnet의 비용은 $3당 백만 입력 토큰, $15당 백만 출력 토큰입니다. 100K 리스팅 디렉토리에 대한 임베딩 생성 시, 일회성 비용은 대략 $50-80입니다. 검색 쿼리 및 챗봇 상호작용에 대한 지속적인 비용은 트래픽에 따라 일반적으로 $100-300/월입니다.
계층 10: 모니터링 -- Vercel Analytics + PostHog
디렉토리 사이트의 경우 두 가지 유형의 모니터링이 필요합니다: 성능 메트릭과 사용자 행동 분석입니다.
Vercel Analytics는 Web Vitals(LCP, CLS, INP), 실사용자 모니터링, 트래픽 데이터를 제공합니다. 이것은 Vercel 대시보드에 내장되어 있고 구성을 요구하지 않습니다. 디렉토리 사이트의 경우, 우리는 리스팅 페이지의 LCP를 밀접하게 모니터합니다 -- 2.5초 이상으로 올라가면, 우리의 ISR 구성이나 이미지 최적화에 뭔가 잘못되었다는 것을 압니다.
PostHog는 제품 분석을 처리합니다: 어느 검색 쿼리가 결과를 반환하지 않습니까(그래서 우리는 어떤 콘텐츠 갭을 채워야 하는지 압니다), 어느 리스팅 카테고리가 가장 많은 조회수를 얻습니까, 사용자들이 예약이나 가입 흐름에서 어디서 떨어져 나갑니까. PostHog의 무료 계층은 월 최대 백만 개의 이벤트를 커버하며, 대부분의 초기 단계 마켓플레이스를 처리합니다.
조합은 당신에게 "사이트가 빠른가?"와 "사용자들이 찾는 것을 찾고 있는가?" -- 두 가지 다르지만 똑같이 중요한 질문을 제공합니다.
전체 스택 비교 표
| 계층 | 우리의 선택 | 대안 | 우리가 선택한 이유 |
|---|---|---|---|
| 프론트엔드 | Next.js 15 | Astro, Remix | 100K+ 페이지에 대한 ISR |
| 데이터베이스 | Supabase PostgreSQL | PlanetScale, Neon | 하나의 DB에서 pgvector + PostGIS |
| 인증 | Supabase Auth | Clerk, Auth.js | 네이티브 RLS 통합 |
| 결제 | Stripe Connect | Paddle, LemonSqueezy | 마켓플레이스 분할, 다중 통화 |
| 검색 | 트리플 패턴 (DB 내) | Algolia, Elasticsearch | 외부 동기화 없음, 낮은 비용 |
| 미디어 | Supabase Storage | Cloudinary, S3 | 같은 생태계, 더 간단한 청구 |
| 호스팅 | Vercel | Netlify, AWS Amplify | 최고의 Next.js ISR 지원 |
| 이메일 | Brevo API | SendGrid, Resend | 가격/전달성 비율 |
| AI | Claude API | OpenAI, Gemini | 콘텐츠 작업을 위한 최고의 추론 |
| 모니터링 | Vercel + PostHog | Datadog, Mixpanel | 무료 계층이 초기 성장을 커버 |
프로덕션에서 이 스택의 비용
~50K 리스팅과 보통 트래픽(월 50K 방문자)이 있는 디렉토리 사이트의 실제 숫자들입니다:
| 서비스 | 플랜 | 월간 비용 |
|---|---|---|
| Vercel | Pro | $20 |
| Supabase | Pro | $25 |
| Stripe | 종량제 | 거래당 2.9% + 30¢ |
| Brevo | Starter | $9 |
| Claude API | 사용량 기반 | ~$150 |
| PostHog | 무료 계층 | $0 |
| 총 고정 비용 | ~$204/월 |
이것은 프로덕션 마켓플레이스 플랫폼으로서 놀랍도록 저렴합니다. Supabase Pro 플랜은 8GB의 데이터베이스 공간, 250GB의 대역폭, 100GB의 스토리지를 제공합니다 -- 50K 리스팅이 있는 디렉토리에는 충분합니다.
100K 리스팅을 넘어서고 더 높은 트래픽으로 확장할 때, 비용이 대략 $500-800/월로 올라갈 것으로 예상하세요. 여전히 전용 서버를 실행하고, 관리되는 Elasticsearch 클러스터를 관리하고, 별도의 벡터 데이터베이스를 관리하는 예전 접근 방식보다 훨씬 저렴합니다.
디렉토리 또는 마켓플레이스 프로젝트를 계획 중이고 가격 책정을 더 자세히 이해하고 싶다면, 가격 책정 페이지를 확인하거나 프로젝트별 추정을 위해 연락하세요.
FAQ
2026년 디렉토리 웹사이트를 위한 최고의 데이터베이스는 무엇입니까?
Supabase를 통한 PostgreSQL이 우리의 최고의 권장사항입니다. pgvector를 의미론적 검색, PostGIS를 지리 쿼리, 내장된 전체 텍스트 검색의 조합은 세 가지 검색 차원 모두를 외부 서비스 없이 처리할 수 있다는 의미입니다. 우리 프로덕션 프로젝트 전체에 걸쳐 253K+ 레코드로, 이것은 디렉토리 규모의 데이터를 처리합니다. PlanetScale(MySQL 기반) 같은 대안은 PostGIS 지원이 부족하여 지리 검색을 크게 더 어렵게 만듭니다.
Next.js가 디렉토리 사이트를 위해 100,000+ 페이지를 처리할 수 있습니까?
예, 하지만 ISR(Incremental Static Regeneration)이 필요합니다. 빌드 시간에 모든 100K 페이지를 생성하지 않습니다. 대신, 가장 높은 트래픽 페이지(어쩌면 상위 1,000-5,000)를 미리 생성하고 ISR이 나머지를 온디맨드로 생성하게 놔둡니다. 우리는 프로덕션에서 137K 페이지로 이를 했습니다. 핵심은 적절한 재검증 간격을 설정하는 것입니다 -- 우리는 리스팅 페이지에 3600초(1시간)를 사용하고 카테고리/검색 페이지에는 더 짧은 간격을 사용합니다.
의미론적 검색이 디렉토리 웹사이트에서 어떻게 작동합니까?
각 리스팅은 Claude 같은 AI 모델을 사용하여 의미를 캡처하는 숫자 벡터("임베딩")로 변환됩니다. 사용자가 자연어로 검색할 때 -- "ADHD를 가진 아이들을 돕는 누군가" -- 그 쿼리도 벡터로 변환됩니다. 데이터베이스는 pgvector의 코사인 유사성 연산자를 사용하여 쿼리 벡터에 수학적으로 가까운 리스팅을 찾습니다. 이것은 리스팅이 검색 쿼리에서 정확한 단어를 포함하지 않을 때도 작동합니다.
Stripe Connect가 마켓플레이스에 필요합니까, 아니면 정규 Stripe를 사용할 수 있습니까?
마켓플레이스가 구매자와 판매자(또는 클라이언트와 서비스 제공자) 간 결제를 포함하는 경우, Stripe Connect가 필요합니다. 정규 Stripe는 당신의 자신의 계정으로만 결제를 받게 합니다. Connect는 분할을 처리합니다 -- 플랫폼 수수료를 취하고 나머지를 서비스 제공자에게 라우팅합니다. 또한 US 기반 판매자를 위한 1099 리포팅을 처리하며, 이는 당신이 직접 만들고 싶지 않은 준수 요구사항입니다.
처음부터 디렉토리 웹사이트를 만드는 데 비용이 얼마나 됩니까?
여기 설명된 스택을 사용하면, 진행 중인 인프라 비용은 중간 규모 디렉토리의 경우 약 $200/월에서 시작합니다. 개발 비용은 기능에 따라 크게 다르지만, 검색, 사용자 계정, 리스팅 관리가 있는 프로덕션 준비 디렉토리는 일반적으로 8-16주가 걸립니다. 결제, 예약, 구독이 있는 전체 마켓플레이스는 추가 4-8주를 더합니다. 디렉토리 및 마켓플레이스 개발 능력을 더 자세히 탐색할 수 있습니다.
데이터베이스 내 검색 대신 Algolia나 Elasticsearch를 사용해야 합니까?
대부분의 디렉토리 사이트의 경우, 아니요. 주요 데이터베이스와 별도 검색 서비스 간 데이터 동기화의 복잡성은 버그를 만들고, 지연을 더하고, 비용을 올립니다. Algolia는 검색 연산을 기반으로 요금을 책정합니다 -- 규모에서 이것이 빠르게 비쌀 수 있습니다(그들의 가격 책정은 Build 플랜에서 1,000개 검색 요청당 $1에서 시작합니다). PostgreSQL의 내장 검색 능력, 특히 pgvector와 결합하여, 디렉토리 규모의 검색을 잘 처리합니다. 예외: 수백만 개의 레코드 위에서 오타 허용과 패싯 필터링이 필요하고 10ms 미만의 응답 시간을 원한다면, Algolia가 복잡성의 가치가 있습니다.
디렉토리와 마켓플레이스를 만드는 것 사이의 차이는 무엇입니까?
디렉토리는 사항들을 나열하고 사용자가 그들을 찾도록 합니다. 마켓플레이스는 거래를 더합니다 -- 결제, 예약, 수수료, 그리고 종종 제공자와 소비자 간 양방향 상호작용. 기술 스택은 대부분 동일하지만, 마켓플레이스는 Stripe Connect(또는 동등)를 더하고, 더 복잡한 인증 역할, 그리고 거래 이메일 흐름을 추가합니다. 데이터베이스 스키마는 또한 주문, 청구서, 지급액 추적 테이블과 함께 더 복잡해집니다.
기존 디렉토리 사이트에 AI 기능을 추가할 수 있습니까?
절대적으로. 우리 스택의 AI 계층은 추가적이고, 기초적이지 않습니다. 기존 리스팅을 위한 임베딩을 생성(일회성 배치 작업), pgvector 열에 저장, 그리고 기존 텍스트 검색과 함께 의미론적 검색 엔드포인트를 추가함으로써 의미론적 검색을 추가할 수 있습니다. 콘텐츠 강화 및 챗봇 기능은 독립적인 API 경로로 추가될 수 있습니다. 가장 어려운 부분은 보통 큰 기존 데이터셋을 위한 임베딩 생성입니다 -- 몇 시간의 처리 시간과 100K 리스팅에 대해 $50-100의 API 비용을 예산으로 하세요.