AI가 만든 웹앱, 어디에 올려야 할까? 초보자를 위한 웹앱 배포 첫걸음

AI가 만든 웹앱, 어디에 올려야 할까? 초보자를 위한 웹앱 배포 첫걸음

핵심 요약

  • 웹앱 배포는 내 컴퓨터에서만 열리던 코드에 인터넷 주소(URL)와 실행 환경을 붙여 다른 사람이 접속하게 하는 일이다. 코드 생성과 배포는 다른 단계다.
  • 단순 HTML·CSS·JavaScript 페이지라면 Netlify나 Cloudflare Pages의 파일 업로드가 더 빠를 수 있다. 화면 뒤에서 데이터를 저장하거나 처리해야 하는 웹앱은 별도의 실행 환경이 필요하다.
  • Google Apps Script는 AWS나 Google Cloud의 축소판이 아니다. Google Workspace와 연결된 관리형 스크립트 환경이며, Google Sheets·Drive·Gmail·Calendar를 쓰는 작은 웹앱에 특히 잘 맞는다.
  • 완성 목표는 Google Sheets에 한 줄을 저장하는 웹앱이다. 배포 → 새 배포 → 웹 앱을 거치면 휴대폰에서도 열리는 URL이 나온다.
  • 공개 URL은 곧 권한과 운영의 시작이다. 공개 범위, 저장 데이터, 사용량 한계를 확인하고, 프로젝트가 커지면 Firebase·Vercel·Cloud Run·AWS로 옮길 수 있다.

AI가 코드를 만들어줬는데, 왜 아직 다른 사람은 못 볼까

ChatGPT나 Claude, Gemini에게 “할 일 입력 페이지를 만들어줘”라고 요청하면 HTML과 JavaScript가 금방 나온다. 파일을 내려받아 더블클릭했을 때 브라우저에서 멋지게 보이기도 한다. 그러나 그 상태는 대개 내 컴퓨터의 브라우저에서 파일을 열어 본 것이다. 친구가 같은 화면을 보려면 파일을 보내야 하고, 입력한 내용은 다른 사람과 공유되지 않으며, 주소창의 경로도 인터넷 주소가 아니다.

배포는 이 간극을 메우는 작업이다. 사람이 URL을 열면 인터넷 어딘가의 컴퓨터가 화면 파일을 보내고, 필요한 경우 입력을 처리하고 데이터를 저장한 뒤 결과를 돌려준다. 그 컴퓨터와 연결된 저장 공간, 접속 규칙을 마련하는 곳을 넓게는 호스팅 또는 실행 환경이라고 부른다. 실제 서버는 사라지는 것이 아니다. Google·AWS·Cloudflare 같은 사업자가 운영하는 서버를 내가 직접 관리하느냐, 미리 준비된 환경을 빌리느냐가 달라질 뿐이다.

내 PC에서 열기
내 브라우저 ──> 내 파일 / 내 컴퓨터에서만 실행

인터넷에 배포하기
다른 사람의 브라우저 ──URL──> 호스팅·실행 환경 ──> 화면 반환
                                      │
                                      └──> 데이터 저장소(필요한 경우)

웹페이지와 웹앱의 차이도 여기서 드러난다. 소개 글, 포트폴리오, 행사 안내처럼 보여 주는 내용이 중심이면 정적 웹페이지에 가깝다. HTML·CSS·브라우저 JavaScript 파일을 올려 두면 된다. 반면 신청 내용을 기록하고, 로그인한 사람별 결과를 보여 주고, AI API를 서버에서 호출하거나 재고를 바꾸는 것은 웹앱이다. 화면 뒤에서 실행되는 코드와 데이터 저장소가 필요하다. 정적 페이지도 버튼과 애니메이션을 가질 수 있고 웹앱도 화면을 가진다. 구분의 기준은 서버 쪽에서 처리하거나 저장할 일이 있는지다.

AI가 코드 생성 장벽을 낮추면서 이 구분은 더 중요해졌다. 생성된 코드가 로컬에서 보인다는 사실은 공개 환경에서 권한, 데이터, 오류 처리까지 안전하게 작동한다는 보증이 아니다. 개인 정보가 제대로 보호되는지와 저장 실패 시 어떻게 되는지를 초보자가 놓치기 쉽다는 지적도 있다. 배포 전에는 누가 URL에 들어올 수 있는지와 무엇을 저장하는지를 먼저 묻는 습관이 필요하다. 출처: The Conversation: 바이브 코딩의 편리함과 함정

