음성봇을 스텁부터 만든 이유 — 테스트로 설계를 먼저 굳히다

시리즈 · 음성봇 직접 만들기 — 실시간 음성봇의 뼈대를 바닥부터 만든 8편 기록입니다. 1. 끼어들기(barge-in) — 재생 중인 TTS를 실시간으로 끊는 법 2. "말이 끝났다"는 판단 — VAD 엔드포인터 3. 엔진을 코드 수정 없이 바꾸는 인터페이스 설계 4. 로컬 LLM 대화 두뇌와 도구 호출 루프 5. 매 턴 지연시간을 재는 이유 6. 브라우저 마이크에서 서버까지 — 오디오 프로토콜 7. 로컬 Whisper STT 연결하기 8. 스텁부터 만든 이유 — 테스트 우선 설계 (완결) — 지금 보는 글 돌아보며 시작하는 마지막 편 이 시리즈를 시작할 때부터 정해 둔 규칙이 하나 있었습니다. 진짜 음성 인식 모델과 진짜 합성 모델이 준비되기 전까지는, 그 둘을 흉내만 내는 가짜 엔진으로 전체 구조를 먼저 돌려본다는 규칙입니다. 여덟 편을 거치며 끼어들기 처리, 상태 흐름, 프로토콜, 엔진 인터페이스를 하나씩 다뤘는데, 그 모든 것이 사실 이 가짜 엔진 위에서 검증됐습니다. 지금 코드를 다시 열어 보면 그때는 별생각 없이 세운 규칙이 결과적으로 시리즈 전체의 진행 속도를 결정했다는 게 눈에 들어옵니다. 이번 편에서는 왜 굳이 가짜부터 만들었는지, 그리고 그 선택이 시리즈 전체에 어떤 영향을 남겼는지 정리하면서 이 시리즈를 마무리하려 합니다. 실제 모델을 기다리지 않기로 한 이유 음성봇의 핵심 로직은 사실 음성 인식이나 음성 합성 자체가 아닙니다. 사용자가 말을 시작했는지 끝냈는지 판단하는 상태머신, 합성 중에 끼어들기가 들어왔을 때 스트림을 멈추는 동시성 처리, 이벤트가 순서대로 오가는 흐름 제어 같은 것들이 훨씬 까다롭습니다. 반면 실제 인식·합성 모델은 무겁고 설치가 까다롭고 실행 속도도 느립니다. 이 두 가지를 한꺼번에 붙잡고 있으면 어려운 동시성 버그를 실제 모델의 로딩 시간과 뒤섞어 디버깅하게 됩니다. 모델이 느려서 반응이 늦은 건지, 상태머신이 잘못 짜여서 늦은 건지 구분이 안 되는 상황을 미...

Claude Code로 회사 홈페이지 만들기 — Next.js + 카페24 쇼핑몰 연동까지


친구 회사 홈페이지를 하루 만에

친구가 운영하는 회사(볼트·너트 도소매 중소기업) 홈페이지를 만들어 줬습니다. 카페24 쇼핑몰은 이미 있는데, "회사 얼굴이 되는 브랜드 사이트가 따로 있으면 좋겠다"는 요청이었습니다.

보통은 외주를 주거나 직접 잡아도 시간이 꽤 드는 일입니다. 이번엔 Claude Code로 설계부터 배포까지 초안 단계를 끝냈습니다. 담고 싶었던 건 이런 것들이었습니다.

  • 회사소개·제품 카탈로그·견적요청을 담은 브랜드 사이트
  • 기존 카페24 쇼핑몰 상품을 실시간으로 끌어와 보여주기
  • B2C 고객은 쇼핑몰로, B2B 고객은 견적요청으로 유도

이 글에서는 그 과정을, 특히 한국에서 자료가 적은 카페24 API 연동을 중심으로 정리합니다.

Claude Code로 만든 회사 브랜드 사이트 (회사 정보는 가림)
Claude Code로 만든 회사 브랜드 사이트 (회사 정보는 가림)

무엇을 만들었나

한 사이트에 두 종류의 고객을 모두 담았습니다.

  • B2C — 일반 고객은 제품을 보다가 자연스럽게 카페24 쇼핑몰로 넘어가 구매
  • B2B — 대량 거래 고객은 견적요청 폼으로 유도

