본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 05. 27. 08:33

채팅으로 지시하면 사이트가 변경되는 웹사이트를 만들었습니다

요약

사용자의 자연어 채팅 명령을 통해 웹사이트의 UI와 코드를 실시간으로 수정하고 Git에 반영하는 'prompt-to-app' 시스템 구현 사례를 소개합니다. LLM의 응답을 JSON 계약(Contract) 형태로 제한하고 MCP 서버를 통해 안전한 도구 실행 환경을 구축한 것이 핵심입니다.

핵심 포인트

  • 자연어 명령을 안전한 UI 조작 명령(JSON)으로 변환
  • MCP 서버를 활용한 도구 실행의 안전한 게이트웨이 구축
  • JSON Schema를 통한 엄격한 입력 검증 및 DOM 조작 방지
  • 변경 사항을 자동으로 Git 커밋 및 푸시하는 워크플로우 구현

「이 문구를 바꿔줘」와 같은 요구사항을 채팅으로 보내는 것만으로 웹사이트에 반영되는 메커니즘을 만들었습니다.

이번에는 실제로 만든 prompt-to-app의 구성과 어떤 부분을 고안했는지를 정리합니다.

다음과 같은 경험을 목표로 한 컨셉 사이트입니다.

  • 사용자가 자연어로 변경 요구사항을 보낸다
  • 서버 측에서 요구사항을 안전한 UI 조작으로 변환한다
  • 화면 프리뷰(Preview)에 반영한다
  • 필요에 따라 public/index.html에 영구 저장하고, git commit && git push까지 실행한다

「대화가 그대로 사양이 되는」 흐름을 가능한 한 짧은 동선으로 재현하고 있습니다.

프론트엔드 측에는 랜딩 페이지와 채팅 UI를 배치했습니다.

  • public/index.html: 랜딩 + 채팅 UI
  • public/app.js: 채팅 전송, 응답 반영, 조작 가드(Guard)
  • public/styles.css: UI 디자인

예를 들어 다음과 같은 의뢰를 보내는 상황을 가정합니다.

  • 「히어로 타이틀을 더 임팩트 있는 표현으로 바꿔줘」
  • 「CTA를 Start Free Pilot으로 변경해줘」
  • 「테마를 sunrise로 해줘」

이번 구성은 크게 4가지입니다.

  • Firebase Hosting
  • Firebase Functions (/api/chat)
  • API 서버 (servers/api)
  • MCP 서버 (servers/mcp)

functions에서 채팅 요청을 받아 LLM (Groq)에 문의합니다.

여기서 중요한 것은, 응답을 자유 텍스트가 아니라 JSON 계약(Contract)으로 받는 것입니다.

  • 허용하는 조작을 한정 (예: setText, setCta, setStatus, setTheme)
  • 프론트엔드에서 적용 전 검증(Validation)
  • 예상치 못한 DOM 변경 방지

이렇게 설계해 두면 「AI가 무엇을 반환할지 모르는 문제」를 상당히 억제할 수 있습니다.

public/app.js 측에서는 받은 JSON 조작을 그대로 실행하지 않고, 허가된 커맨드(Command)만 반영합니다.

즉 「채팅으로 무엇이든 변경할 수 있는」 것이 아니라, 변경 가능한 범위를 명시적으로 좁히는 구성입니다.

프리뷰뿐만 아니라 실제 파일에 반영하고 싶은 경우, MCP를 통해 API 서버를 호출합니다.

  • update_index_html: public/index.html의 대상 부분을 업데이트
  • git_commit_push_index_html: public/index.html을 커밋하여 origin/main으로 push

Git 플로우는 고정되어 있으며, 다음 순서를 따릅니다.

git add public/index.html
git commit -m <message>
git push origin main

변경 사항이 없는 경우에는 push를 스킵하도록 했습니다.

이 구성에서 MCP 서버는 **AI (Functions)로부터의 실행 요구를 안전하게 API 서버로 전달하는 툴 게이트웨이(Tool Gateway)**입니다.

바꿔 말하면, Functions가 직접 git이나 파일 편집을 다루는 것이 아니라, MCP가 「사용해도 좋은 조작만」 공개합니다.

  • 툴 정의 공개
    • update_index_html: 허가된 target만 업데이트, 값의 글자 수 제한 있음
    • git_commit_push_index_html: 커밋 메시지 길이를 제한, public/index.html 대상의 고정된 플로우만 실행
  • 입력 스키마(Schema)에 의한 검증
    • 툴마다 JSON Schema를 정의
    • additionalProperties: false로 불필요한 인자 거부
    • target은 열거형(Enum)으로 제한 (임의의 DOM 조작 방지)
  • API 서버로의 중계
    • MCP는 스스로 HTML 편집/Git 조작을 수행하지 않음
    • 실제 업무 처리는 API 서버의 /edit-index-html/git-commit-push-index-html에 위임
    • MCP는 「정규화된 인자로 호출하는」 역할에 집중
  • 인증 헤더(Header) 부여
    • API를 위해 x-api-key를 반드시 부여
    • Cloud Run 구성에서는 ID Token을 취득하여 Authorization: Bearer ... 사용