배포 서비스는 이름이 아니라 앱의 구조로 고른다

AWS와 Google Cloud는 웹사이트를 올리는 버튼 하나를 가리키는 이름이 아니다. 서버, 파일 저장소, 데이터베이스, 네트워크, 권한 관리, 서버리스 실행 환경 등 여러 인프라를 조합하는 종합 클라우드 플랫폼이다. 같은 Google Cloud 안에서도 단순 정적 파일에는 다른 도구를, Python·Node.js 서버에는 Cloud Run 같은 다른 서비스를 쓴다. AWS도 Amplify Hosting, Lambda, API Gateway, 데이터베이스 등 목적별 서비스를 조합한다. AWS Amplify Hosting은 Git 기반의 지속 배포 방식으로 풀스택 서버리스 웹앱을 호스팅하는 대표적인 선택지다. 출처: AWS Amplify Hosting 안내

아래 표는 순위표가 아니라 첫 질문을 정리한 표다. 어떤 앱을 만들려는지에 따라 답이 갈린다. 실제 요금·무료 구간·조직의 공개 정책은 바뀔 수 있으므로 시작 전 각 서비스의 최신 공식 요금표도 확인해야 한다.

선택지 잘 맞는 앱 시작할 때 GitHub·터미널이 필요한가 데이터는 어디에 쌓이나 Google 서비스 연결 첫 배포가 쉬운 이유와 걸림돌
Google Apps Script Sheets·Drive·Gmail·Calendar와 연결된 소규모 동적 도구 둘 다 없어도 된다. 브라우저에서 코드를 쓰고 그대로 배포한다. Google Sheets를 비롯한 Workspace 데이터에 바로 기록된다. 같은 Google 계정 권한으로 곧바로 연결된다. 설치할 도구가 없어 URL까지 가장 빠르다. 대신 실행 시간과 사용량 quota가 있어 큰 서비스로 키우기는 어렵다.
Netlify 정적 페이지와 프론트엔드 빌드 결과물 Netlify Drop은 폴더나 ZIP을 끌어다 놓으면 끝난다. Git 연동과 CLI도 선택할 수 있다. 저장이 필요하면 Netlify의 백엔드 기능이나 외부 데이터 서비스를 붙인다. 전용 연결 기능은 없고 API로 따로 설계한다. 파일만 올리면 주소가 나온다. 저장과 로그인 기능은 그다음에 따로 배워야 한다.
Cloudflare Pages 정적 사이트, Pages Functions를 더한 앱 대시보드 Direct Upload는 GitHub 없이 가능하다. 다만 functions 폴더를 쓰려면 Wrangler CLI나 Git 연동이 필요하다. Cloudflare의 저장 제품이나 외부 데이터베이스를 연결한다. 전용 연결 기능은 없고 API로 따로 설계한다. 정적 파일 공개는 끌어놓기로 끝난다. 서버 기능을 붙이는 순간 명령줄 도구가 등장한다.
Vercel React·Next.js 같은 현대 웹 프로젝트 Git 연동이 대표 흐름이지만 필수는 아니다. CLI나 Vercel Drop으로도 배포할 수 있다. 데이터베이스와 인증 서비스를 따로 연결한다. 전용 연결 기능은 없고 API로 따로 설계한다. 커밋마다 미리보기 주소가 생겨 협업에 강하다. 대신 프로젝트의 빌드 구조를 이해하고 있어야 한다.
Firebase Hosting 정적 사이트와 단일 페이지 앱(SPA) 보통 Firebase CLI로 배포한다. 터미널을 한 번은 열게 된다. Firestore 같은 Firebase 서비스를 추가한다. Google 계정으로 쓰지만 Workspace 데이터 연결은 별도 설계다. HTTPS와 CDN이 기본으로 붙는다. 동적 기능은 Cloud Functions나 Cloud Run과 조합해야 한다.
Firebase App Hosting Next.js·Angular처럼 서버에서 렌더링하는 풀스택 앱 GitHub 저장소를 연결해 커밋할 때마다 배포하는 방식이 기본이다. Firebase와 Google Cloud의 데이터 서비스를 함께 쓴다. Firebase 프로젝트 단위로 연결한다. 프레임워크를 자동 인식해 빌드해 준다. 대신 Git 사용이 사실상 전제다.
Cloud Run Python·Node.js 등 범용 서버 코드와 HTTP API 소스나 컨테이너를 올리는 배포 흐름을 익혀야 한다. Cloud SQL 등 별도 데이터 서비스를 고른다. API를 통해 연결한다. 언어와 구조를 마음대로 고를 수 있다. 포트·환경변수·데이터베이스를 직접 챙겨야 해 설정량이 늘어난다.
AWS 여러 인프라를 조합해 만드는 웹앱 전반 서비스마다 다르다. 웹 콘솔, Git, CLI를 모두 쓸 수 있다. AWS의 데이터베이스와 스토리지 중에서 고른다. 전용 연결 기능은 없고 API로 따로 설계한다. 요구가 복잡할수록 강해진다. 첫 프로젝트에서는 고를 것이 너무 많다는 점이 걸림돌이다.

