HTML 직접 보기
<!doctype html><html lang="ko"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>전체 프롬프트·SQL 모음</title><style>*{box-sizing:border-box}body{margin:0;background:#f5f3ee;color:#203d35;font:17px/1.6 system-ui,-apple-system,"Noto Sans KR",sans-serif}main{max-width:900px;margin:36px auto;padding:32px;background:#fff;border:1px solid #dcded5;border-radius:24px}h1{font-size:clamp(26px,5vw,40px);line-height:1.3;letter-spacing:-.03em;margin:8px 0 12px}h2{font-size:22px}p{margin:10px 0}.eyebrow{font-size:13px;font-weight:800;letter-spacing:.09em;color:#547060}.muted{color:#68786f;font-size:15px}.note{padding:14px 18px;background:#fff2c8;border-radius:12px;color:#4f481f}.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:16px 0}button,input,textarea,select{font:inherit;border-radius:10px;padding:12px 16px}button{background:#245445;color:white;border:0;cursor:pointer;font-weight:700}button.secondary{background:#edf2ed;color:#245445}button:disabled{opacity:.45;cursor:wait}input,textarea,select{border:1px solid #bacbc2;background:#fff;color:#203d35;max-width:100%}input,textarea{flex:1;min-width:180px}textarea{width:100%;min-height:150px;resize:vertical}label{font-weight:700}button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:3px solid #e3ae35;outline-offset:3px}.card{border:1px solid #dce5df;padding:18px;border-radius:16px;margin:12px 0}.big{font-size:clamp(36px,8vw,72px);font-weight:800;text-align:center;padding:40px 0;background:#edf3ed;border-radius:18px}.badge{display:inline-block;background:#ecf2ed;color:#245445;padding:4px 10px;border-radius:7px;font-size:14px}.todo{display:flex;align-items:center;gap:10px;padding:12px 0;border-bottom:1px solid #e7ece8}.todo span{flex:1;overflow-wrap:anywhere}.todo.done span{text-decoration:line-through;color:#809086}.error{color:#9c382a}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:16px/1.7 system-ui,"Noto Sans KR",sans-serif}.hidden{display:none}nav{display:flex;gap:8px;flex-wrap:wrap}@media(max-width:600px){main{margin:0;padding:22px 18px;border-radius:0;border:0;min-height:100vh}.todo{flex-wrap:wrap}button{min-height:48px}}</style></head><body><main><div class="eyebrow">실습 요청문 · 복구 요청문 · SQL</div><h1>필요한 요청을 골라 쓰세요</h1><p>단계별 요청 복사 버튼과 같은 전체 문장입니다. 대괄호는 내 상황과 주소로 바꾸세요. 비밀번호나 secret key를 넣지 마세요.</p><label for="choose">요청문 선택</label><div class="row"><select id="choose"></select><button id="select" class="secondary">내용 선택</button></div><p id="status" class="muted" aria-live="polite">내용 선택 뒤 Mac은 ⌘C, Windows는 Ctrl+C로 복사하세요.</p><pre id="text" tabindex="0" class="card"></pre><p class="note">AI 요청문은 해당 Agent에 보내고, SQL은 안내한 새 연수용 Supabase 프로젝트의 SQL Editor에서 실행하세요.</p></main><script>const items=[{"title": "로그인과 DB 저장", "text": "현재 할 일 앱에 Supabase 이메일·비밀번호 회원가입, 로그인, 로그아웃을 붙여 줘.\n이메일 확인이 켜져 있으면 회원가입 직후 로그인 성공으로 표시하지 말고 확인 메일을 열도록 안내해 줘. 실제 인증 세션으로 로그인 상태를 판단하고 새로고침 뒤에도 세션을 복원해 줘.\n로그인 사용자의 uid를 todos.user_id에 넣어 추가하고, 해당 사용자 목록을 DB에서 읽어 완료 전환과 삭제를 구현해 줘. 기본키 id로 수정/삭제하고, user_id와 title 길이 검사를 지켜 줘. DB의 RLS 정책은 별도로 실행할 거야. SQL 실행이 필요하면 제안만 하고 권한을 끄지 마.\n로그아웃과 계정 전환 시 이전 사용자의 목록·오류·입력값을 비워 줘. DB 작업 실패를 성공으로 표시하지 말고 이전 상태를 되돌려 줘. SDK는 버전을 고정하고 저장소에 lock 파일이 필요하면 함께 관리해 줘.\n추가·완료·삭제·로그인/로그아웃·새로고침·다른 계정 확인 순서를 알려 줘. UI에서 목록을 숨기는 것만으로 권한을 구현하지 마."}, {"title": "Supabase 연결 준비", "text": "현재 할 일 앱에 Supabase 연결 구조를 준비해 줘. 로그인과 데이터 저장 완료는 다음 단계에서 확인할 거야.\n표는 public.todos이고 열은 id(uuid), user_id(uuid), title(text), completed(boolean), created_at(timestamptz)이야. RLS는 이미 켜져 있고 아직 행 정책은 없어서 쓰기는 허용되지 않아.\nSupabase Project URL과 publishable key만 사용해. secret key나 service_role은 브라우저, 저장소, Vercel 공개 환경 변수에 넣지 마.\n지금 프로젝트가 index.html인지 프레임워크 프로젝트인지 확인하고 맞는 Supabase 클라이언트 구성과 환경 변수 이름을 정해 줘. 정적 HTML은 환경 변수 이름을 적는 것만으로 값이 주입되지 않으니 실제 빌드가 없으면 공개 URL/publishable 설정 파일 방식임을 분명히 안내해 줘. 개인 키를 대화에 다시 출력하지 마.\n미로그인 상태에서는 ‘다음 단계에서 로그인 후 저장할 수 있어요’라고 표시하고 쓰기 버튼은 비활성화해 줘. 연결 오류는 화면에 알려 주되 브라우저 메모리 저장을 DB 성공인 것처럼 표시하지 마. 현재 레이아웃은 유지해 줘."}, {"title": "Vercel 설정 확인", "text": "현재 GitHub 저장소의 앱을 Vercel로 배포하려고 해.\n프로젝트 파일을 먼저 살펴서 필요한 Framework Preset, Root Directory, Build Command, Output Directory를 판단해 줘. index.html 한 파일이면 그에 맞는 설정을 안내하고, 임의의 설정을 추측하지 마.\n내가 Vercel에서 이 저장소를 Import해서 Deploy할 순서와 배포가 끝난 공개 URL에서 추가·완료·삭제를 확인하는 방법을 알려 줘. 실패하면 로그에서 첫 오류를 확인하고 한 가지씩 수정하게 안내해 줘."}, {"title": "막힌 상황을 설명하기", "text": "지금 단계에서 문제가 생겼어.\n단계: [설치/미리보기/push/배포/DB/로그인]\n내가 한 일: [재현 순서]\n기대 결과: [원하는 결과]\n실제 결과: [오류 문구 또는 개인정보를 가린 화면]\n프로젝트 파일과 관련 로그를 확인해서 원인을 하나씩 좁혀 줘. 마지막으로 작동한 기능을 유지하고 가장 작은 수정부터 제안해 줘. 비밀번호·secret key를 요청하거나 권한을 끄거나 기존 기록을 삭제하는 방식으로 우회하지 마."}, {"title": "GitHub 올리기", "text": "이 작업 폴더의 첫 할 일 앱을 내가 만든 GitHub 저장소 [내 저장소 HTTPS 주소]에 올릴 준비를 해 줘.\n현재 Git 상태와 연결된 원격 저장소부터 확인해 줘. PRD.md와 실행에 필요한 파일만 포함하고 .env, 비밀번호, API secret, 개인 정보는 제외하도록 .gitignore를 점검해 줘.\n기존 저장소나 기록을 삭제하거나 force push하지 마. 인증이 필요하면 공식 로그인 화면에서 내가 직접 진행하게 해 줘. 변경 파일을 보여 준 뒤 일반 commit과 push를 안내하고, GitHub에 index.html이 보이는지 확인할 방법을 알려 줘."}, {"title": "수정 · 타이머만", "text": "지금 만든 면접 연습 프로그램의 답 입력 화면만 수정해 줘.\n현재 질문을 연 순간부터 지난 시간을 초 단위로 보여 줘. 다음 질문을 열면 0초부터 다시 시작하고 결과 화면이나 다시 시작에서 기존 타이머를 정리해 줘.\n질문 5개, 빈 답 검사, 답 입력, 다음 질문, 최종 정리 기능과 디자인은 그대로 유지해 줘."}, {"title": "수정 · 문구만", "text": "현재 면접 연습 프로그램의 안내 문구만 바꿔 줘.\n질문 위와 다음 버튼 옆에 부담을 줄이는 짧고 친절한 표현을 써 줘. 실제 AI 채점이나 평가를 하는 것처럼 쓰지 마.\n질문, 입력한 답, 타이머, 빈 답 검사, 최종 정리, 버튼 동작은 그대로 유지해 줘. 바뀐 안내 문구를 먼저 목록으로 알려 줘."}, {"title": "면접 앱 · 첫 요청", "text": "면접 연습 프로그램을 HTML 한 파일로 만들어 줘.\n미리 정한 질문 5개를 한 개씩 보여 주고 답을 입력한 뒤 다음 질문으로 이동하게 해. 질문은 자기소개, 지원 동기, 협력 경험, 어려움 해결 경험, 앞으로의 계획이야.\n빈 답은 다음으로 넘어가지 않게 하고 5개 답을 끝내면 질문과 답을 함께 정리해 줘. 다시 시작 버튼도 넣어 줘. 휴대폰에서 입력칸과 버튼이 보이게 해. 답은 이 화면에서만 유지하고 외부 API로 보내지 마."}, {"title": "PRD대로 구현", "text": "이 작업 폴더의 PRD.md를 읽고 첫 할 일 관리 앱을 구현해 줘.\nindex.html 한 파일로 시작하고 외부 라이브러리와 API 없이 실행되게 해. 입력값은 textContent 등 안전한 방법으로 표시해 줘. 데이터는 화면 메모리에만 두고 새로고침하면 초기화된다는 안내를 보여 줘.\n완료 후 내가 열 파일과 브라우저 미리보기 방법을 알려 줘. 임의로 설치·배포하거나 다음 단계의 로그인 기능을 만들지 마. 추가·완료·삭제·빈 입력·휴대폰 화면을 확인해 줘."}, {"title": "문제 한 가지 수정", "text": "지금 만든 할 일 앱에서 [문제가 생긴 화면/버튼]만 수정해 줘.\n재현 순서: [내가 누른 순서]\n현재 결과: [실제로 보이는 결과]\n원하는 결과: [기대하는 결과]\n추가·완료·삭제·빈 입력 검사는 유지해 줘. 원인을 설명하고 수정한 부분과 내가 다시 확인할 순서를 알려 줘. 나머지 기능과 디자인은 그대로 유지해 줘."}, {"title": "첫 요청 · 발표자 뽑기", "text": "교실에서 쓸 발표자 뽑기 프로그램을 만들어 줘.\n무엇을: 가상 이름 학생1~학생10 중 한 명을 뽑아.\n어떻게: 뽑기 버튼을 누르면 이름이 빠르게 바뀌다가 3초 뒤 한 명에서 멈춰. 실행 중 버튼은 비활성화하고 결과를 크게 보여 줘.\n모습: 노란색 강조, 큰 글씨, 휴대폰에서도 버튼이 잘 보여.\n형태: HTML 한 파일 안에 CSS와 JavaScript를 넣어 브라우저에서 바로 실행되게 해. 외부 API나 실제 학생 정보는 사용하지 마."}, {"title": "권한 문제 복구", "text": "할 일 앱에서 두 계정의 자료가 섞이거나 저장이 실패해.\n재현 순서와 오류: [계정 A/B에서 확인한 순서와 오류 문구, 비밀번호 제외]\npublic.todos의 grants, RLS 활성화 상태와 기존 정책을 먼저 확인할 SQL을 제안해 줘. SELECT/INSERT/UPDATE/DELETE는 authenticated에만 허용하고 user_id = auth.uid()로 자기 행에만 접근해야 해. UPDATE는 USING과 WITH CHECK를 모두 검사해야 해.\n기존 ‘모두 허용’ 정책이 있으면 정확한 정책 이름과 영향을 알려 주고 제거할 SQL을 제안해 줘. RLS를 끄거나 using(true)로 우회하거나 데이터를 전부 삭제하지 마. 화면 숨김으로 해결하지 마.\n수정 후 로그인 A, 새로고침, 로그아웃, 로그인 B, 타인 행 수정/삭제 거절을 확인할 순서를 알려 줘."}, {"title": "PRD 문서 먼저", "text": "할 일 관리 앱을 만들기 전에 PRD.md를 작성해 줘. 아직 코드는 작성하지 마.\n무엇: 연수 참가자가 자기 할 일을 기록하는 앱.\n누구: 코딩이 처음인 교사, 노트북과 휴대폰 사용자.\n기능: 할 일 추가·완료 전환·삭제, 빈 입력 검사, 200자 제한. 첫 버전은 화면 메모리에만 저장하고 새로고침하면 사라짐을 안내. 나중에 Supabase 로그인과 사용자별 저장을 붙일 예정.\n화면: 앱 제목, 입력칸과 추가 버튼, 목록, 완료/삭제 버튼, 빈 목록 안내.\n지금은 로그인, 데이터베이스, 알림, 협업은 만들지 마.\n완료 기준: 추가 후 입력칸 비움, 완료 전환, 삭제, 빈 입력 거절, 휴대폰 화면에서 버튼 사용. 파일 구조·실행 방법·확인 순서를 짧게 적어 줘."}, {"title": "같은 주소로 수정", "text": "현재 할 일 앱의 제목만 ‘나의 오늘 할 일’로 바꿔 줘.\n나머지 추가·완료·삭제·검사 기능은 그대로 유지해 줘. 변경 파일을 확인한 뒤 같은 GitHub 저장소에 일반 commit/push해서 같은 Vercel 주소로 반영할 방법을 안내해 줘. 새 배포가 Ready인지와 기존 주소의 제목을 함께 확인하자."}, {"title": "SQL · todos 표 준비", "text": "-- 연수용 새 프로젝트의 SQL Editor에서 한 번 실행합니다.\n-- 기존 업무용 todos 테이블에 실행하지 않습니다.\ncreate table public.todos (\n id uuid primary key default gen_random_uuid(),\n user_id uuid not null references auth.users(id) on delete cascade,\n title text not null check (char_length(trim(title)) between 1 and 200),\n completed boolean not null default false,\n created_at timestamptz not null default now()\n);\ncreate index todos_user_id_idx on public.todos(user_id);\nalter table public.todos enable row level security;\nrevoke all on table public.todos from anon, authenticated;\ngrant select, insert, update, delete on table public.todos to authenticated;\n-- 아직 정책이 없으므로 앱의 행 조회/쓰기는 허용되지 않습니다.\n-- 6강에서 로그인과 소유자 정책을 붙인 뒤 저장을 확인합니다."}, {"title": "SQL · 자기 행만 허용하는 정책", "text": "-- 5강에서 만든 새 연수용 public.todos에 한 번 실행합니다.\n-- SQL Editor에서 기존 정책이 없는지 먼저 확인합니다.\ncreate policy todos_select_own on public.todos for select to authenticated\n using ((select auth.uid()) = user_id);\ncreate policy todos_insert_own on public.todos for insert to authenticated\n with check ((select auth.uid()) = user_id);\ncreate policy todos_update_own on public.todos for update to authenticated\n using ((select auth.uid()) = user_id)\n with check ((select auth.uid()) = user_id);\ncreate policy todos_delete_own on public.todos for delete to authenticated\n using ((select auth.uid()) = user_id);\n-- 모든 사람 허용 정책을 추가하지 않습니다."}];const choose=document.getElementById('choose'),text=document.getElementById('text'),status=document.getElementById('status');items.forEach((item,i)=>{const option=document.createElement('option');option.value=i;option.textContent=item.title;choose.append(option)});function show(){text.textContent=items[Number(choose.value)].text;status.textContent='내용 선택 뒤 Mac은 ⌘C, Windows는 Ctrl+C로 복사하세요.'}choose.addEventListener('change',show);document.getElementById('select').addEventListener('click',()=>{text.focus();const selection=window.getSelection(),range=document.createRange();range.selectNodeContents(text);selection.removeAllRanges();selection.addRange(range);status.textContent='전체 내용을 선택했어요. ⌘C 또는 Ctrl+C로 복사하세요.'});show();</script></body></html>