시작하기 전에 준비할 것
- 1Windows 10 또는 11 PC와 인터넷 화면은 Chrome 기준입니다. Edge, Whale 등 다른 브라우저도 똑같이 됩니다.
- 2내가 쓰는 이메일 주소 계정 세 개를 만드는 데 씁니다. 하나의 이메일로 셋 다 만들 수 있습니다.
- 3웹페이지 파일 이미 만든 것이 있으면 그것을 씁니다. 없으면 이 가이드가 주는 예제 파일을 그대로 복사해 쓰면 됩니다.
- 4결제 수단은 필요 없습니다 세 서비스 모두 무료 플랜으로 이 가이드의 끝까지 갈 수 있습니다. 카드 등록을 요구하는 화면은 나오지 않습니다.
세 가지 도구가 각각 하는 일
파일 보관소
내 웹페이지 파일을 인터넷에 보관합니다. 언제 무엇을 고쳤는지 기록이 남고, 실수해도 되돌릴 수 있습니다.
배포와 주소
GitHub에 있는 파일을 실제 웹사이트로 띄우고 주소를 줍니다. 파일을 고치면 알아서 다시 배포합니다.
데이터 저장
방문자가 남긴 글이나 신청 내역처럼 계속 쌓이는 데이터를 저장합니다. 새로고침해도 사라지지 않습니다.
파일은 GitHub를 거쳐 Vercel이 배포하고, 데이터는 방문자의 브라우저가 Supabase와 직접 주고받습니다. 그래서 서버를 직접 만들 필요가 없습니다.
계정 세 개 만들기
GitHub를 먼저 만들어야 합니다. Vercel과 Supabase는 GitHub 계정으로 로그인하는 방식을 쓰면 따로 아이디와 비밀번호를 만들지 않아도 되고, 나중 단계에서 연결도 저절로 됩니다.
1-1. GitHub 계정
- 주소창에
github.com/signup을 입력해 엽니다. - Email에 내 이메일 주소를 적습니다.
- Password에 비밀번호를 적습니다. 15자 이상이거나, 8자 이상이면서 숫자와 소문자를 포함해야 합니다.
- Username에 영문 아이디를 적습니다. 이 아이디가 나중에
github.com/아이디주소가 됩니다. 한글과 공백은 쓸 수 없습니다. - Your Country/Region에서 Korea, South를 고릅니다.
- Create account를 누릅니다.
- 이메일로 온 여섯 자리 숫자를 화면에 입력하면 가입이 끝납니다.
1-2. Vercel 계정
vercel.com/signup을 엽니다.- Continue with GitHub를 누릅니다.
- GitHub 화면이 뜨면 Authorize Vercel을 눌러 허용합니다.
- 이름을 묻는 화면이 나오면 적고 넘어갑니다. 플랜을 고르라고 하면 Hobby(무료)를 고릅니다.
- Secure Your Account with 2FA 화면이 나오면 아래 "가입 중에 나오는 2단계 인증 화면"을 보고 고릅니다. 지금 건너뛰어도 가입은 그대로 끝납니다.
1-2-1. "Secure Your Account with 2FA" 화면이 나오면
가입은 이미 끝난 상태입니다. 연습용이면 아래 작은 글씨 Skip securing my account를 눌러 넘어갑니다. 남이 쓸 사이트라면 Set Up Authenticator App으로 켜고 마지막에 나오는 복구 코드를 안전한 곳에 보관합니다.
1-3. Supabase 계정
supabase.com/dashboard/sign-up을 엽니다.- Continue with GitHub를 누르고, GitHub 화면에서 허용합니다.
- 조직(Organization) 이름을 물으면 아무 이름이나 적습니다. 본인 이름도 괜찮습니다. 플랜은 Free를 고릅니다.
- 브라우저 탭 세 개에서
github.com,vercel.com/dashboard,supabase.com/dashboard가 각각 로그인된 상태로 열린다. - 세 화면 어디에서도 로그인이나 가입을 다시 요구하지 않는다.
한 곳이라도 로그인 화면으로 돌아간다면 그 서비스의 가입을 끝내지 못한 것입니다. 다음 단계로 넘어가기 전에 마무리하세요.
올릴 파일 준비하기
배포에는 규칙이 하나 있습니다. 첫 화면이 될 파일의 이름은 반드시 index.html이어야 합니다. 소문자로 정확히 그 이름이어야 하고, 폴더 맨 바깥(최상단)에 있어야 합니다. 이 규칙만 지키면 나머지는 자유입니다.
이미 만든 페이지가 있다면
- 첫 화면 파일 이름을
index.html로 바꿉니다. - 이미지, CSS, JS 같은 딸린 파일은 같은 폴더 구조 그대로 함께 올립니다.
- 파일 이름에 한글, 공백, 괄호가 있으면 영문과 하이픈으로 바꿉니다. 예:
내 사진 (1).png→photo-1.png. 바꾼 뒤에는 HTML 안에서 그 파일을 부르는 부분도 같이 고쳐야 합니다.
없다면 이 예제로 시작하세요
아래 코드가 이 가이드에서 끝까지 쓰는 예제 페이지입니다. 자기소개 한 줄과 방명록이 있는 한 장짜리 페이지이고, 파일은 index.html 하나뿐입니다. 지금은 데이터베이스가 연결되지 않은 상태라 방명록 자리에 안내문이 보이고, 5단계와 6단계를 마치면 실제로 글이 쌓입니다.
예제 index.html 전체 코드 (눌러서 펼치고 복사)
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>내 웹페이지</title>
<style>
:root{ --bg:#f6f7f9; --card:#fff; --ink:#1b2030; --muted:#5b6472; --line:#e6e8ee; --accent:#3f5bd9; }
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Malgun Gothic",sans-serif;line-height:1.7}
.wrap{max-width:640px;margin:0 auto;padding:56px 20px 80px}
h1{font-size:34px;margin:0 0 8px}
.sub{color:var(--muted);margin:0 0 32px}
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:22px 24px;margin:0 0 20px}
h2{font-size:18px;margin:0 0 14px}
label{display:block;font-size:14px;color:var(--muted);margin:12px 0 6px}
input,textarea{width:100%;padding:11px 13px;border:1px solid var(--line);border-radius:9px;
font:inherit;background:#fff;color:var(--ink)}
textarea{min-height:88px;resize:vertical}
button{margin-top:16px;width:100%;padding:12px;border:0;border-radius:9px;background:var(--accent);
color:#fff;font:inherit;font-weight:700;cursor:pointer}
button:disabled{opacity:.5;cursor:default}
.msg{margin-top:12px;font-size:14px;color:var(--muted);min-height:20px}
.entry{border-top:1px solid var(--line);padding:14px 0}
.entry:first-child{border-top:0}
.entry b{display:block}
.entry time{font-size:12.5px;color:var(--muted)}
.notice{background:#fef4ec;border:1px solid #e2a06a;border-radius:10px;padding:14px 16px;font-size:14.5px}
</style>
</head>
<body>
<div class="wrap">
<h1>안녕하세요, 강동민입니다</h1>
<p class="sub">이 페이지는 GitHub에 올려 Vercel로 배포했습니다.</p>
<section class="card">
<h2>방명록 남기기</h2>
<form id="guestbook-form">
<label for="name">이름</label>
<input id="name" maxlength="20" required placeholder="이름을 적어 주세요">
<label for="message">한마디</label>
<textarea id="message" maxlength="200" required placeholder="하고 싶은 말을 적어 주세요"></textarea>
<button type="submit" id="submit-btn">남기기</button>
</form>
<p class="msg" id="form-msg"></p>
</section>
<section class="card">
<h2>남겨진 글</h2>
<div id="entries"><p class="msg">불러오는 중...</p></div>
</section>
</div>
<!-- Supabase 라이브러리 -->
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<script>
// 여기 두 줄만 내 값으로 바꿉니다 (가이드 6단계)
const SUPABASE_URL = "";
const SUPABASE_KEY = "";
const entriesEl = document.getElementById("entries");
const formEl = document.getElementById("guestbook-form");
const msgEl = document.getElementById("form-msg");
const btnEl = document.getElementById("submit-btn");
const connected = SUPABASE_URL !== "" && SUPABASE_KEY !== "";
const db = connected ? supabase.createClient(SUPABASE_URL, SUPABASE_KEY) : null;
if (!connected) {
entriesEl.innerHTML =
'<div class="notice">아직 데이터베이스를 연결하지 않았습니다. ' +
"가이드의 데이터베이스 단계를 마치고 index.html 위쪽 두 줄을 채우면 " +
"여기에 방명록이 쌓입니다.</div>";
btnEl.disabled = true;
} else {
loadEntries();
}
async function loadEntries() {
const { data, error } = await db
.from("guestbook")
.select("*")
.order("created_at", { ascending: false })
.limit(50);
if (error) {
entriesEl.innerHTML = '<p class="msg">불러오지 못했습니다: ' + error.message + "</p>";
return;
}
if (!data.length) {
entriesEl.innerHTML = '<p class="msg">아직 남겨진 글이 없습니다.</p>';
return;
}
entriesEl.innerHTML = data
.map(function (row) {
return (
'<div class="entry"><b>' +
escapeHtml(row.name) +
"</b>" +
escapeHtml(row.message) +
"<time>" +
new Date(row.created_at).toLocaleString("ko-KR") +
"</time></div>"
);
})
.join("");
}
formEl.addEventListener("submit", async function (event) {
event.preventDefault();
btnEl.disabled = true;
msgEl.textContent = "저장하는 중...";
const { error } = await db.from("guestbook").insert({
name: document.getElementById("name").value.trim(),
message: document.getElementById("message").value.trim(),
});
if (error) {
msgEl.textContent = "저장하지 못했습니다: " + error.message;
} else {
msgEl.textContent = "남겼습니다.";
formEl.reset();
loadEntries();
}
btnEl.disabled = false;
});
function escapeHtml(text) {
return String(text).replace(/[&<>"']/g, function (ch) {
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch];
});
}
</script>
</body>
</html>
파일 만드는 법 (메모장으로 충분합니다)
- 바탕화면에서 오른쪽 클릭 후 새로 만들기 > 폴더를 눌러
my-webpage라는 폴더를 만듭니다. - 그 폴더 안에서 오른쪽 클릭 후 새로 만들기 > 텍스트 문서를 만듭니다.
- 메모장으로 열고 위 코드를 붙여 넣습니다.
- 파일 > 다른 이름으로 저장을 누릅니다. 파일 이름에
index.html, 파일 형식은 모든 파일, 인코딩은 UTF-8로 맞추고 저장합니다. - 저장한
index.html을 더블클릭하면 브라우저에서 내 페이지가 열립니다.
index.html.txt가 됩니다. 겉보기에는 index.html처럼 보이는데 배포하면 화면이 안 뜹니다. 확인 방법은 아래 Windows에서만 겪는 함정에 있습니다.
file:///C:/Users/...일 것입니다. 이것은 인터넷 주소가 아니라 내 컴퓨터 안의 파일 경로입니다. 이 주소를 다른 사람에게 보내면 열리지 않습니다. 진짜 주소는 4단계에서 받습니다.- 폴더 안에
index.html이 있고, 파일 확장명을 켠 상태에서 이름이 정확히 그렇게 보인다(index.html.txt가 아니다). - 더블클릭하면 브라우저에서 페이지가 열리고, 한글이 깨지지 않는다.
한글이 깨졌다면 메모장에서 다시 열어 다른 이름으로 저장 > 인코딩 UTF-8로 저장하면 됩니다.
GitHub에 파일 올리기
GitHub에 저장소(repository)를 하나 만들고, 그 안에 파일을 끌어다 놓습니다. 프로그램을 설치할 필요도, 명령어를 칠 필요도 없습니다.
3-1. 저장소 만들기
- 로그인한 상태로
github.com/new를 엽니다. - Repository name에 영문 이름을 적습니다. 예:
my-webpage. 공백 대신 하이픈을 씁니다. - Choose visibility에서 Private를 고릅니다. 비공개로 두어도 Vercel 배포는 문제없이 됩니다. 소스를 공개하고 싶으면 Public을 골라도 됩니다.
- 아래의 Add README, Add .gitignore, Add license는 모두 그대로 둡니다.
- 맨 아래 Create repository를 누릅니다.
3-2. 파일 올리기
저장소를 만들면 아직 비어 있는 화면이 나옵니다. 가운데 파란 상자 안의 uploading an existing file을 누릅니다.
- 파일 탐색기에서
index.html을 포함한 파일들을 선택해 브라우저의 점선 상자 안으로 끌어다 놓습니다. 마우스로 끌어 놓는 대신 choose your files를 눌러 골라도 됩니다. - 하위 폴더(예:
images)가 있으면 폴더째로 끌어다 놓으면 구조가 유지됩니다. - 아래 Commit changes 칸에는 아무 설명이나 적어도 되고, 비워 두면 자동으로 채워집니다.
- 초록색 Commit changes 버튼을 누릅니다.
3-3. 잘 올라갔는지 확인
저장소 첫 화면으로 돌아오면 파일 목록이 보입니다. index.html이 목록 맨 바깥에 있는지 꼭 확인하세요. 폴더 안에 들어가 있으면 다음 단계에서 페이지가 안 뜹니다.
index.html이 최상단에 보이면 정상입니다.- 저장소 첫 화면의 파일 목록에
index.html이 바로 보인다(폴더를 열고 들어가지 않아도 된다). - 파일 이름을 누르면 내가 쓴 코드 내용이 그대로 보인다.
- 화면 위쪽에
1 Commit처럼 커밋 기록이 하나 이상 있다.
Vercel로 배포하기
이제 GitHub에 있는 파일을 실제 웹사이트로 띄웁니다. 여기까지 하면 누구나 접속할 수 있는 주소가 생깁니다.
4-1. 저장소 가져오기
- 로그인한 상태로
vercel.com/new를 엽니다. - Import Git Repository 목록에서 방금 만든 저장소(
my-webpage)를 찾아 오른쪽 Import를 누릅니다.
4-2. 설정 확인하고 배포
- Project Name은 그대로 두어도 됩니다. 이 이름이 주소의 앞부분이 됩니다.
- Application Preset(또는 Framework Preset)이 Other인지 확인합니다. HTML 파일만 있는 페이지는 Other가 맞습니다.
- Root Directory는
./그대로 둡니다. 단, GitHub에서 파일이 폴더 안에 들어가 있다면 Edit를 눌러 그 폴더를 골라야 합니다. - Build and Output Settings와 Environment Variables는 건드리지 않습니다.
- Deploy를 누릅니다.
4-3. 주소 확인
보통 10초에서 1분 사이에 배포가 끝나고 Congratulations! 화면이 나옵니다. 가운데 미리보기에 내 페이지가 그대로 보이면 성공입니다.
index.html입니다. (계정 이름은 가렸습니다.)미리보기 이미지나 아래쪽 Continue to Dashboard를 누르면 프로젝트 화면으로 갑니다. 여기서 Domains 아래 주소가 내 웹사이트 주소입니다. 보통 프로젝트이름.vercel.app 형태입니다.
- Vercel 프로젝트 화면의 Status가 Ready다.
- Domains의 짧은 주소를 브라우저 주소창에 붙여 넣으면 내 페이지가 열린다.
- 휴대폰(또는 다른 사람의 기기)에서도 같은 주소가 열린다.
데이터를 받을 일이 없는 페이지라면 여기서 끝내도 됩니다. 방명록, 신청서, 문의처럼 방문자 입력을 저장해야 한다면 5단계로 이어집니다.
Supabase에 표 만들기
지금 페이지는 글을 남겨도 저장할 곳이 없습니다. Supabase에 표(table)를 하나 만들어 그 자리를 마련합니다. 엑셀 시트를 하나 만든다고 생각하면 됩니다.
<form>부터 </script>까지를 통째로 복사해 내 페이지에 붙여 넣으면 같은 방명록이 생깁니다. 붙여 넣을 위치는 </body> 바로 위입니다.5-1. 프로젝트 만들기
supabase.com/dashboard를 열고 New project를 누릅니다.- Project name에 영문 이름을 적습니다. 예:
my-webpage. - Database Password는 Generate a password를 눌러 만들고, 따로 적어 둡니다. 이 가이드에서는 쓰지 않지만 나중에 필요할 때 다시 볼 수 없습니다.
- Region은 Northeast Asia (Seoul)을 고릅니다. 한국에서 접속할 때 가장 빠릅니다.
- 아래 Security의 체크박스 세 개는 기본값 그대로 둡니다. 위의 두 개가 켜져 있어야 브라우저가 데이터베이스와 대화할 수 있습니다.
- Create new project를 누르고 1분에서 2분 정도 기다립니다.
준비가 끝나면 아래 같은 화면이 나옵니다. 제목 아래에 https://...supabase.co 형태의 주소가 보이면 완성입니다. 이 주소는 6단계에서 씁니다.
5-2. SQL Editor 열기
프로젝트가 준비되면 위 그림 같은 Project Overview 화면에 서 있게 됩니다. 여기서 바로 표를 만들 수는 없고, 왼쪽 메뉴로 자리를 한 번 옮겨야 합니다.
- 화면 맨 왼쪽 끝을 봅니다. 아이콘만 세로로 늘어선 좁은 띠가 있습니다. 글씨가 없어서 메뉴처럼 보이지 않지만 이것이 프로젝트 메뉴입니다.
- 그 띠에 마우스를 올리면 폭이 넓어지면서 이름이 나타납니다. 위에서부터 Project Overview, Table Editor, SQL Editor, Database, Authentication, Storage 순입니다.
- 위에서 세 번째 SQL Editor를 누릅니다. 아이콘은
>_모양입니다. 키보드로는 G를 누르고 이어서 S를 눌러도 같은 화면으로 갑니다. - 빈 편집기가 열립니다. 가운데 넓은 칸에
Hit CTRL+SHIFT+K to generate query or just start typing이라는 흐린 안내가 있고, 오른쪽 위에 초록색 Run 버튼이 있습니다. 아래쪽에는 Results 칸이 비어 있습니다. 여기가 다음 절에서 코드를 붙여 넣을 자리입니다.
5-3. SQL 한 번 붙여 넣기
표를 만들고, 누가 읽고 쓸 수 있는지까지 한 번에 정합니다. 방금 연 SQL Editor의 빈 칸에 아래 코드를 전부 붙여 넣고 오른쪽 위 Run을 누릅니다. 코드 블록 오른쪽 위의 복사 버튼을 쓰면 한 번에 복사됩니다. 아래 Results 칸에 Success. No rows returned가 뜨면 성공입니다.
create table guestbook ( id bigint generated always as identity primary key, name text not null, message text not null, created_at timestamptz not null default now() ); alter table guestbook enable row level security; create policy "누구나 읽기" on guestbook for select to anon using (true); create policy "누구나 쓰기" on guestbook for insert to anon with check (true);
5-4. 확인
확인도 5-2에서 쓴 왼쪽 아이콘 띠로 자리를 옮겨서 합니다. 띠에서 네 번째 Database를 누르면 가운데에 하위 목록이 생기는데, 그중 Policies를 누릅니다. guestbook 표 아래에 정책 두 개가 보이면 제대로 된 것입니다. 같은 띠의 두 번째 Table Editor로 가면 방금 만든 표가 목록에 보입니다.
guestbook 아래에 INSERT와 SELECT 정책이 하나씩 보이면 정상입니다. 정책이 하나도 없는 표에는 "No data will be returned"라고 표시됩니다.주소와 키를 페이지에 넣기
마지막으로 내 페이지에게 "데이터는 여기에 저장해"라고 알려 줍니다. 값 두 개만 넣으면 됩니다.
6-1. 값 두 개 복사하기
- Project URL: 5단계 그림 14의 화면 위쪽에 있는
https://...supabase.co주소입니다. Copy를 눌러 복사합니다. - Publishable key: 왼쪽 아래 톱니바퀴(Project Settings) > API Keys로 갑니다. Publishable key 칸의
sb_publishable_...로 시작하는 값을 복사 아이콘으로 복사합니다.
anon과 service_role이라고 불렀습니다.6-2. index.html에 붙여 넣기
index.html에 두 줄을 채운 뒤 Add file > Upload files로 덮어쓰는 편이 낫습니다. 결과는 똑같고, 내 PC 원본과 GitHub 파일이 어긋나지 않습니다.- GitHub 저장소에서
index.html을 누릅니다. - 파일 내용 오른쪽 위의 연필 아이콘을 눌러 편집 화면을 엽니다.
- 편집 화면 안을 한 번 클릭한 뒤 Ctrl + F를 누르고
SUPABASE_URL을 입력하면 해당 줄로 바로 이동합니다. 마우스로 내려서 찾아도 됩니다(60행 부근).
const SUPABASE_URL = ""; const SUPABASE_KEY = "";
const SUPABASE_URL = "https://abcdefghijklmnop.supabase.co"; const SUPABASE_KEY = "sb_publishable_XXXXXXXXXXXXXXXXXXXXXX";
- 따옴표
"는 지우지 말고 그 안에만 붙여 넣습니다. - 줄 끝의 세미콜론
;도 그대로 둡니다. - 주소 끝에
/가 붙어 있으면 지웁니다.
다 고쳤으면 오른쪽 위 Commit changes...를 누릅니다. 창이 하나 뜨는데, 설명은 비워 두어도 되고 Commit directly to the main branch가 선택된 그대로 두면 됩니다. 초록색 Commit changes를 눌러 저장합니다.
6-3. 자동 재배포 확인
저장하는 순간 Vercel이 알아서 다시 배포합니다. Vercel 프로젝트의 Deployments 화면에 새 줄이 하나 생기고, 상태가 Ready로 바뀌면 끝입니다. 보통 1분 안에 끝납니다.
이제 내 주소로 접속해 이름과 한마디를 적고 남기기를 눌러 보세요. 주황색 안내문이 사라지고 남긴 글이 아래 목록에 나타납니다.
- 배포된 주소에서 남기기 버튼이 눌린다(회색으로 비활성화돼 있지 않다).
- 글을 남기면 남겼습니다.라는 문구가 뜨고, 아래 목록에 방금 쓴 글이 나타난다.
- 페이지를 새로고침해도 그 글이 그대로 있다.
빨간 오류 문구가 뜬다면 그 문구를 그대로 문제 해결에서 찾아보세요. 대부분 정책(RLS) 아니면 주소, 키 오타입니다.
확인하고 마무리하기
7-1. 저장이 실제로 됐는지 확인
Supabase의 Table Editor에서 왼쪽 목록의 guestbook을 눌러 보세요. 방금 남긴 글이 한 줄씩 들어와 있으면 진짜로 저장된 것입니다. 여기서 이상한 글을 직접 지울 수도 있습니다(줄 왼쪽 체크 후 삭제).
id는 자동으로 붙는 번호이고, created_at도 자동으로 채워집니다. (다른 표 이름은 가렸습니다.)7-2. 앞으로 페이지를 고치는 방법
GitHub에서 파일을 열고 연필 아이콘을 눌러 고친 뒤 Commit changes를 누르면 됩니다. 그때마다 Vercel이 1분 안에 다시 배포합니다. 잘못 고쳤으면 그 파일의 History에서 예전 버전을 열어 되돌립니다.
7-3. 알아 둘 한 가지
Supabase 무료 플랜은 일주일 동안 아무도 안 들어오면 프로젝트를 일시정지합니다. 그러면 방명록만 안 되고 페이지는 그대로 열립니다. 대시보드에서 Restore를 누르면 다시 살아나고 데이터는 지워지지 않습니다.
Windows에서만 겪는 함정
.html, .txt 같은 확장자를 감춥니다. 그래서 index.html.txt가 화면에는 index.html로 보입니다. 파일 탐색기에서 보기 > 표시 > 파일 확장명에 체크하면(Windows 10은 보기 탭의 파일 확장명) 진짜 이름이 보입니다. 배포하기 전에 반드시 한 번 켜서 확인하세요.Style.css와 style.css가 같은 파일 취급이라 잘 열립니다. 그런데 배포된 서버는 이 둘을 다른 파일로 봅니다. 그래서 "내 컴퓨터에서는 되는데 배포하니 디자인이 깨진다"가 생깁니다. HTML에 적은 파일 이름과 실제 파일 이름의 대소문자를 글자 하나까지 똑같이 맞추세요.?나 깨진 글자로 나옵니다. "UTF-8(BOM)"이 아니라 그냥 UTF-8이면 가장 무난합니다.문제 해결
배포는 됐는데 404 NOT_FOUND가 뜹니다
거의 항상 원인은 셋 중 하나입니다. 첫째, 첫 화면 파일 이름이 index.html이 아닙니다(대문자 Index.html도 안 됩니다). 둘째, index.html이 폴더 안에 들어가 있습니다. 이 경우 Vercel 프로젝트의 Settings > General > Root Directory를 그 폴더로 바꾸면 됩니다. 셋째, 진짜 파일 이름이 index.html.txt입니다. GitHub 저장소 파일 목록에서 실제 이름을 눈으로 확인하세요.
메모장에서 코드를 붙여 넣었더니 줄이 다 붙어 버립니다
메모장 상단 메뉴에서 서식 > 자동 줄 바꿈을 꺼도 되지만, 사실 화면에 어떻게 보이든 저장되는 내용은 같습니다. 코드 블록 오른쪽 위의 복사 버튼으로 복사했다면 줄바꿈은 그대로 들어가 있으니 그냥 저장하면 됩니다.
고쳤는데 사이트에 예전 화면이 그대로입니다
순서대로 확인하세요. 1) GitHub에서 실제로 커밋이 됐는지(파일 목록에 방금 시각이 찍혔는지), 2) Vercel의 Deployments에 새 줄이 생겼고 상태가 Ready인지, 3) 브라우저 캐시인지. 셋째라면 Ctrl + Shift + R로 강력 새로고침하거나 시크릿 창에서 열어 보세요.
글을 남기면 "new row violates row-level security policy"가 뜹니다
쓰기 정책이 없다는 뜻입니다. 5단계 SQL에서 create policy "누구나 쓰기" 부분이 실행되지 않았을 가능성이 큽니다. SQL Editor에서 아래 한 덩어리만 다시 실행하세요.
create policy "누구나 쓰기" on guestbook for insert to anon with check (true);
"Invalid API key"가 뜹니다
키를 복사하다 앞뒤 공백이나 줄바꿈이 섞였거나, 키 일부만 복사된 경우입니다. API Keys 화면의 복사 아이콘을 눌러 다시 복사하고, 따옴표 안에 공백 없이 붙여 넣으세요.