Vercel은 연결한 Git 저장소의 커밋이나 풀 리퀘스트마다 배포를 만들 수 있고, 명령줄이나 Vercel Drop으로도 배포할 수 있다. GitHub가 반드시 필요한 것은 아니지만, 이 서비스의 대표 흐름은 코드 변경 이력과 배포를 연결하는 방식이다. 출처: Vercel 배포 개요, Vercel Git 배포

Netlify Drop은 프로젝트 폴더나 ZIP을 끌어다 놓아 URL을 만들 수 있다. Cloudflare Pages도 Direct Upload에서 폴더나 ZIP을 대시보드에 올릴 수 있다. 단순 HTML 사이트를 공개할 목적이라면 이 방법들이 Apps Script보다 더 직선적이다. 출처: Netlify Drop 빠른 시작, Cloudflare Pages Direct Upload

다만 파일을 올리는 것만으로 서버 기능까지 함께 배포되지는 않는다. 정적 페이지 배포와 동적 기능 배포는 다른 일이다. Cloudflare 문서는 대시보드 끌어놓기 배포가 functions 폴더를 컴파일하지 못한다고 명시한다. 즉 Pages Functions로 서버 쪽 처리를 붙이려면 Wrangler 명령줄 도구나 Git 연동 배포가 필요하다. 정적 파일만 올릴 때는 끌어놓기로 충분하지만, 저장·인증·API 같은 기능이 필요해지는 순간 배포 방식부터 달라진다고 기억해 두면 된다. 출처: Cloudflare Pages Functions

Firebase는 목적이 다른 두 가지 호스팅을 함께 제공한다. Firebase Hosting은 정적 사이트와 단일 페이지 앱을 빠르게 서비스하는 데 최적화되어 있고, 동적 기능은 Cloud Functions 또는 Cloud Run과 조합해 구현한다. 반면 서버에서 렌더링하는 Next.js·Angular 같은 풀스택 앱은 Firebase App Hosting이 담당한다. GitHub 저장소를 연결해 커밋마다 배포하고, 프레임워크를 자동으로 인식해 빌드한다. Next.js 앱 전체를 Firebase Hosting의 대표 사용 사례로 오해하지 않는 편이 좋다. 출처: Firebase Hosting, Firebase App Hosting

Cloud Run은 Google의 확장 가능한 인프라 위에서 코드·함수·컨테이너를 실행하는 관리형 애플리케이션 플랫폼이며, 서비스는 고유 HTTPS 주소로 HTTP 요청을 받는다. Docker를 직접 배우지 않아도 일부 언어는 소스 기반 배포가 가능하지만, 앱의 포트·환경변수·데이터베이스 같은 책임이 Apps Script보다 더 많이 따라온다. 출처: Cloud Run이란 무엇인가

첫 선택은 대체로 다음 네 갈래로 갈린다.

  • AI가 만든 index.html 하나를 보기 좋게 공개하고 싶다. Netlify Drop이나 Cloudflare Pages Direct Upload 같은 정적 호스팅부터 본다.
  • React·Next.js 프로젝트를 GitHub에 저장하고 미리보기 URL과 자동 배포를 원한다. Vercel, Netlify, Firebase App Hosting을 검토한다.
  • Python·Node.js 서버, 자체 API, 복잡한 데이터베이스가 필요하다. Cloud Run 또는 AWS의 목적별 서비스로 간다.
  • Google Sheets에 기록하고 Drive·Gmail·Calendar를 연결한 작은 업무 도구를 가장 적은 설정으로 공개하고 싶다. Google Apps Script가 좋은 출발점이다.

