# AI 웹사이트 빌더를 만들었더니 실제로 일어난 일

발행: (2026년 5월 23일 PM 10:47 GMT+9)
8 분 소요
원문: Dev.to

출처: Dev.to

AI를 활용한 뭔가를 만들고 싶었던 지 꽤 오래됐습니다. 단순히 ChatGPT를 감싸는 수준이 아니라 실제로 유용하게 느껴지는 무언가를요. 그래서 저는 CrafticWeb을 만들었습니다 — 웹사이트를 설명하면 실제 HTML/CSS/JS 코드를 생성해 줍니다. 후속 프롬프트로 미세 조정하고, 코드를 직접 편집하고, 실시간으로 미리 보며, 한 번의 클릭으로 배포할 수 있죠.
서비스가 라이브로 운영되고, 제대로 동작하며, 개발 과정에서 정말 많은 것을 배웠습니다. 솔직한 이야기를 전합니다.

핵심 개념은 아주 단순합니다. “다크 테마를 가진 커피숍 랜딩 페이지” 같은 문장을 입력하면, 앱이 이를 AI 모델(DeepSeek via OpenRouter)에게 전달하고, 완전한 HTML 문서를 받아 브라우저에 즉시 렌더링합니다. 채팅을 이어가며 다듬을 수 있고, Monaco 편집기로 열어 직접 수정할 수 있으며, 만족스러우면 공개 URL에 배포합니다.

또한 실제 제품 같은 느낌을 주기 위해 Google 로그인, 크레딧 시스템, Stripe 결제까지 구현했습니다. 주말에 만든 데모가 아니라 진짜 서비스라고 생각하고 만들었습니다.

사용한 기술 스택

  • 프론트엔드: React + Vite, Redux(상태 관리), Tailwind(스타일링), Framer Motion(애니메이션), Monaco Editor(브라우저 내 코드 편집기)
  • 백엔드: Node.js + Express, MongoDB(저장소), JWT + httpOnly 쿠키(인증), OpenRouter(AI 연동)
  • 결제: Stripe

특별히 복잡한 부분은 없지만, 전체가 어떻게 맞물리는지가 흥미롭습니다.


1️⃣ 마스터 프롬프트와 JSON 파싱 문제

백엔드는 DeepSeek에 마스터 프롬프트를 보내고 아래와 같은 깨끗한 JSON을 기대합니다.

{
  "message": "Your website is ready",
  "code": ""
}

이론적으로는 간단하지만, 실제로 모델이 응답을 마크다운 코드 펜스() 안에 감싸서 반환했습니다. 그래서 JSON.parse()가 오류를 일으키고 전체 흐름이 중단됐죠.

해결 방법은 펜스를 제거하는 작은 유틸 함수를 만든 뒤, JSON이 여전히 깨졌을 경우 API 호출을 최대 3번 재시도하도록 백엔드에 로직을 추가한 것입니다. 이 조치 이후에는 생성이 안정적으로 동작했습니다.


2️⃣ 실시간 프리뷰와 CORS 문제

첫 번째 실시간 프리뷰는 srcdoc 속성을 이용해 iframe에 HTML을 주입했는데, Vite가 자체 스크립트를 삽입하면서 CORS 오류가 발생했습니다.

해결책은 Blob URL을 사용하는 것이었습니다.

const blob = new Blob([html], { type: "text/html" });
const url = URL.createObjectURL(blob);
iframeRef.current.src = url;

브라우저는 이를 로컬 파일처럼 취급해 완전히 샌드박스화되므로 CORS 문제와 Vite 간섭이 사라졌습니다. 작은 변경 하나로 완전 해결되었습니다.


3️⃣ Google 인증 흐름

외관상 간단해 보이지만 여러 파트가 맞물려야 합니다. 전체 흐름은 다음과 같습니다.

  1. 사용자가 “Continue with Google” 클릭
  2. Firebase signInWithPopup이 사용자 정보 반환
  3. 프론트엔드가 { name, email, avatar }/api/v1/auth/registerPOST
  4. 백엔드가 MongoDB에서 사용자 조회/생성 → JWT 발급 → httpOnly 쿠키에 저장
  5. 이후 모든 요청은 쿠키를 자동으로 포함 (withCredentials: true)

프론트엔드 설정:

axios.defaults.withCredentials = true;

백엔드 쿠키 설정 예시:

res.cookie("token", jwt, {
  httpOnly: true,
  secure: true,
  sameSite: "none",
});
  • httpOnly 플래그: 클라이언트 JavaScript가 토큰을 직접 읽을 수 없게 하여 보안 강화
  • sameSite: "none" + secure: true: 배포된 프론트엔드와 백엔드 간 크로스 오리진 쿠키 전송을 허용

실패 사례: Render에 배포했을 때 인증 요청이 동작하지 않았습니다. 쿠키는 설정됐지만 브라우저가 전송하지 않았죠. 원인은 두 가지가 동시에 필요했기 때문입니다.

  • 쿠키 옵션 sameSite: "none" + secure: true
  • NODE_ENV=production 환경 변수 설정 (없으면 백엔드가 개발 모드로 동작해 프로덕션 쿠키 설정이 적용되지 않음)

두 조건을 모두 만족시키니 문제없이 작동했습니다.


4️⃣ Firebase 팝업 오류

signInWithPopupauth/popup-closed-by-user 오류를 내던 이유는 백엔드에 추가한 Cross-Origin-Opener-Policy 헤더였습니다. 이 헤더가 OAuth 팝업이 부모 창과 통신하는 것을 차단했죠. 헤더를 제거하니 오류가 완전히 사라졌습니다.


5️⃣ 크레딧·결제 시스템

솔직히 가장 까다로웠던 부분은 크레딧·결제 로직이었습니다. 처음엔 겁이 났지만, 지금은 꽤 깔끔하게 동작합니다.

  • Stripe Checkout: 결제 UI 담당
  • Stripe Webhooks: 결제 성공을 서버에서 확인하고, 사용자의 MongoDB 계정에 크레딧을 추가
  • 크레딧 차감: 각 성공적인 코드 생성 시 차감

주의점: 웹훅 라우트에서는 express.raw()body parser로 사용해야 합니다. express.json()을 쓰면 Stripe 서명 검증이 실패해 웹훅이 작동하지 않는 문제를 한 시간 동안 디버깅하게 됩니다.


앞으로의 계획

  • 스트리밍: HTML이 한 번에 다 로드되는 대신 점진적으로 표시되게 하여 더 빠른 느낌 제공
  • 버전 히스토리: 이전 생성 결과로 되돌아갈 수 있게 해서 프롬프트 과다 사용 시 발생하는 혼란 방지

관심 있으면 코드를 직접 확인해 보세요. 모두 오픈소스입니다.

👉 GitHub: https://github.com/KunalChakraborty445/Crafticweb

🌐 Live Demo: https://crafticwebai-beta.onrender.com

Built with too much console.log and not enough sleep — Kunal


webdev #javascript #react #nodejs #ai #buildinpublic #opensource #fullstack

0 조회
Back to Blog

관련 글

더 보기 »

내 스킬

프로젝트를 위한 AI 지시문을 만들고, 설치하고, 관리하세요 — 코딩이 필요 없습니다. CREATE 이름을 정하고, 카테고리를 선택하고, 원하는 것을 설명하세요 — 마법사가 자동으로 구성합니다.