
채팅으로 지시하면 사이트가 변경되는 웹사이트를 만들었습니다
요약
사용자의 자연어 채팅 명령을 통해 웹사이트의 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: 랜딩 + 채팅 UIpublic/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 ...사용
- API를 위해
부여 가능 - 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 호출 및 채팅 APIservers/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가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기