Google Apps Script를 예제 환경으로 고른 이유와 한계

Google Apps Script는 브라우저에서 JavaScript로 자동화와 웹앱을 만들 수 있는 Google의 관리형 스크립트 환경이다. HTML Service를 사용하면 HTML 파일을 화면으로 제공하고, 그 화면에서 서버 쪽 Apps Script 함수를 호출할 수 있다. 스크립트에 doGet() 또는 doPost()가 있고 HTML 또는 텍스트 출력을 돌려주면 웹앱으로 배포할 수 있다. 출처: Apps Script 웹앱 안내, HTML Service 안내

초보자에게 중요한 장점은 서버를 직접 만들고 운영하지 않아도 된다는 점이다. 브라우저에서 프로젝트와 파일을 만들고, Google 계정 권한으로 Sheets를 열어 한 줄을 기록할 수 있으며, 배포가 끝나면 Google이 웹앱 URL을 제공한다. Google Sheets는 격자형 데이터 도구이므로 소규모 신청 목록, 아이디어 수집함, 사내 간단 대시보드의 초기 저장소로 쓰기 좋다. Apps Script의 Spreadsheet 서비스는 시트를 2차원 배열처럼 다룬다. 출처: Apps Script의 Google Sheets 연동

그렇다고 Apps Script가 무료 서버이거나 AWS·Google Cloud를 대체하는 범용 클라우드는 아니다. 실제 실행은 Google 서버에서 이뤄지며, 스크립트 실행 시간, 하루 서비스 사용량, 동시 실행 수 등 여러 quota가 걸려 있다. 한도는 개인 Google 계정과 Workspace 계정이 다르고 정책에 따라 바뀔 수 있으므로, 숫자가 필요할 때는 공식 문서를 그때그때 확인하는 편이 안전하다. 사용자가 늘거나 긴 작업·무거운 계산·대량 파일 처리·복잡한 DB·세밀한 네트워크와 보안 제어가 필요해지면 더 적합한 환경을 택해야 한다. 출처: Apps Script 서비스 quota

예제로 만드는 도구는 공개 소비자 서비스가 아니라 첫 구조를 이해하기 위한 작은 입력 도구다. 누구나 입력하는 공개 URL에 주민번호, 비밀번호, 고객 명단, 민감한 업무 내용은 저장하지 않는다. Apps Script를 작성자 권한으로 실행되도록 배포하면 방문자의 입력이 작성자가 접근할 수 있는 Sheet에 기록된다. 편리함과 권한 위임은 함께 온다.

실습 전 준비: 성공 기준을 작게 잡는다

완성 목표는 제목과 메모를 입력하면 Google Sheets에 한 행이 저장되는 아이디어 접수함이다. 필요한 것은 Google 계정, PC 브라우저, 그리고 테스트할 휴대폰 또는 시크릿 창뿐이다. GitHub, 터미널, AWS 계정은 필요 없다.

먼저 Google Drive에서 새 Google Sheets를 만든다. 파일 이름은 첫 웹앱 데이터처럼 알아보기 쉽게 정하고, 첫 시트의 1행에 제목, 메모, 기록 시각을 입력한다. 이 머리글 행은 나중에 사람이 표를 읽기 쉽게 하려는 것이고, 코드는 이 행 아래에 새 줄을 계속 덧붙인다. 시트 탭 이름은 시트1 그대로 두어도 된다. 아래 코드가 이름 대신 첫 번째 시트를 가리키기 때문이다.

주소창의 URL은 보통 https://docs.google.com/spreadsheets/d/긴문자열/edit 형태다. /d/와 그다음 /edit 사이의 긴 문자열이 스프레드시트 ID다. 이것을 잠시 메모해 둔다. 이 값 자체가 비밀번호는 아니지만, 시트를 열 수 있는 권한을 주는 열쇠도 아니다. 시트의 공유 설정은 계속 제한해 둔다.

다음으로 Apps Script에 접속해 새 프로젝트를 누른다. 왼쪽 위 프로젝트 제목은 제목 없는 프로젝트로 시작하는데, 첫 웹앱처럼 바꿔 두면 나중에 프로젝트가 여러 개가 됐을 때 찾기 쉽다. 이름을 바꾸지 않아도 동작에는 영향이 없다.