페이지는 회사소개(about) · 제품 카탈로그(products) · 견적요청(quote) · 문의(contact)로 구성했습니다. 제품 카탈로그는 직접 입력하지 않고 카페24 쇼핑몰의 상품 데이터를 그대로 가져와 채웠습니다. 상품이 5,000개라 수기 입력은 불가능했고, 쇼핑몰에서 상품이 바뀌면 사이트에도 반영돼야 했기 때문입니다.

회사소개 페이지 — 비전·미션·연혁 (회사명은 가림)
회사소개 페이지 — 비전·미션·연혁 (회사명은 가림)

기술 스택


- Next.js 14 (App Router)   ← 페이지 + API Route를 한 프로젝트에서
- TypeScript
- Tailwind CSS              ← 빠른 스타일링
- 카페24 REST API           ← 상품 데이터 소스
- Vercel                    ← 무료 배포

거창하지 않습니다. 핵심은 Next.js 하나로 프론트(페이지)와 백엔드(API 프록시)를 같이 처리하고, 상품 데이터는 카페24에서 끌어오는 구조입니다.


1. Claude Code로 — 계획부터 세우고 시작

바로 코딩하지 않았습니다. 먼저 Claude Code에게 설계서와 구현 계획서를 쓰게 했습니다. "볼트·너트 도소매 회사의 브랜드 사이트, 카페24 연동, B2C/B2B 분기"라는 요구를 주니, 페이지 구조·API 설계·작업 순서를 문서로 정리해 줬습니다.

그다음 그 계획을 작업 단위로 하나씩 구현하게 했습니다. 계획을 먼저 세우고 시작하니, 중간에 구조가 흔들리지 않고 페이지·API·연동이 일관되게 쌓였습니다. "잘 시키기"보다 "먼저 설계를 시키고, 그 설계대로 구현시키기"가 훨씬 안정적입니다.


2. 카페24 쇼핑몰 API 연동 (핵심)

이 프로젝트에서 한국어 자료가 가장 적었던 부분입니다. 카페24 REST API로 상품을 가져오는 것이죠.

핵심 원칙은 하나입니다. 카페24 API 키(토큰)를 브라우저에 노출하면 안 됩니다. 그래서 Next.js의 Route Handler(서버)가 카페24를 대신 호출하고, 브라우저는 우리 서버에만 요청하게 했습니다. 일종의 프록시입니다.


// app/api/products/route.ts — 카페24를 서버에서 대신 호출 (키 숨김)
import { NextResponse } from 'next/server'
import { getProducts } from '@/lib/cafe24/client'

export const revalidate = 3600   // ISR: 1시간마다 갱신 (매 요청마다 카페24 안 두드림)

export async function GET() {
  try {
    const products = await getProducts()
    return NextResponse.json({ products })
  } catch {
    return NextResponse.json({ error: 'Failed to fetch products' }, { status: 500 })
  }
}

실제 카페24 호출부는 이렇습니다. 토큰은 환경변수에 두고 서버에서만 씁니다.


// lib/cafe24/client.ts
const MALL_ID = process.env.CAFE24_MALL_ID
const TOKEN = process.env.CAFE24_ACCESS_TOKEN   // 서버 전용. 절대 클라이언트로 X

export async function getProducts() {
  const res = await fetch(
    `https://${MALL_ID}.cafe24api.com/api/v2/admin/products?limit=100`,
    { headers: { Authorization: `Bearer ${TOKEN}` } }
  )
  if (!res.ok) throw new Error('cafe24 fetch failed')
  const data = await res.json()
  return data.products
}
카페24 토큰을 서버에만 두고 Route Handler가 대신 호출하는 프록시 구조
카페24 토큰을 서버에만 두고 Route Handler가 대신 호출하는 프록시 구조

여기서 놓치면 안 되는 게 있습니다.

  • 프록시 구조 — 카페24 토큰은 서버(Route Handler)에만 있습니다. 브라우저 코드엔 절대 들어가지 않습니다.
  • ISR 캐싱(revalidate) — 상품 5,000개를 매 방문마다 카페24에서 끌어오면 느리고 API 한도도 빨리 닳습니다. revalidate=3600으로 1시간마다 한 번만 갱신하게 했습니다.

3. B2C / B2B 분기

같은 사이트에서 두 고객을 다르게 안내했습니다.

  • 제품 카탈로그에서 일반 고객은 "쇼핑몰에서 구매" 버튼으로 카페24 본몰로
  • 대량 거래 고객은 "견적 요청" 버튼으로 견적 폼으로