부여 가능 - HTTP 모드 시, MCP 자체에도 Bearer 토큰 보호를 적용할 수 있음

  • 에러 정형화 (Error Formatting)

  • API 응답을 JSON으로 취급하며, 실패 시에는 MCP 에러로서 호출 측으로 반환

  • Functions 측에서는 해당 결과를 사용자용 메시지에 반영하기 쉬움

MCP를 도입하는 이점은 조작 경계(Operation Boundary)를 명확히 할 수 있다는 점입니다.

  • Functions의 책임: 자연어를 해석하여 "무엇을 하고 싶은지"를 결정
  • MCP의 책임: 실행 가능한 조작을 정의하고 안전하게 중계
  • API의 책임: 실제 파일 편집 및 Git 실행 수행

이러한 분리를 통해, 향후 도구를 늘릴 경우에도 "MCP에 새로운 안전한 도구를 추가"하는 것만으로 확장이 용이해집니다.

  • 사용자가 채팅으로 "문구를 변경하고, 커밋해서 push해줘"라고 입력

  • Functions가 LLM 응답을 JSON화하고, 필요 시 tools/call을 발행

  • MCP가 도구 이름과 인자(Argument)를 검증

  • MCP가 API 서버로 인증을 포함한 POST 요청 전송

  • API 서버가 HTML 업데이트 및 Git 조작 실행

  • 결과가 MCP → Functions → 프론트엔드로 반환

  • MCP가 공개하는 도구를 최소화 (이번에는 2개)

  • 도구 인자를 엄격하게 스키마 제약(Schema Constraint) 적용

  • API 키와 Bearer 토큰을 분리하여, 유출되더라도 영향 범위를 한정

  • "자유로운 명령 실행"을 만들지 않음

이 구성이라면 AI에게 권한을 통째로 맡기지 않고, 조작을 선언적으로 제어한 상태로 자동화할 수 있습니다.

public/
index.html
app.js
...

역할 분리의 포인트는 다음과 같습니다.

  • functions: LLM 호출 및 채팅 API
  • servers/api: HTML 편집 및 Git 조작 실무
  • servers/mcp: 도구 공개 레이어 (Tool Exposure Layer)
npm install -g firebase-tools
npm --prefix functions install
npm --prefix servers/api install
...
cp functions/.env.example functions/.env
cp servers/api/.env.example servers/api/.env
cp servers/mcp/.env.example servers/mcp/.env

최소한 functions/.env에는 GROQ_API_KEY를 설정합니다.

firebase emulators:start --only hosting,functions

필요하다면 별도의 터미널에서 API/MCP 서버도 기동합니다.

npm run api:start
npm run mcp:start

처음에는 유연성을 중시했으나, 곧 위험하다는 것을 깨달았습니다.

  • 변경 대상을 ID 기반으로 한정
  • 조작 유형을 화이트리스트(Whitelist)화
  • 응답을 JSON Schema로 검증

이 세 가지만 추가해도 안정성이 크게 달라집니다.

실행 환경에 따라 git 바이너리가 존재하지 않아 spawn git ENOENT 에러가 발생할 수 있습니다.

API 서버를 구동하는 런타임에 git을 포함하도록(Dockerfile 측에서 보장) 설계하면 해결하기 쉽습니다.

non-fast-forward 등으로 인해 push가 실패하는 케이스는 반드시 발생합니다.

  • 에러 메시지를 UI에 반환
  • 어느 단계에서 실패했는지(add/commit/push)를 반환

이 정보가 있으면 운영 시 복구가 빨라집니다.

  • 요구사항의 입구를 "자연어"로 가져갈 수 있음
  • 변경을 "안전한 조작"으로 구체화할 수 있음
  • 프리뷰와 영속화(Persistence)를 분리할 수 있음
  • 기존 Git 운영 방식에 연결하기 쉬움

"채팅 UI를 붙였을 뿐"에서 끝나지 않고, 실운영에 필요한 가드레일(Guardrail)까지 포함하여 구축할 수 있었던 것이 수확이었습니다.

채팅 지시로 사이트가 변하는 경험은 겉보기보다 설계가 중요했습니다.

특히,

  • 자유 입력을 어떻게 제약이 있는 조작으로 변환할 것인가
  • 어디까지를 프리뷰로 하고, 어디서부터를 실제 반영으로 할 것인가
  • Git 연동을 실패 케이스를 포함하여 어떻게 다룰 것인가

이 세 가지 포인트를 짚어낸다면 PoC(Proof of Concept)에서 실운영에 가까워지기 쉽습니다.

마찬가지로 "대화를 기점으로 UI를 업데이트하는 프로덕트"를 만드는 분들에게 참고가 된다면 기쁘겠습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0