처음 보이는 Code.gs는 Google 서버에서 실행되는 코드 파일이다. 화면을 열어 주고 Sheets에 기록하는 일을 맡는다. HTML 파일은 방문자가 보는 화면이다. 둘은 주방과 주문 화면처럼 역할이 다르다.

왼쪽 파일 목록에서 파일 추가(+) 버튼을 눌러 HTML을 선택하고 이름을 Index로 입력한다. 편집기에 Index.html이 생긴다. 파일명 끝의 .html은 목록에서 보이는 표시일 뿐이고, 아래 코드의 createHtmlOutputFromFile('Index')에는 확장자를 쓰지 않는다.

AI에게 코드를 요청할 때는 배포 조건까지 알려준다

AI에게 “예쁜 입력 폼을 만들어줘”라고만 말하면 일반 웹 서버나 다른 프레임워크용 코드를 줄 수 있다. 아래처럼 Apps Script라는 실행 환경과 파일 구성을 분명히 요구한다. 코드 문법을 외우는 대신, 결과물이 어느 파일에 가야 하는지와 무엇을 하는지를 확인하는 것이 목표다.

Google Apps Script 웹앱을 만들고 싶어.
Code.gs와 Index.html 두 파일의 완성 코드를 각각 만들어줘.
Index.html에는 제목과 메모 입력칸, 저장 버튼, 성공·실패 메시지가 필요해.
저장 버튼은 google.script.run으로 서버 함수 addEntry를 호출해야 해.
Code.gs에는 doGet()과 addEntry(title, memo)가 필요하고,
SPREADSHEET_ID 상수에 넣은 Google Sheets의 첫 시트에
제목·메모·현재 시각을 한 행으로 추가해줘.
민감한 개인정보를 다루지 않는 작은 데모로, 오류 메시지도 반환해줘.

아래 코드는 그 요청으로 얻을 수 있는 최소 예시다. AI가 다른 코드를 만들었다면 doGet, HTML 출력, Sheet ID, google.script.run 호출이 같은 역할을 하는지 비교한다. API 키를 HTML 파일에 넣으라거나 비밀번호를 코드에 직접 쓰라고 하면 멈추고 다시 요청한다. HTML은 방문자가 볼 수 있는 쪽이고, 비밀값은 여기에 두면 안 된다.

Code.gs에 붙여 넣을 코드

기존 Code.gs 내용을 모두 지우고 아래를 붙인다. 여기에_스프레드시트_ID만 앞에서 복사한 값으로 교체하고 작은따옴표는 그대로 남긴다.

const SPREADSHEET_ID = '여기에_스프레드시트_ID';

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index')
    .setTitle('첫 웹앱 아이디어 접수함')
    .addMetaTag('viewport', 'width=device-width, initial-scale=1');
}

function addEntry(title, memo) {
  if (!title || !String(title).trim()) {
    throw new Error('제목을 입력하세요.');
  }

  const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheets()[0];
  sheet.appendRow([String(title).trim(), String(memo || '').trim(), new Date()]);
  return '저장했습니다.';
}

마지막 줄의 addMetaTag는 휴대폰에서 열었을 때 화면이 축소되지 않게 해 준다. 뒤에서 휴대폰으로 확인하는 단계가 있으므로 함께 넣어 둔다.

Index.html에 붙여 넣을 코드

Index.html을 열어 원래 있던 내용을 지우고 아래 전체를 붙인다. 이 파일은 화면과 버튼 동작만 담당한다. Sheets를 직접 열 수 있는 권한은 서버 쪽 Code.gs가 가진다.

<!doctype html>
<html>
  <head>
    <base target="_top">
    <style>
      body { font-family: Arial, sans-serif; max-width: 560px; margin: 40px auto; padding: 0 16px; }
      label, input, textarea, button { display: block; width: 100%; box-sizing: border-box; }
      input, textarea { margin: 8px 0 18px; padding: 10px; }
      button { padding: 12px; cursor: pointer; }
      #message { margin-top: 16px; }
    </style>
  </head>
  <body>
    <h1>아이디어 접수함</h1>
    <label>제목<input id="title" placeholder="예: 다음 회의 안건"></label>
    <label>메모<textarea id="memo" rows="5" placeholder="짧은 설명을 입력하세요"></textarea></label>
    <button id="saveButton" onclick="saveEntry()">Google Sheets에 저장</button>
    <p id="message"></p>

    <script>
      function saveEntry() {
        const button = document.getElementById('saveButton');
        const message = document.getElementById('message');
        button.disabled = true;
        message.textContent = '저장 중입니다.';

        google.script.run
          .withSuccessHandler(function(result) {
            message.textContent = result;
            document.getElementById('title').value = '';
            document.getElementById('memo').value = '';
            button.disabled = false;
          })
          .withFailureHandler(function(error) {
            message.textContent = '저장하지 못했습니다: ' + error.message;
            button.disabled = false;
          })
          .addEntry(
            document.getElementById('title').value,
            document.getElementById('memo').value
          );
      }
    </script>
  </body>