견적 폼은 회사명·담당자·연락처·품목을 받습니다. 초기(MVP)에는 접수 내용을 서버 로그로만 남기고, 이메일 발송(Resend 등)은 다음 단계로 두었습니다. 완벽한 자동화보다 "일단 접수되는 것"을 먼저 띄운 겁니다.

B2B 고객용 견적요청 폼 (입력값은 예시)
B2B 고객용 견적요청 폼 (입력값은 예시)

// app/api/quote/route.ts — 견적요청 접수 (MVP: 로그 → 추후 이메일 연동)
export async function POST(request: NextRequest) {
  const { company, name, phone, email, items, message } = await request.json()
  if (!company || !name || !phone || !email || !items) {
    return NextResponse.json({ error: '필수 항목을 입력해주세요.' }, { status: 400 })
  }
  console.log('견적요청 접수:', { company, name, phone, email, items })
  return NextResponse.json({ success: true, message: '견적요청이 접수되었습니다.' })
}

4. Vercel 배포

배포는 Vercel 무료 플랜으로 했습니다. GitHub에 올리고 Vercel에 연결하면 push할 때마다 자동 배포됩니다. 카페24 토큰 같은 비밀값은 코드가 아니라 Vercel 환경변수에 넣어 관리했습니다.

  • 코드 push → 자동 빌드·배포
  • 환경변수(CAFE24_MALL_ID, CAFE24_ACCESS_TOKEN)는 Vercel 대시보드에
  • 도메인 연결도 Vercel에서 클릭 몇 번

막혔던 지점들

  • 카페24 토큰 만료 — 액세스 토큰은 유효기간이 있어, 갱신(refresh) 처리를 빼먹으면 어느 날 상품이 안 뜹니다. 토큰 갱신 로직을 client에 넣어야 합니다.
  • API 한도 — 매 요청마다 카페24를 두드리면 한도에 걸립니다. ISR 캐싱이 선택이 아니라 필수인 이유입니다.
  • 키 노출 — 처음엔 무심코 클라이언트에서 직접 호출하려다, 토큰이 브라우저에 노출되는 걸 깨닫고 Route Handler 프록시로 바꿨습니다.
  • 완벽주의 함정 — 이메일·결제까지 다 붙이려다 보면 영영 못 띄웁니다. 먼저 뜨게 하고, 견적은 로그로 받고, 나머지는 운영하며 붙였습니다.

마무리

Claude Code로 설계서를 먼저 쓰고, 그 계획대로 페이지와 API를 쌓고, 카페24 상품을 프록시로 끌어와, Vercel에 배포했습니다. 혼자 처음부터 붙잡았으면 한참 걸렸을 회사 사이트 초안이, 이렇게 매듭지어졌습니다.

특히 한국에서 자료가 적은 카페24 API 연동도, 토큰을 서버 밖으로 내보내지 않고 프록시로만 호출하면서 ISR로 캐싱해 두면 생각보다 걸릴 게 없었습니다.


자주 묻는 질문

Q. 카페24 쇼핑몰이 없어도 되나요?

A. 됩니다. 상품 연동 부분만 빼면 일반 회사 브랜드 사이트가 됩니다. 카페24 연동은 "이미 쇼핑몰이 있는데 브랜드 사이트를 따로 두고 싶을 때" 유용합니다.

Q. 코딩을 잘 몰라도 만들 수 있나요?

A. 핵심은 "먼저 Claude Code에게 설계를 시키고, 그 계획대로 구현시키는" 흐름입니다. 카페24 토큰 발급·Vercel 배포 같은 설정은 안내를 따라가면 됩니다.

Q. 왜 워드프레스나 노코드 툴 대신 Next.js인가요?

A. 카페24 API 실시간 연동과 B2C/B2B 분기처럼 "내 요구에 딱 맞는 동작"이 필요할 때는 직접 만드는 편이 자유롭습니다. 단순 소개 페이지라면 노코드도 좋은 선택입니다.


이 글의 회사 정보·도메인·상품 데이터는 모두 예시로 일반화했습니다. 실제 의뢰처 정보는 포함하지 않습니다. (2026년 6월 기준)

댓글

이 블로그의 인기 게시물

디스코드 구글캘린더 연동 봇 만들기: 카톡 캡처 OCR 자동 등록 시스템 구축

클로드 코드 비용 줄이는 법 — ollama 로컬 LLM 연동 (M3 맥북)

유튜브 노션 자동 저장 봇 만들기 — Claude CLI MCP + Discord 233일 운영기