</html>

둘 다 붙인 뒤 저장 아이콘을 누르거나 Ctrl/Cmd+S로 저장한다. Code.gsIndex.html은 파일명과 대소문자까지 맞아야 한다. doGet()Index라는 이름의 파일을 찾으므로, 파일을 index로 만들었다면 코드 쪽도 같은 이름으로 바꿔야 한다.

권한 승인: 무서운 화면이 아니라 누구의 권한으로 실행할지 정하는 단계

코드를 붙였다고 곧바로 편집기 상단에서 addEntry를 실행할 필요는 없다. 매개변수를 받는 함수라 편집기에서 그냥 실행하면 오류가 난다. 권한 승인은 배포 과정에서 자연스럽게 요청된다.

배포 버튼을 누른 뒤 새 배포를 선택한다. 유형 선택 옆의 톱니바퀴를 눌러 웹 앱을 고른다. Google 공식 문서도 이 흐름을 배포 > 새 배포 > 웹 앱으로 안내한다. 출처: Apps Script 웹앱 배포 절차

설명란에는 첫 공개 버전처럼 적는다. 그다음 두 항목이 이 화면의 핵심이다.

  • 다음 사용자로 실행(Execute as): 이 앱이 누구의 권한으로 움직일지 정한다. 예제의 Sheet는 내 Drive에 있고 방문자는 그 시트 권한이 없으므로 보통 를 고른다. 그러면 Sheets에 쓰는 작업이 내 권한으로 수행된다. 웹 앱에 액세스하는 사용자를 고르면 방문자 각자가 Google 로그인과 해당 리소스 권한을 갖고 있어야 한다.
  • 액세스 권한이 있는 사용자(Who has access): 누가 URL을 열 수 있는지를 정한다. 혼자 시험할 때는 나만을, 로그아웃 상태의 휴대폰이나 외부인도 열어야 하는 공개 데모라면 화면에 보이는 모든 사용자 또는 Google 계정이 있는 모든 사용자 중 의도에 맞는 범위를 고른다. 회사·학교 계정은 관리자가 외부 공개를 막아 이 선택지가 보이지 않을 수 있다.

두 항목은 이름이 비슷해 보이지만 서로 다른 질문에 답한다. 앞은 누구의 권한으로 실행되는가이고, 뒤는 누가 접속할 수 있는가다. 나로 실행하면서 접근 범위를 모든 사용자로 열면, 누구나 내 시트에 줄을 추가할 수 있는 상태가 된다. 그래서 이 예제를 공개로 돌릴 때는 스팸과 장난 입력을 예상해야 하고, 민감한 정보 수집이나 승인·결제·파일 업로드용으로 재사용하지 않는 편이 좋다. 웹앱의 실행 주체에 따라 권한이 달라진다는 점은 Google 문서도 명시한다. 출처: Apps Script 웹앱의 권한

배포를 누르면 권한 검토 창이 나온다. 이 창이 나오는 이유는 분명하다. Apps Script는 코드를 훑어 어떤 Google 서비스가 필요한지 판단하는데, 이 코드에는 SpreadsheetApp.openById가 있으므로 스프레드시트를 열고 수정할 권한이 필요하다고 표시한다. 남의 데이터가 아니라 내 Sheet를 다루는 스크립트여도, Google은 계정 데이터에 접근하기 전에 사용자 동의를 받도록 되어 있다. 출처: Apps Script 권한 승인 안내

이때 Google이 이 앱을 확인하지 않았다는 경고 화면이 함께 뜰 수 있다. 개인이 만든 스크립트는 Google의 앱 심사를 거치지 않았기 때문에 나오는 안내이며, 내가 방금 작성한 프로젝트라는 사실을 확인했다면 고급 옵션을 통해 계속 진행해도 된다. 반대로 다음 경우에는 멈추는 편이 안전하다. 다른 사람이 보내 준 스크립트이거나 출처가 불분명한 프로젝트일 때, 코드에 없던 Gmail 전체 읽기나 Drive 전체 접근처럼 예상보다 넓은 권한을 요구할 때, 승인 화면의 계정이 내가 의도한 계정이 아닐 때다. 요청 권한 목록을 한 번 읽고 스프레드시트 관련 항목만 있는지 확인한 뒤 허용한다.

배포가 완료되면 웹 앱 URL을 복사한다. 다른 사람에게 공유할 주소는 /exec로 끝나는 실행용 URL이다. 개발 중 빠르게 확인하는 테스트 배포 URL은 /dev로 끝나는데, 공식 문서에 따르면 스크립트 수정 권한이 있는 사용자만 접근할 수 있고 항상 가장 최근에 저장된 코드를 실행한다. 그래서 외부 사용자 테스트에는 반드시 /exec를 써야 한다. 출처: 웹앱 테스트 배포

URL을 열어 보고, 내 컴퓨터에서만 됐던 착각을 끝낸다

배포 창의 URL을 새 시크릿 창에 붙여 넣는다. 시크릿 창은 평소 로그인한 Google 계정의 권한과 쿠키가 덜 섞여 있어 공개 범위 문제를 발견하기 좋다. 제목과 메모를 입력하고 Google Sheets에 저장을 누른다. 저장했습니다라는 메시지가 나오면 앞에서 만든 Google Sheets를 새로고침한다. 머리글 행 아래에 새 줄이 생겼다면 화면에서 서버 코드를 거쳐 Sheet까지 이어지는 흐름이 완성된 것이다.

이어서 휴대폰에서도 URL을 연다. PC와 같은 Google 계정으로 로그인한 상태만 시험하지 말고, 가능하다면 다른 계정이나 로그아웃 상태에서 의도한 공개 범위대로 열리는지 확인한다. 회사·학교 계정에서 외부 접근이 막히면 코드 오류가 아니라 조직 정책일 수 있다. 이때 공개 범위를 무리하게 넓히기보다 개인 Google 계정으로 시험하거나 조직 관리자에게 정책을 확인한다.

이 단계에서 자주 막히는 곳은 대체로 정해져 있다.

  • Exception: Unexpected error 또는 Sheet 접근 오류가 난다. SPREADSHEET_ID에 URL 전체가 아니라 ID만 들어갔는지, 양쪽 작은따옴표가 남아 있는지, 스크립트를 만든 계정이 그 Sheet의 편집자인지 확인한다.
  • 화면에 파일을 찾을 수 없다는 메시지가 나온다. HTML 파일 이름이 Index인지, createHtmlOutputFromFile('Index')와 대소문자가 일치하는지 확인한다.
  • 내 PC에서는 되는데 다른 사람은 못 연다. /dev 대신 /exec URL을 공유했는지, 액세스 권한이 대상 사용자를 포함하는지, Workspace 정책이 막고 있지는 않은지 확인한다.
  • 버튼을 눌러도 Sheet가 비어 있다. 화면의 실패 메시지를 읽고, Apps Script 편집기 왼쪽의 실행 기록에서 오류를 본다. 공개 앱의 입력값은 신뢰하지 말고, AI에게 빈 값·길이 제한·중복·스팸 방지를 추가해 달라고 요청한다.
  • 코드를 고쳤는데 공개 앱이 예전 화면이다. 저장만으로 공개 버전은 바뀌지 않는다. 아래 절차로 새 버전을 배포한다.

코드를 고친 뒤에는 저장과 공개 버전 갱신을 구분한다

Apps Script에서 저장은 편집 중인 원본을 바꾸는 일이고, 배포는 특정 공개 버전을 URL에 연결하는 일이다. 이 구분 덕분에 작업 중인 코드를 곧바로 사용자에게 노출하지 않고 시험할 수 있다. Google은 현재 코드와 동기화되는 head deployment와 특정 프로젝트 버전에 연결되는 versioned deployment를 구분한다. 출처: Apps Script 배포 관리

수정 후에는 배포 → 배포 관리로 들어가 기존 웹앱 배포를 찾아 연필 아이콘을 누른다. 버전 목록에서 새 버전을 선택하고 배포를 누르면 같은 URL이 새 코드로 갱신된다. 메뉴의 세부 표현은 달라질 수 있어도 하는 일은 같다. 새 프로젝트 버전을 기존 웹앱 배포에 연결하는 것이다. 이 자리에서 새 배포를 다시 만들면 URL이 달라지므로, 이미 공유한 주소를 유지할지부터 결정한다. 공개 범위를 바꿨다면 시크릿 창 테스트를 한 번 더 한다.

작업 중인 변경은 배포 → 테스트 배포로 먼저 확인한다. 단, /dev는 편집 권한이 있는 사람만 열 수 있다. 외부 사용자가 쓰는 주소는 /exec다. 이 두 URL의 역할을 구분하는 습관이 첫 배포 뒤에 가장 오래 쓰이는 운영 습관이 된다.

Apps Script를 계속 써도 되는 때와 다음 환경으로 갈 때

Apps Script는 개인용 체크리스트, 팀 회의 신청, 소규모 아이디어 수집, Drive 파일 목록 도구, Calendar·Gmail 업무 자동화처럼 Google Workspace가 데이터와 업무 흐름의 중심인 경우에 강점이 있다. 화면, 서버 측 함수, Sheet를 하나의 Google 계정 흐름으로 연결해 배포의 전체 구조를 작게 경험하게 해 준다.

다만 다음 신호가 보이면 이전을 설계한다.

  • 단순 화면만 공개하면 된다면 Sheets와 서버 코드를 억지로 붙이지 않는다. Netlify Drop이나 Cloudflare Pages 같은 정적 호스팅으로 파일을 올리는 편이 더 단순하다. Cloudflare Pages는 Git 연동, Direct Upload, 명령줄 배포를 모두 제공한다. 출처: Cloudflare Pages 개요
  • 화면이 React·Next.js로 커지고 Git의 변경 이력, 미리보기 배포, 팀 코드 리뷰가 필요해지면 Vercel·Netlify의 Git 연동 흐름을 검토한다. Google 생태계를 유지하고 싶다면 정적 사이트와 SPA는 Firebase Hosting, 서버 렌더링이 필요한 Next.js·Angular 앱은 Firebase App Hosting이 각각의 자리다. 출처: Firebase Hosting 빠른 시작, Firebase App Hosting 시작하기
  • Python·Node.js API, 사용자 인증, 관계형 데이터베이스, 작업 대기열, 높은 트래픽과 세밀한 관측·보안 정책이 필요해지면 Cloud Run 또는 AWS의 조합을 검토한다. 이때 필요한 것은 더 큰 서버로 이사하는 일이 아니라 데이터, 비밀값, 인증, 배포 흐름을 목적에 맞게 다시 설계하는 일이다.

이전은 실패가 아니다. Apps Script로 문제와 화면, 필요한 데이터, 실제 사용자를 먼저 확인한 뒤 더 범용적인 환경으로 옮기면 어떤 구성요소가 필요한지 훨씬 선명해진다. 처음부터 불특정 다수를 대상으로 하는 서비스나 민감 정보를 다루는 앱이라면, Apps Script의 단순함보다 보안·인증·데이터 설계를 먼저 갖춘 환경을 택해야 한다.

첫 배포에서 얻어야 할 것은 URL 하나보다 큰 구조다

여기까지 따라왔다면 웹페이지 하나를 올린 것에 그치지 않는다. AI가 만든 코드가 화면 파일, 실행 환경, Sheets라는 데이터 저장소, Google 권한, 배포된 URL을 거쳐 서비스가 되는 흐름을 직접 통과했다. 다음에는 입력칸에 필수 항목을 추가하거나, Sheet의 데이터를 읽어 목록으로 보여 주거나, 팀원만 접근하도록 범위를 좁혀 보자. 그때마다 누가 실행하는지, 누가 URL을 여는지, 무엇을 저장하는지를 다시 확인하면 된다.

AI가 코드를 만들어주는 시대에도 배포는 사라지지 않는다. 다만 첫 배포를 경험하는 방법은 훨씬 쉬워졌고, Google Apps Script는 Google 서비스와 연결된 작은 웹앱의 구조를 가장 단순하게 경험할 수 있는 선택지 중 하나다. 앱이 커지면 정적 호스팅, Firebase, Vercel, Cloud Run, AWS가 다음 선택지가 된다. 지금 필요한 것은 모든 클라우드를 배우는 일이 아니라, 내 앱의 구조에 맞는 한 가지 환경에서 URL을 만들고 다른 기기에서 실제로 열어 보는 일이다.

참고 출처