D1-1 무료 AI로 시작하는 나의 앱

구현 요청
학생인 내가 진짜 만들고 싶은 앱을 구체화하도록 도와줘. 먼저 “당신이 진짜 만들고 싶은 앱은 무엇인가요?”를 묻고 답을 기다려줘. 이어 “어디에서 운영하고 싶은가요? 웹 vs 모바일?”을 묻고, 웹·휴대폰 설치형 웹앱(PWA)·모바일 앱 중 선택 이유를 확인해줘. 사용자·해결할 문제·핵심 동작1개·첫 화면·데이터 저장 위치·무료 개발 도구와 배포 대상·성공 기준을 한 장 기획 카드로 정리해줘. 첫 수업에서는 단일 HTML과 가상 데이터로 20분 안에 만들 범위로 줄여줘. 앱 안에서 AI를 호출해야 하는 기능은 다음 단계 후보로 남기고 별도 API 비용을 설명해줘.

검증 요청
이 학생 앱 기획 카드를 검토해줘. 사용자가 겪는 문제가 구체적인지, 첫 기능이 하나인지, 웹·PWA·모바일 선택에 이유가 있는지, 실제 무료 경로와 사용 조건을 구분했는지 확인해줘. 첫 버전은 유료 API나 서버 없이 실행 가능해야 한다. localStorage가 이 브라우저·기기에만 저장됨을 설명하고, 동작을 확인할 입력과 예상 출력 한 쌍을 만들어줘. 범위가 크면 오늘 만들 기능 하나로 줄여줘.

실습 절차
1. 8분 시연: 무료 AI 한 개로 만든 index.html을 GitHub Pages에서 공개하는 경로를 본다.
2. 7분 질문: 진짜 만들고 싶은 앱과 사용할 사람, 웹·PWA·모바일 운영 형태를 고른다.
3. 5분 카드: 사용자·문제·기능1개·첫 화면·저장·성공 기준을 적는다.
4. 12분 생성: S08의 무료 시작 프롬프트로 단일 HTML 앱을 만들고 로컬에서 실행한다.
5. 8분 배포: 준비된 공개 저장소에 index.html을 올리고 Settings→Pages→Deploy from a branch→main/(root)→Save, 배포 URL을 확인한다.
6. 6분 짝 검증: 휴대폰 또는 다른 브라우저로 링크를 열고 핵심 동작과 빈 입력·저장 범위를 확인한다.
7. 4분 정리: 30초 앱 소개와 다음에 구현할 기능 한 개를 남긴다.

완료 확인
나의 앱과 사용자·해결할 문제를 한 문장으로 말한다.
웹·PWA·모바일 선택 이유와 무료 배포 조건을 설명한다.
공개 URL에서 핵심 동작1개를 짝이 확인한다.

D1-2 파일 정리 도구 만들기

구현 요청
현재 작업 폴더에서 tools/organize-files.mjs를 만들어줘. Node.js 내장 모듈을 사용하고 입력은 labs/sample-files의 복사본으로 제한해줘.
.txt와 .csv는 documents, .svg는 images로 분류해줘. 기본 실행은 원본을 바꾸지 않는 미리보기이고 --apply일 때만 이동해줘. 기존 파일을 덮어쓰지 말고 중복은 건너뛰어 이유를 출력해줘. 심볼릭 링크와 작업 폴더 밖 경로는 거부해줘. 실행 방법을 설명하고 미리보기를 먼저 실행해줘.

검증 요청
도구의 변경 범위를 확인해줘. 아직 적용하지 말고 예상 이동 건수와 제외 이유를 보여줘. 적용 후 같은 입력으로 재실행해 추가 이동이 없는지 확인할 명령도 제안해줘. 실제 사용자 폴더를 탐색하지 마.

실습 절차
1. 실습 ZIP의 sample-files 폴더를 작업 폴더로 복사하고 원본을 보존한다.
2. 확장자를 기준으로 documents와 images에 분류하는 규칙을 적는다.
3. Codex에게 Node.js 도구를 만들되 기본 동작을 미리보기로 지정한다.
4. 예정 목록을 보고 대상 폴더 밖의 파일이 없는지, 중복 이름이 생기는지 확인한다.
5. 복사본에만 --apply 옵션으로 실행하고 결과 목록을 저장한다.
6. 같은 명령을 다시 실행해 추가 변경이 없는지 확인한다.

완료 확인
미리보기에서 파일 내용과 위치가 유지된다.
가상 파일만 지정한 하위 폴더로 이동한다.
재실행과 중복 이름에서 덮어쓰기가 없다.

D1-3 CSV 정리와 보고서 자동화

구현 요청
labs/registrations.csv를 읽는 tools/summarize-csv.mjs를 만들어줘. 따옴표가 포함된 CSV도 처리할 수 있게 해줘.
id 중복은 처음의 유효 행을 유지하고, amount가 음수이거나 유한한 숫자가 아니면 errors.csv로 분리해줘. 원본은 변경하지 마.
output/clean.csv와 summary.json, 사람이 읽을 수 있는 요약 보고서를 만들어줘. program별 인원과 금액, status별 인원을 집계해줘. 실제 결과를 expected-summary.json과 비교해 차이를 설명해줘.

검증 요청
원본 행 수, 유효 고유 행 수, 중복 수, 오류 수가 맞는지 검산해줘. 합계만 맞추려고 자료를 수정하지 말고, 제외한 행의 id와 이유를 보여줘. CSV 셀의 쉼표·따옴표·빈값도 확인해줘.

실습 절차
1. labs/registrations.csv의 열 이름과 12개 원본 행을 확인한다.
2. 신청 id가 같은 행은 중복이며 처음의 유효 행을 유지하는 규칙을 정한다.
3. 금액이 음수이거나 숫자가 아닌 행은 별도 오류 목록에 남기도록 요청한다.
4. program별 인원·금액 합계와 상태별 집계를 만드는 도구를 실행한다.
5. 제공된 expected-summary.json과 행 수·금액을 대조한다.
6. 정리된 CSV를 다시 입력해 결과가 안정적인지 확인하고 처리 규칙을 설명한다.

완료 확인
원본 12행에서 유효 고유 8행이 나온다.
중복 2행과 오류 2행을 구분한다.
유효 금액 합계 160000원과 프로그램별 집계가 맞다.

D1-4 작은 서비스의 요구사항과 설계

구현 요청
미니 교육 운영 포털의 요구사항을 정리해줘. 대상은 방문자와 로그인 회원이다. 필수 기능은 프로그램 목록, 신청, 본인 신청 조회·메모 수정·취소다.
Next.js App Router와 TypeScript, Supabase Auth·DB·RLS, Vercel을 사용한다. Git으로 변경을 기록하고 Docker는 로컬 Supabase 실험에 사용한다.
화면, 두 테이블의 데이터 모델, 사용자별 권한, 구현 순서와 완료 기준을 먼저 작성해줘. 결제·정원 동시성·실제 문자 발송은 이번 범위에 넣지 마.

검증 요청
계획의 각 기능에 사용자가 실행할 검증 한 가지를 붙여줘. 개인정보 없이 만들 수 있는 샘플 3개를 제안하고, 아직 결정하지 않은 요구사항을 구분해줘. 설계와 무관한 기능을 추가하지 마.

실습 절차
1. 공통 프로젝트인 미니 교육 운영 포털의 방문자·회원·운영자 역할을 정리한다.
2. 필수 기능을 프로그램 조회, 회원 신청, 내 신청 조회로 제한한다.
3. programs와 applications의 필요한 열을 적고 user_id의 의미를 설명한다.
4. 목록·내 신청·로그인·보안 화면을 종이에 그리거나 문서로 작성한다.
5. Codex에게 파일을 만들기 전에 계획과 구현 순서, 검증 방법을 요청한다.
6. 짝과 계획을 검토하고 민감한 작업은 MFA 단원에서 확장한다고 표시한다.

완료 확인
필수 기능이 세 묶음 이내로 정리된다.
각 화면이 읽고 쓰는 데이터와 권한이 연결된다.
완료 기준을 실제 행동으로 표현한다.

D2-1 Codex로 기존 앱 읽고 수정하기

구현 요청
현재 폴더의 과제 체크 앱을 읽고 현재 경로, Git 브랜치와 status, index.html의 역할, localStorage 키와 데이터 구조를 먼저 요약해줘. 이어 각 과제에 수정 버튼을 추가하고 기존 입력 폼에서 제목·과목·마감일을 수정·저장·취소할 수 있게 최소 변경해줘. 같은 id 항목만 변경하고 done·createdAt·과제 개수와 기존 저장 데이터는 유지해줘. 취소는 저장하지 않고 수정 버튼으로 초점을 돌려줘. 빈값·공백·길이·날짜 검증, textContent, 입력 label과 네이티브 버튼을 유지해줘. 수정 중인 항목을 삭제할 때 폼도 정리해줘. 변경 파일과 diff, 실제 검증 결과를 설명해줘. 서버·DB·외부 패키지·API는 추가하지 마. 아직 commit·push·PR·merge는 실행하지 마.

검증 요청
현재 과제 체크 앱의 diff를 검토하고 실제 실행한 검사와 미확인을 구분해줘. 전날 데이터가 유지되는지, 완료한 과제를 수정해도 id·done·createdAt·개수가 같은지, 취소 전후 저장 데이터가 같은지, 공백 수정 거절, 제목/과목 검색·대소문자·빈 결과·완료 필터 조합·검색 지우기, 수정 중 삭제/되돌리기, 추가·완료·새로고침·날짜 집계·저장 실패 안내를 확인해줘. 390px 화면과 키보드 초점도 확인해줘. 문제는 재현 절차와 최소 수정으로 보고해줘. 마지막으로 기능별 커밋 메시지와 PR 제목·본문 초안을 제안해줘. 현재 경로·브랜치·remote와 대상 파일을 다시 확인해주고 사용자 데이터·비밀값을 출력하지 마. 원격 push·PR 생성·merge·배포는 내가 해당 단계에서 실행할게.

실습 절차
1. 전날 앱과 오늘의 성공 기준 확인: 본인의 1일차 Pages 앱에서 가상 과제 2개를 만들고 하나를 완료한다. 현재 공개 URL과 수정 전 화면을 기록한다. 오늘은 수정·검색을 추가하고 커밋 2개와 PR로 변경 이유를 남긴다.
2. 내 저장소 clone → 같은 폴더를 Codex에서 열기: YOUR_ID를 본인 GitHub 아이디로 바꾸고 아래 명령을 한 줄씩 실행한다. 이미 clone했다면 해당 폴더에서 status·remote만 확인한다. Codex 프로젝트에 그 폴더를 연결하고 index.html과 .git이 있는 저장소를 기본 작업 폴더로 선택한다. Codex에 현재 실행 환경을 확인시켜 로컬 미리보기를 연다.
3. 깨끗한 main → 기능 브랜치 만들기: status에 미커밋 변경이 없고 기본 브랜치가 main인 것을 확인한 뒤 아래 명령을 실행한다. 기본 브랜치가 다르면 실제 이름으로 바꾼다. feature/edit-tasks가 이미 있으면 기존 작업을 확인하고 새 이름을 정한다.
4. Codex로 과제 수정 구현 → 첫 커밋: 아래 수정 요청을 Codex에 입력한다. 수정 버튼→기존 값 불러오기→변경 저장→취소를 직접 실행한다. 완료한 과제를 수정해도 완료 상태와 개수가 같은지 확인한다. 통과한 뒤 diff를 읽고 index.html만 stage·commit한다.

완료 확인
수정 후 id·완료·생성시각·개수가 유지된다.
취소와 공백 입력을 확인한다.
첫 기능 커밋을 만들고 diff를 설명한다.

D2-2 과제 검색과 기존 기능 검증

구현 요청
현재 수정 기능과 기존 추가·완료·삭제/되돌리기·저장·날짜 집계를 보존하고 제목·과목 부분 검색을 추가해줘. 앞뒤 공백을 제거하고 대소문자를 무시하며 전체/미완료/완료 필터와 함께 적용해줘. 검색어가 비면 기존 필터 목록, 결과가 없으면 안내, 검색 지우기 버튼과 현재 표시 개수를 제공해줘. 검색은 과제를 삭제하거나 전체 집계·저장 데이터를 바꾸지 않는 화면 조건이야. label이 연결된 네이티브 search 입력을 쓰고 검색 조건은 저장하지 마. 추가·수정 저장 뒤 검색과 완료 필터를 초기화해 결과를 보여줘. 모바일 가로 넘침과 키보드 조작을 확인해줘. 변경 파일·diff·검증 결과를 보여주되 commit·push·PR·merge는 아직 하지 마.

검증 요청
현재 과제 체크 앱의 diff를 검토하고 실제 실행한 검사와 미확인을 구분해줘. 전날 데이터가 유지되는지, 완료한 과제를 수정해도 id·done·createdAt·개수가 같은지, 취소 전후 저장 데이터가 같은지, 공백 수정 거절, 제목/과목 검색·대소문자·빈 결과·완료 필터 조합·검색 지우기, 수정 중 삭제/되돌리기, 추가·완료·새로고침·날짜 집계·저장 실패 안내를 확인해줘. 390px 화면과 키보드 초점도 확인해줘. 문제는 재현 절차와 최소 수정으로 보고해줘. 마지막으로 기능별 커밋 메시지와 PR 제목·본문 초안을 제안해줘. 현재 경로·브랜치·remote와 대상 파일을 다시 확인해주고 사용자 데이터·비밀값을 출력하지 마. 원격 push·PR 생성·merge·배포는 내가 해당 단계에서 실행할게.

실습 절차
1. 제목·과목 검색 구현 → 두 번째 커밋: 검색 요청을 Codex에 입력한다. 제목 일부와 과목을 각각 검색하고 영어 대소문자와 공백, 결과 없음, 완료 필터를 함께 시험한다. 검색 지우기 뒤 정상 목록이 돌아오는지 확인한 뒤 두 번째 commit을 만든다.
2. 회귀 검사와 변경 파일 검토: 검증 요청과 아래 체크표로 확인한다. 새로고침 뒤 수정 내용과 완료 상태를 확인한다. 같은 제목을 새로 추가한 것이 아닌지, 취소가 저장하지 않는지 검사한다. 수정 중 삭제와 되돌리기, 빈 검색, 휴대폰 화면도 확인한다.

완료 확인
제목·과목 검색과 완료 필터가 함께 작동한다.
검색·취소가 저장 데이터를 바꾸지 않는다.
회귀 검사표와 두 번째 커밋을 남긴다.

D2-3 Git·GitHub PR과 Pages 재배포

구현 요청
현재 과제 체크 앱의 diff를 검토하고 실제 실행한 검사와 미확인을 구분해줘. 전날 데이터가 유지되는지, 완료한 과제를 수정해도 id·done·createdAt·개수가 같은지, 취소 전후 저장 데이터가 같은지, 공백 수정 거절, 제목/과목 검색·대소문자·빈 결과·완료 필터 조합·검색 지우기, 수정 중 삭제/되돌리기, 추가·완료·새로고침·날짜 집계·저장 실패 안내를 확인해줘. 390px 화면과 키보드 초점도 확인해줘. 문제는 재현 절차와 최소 수정으로 보고해줘. 마지막으로 기능별 커밋 메시지와 PR 제목·본문 초안을 제안해줘. 현재 경로·브랜치·remote와 대상 파일을 다시 확인해주고 사용자 데이터·비밀값을 출력하지 마. 원격 push·PR 생성·merge·배포는 내가 해당 단계에서 실행할게.

검증 요청
현재 과제 체크 앱의 diff를 검토하고 실제 실행한 검사와 미확인을 구분해줘. 전날 데이터가 유지되는지, 완료한 과제를 수정해도 id·done·createdAt·개수가 같은지, 취소 전후 저장 데이터가 같은지, 공백 수정 거절, 제목/과목 검색·대소문자·빈 결과·완료 필터 조합·검색 지우기, 수정 중 삭제/되돌리기, 추가·완료·새로고침·날짜 집계·저장 실패 안내를 확인해줘. 390px 화면과 키보드 초점도 확인해줘. 문제는 재현 절차와 최소 수정으로 보고해줘. 마지막으로 기능별 커밋 메시지와 PR 제목·본문 초안을 제안해줘. 현재 경로·브랜치·remote와 대상 파일을 다시 확인해주고 사용자 데이터·비밀값을 출력하지 마. 원격 push·PR 생성·merge·배포는 내가 해당 단계에서 실행할게.

실습 절차
1. push → 내 저장소의 Pull Request 만들기: 대상 remote와 브랜치를 확인한 뒤 push한다. 본인 GitHub 저장소의 Pull requests→New pull request에서 base:main, compare:feature/edit-tasks를 선택한다. 제목과 아래 PR 본문 예시를 채우고 Files changed에서 변경을 읽은 뒤 PR을 만든다.
2. 짝 검토 → 병합 → Pages 재배포 확인: 짝이 PR의 변경과 검사 결과를 읽고 개선점을 한 가지 적는다. 본인 PR을 Merge pull request로 병합한다. Settings→Pages/Actions에서 배포 완료와 사용한 커밋을 확인하고 본인 github.io 주소에서 수정·검색·새로고침을 다시 검사한다. 로컬 main을 아래 명령으로 갱신한다.

완료 확인
내 저장소에 커밋2개를 포함한 PR을 만든다.
병합과 Pages 배포 상태를 구분한다.
공개 URL에서 실제 기능을 검사한다.

D2-4 Docker와 로컬 Supabase

구현 요청
Docker와 Supabase CLI의 현재 상태를 읽기 전용으로 확인해줘. 기존 다른 프로젝트의 컨테이너와 포트를 보존해줘.
이 새 수업용 폴더에만 로컬 Supabase를 초기화하고 실행할 명령을 --help 기준으로 설명해줘. 다른 프로젝트와 포트가 겹치면 이 프로젝트 설정만 바꿔줘.
앱에 로컬 URL과 공개 가능한 키를 연결하는 방법을 알려주되 비밀값은 출력하지 마. 원격 DB 연결·초기화·컨테이너 전체 정리는 하지 마.

검증 요청
실행 실패의 원인을 Docker 엔진, 포트 충돌, 이미지 다운로드, 프로젝트 설정 순서로 확인해줘. 대상 프로젝트의 컨테이너와 로그만 읽고 원인을 설명해줘. docker system prune이나 다른 프로젝트 삭제로 해결하지 마.

실습 절차
1. Docker Desktop을 실행하고 docker info로 엔진 연결을 확인한다.
2. 새 수업용 프로젝트에서 Supabase CLI의 --help와 버전을 확인한다.
3. 프로젝트를 초기화하고 supabase start로 로컬 서비스를 실행한다.
4. CLI가 안내한 로컬 URL과 Studio·메일 확인 도구 주소를 기록한다. 키는 공개 화면에 출력하지 않는다.
5. 수업용 환경 파일에 로컬 접속 설정을 입력하고 앱과 연결한다.
6. docker ps로 컨테이너를 확인하고 한 서비스의 로그를 읽는다. 종료·재시작 후 설정과 데이터를 구분한다.

완료 확인
수업용 로컬 프로젝트를 식별한다.
앱과 로컬 DB의 연결을 확인한다.
대상 서비스의 시작·로그·종료 방법을 설명한다.

D3-1 과제 앱의 Supabase DB·RLS 설계

구현 요청
2일차 과제 체크 앱과 현재 Git 브랜치·diff를 먼저 읽어줘. 기존 HTML/CSS·수정·검색·완료·집계를 유지하면서 Vite와 @supabase/supabase-js로 구성해줘. 현재 공식 문서와 설치 버전의 Node 요구를 확인하고 패키지 버전을 고정해 lockfile을 남겨줘. DB는 새 수업용 Supabase의 public.tasks에 id uuid, user_id(auth.users 참조), title(공백만 불가·60자), subject(20자), due(date), done, created_at을 둬. user_id·due 인덱스, RLS, authenticated의 최소 GRANT와 본인 user_id=auth.uid() 조건의 SELECT/INSERT/UPDATE/DELETE 정책을 SQL 파일로 만들어줘. UPDATE는 USING과 WITH CHECK를 모두 쓰고 소유자·id·생성시각은 수정하지 못하게 해줘. anon은 데이터에 접근하지 못하게 해줘. 공개 URL과 publishable key만 VITE_SUPABASE_URL/VITE_SUPABASE_PUBLISHABLE_KEY로 읽고 .env.example과 .gitignore를 만들어줘. secret/service_role·DB 비밀번호는 브라우저나 Git에 넣지 마. 기존 운영 DB에 SQL을 실행하지 말고 새 수업 프로젝트에서 적용할 SQL과 검증 절차를 보여줘.

검증 요청
수업용 Supabase에서 계정 A와 B를 만들어 실제 가입 메일·로그인을 확인해줘. A가 만든 과제를 새로고침과 다른 브라우저의 A 로그인으로 다시 읽고 수정·완료·삭제·되돌리기를 검사해줘. B에서는 A 행 조회0건, A id를 지정한 수정·삭제0건 또는 거절, user_id=A로 위조INSERT 거절, 소유자변경 거절을 Data API 요청으로 검사해줘. 미로그인 요청도 차단되는지 확인해줘. SQL Editor 관리자 조회를 RLS 통과 증거로 쓰지 마. 계정 전환 직후와 늦은 이전 요청 응답에서 A 목록이 B 화면에 나타나지 않는지, 네트워크 실패가 저장 성공으로 표시되지 않는지 확인해줘. 공개 Vercel URL의 배포commit·환경·가입복귀URL도 대조하고 결과를 통과/실패/미확인으로 기록해줘. 이메일·비밀번호·토큰은 보고서에 넣지 마.

실습 절차
1. 같은 앱의 저장 위치와 완료 기준 정하기: 전날 수정·검색 앱을 열고 “이 브라우저 저장”과 “계정별 DB 저장”의 차이를 적는다. 오늘의 완료 기준은 회원가입→이메일 확인→로그인→내 과제 저장→새로고침·다른 브라우저 재조회→다른 계정 차단→공개 Vercel URL이다.
2. 기능 브랜치와 Vite·공개 설정 준비: 본인 저장소의 깨끗한 main을 최신화하고 feature/supabase-tasks를 만든다. Codex에 요청1을 입력해 기존 화면을 Vite 프로젝트로 구성한다. ZIP solution을 참고하면 index.html/styles.css/src/·package.json·lockfile·sql/을 저장소 루트에 둔다. .env.example을 .env.local로 복사해 본인 수업 프로젝트 URL과 publishable key를 입력하고 npm ci, npm run dev를 실행한다.
3. 새 수업용 DB에 tasks·GRANT·RLS 적용: 자신의 새 수업용 Supabase 프로젝트에서 SQL Editor를 열어 sql/setup.sql 전체를 한 번 실행한다. 기존 tasks가 있으면 구조와 정책부터 비교하고 임의로 삭제하지 않는다. sql/inspect.sql로 RLS·네 정책·authenticated의 접근 권한과 편집 가능한 열을 확인한다. Data API의 public 노출과 테이블 권한을 함께 확인한다.

완료 확인
필수 범위는 이메일 로그인과 회원별 tasks CRUD다. Google·Kakao·Naver·MFA는 4일차에서 이어간다. 브라우저의 전날 목록은 자동 업로드하지 않으며 가상 과제를 새로 만든다.
Node는 설치 버전의 요구사항을 만족해야 한다(제공 예제는22.12이상). .env.local은 Git에서 무시된다. VITE_ 값은 브라우저에 포함되는 공개 설정이며 secret/service_role을 숨겨주는 기능이 아니다.
tasks.user_id는 auth.users.id와 연결된다. SELECT/DELETE는 소유자 USING, INSERT는 WITH CHECK, UPDATE는 USING+WITH CHECK다. anon에는 권한이 없다. 명시적 GRANT와 RLS는 각각 “테이블 접근”과 “행 접근”을 담당한다.

D3-2 과제 앱 회원가입·이메일 확인·로그인

구현 요청
현재 앱에 Supabase 이메일 회원가입·확인 대기·로그인·로그아웃과 회원별 과제 CRUD를 연결해줘. 가입 응답에서 session이 없으면 메일 확인 대기로 표시하고 로그인 성공과 구분해줘. 단일 브라우저 앱은 PKCE·현재 루트 복귀 주소·SDK 세션 복원을 일관되게 사용하고 허용 Redirect URL을 안내해줘. 가입 new-password/로그인 current-password, label·POST form·비밀번호 표시·12자 가입 규칙·중복 제출 방지를 넣어줘. 비밀번호·토큰·계정 존재 여부를 로그나 오류에 노출하지 마. 로그인 전 목록을 숨기고 계정 변경 즉시 이전 목록·편집·되돌리기를 지워줘. 이전 계정의 늦은 요청 응답도 무시해줘. DB select/insert/update/delete의 error와 반환 행을 확인한 뒤만 성공을 표시해줘. 수정은 동일 id·완료·생성시각을 보존하고 삭제 되돌리기도 DB에 반영해줘. 검색·완료 필터·날짜 집계를 유지하며 다른 기기 변경은 새로 불러오기로 조회해줘. localStorage의 전날 과제를 자동 업로드하지 말고 가상 DB 과제로 시작해줘. 네트워크 실패 때 미저장 입력을 유지해줘. 실제 실행 결과와 미실행을 구분해줘.

검증 요청
수업용 Supabase에서 계정 A와 B를 만들어 실제 가입 메일·로그인을 확인해줘. A가 만든 과제를 새로고침과 다른 브라우저의 A 로그인으로 다시 읽고 수정·완료·삭제·되돌리기를 검사해줘. B에서는 A 행 조회0건, A id를 지정한 수정·삭제0건 또는 거절, user_id=A로 위조INSERT 거절, 소유자변경 거절을 Data API 요청으로 검사해줘. 미로그인 요청도 차단되는지 확인해줘. SQL Editor 관리자 조회를 RLS 통과 증거로 쓰지 마. 계정 전환 직후와 늦은 이전 요청 응답에서 A 목록이 B 화면에 나타나지 않는지, 네트워크 실패가 저장 성공으로 표시되지 않는지 확인해줘. 공개 Vercel URL의 배포commit·환경·가입복귀URL도 대조하고 결과를 통과/실패/미확인으로 기록해줘. 이메일·비밀번호·토큰은 보고서에 넣지 마.

실습 절차
1. 회원가입·메일 확인·로그인·로그아웃 연결: 요청2를 Codex에 입력하고 Authentication의 Email 가입과 Confirm email을 확인한다. 비밀번호 최소12자 정책을 앱과 서버에서 맞춘다. URL Configuration의 Site URL과 Redirect URLs에 실제 로컬 루트(http://localhost:5173/ 등)를 설정한다. 수신 가능한 수업 계정 A로 가입하고 메일 확인 뒤 로그인·새로고침·로그아웃을 각각 시험한다.

완료 확인
가입 요청 성공과 메일 도착·확인·로그인 성공을 따로 기록한다. PKCE 확인 링크는 요청한 같은 브라우저에서 연다. 다른 브라우저에서 확인했다면 이메일 확인 완료 후 원래 앱에서 이메일/비밀번호 로그인을 다시 시도한다.

D3-3 회원별 과제 CRUD와 A/B 권한 검증

구현 요청
현재 앱에 Supabase 이메일 회원가입·확인 대기·로그인·로그아웃과 회원별 과제 CRUD를 연결해줘. 가입 응답에서 session이 없으면 메일 확인 대기로 표시하고 로그인 성공과 구분해줘. 단일 브라우저 앱은 PKCE·현재 루트 복귀 주소·SDK 세션 복원을 일관되게 사용하고 허용 Redirect URL을 안내해줘. 가입 new-password/로그인 current-password, label·POST form·비밀번호 표시·12자 가입 규칙·중복 제출 방지를 넣어줘. 비밀번호·토큰·계정 존재 여부를 로그나 오류에 노출하지 마. 로그인 전 목록을 숨기고 계정 변경 즉시 이전 목록·편집·되돌리기를 지워줘. 이전 계정의 늦은 요청 응답도 무시해줘. DB select/insert/update/delete의 error와 반환 행을 확인한 뒤만 성공을 표시해줘. 수정은 동일 id·완료·생성시각을 보존하고 삭제 되돌리기도 DB에 반영해줘. 검색·완료 필터·날짜 집계를 유지하며 다른 기기 변경은 새로 불러오기로 조회해줘. localStorage의 전날 과제를 자동 업로드하지 말고 가상 DB 과제로 시작해줘. 네트워크 실패 때 미저장 입력을 유지해줘. 실제 실행 결과와 미실행을 구분해줘.

검증 요청
수업용 Supabase에서 계정 A와 B를 만들어 실제 가입 메일·로그인을 확인해줘. A가 만든 과제를 새로고침과 다른 브라우저의 A 로그인으로 다시 읽고 수정·완료·삭제·되돌리기를 검사해줘. B에서는 A 행 조회0건, A id를 지정한 수정·삭제0건 또는 거절, user_id=A로 위조INSERT 거절, 소유자변경 거절을 Data API 요청으로 검사해줘. 미로그인 요청도 차단되는지 확인해줘. SQL Editor 관리자 조회를 RLS 통과 증거로 쓰지 마. 계정 전환 직후와 늦은 이전 요청 응답에서 A 목록이 B 화면에 나타나지 않는지, 네트워크 실패가 저장 성공으로 표시되지 않는지 확인해줘. 공개 Vercel URL의 배포commit·환경·가입복귀URL도 대조하고 결과를 통과/실패/미확인으로 기록해줘. 이메일·비밀번호·토큰은 보고서에 넣지 마.

실습 절차
1. 내 과제 CRUD를 DB에 저장하고 다시 읽기: A로 로그인해 가상 과제 두 개를 DB에 추가한다. 완료 상태를 바꾸고 제목·과목·마감일을 수정한다. 새로고침과 새로 불러오기로 DB 값을 재조회하고 삭제/되돌리기도 확인한다. 제목·과목 검색과 완료 필터·오늘/기한 지남 집계가 현재 계정 목록에서 작동하는지 확인한다.
2. A·B 계정과 미로그인으로 권한 검증: 브라우저 일반 창의 A와 별도 프로필/다른 브라우저의 B로 각각 로그인한다. A의 과제 id를 가상 테스트 기록에만 적고, B의 Data API로 A id 조회·수정·삭제와 user_id 위조 INSERT를 검사한다. 로그아웃 직후 목록과 폼이 비워지는지도 확인한다. 상세 검증표의 실제 결과를 기록한다.

완료 확인
insert/update/delete의 error와 반환 행을 확인해야 성공이다. 수정 후 id·done·created_at과 개수는 유지된다. 다른 기기에서 같은 계정으로 로그인한 뒤 다시 조회하면 같은 DB 목록을 읽는다. 이 예제의 새로 불러오기는 Realtime 자동 동기화가 아니다.
A는 자기 행만 CRUD, B의 A 조회는0건, 수정·삭제는0건 또는 오류, 위조 INSERT·소유자 변경은거절되어야 한다. 미로그인도 데이터에 접근하지 못한다. 화면이 숨겨졌다는 이유만으로 RLS 통과라 하지 않는다.

D3-4 과제 앱 Vercel 배포와 공개 검증

구현 요청
이 과제 앱을 학생 본인 GitHub 저장소에서 Vercel로 배포할 절차를 준비해줘. 현재 경로·브랜치·remote, package.json·lockfile·build를 확인해줘. Vercel Root Directory는 실제 앱 위치, Framework는 Vite, Build는 npm run build, Output은 dist로 맞추고 두 VITE 공개 변수의 Production/필요한 Preview 범위를 설명해줘. .env.local·키·토큰은 commit하지 마. Supabase Site URL은 실제 고정 Production 루트, Redirect URLs는 로컬5173/와 정확한 공개 루트로 맞추고 환경값 변경 뒤 재빌드하도록 안내해줘. PR diff·검증표·병합·배포 SHA를 남겨줘. 가입 요청/메일 도착/확인/로그인/DB 저장/재조회/다른 브라우저 동일계정 복원/A·B 분리/미로그인 차단을 따로 검사해줘. RLS 실패를 UI 숨김·RLS 해제·service_role 노출로 우회하지 마. 원격 변경과 SQL 적용은 수업 단계에서 대상 프로젝트를 확인한 뒤 실행하게 해줘.

검증 요청
수업용 Supabase에서 계정 A와 B를 만들어 실제 가입 메일·로그인을 확인해줘. A가 만든 과제를 새로고침과 다른 브라우저의 A 로그인으로 다시 읽고 수정·완료·삭제·되돌리기를 검사해줘. B에서는 A 행 조회0건, A id를 지정한 수정·삭제0건 또는 거절, user_id=A로 위조INSERT 거절, 소유자변경 거절을 Data API 요청으로 검사해줘. 미로그인 요청도 차단되는지 확인해줘. SQL Editor 관리자 조회를 RLS 통과 증거로 쓰지 마. 계정 전환 직후와 늦은 이전 요청 응답에서 A 목록이 B 화면에 나타나지 않는지, 네트워크 실패가 저장 성공으로 표시되지 않는지 확인해줘. 공개 Vercel URL의 배포commit·환경·가입복귀URL도 대조하고 결과를 통과/실패/미확인으로 기록해줘. 이메일·비밀번호·토큰은 보고서에 넣지 마.

실습 절차
1. Git PR → Vercel Vite 배포: 아래처럼 소스·SQL·lockfile·환경 예시만 stage하고 diff를 검토해 commit·push한다. 본인 저장소의 main을 대상으로 PR을 만들고 검토·병합한다. Vercel Add New Project→본인 GitHub 저장소 Import에서 앱이 있는 Root Directory, Vite, npm run build, dist를 확인한다. 두 VITE 공개 변수를 Production과 필요한 Preview에 입력하고 Deploy한다.
2. 공개 URL·인증 복귀·DB 재조회 최종 확인: Vercel의 실제 Production URL을 Supabase Site URL로 넣고 Redirect URLs에 정확한 공개 루트와 필요한 로컬/Preview 루트를 허용한다. 설정 변경 뒤 새 배포를 만들고 가입메일이 공개 앱으로 돌아오는지 확인한다. 공개 앱에서 추가·수정·검색·완료·로그아웃·새로고침·다른 브라우저 A 재조회·B 차단을 다시 검사한다.

완료 확인
solution 안 파일을 저장소 루트에 넣었다면 Root Directory는 루트다. solution 폴더 자체를 저장소에 넣었다면 실제 경로를 지정한다. Vercel이 Next.js 또는 public/을 빌드하도록 설정되어 있으면 고친다. DB 비밀번호나 service_role은 입력하지 않는다.
제출: PR URL·main 커밋·배포 커밋·Vercel URL·Supabase 프로젝트 식별자(키 제외)·메일/로그인/DB/권한 체크표. READY는 빌드·배포 상태이며 실제 가입·저장 통과와 다르다. 대기나 미실행은 미확인으로 기록한다.

D4-1 과제 앱 Google 로그인과 callback

구현 요청
3일차 Vite·Supabase 과제 앱과 현재 Git 상태를 먼저 읽어줘. 기존 이메일 로그인·DB CRUD·수정·검색·소유자 RLS를 보존하고 Google·Kakao SNS 로그인 버튼을 추가해줘. signInWithOAuth의 provider는 google/kakao, redirectTo는 현재 앱의 고정 루트로 연결하고 기존 PKCE 클라이언트를 사용해줘. 제공자 개발자센터의 callback은 Supabase가 표시한 Callback URL, Supabase Redirect URLs는 앱 복귀 주소라는 차이를 설정표로 만들어줘. Client Secret·Admin 키는 앱이나 VITE_에 넣지 말고 Supabase 제공자 설정에만 보관해줘. 취소·동의 누락·메일 없는 SNS 계정·중복 클릭·허용되지 않은 복귀 주소를 처리해줘. 에러 설명·코드·토큰을 URL에서 그대로 화면/로그에 출력하지 마. 사용자 소유권은 Supabase user.id로 유지하고 이메일이 같다는 이유로 과제나 역할을 클라이언트에서 옮기지 마. 최초 가입·취소·재로그인과 기존 사용자 identity 연결 조건을 확인할 체크표를 줘. 실제 제공자 계정으로 실행하지 않은 항목은 미확인으로 써줘.

검증 요청
수업용 앱의 Google·Kakao·Naver 각각을 최초 가입/취소/재로그인/필요 동의 누락으로 검사해줘. Supabase user.id와 identities를 비교하고 같은 이메일만으로 기존 과제/역할을 옮기지 않는지 확인해줘. 네이버 어댑터는 고정 upstream·Bearer 검증·응답 오류·sub 매핑·이메일 합성 없음·no-store를 검사해줘. MFA는 등록 전→등록만 완료→잘못된 코드→검증 성공→로그아웃/재로그인→추가 인증을 확인해줘. DB Data API에서 본인 AAL1 조회0/쓰기거절, 본인 AAL2 CRUD 성공, B의 A 과제 접근 차단을 실제 세션으로 검사해줘. SQL Editor 관리자 결과나 버튼 숨김만으로 통과라 하지 마. QR·비밀키·인증코드·토큰은 보고서/스크린샷에 넣지 마. 실제 SNS 계정·배포·설정과 모의/로컬 검증을 따로 기록하고 공개 Vercel URL에서의 동작은 미실행이면 미확인으로 남겨줘.

실습 절차
1. 전날 앱·세션·callback 왕복 정리: 본인3일차 앱에서 이메일 로그인과 과제 저장을 확인한다. 깨끗한 main에서 feature/social-mfa 브랜치를 만든다. 앱→제공자→Supabase callback→앱 루트→세션→내 과제 순서를 그린다. starter는 전날 코드, checkpoint-sns는 SNS 중간 코드, solution은 MFA까지 포함한 완성 코드다.
2. Google Cloud와 Google 로그인 연결: Google Cloud의 수업 프로젝트에서 Google Auth Platform의 Branding·Audience·Data Access와 테스트 대상 사용자를 확인한다. 웹 OAuth Client의 Authorized redirect URI에 Supabase Google 설정의 Callback URL을 넣는다. Client ID·Secret은 Supabase Google provider에 등록하고 요청1로 버튼을 연결한다.

완료 확인
새 수업용 개발자 앱과 테스트 사용자를 준비한다. 제공자 callback은 Supabase 화면에서 복사하고, 앱의 복귀 루트는 Supabase Redirect URLs에 등록한다. 두 주소를 서로 바꾸지 않는다.
기본 로그인에 필요한 정보만 요청한다. 첫 로그인·동의 취소·재로그인을 확인하고 Supabase user.id/identities와 기존 과제를 비교한다. Google 계정의2단계 인증이 Supabase AAL2를 자동 의미하지는 않는다.

D4-2 과제 앱 Kakao 로그인과 계정 연결

구현 요청
3일차 Vite·Supabase 과제 앱과 현재 Git 상태를 먼저 읽어줘. 기존 이메일 로그인·DB CRUD·수정·검색·소유자 RLS를 보존하고 Google·Kakao SNS 로그인 버튼을 추가해줘. signInWithOAuth의 provider는 google/kakao, redirectTo는 현재 앱의 고정 루트로 연결하고 기존 PKCE 클라이언트를 사용해줘. 제공자 개발자센터의 callback은 Supabase가 표시한 Callback URL, Supabase Redirect URLs는 앱 복귀 주소라는 차이를 설정표로 만들어줘. Client Secret·Admin 키는 앱이나 VITE_에 넣지 말고 Supabase 제공자 설정에만 보관해줘. 취소·동의 누락·메일 없는 SNS 계정·중복 클릭·허용되지 않은 복귀 주소를 처리해줘. 에러 설명·코드·토큰을 URL에서 그대로 화면/로그에 출력하지 마. 사용자 소유권은 Supabase user.id로 유지하고 이메일이 같다는 이유로 과제나 역할을 클라이언트에서 옮기지 마. 최초 가입·취소·재로그인과 기존 사용자 identity 연결 조건을 확인할 체크표를 줘. 실제 제공자 계정으로 실행하지 않은 항목은 미확인으로 써줘.

검증 요청
수업용 앱의 Google·Kakao·Naver 각각을 최초 가입/취소/재로그인/필요 동의 누락으로 검사해줘. Supabase user.id와 identities를 비교하고 같은 이메일만으로 기존 과제/역할을 옮기지 않는지 확인해줘. 네이버 어댑터는 고정 upstream·Bearer 검증·응답 오류·sub 매핑·이메일 합성 없음·no-store를 검사해줘. MFA는 등록 전→등록만 완료→잘못된 코드→검증 성공→로그아웃/재로그인→추가 인증을 확인해줘. DB Data API에서 본인 AAL1 조회0/쓰기거절, 본인 AAL2 CRUD 성공, B의 A 과제 접근 차단을 실제 세션으로 검사해줘. SQL Editor 관리자 결과나 버튼 숨김만으로 통과라 하지 마. QR·비밀키·인증코드·토큰은 보고서/스크린샷에 넣지 마. 실제 SNS 계정·배포·설정과 모의/로컬 검증을 따로 기록하고 공개 Vercel URL에서의 동작은 미실행이면 미확인으로 남겨줘.

실습 절차
1. Kakao Developers와 카카오 로그인 설정: Kakao Developers의 수업 앱에서 웹 서비스 정보와 카카오 로그인 활성화를 확인한다. App Settings→App→Platform Key에서 사용할 REST API 키와 Kakao Login Client Secret을 확인·활성화하고, 해당 키의 Kakao Login Redirect URI에 Supabase callback을 등록한다. Supabase Kakao provider에 두 값을 넣고 버튼을 연결한다.
2. 이메일 누락·취소·기존 회원 연결 검사: 카카오 동의 화면을 취소하거나 선택 정보 제공을 거절해 본다. 이메일 정보가 없을 때도 필요한 서비스라면 Supabase Kakao 설정의 Allow users without an email을 확인한다. 이 수업 앱은 연락 이메일 대신 user.id로 과제 소유권을 관리한다. 이메일 계정과 SNS의 identity 연결 여부를 실제 계정에서 확인한다.

완료 확인
REST API 키는 client_id, Kakao Login Client Secret은 client_secret이다. JavaScript 키·Admin 키를 대신 쓰지 않는다. 제공자 동의 항목은 앱이 실제 필요한 범위로만 요청한다.
없는 이메일을 만들거나 확인되지 않은 메일을 verified로 표시하지 않는다. 이메일이 같아도 클라이언트에서 계정/역할/과제를 합치지 않는다. Supabase의 확인된 identity 연결 결과를 확인하며, 별개 uid라면 과제도 별개다.

D4-3 과제 앱 Naver Custom OAuth와 어댑터

구현 요청
과제 앱에 네이버 로그인을 custom:naver로 추가해줘. 네이버는 OIDC 자동검색이 아닌 Custom OAuth2로 구성하고 Authorization/Token/UserInfo 설정, 네이버 개발자 앱과 Supabase가 표시한 Callback URL의 관계를 설명해줘. 서버의 GET /api/naver-userinfo는 Supabase가 보낸 Bearer 토큰으로 고정 https://openapi.naver.com/v1/nid/me만 조회해줘. redirect 거절·timeout·응답 크기 제한·resultcode와 response.id 검증을 넣고 검증한 앱별 id만 sub로 반환해줘. 요청 body나 임의 JSON을 신뢰하거나 이메일을 합성·email_verified=true로 만들지 마. 수업 앱은 연락 이메일이 필요 없으므로 이 제공자만 email_optional=true로 구성하고 이메일 없는 SNS 계정도 표시해줘. Naver Client Secret은 Supabase 설정에 보관하고 어댑터는 토큰을 저장/로그/응답하지 않게 해줘. 이 서버 함수는 Vite 정적 미리보기에서 실행되지 않는다고 설명하고, 접근 가능한 고정 Vercel 수업 URL에 배포한 뒤 UserInfo URL에 등록하게 해줘. 실패·이메일 누락·취소·재로그인·같은 identity 검증을 포함하고 실계정 미실행과 모의 검사 결과를 구분해줘.

검증 요청
수업용 앱의 Google·Kakao·Naver 각각을 최초 가입/취소/재로그인/필요 동의 누락으로 검사해줘. Supabase user.id와 identities를 비교하고 같은 이메일만으로 기존 과제/역할을 옮기지 않는지 확인해줘. 네이버 어댑터는 고정 upstream·Bearer 검증·응답 오류·sub 매핑·이메일 합성 없음·no-store를 검사해줘. MFA는 등록 전→등록만 완료→잘못된 코드→검증 성공→로그아웃/재로그인→추가 인증을 확인해줘. DB Data API에서 본인 AAL1 조회0/쓰기거절, 본인 AAL2 CRUD 성공, B의 A 과제 접근 차단을 실제 세션으로 검사해줘. SQL Editor 관리자 결과나 버튼 숨김만으로 통과라 하지 마. QR·비밀키·인증코드·토큰은 보고서/스크린샷에 넣지 마. 실제 SNS 계정·배포·설정과 모의/로컬 검증을 따로 기록하고 공개 Vercel URL에서의 동작은 미실행이면 미확인으로 남겨줘.

실습 절차
1. 네이버 Custom OAuth와 서버 어댑터 배포: Naver Developers에 서비스 URL·callback·테스트 계정을 등록한다. Supabase Custom OAuth Provider를 Manual OAuth2·custom:naver로 구성하고 생성 화면의 Callback URL을 네이버에 복사한다. Authorization/Token URL과 Client ID·Secret을 설정한다. checkpoint-sns의 api/와 server/를 포함해 본인 고정 Vercel 수업 앱에 배포하고 UserInfo URL을 /api/naver-userinfo로 지정한다.
2. 네이버 id→sub와 이메일 없는 계정 검증: 요청2로 서버 어댑터의 고정 네이버 프로필 조회와 응답 검증을 구현한다. resultcode=00과 response.id를 확인한 뒤 sub만 반환한다. 이 제공자에 email_optional=true를 설정하고 custom:naver 버튼을 연결한다. 취소·토큰 오류·이메일 없음·재로그인에서 동일 네이버 identity가 유지되는지 확인한다.

완료 확인
UserInfo URL은 Supabase가 호출 가능한 HTTPS 서버 주소다. localhost·Vite 정적 서버·인증 보호된 Preview 주소를 그대로 넣지 않는다. GET 무토큰 호출이401이면 라우트 응답은 확인한 것이며 실제 네이버 로그인을 확인한 것은 아니다.
브라우저가 임의로 보낸 사용자 JSON을 믿지 않는다. 네이버 response.id는 해당 개발자 앱의 고유 식별자다. Client ID를 바꾸면 같은 개인이어도 연결 결과가 달라질 수 있다. 이메일 자동 확인을 전역으로 해제하지 않는다.

D4-4 과제 앱 TOTP MFA와 DB AAL2 강제

구현 요청
현재 앱에 Supabase TOTP MFA를 추가해줘. final 앱은 이메일/SNS 로그인 후 모두 AAL2를 확인해야 tasks를 열게 해줘. getAuthenticatorAssuranceLevel과 listFactors로 미등록·등록대기·기등록 AAL1·AAL2를 구분하고 enroll→QR/수동 키→challengeAndVerify→AAL2 확인을 연결해줘. 등록은 사용자 버튼으로 한 번만 시작하고 TOTP는 text/inputmode numeric/one-time-code/6자리 문자열로 받아 앞자리0을 보존해줘. SDK QR은 img로 표시하고 원본SVG를 innerHTML로 삽입하지 마. QR·비밀키·코드는 로그·URL·영구 저장·과제물에 남기지 않고 완료/취소/계정전환 시 메모리와 DOM에서 지워줘. 같은 세션의 중복 이벤트는 폼을 지우지 않게 하고 새 세션/계정 전환은 이전 요청을 무시해줘. 기존 user_id RLS/GRANT는 보존하고 tasks에 AS RESTRICTIVE FOR ALL TO authenticated, USING과 WITH CHECK로 aal2를 요구하는 SQL을 별도 파일로 만들어줘. 가상 계정의 TOTP 성공을 먼저 확인한 뒤 새 수업 DB에 적용하게 해줘. 틀린/지난 코드·재로그인 challenge·AAL1 차단/AAL2 본인 허용·타인 차단·미완료 등록 취소·분실 시 정식 복구 안내를 포함해줘. 운영 DB/기존 계정은 변경하지 마.

검증 요청
수업용 앱의 Google·Kakao·Naver 각각을 최초 가입/취소/재로그인/필요 동의 누락으로 검사해줘. Supabase user.id와 identities를 비교하고 같은 이메일만으로 기존 과제/역할을 옮기지 않는지 확인해줘. 네이버 어댑터는 고정 upstream·Bearer 검증·응답 오류·sub 매핑·이메일 합성 없음·no-store를 검사해줘. MFA는 등록 전→등록만 완료→잘못된 코드→검증 성공→로그아웃/재로그인→추가 인증을 확인해줘. DB Data API에서 본인 AAL1 조회0/쓰기거절, 본인 AAL2 CRUD 성공, B의 A 과제 접근 차단을 실제 세션으로 검사해줘. SQL Editor 관리자 결과나 버튼 숨김만으로 통과라 하지 마. QR·비밀키·인증코드·토큰은 보고서/스크린샷에 넣지 마. 실제 SNS 계정·배포·설정과 모의/로컬 검증을 따로 기록하고 공개 Vercel URL에서의 동작은 미실행이면 미확인으로 남겨줘.

실습 절차
1. 인증앱 등록→6자리 검증→AAL2: Supabase의 서비스 이용자 Auth MFA에서 TOTP 등록·검증을 활성화한다. 요청3으로 MFA 화면을 추가한다. 수업용 가상 계정으로 로그인해 등록 시작을 누르고 본인 인증앱에서 QR 또는 수동 키를 등록한다. 먼저 틀린 코드로 거절을 확인한 뒤 최신6자리 코드로 검증한다. 등록만 된 상태와 AAL2 세션을 구분한다.
2. DB AAL2 강제·재로그인·Vercel 최종 검증: 가상 계정의 AAL2 성공을 먼저 확인한 뒤 본인 수업 DB에서 sql/mfa-required.sql을 실행한다. 기존 user_id 소유자 정책을 유지한다. 로그아웃·재로그인하면 AAL1과 추가 인증 화면이 나오는지 검사한다. Data API에서 AAL1은 차단되고 AAL2 본인만 CRUD가 가능한지 A/B 계정으로 확인한다. Git PR 검토·병합과 Vercel 배포 후 같은 검사를 반복한다.

완료 확인
TOTP 입력은 문자열로 받아 앞자리0을 보존한다. 등록 성공 뒤 QR·키·입력 코드는 화면에서 제거한다. 코드를 자동 제출하지 않고 확인 버튼으로 검증한다. 이 설정은 Supabase 관리자 대시보드 MFA와 다르다.
AS RESTRICTIVE를 빠뜨리지 않는다. MFA 조건과 소유자 조건을 모두 만족해야 한다. 정책 적용 뒤 A/B 모두 MFA가 필요하다. 결과물에는 PR·배포commit·공개URL·제공자별 최초/취소/재로그인·MFA 전후 검사 결과를 기록하되 키·토큰·QR·코드는 넣지 않는다.

D5-1 과제 앱 본인 과제 CSV 내보내기

구현 요청
4일차 과제 체크 앱의 이메일/SNS 로그인, TOTP MFA, 소유자 RLS를 유지하고 CSV 내보내기를 추가해줘.
먼저 현재 파일·DB 정책·세션 전환 처리를 읽고 변경할 파일과 검증 기준을 제시해줘.
검색 화면의 배열 대신 로그인한 본인의 과제 전체를 DB에서 새로 조회해. getUser의 uid와 currentLevel=aal2를 확인하고 user_id 필터와 DB의 소유자 RLS AND restrictive AAL2를 모두 유지해.
500행씩 created_at,id 순으로 페이지 조회하고 exact count와 중복/누락을 검사해. 10,000행 초과·중간 오류·건수 변경은 부분 파일 없이 실패시켜. 동시 편집의 일관된 스냅샷을 보장한다고 설명하지 마.
열은 과제명/과목/마감일/완료만. UTF-8 BOM, CRLF, 모든 셀 큰따옴표, 내부 따옴표 두 번 처리. 수식 위험 시작문자(공백 뒤 = + - @와 전각 변형, 탭/개행)에는 quoted 셀 안에 탭을 앞붙여 스프레드시트 열람 위험을 줄이고 원본 DB는 바꾸지 마. 모든 앱/재저장에 안전하다거나 원본 복원 백업이라고 설명하지 마.
조회 중 progress와 aria-busy, 중복 실행 방지, 완료/오류 알림, Blob URL 해제를 구현해. 계정/토큰 변경·로그아웃은 AbortController와 epoch 확인으로 취소하고 늦은 A 응답을 B 화면에서 다운로드하지 않게 해.
빈 목록·한글·쉼표·따옴표·줄바꿈·수식 위험·1,000행 초과·중간 실패·계정 전환을 검증하고 npm run build 결과와 미확인 범위를 알려줘. 비밀값을 출력하거나 RLS/MFA를 끄지 마.

검증 요청
내 과제 앱의 최종 검증을 실행 가능한 범위에서 진행하고 checks.csv에 근거를 남겨줘.
가상 A/B 계정, 익명, AAL1과 AAL2로 직접 Data API를 검사해. 본인 CRUD 허용, 타인 조회/수정/삭제의 0행 또는 거절과 원본 불변, user_id 위조 추가/변경 거절을 확인해. UI가 숨겨졌다는 이유로 권한 검사를 통과시키지 마.
CSV가 본인 전체 DB 과제만 새 조회하는지, 검색 결과와 무관한지, 한글/인용/CRLF/위험 입력/빈 목록/1,203행을 처리하는지 검사해. AAL1 내보내기 거절, 페이지 실패와 계정 전환에서 다운로드 0회를 확인해.
비밀은 값 대신 존재 여부와 변수 이름만 보고해. git diff --cached, .gitignore, 번들에 secret/service_role/SMTP/OAuth secret이 없는지 점검해. publishable key는 공개 식별값이며 RLS를 대신하지 않음을 명시해.
빌드·PR·배포상태와 실제 URL 기능 검사를 분리해. 로컬 통과, 모의 통과, staging 실제, Production 실제, 미실행을 구분해 결과/시각/commit/검증자를 기록해. 실패·미확인 항목이 남으면 출시 완료라고 쓰지 마.

실습 절차
1. 전날 앱 복원과 두 환경 준비: 4일차 완성 앱이 있는 본인 clone 폴더를 연다. ZIP의 starter는 비교용이며 기존 .git과 .env.local을 덮어쓰지 않는다. 강사는 staging 브랜치와 수업용 staging/production Supabase 두 환경을 사전 준비한다. 학생은 main의 최신 이력에서 feature/csv-release를 만들고 로컬 가입·로그인·MFA·과제 CRUD부터 확인한다.
2. 본인 전체 과제를 CSV로 내보내기: 요청1을 Codex에 입력하고 변경 파일을 검토한다. solution의 csv-export.js·task-store.js·main.js·HTML/CSS와 비교한다. 추가 인증 후 내 과제 CSV 내보내기를 누른다. 검색 결과가 1건이어도 본인 전체 DB 과제가 들어가는지 확인한다. 내려받은 파일은 스프레드시트에서 텍스트로 가져오고 한글·열 이름·완료 상태를 확인한다.

완료 확인
미커밋 변경을 먼저 정리하고 잘못된 저장소에서 실행하지 않는다. 환경표에 URL·브랜치·DB 이름만 적는다. 두 환경은 같은 DB를 가리키지 않는다.
한글·쉼표·큰따옴표·줄바꿈·=1+2·전각＝1+2와 빈 목록을 검사한다. 위험한 자유 입력 앞의 탭은 열람용 변환이며 DB 원본은 유지된다. 토큰·이메일·uid가 CSV에 없다.

D5-2 과제 앱 계정별 권한·보안 최종 검증

구현 요청
내 과제 앱의 최종 검증을 실행 가능한 범위에서 진행하고 checks.csv에 근거를 남겨줘.
가상 A/B 계정, 익명, AAL1과 AAL2로 직접 Data API를 검사해. 본인 CRUD 허용, 타인 조회/수정/삭제의 0행 또는 거절과 원본 불변, user_id 위조 추가/변경 거절을 확인해. UI가 숨겨졌다는 이유로 권한 검사를 통과시키지 마.
CSV가 본인 전체 DB 과제만 새 조회하는지, 검색 결과와 무관한지, 한글/인용/CRLF/위험 입력/빈 목록/1,203행을 처리하는지 검사해. AAL1 내보내기 거절, 페이지 실패와 계정 전환에서 다운로드 0회를 확인해.
비밀은 값 대신 존재 여부와 변수 이름만 보고해. git diff --cached, .gitignore, 번들에 secret/service_role/SMTP/OAuth secret이 없는지 점검해. publishable key는 공개 식별값이며 RLS를 대신하지 않음을 명시해.
빌드·PR·배포상태와 실제 URL 기능 검사를 분리해. 로컬 통과, 모의 통과, staging 실제, Production 실제, 미실행을 구분해 결과/시각/commit/검증자를 기록해. 실패·미확인 항목이 남으면 출시 완료라고 쓰지 마.

검증 요청
내 과제 앱의 최종 검증을 실행 가능한 범위에서 진행하고 checks.csv에 근거를 남겨줘.
가상 A/B 계정, 익명, AAL1과 AAL2로 직접 Data API를 검사해. 본인 CRUD 허용, 타인 조회/수정/삭제의 0행 또는 거절과 원본 불변, user_id 위조 추가/변경 거절을 확인해. UI가 숨겨졌다는 이유로 권한 검사를 통과시키지 마.
CSV가 본인 전체 DB 과제만 새 조회하는지, 검색 결과와 무관한지, 한글/인용/CRLF/위험 입력/빈 목록/1,203행을 처리하는지 검사해. AAL1 내보내기 거절, 페이지 실패와 계정 전환에서 다운로드 0회를 확인해.
비밀은 값 대신 존재 여부와 변수 이름만 보고해. git diff --cached, .gitignore, 번들에 secret/service_role/SMTP/OAuth secret이 없는지 점검해. publishable key는 공개 식별값이며 RLS를 대신하지 않음을 명시해.
빌드·PR·배포상태와 실제 URL 기능 검사를 분리해. 로컬 통과, 모의 통과, staging 실제, Production 실제, 미실행을 구분해 결과/시각/commit/검증자를 기록해. 실패·미확인 항목이 남으면 출시 완료라고 쓰지 마.

실습 절차
1. A/B·익명·인증 강도별 권한 검사: 가상 A/B 계정을 서로 다른 브라우저 프로필에서 사용한다. 두 계정 모두 TOTP를 준비하고 본인 과제를 하나씩 만든다. A의 id를 검사에만 사용해 B의 실제 Data API로 조회·수정·삭제·user_id 위조를 시도한다. 로그아웃 뒤 재로그인한 AAL1과 추가 인증 후 AAL2를 비교한다. 아래 개발 서버 전용 검사 예시와 checks.csv에 실제 결과를 적는다.
2. 페이지 실패·계정 전환·비밀 노출 점검: 네트워크 차단 또는 모의 테스트로 CSV 조회 중간 실패를 재현한다. 다운로드가 한 번도 발생하지 않아야 한다. 조회 중 로그아웃/다른 계정으로 전환했을 때 이전 계정 파일이 생성되지 않는지도 검사한다. 로컬 가상 데이터1,203건으로 페이지 조회를 확인한다. .gitignore·git diff·빌드 산출물을 검사하고 실패/미확인 사유를 남긴다.

완료 확인
AAL2는 관리자 권한이 아니다. B의 A행 조회/수정/삭제는0행 또는 거절이며 A원본 불변. 익명은 거절. AAL1은 본인 조회0행·쓰기거절/0행이며 내보내기 불가. 소유권 위조 추가/변경은 거절.
부분 CSV·이전 계정 CSV·중복 다운로드0회. 예제 상한10,000건을 넘으면 명시 오류. 위 git ls-files 결과는 비어야 한다. 공개 URL/key 외 secret·service_role·SMTP·OAuth 비밀이 소스/번들에 없다.

D5-3 과제 앱 staging→main 최종 배포

구현 요청
현재 과제 앱을 staging→main 최종 릴리스할 수 있도록 점검해줘.
git status, remotes, 브랜치, PR base/head와 배포 설정을 먼저 읽어줘. git staging area와 staging 브랜치를 구분해 설명해줘.
feature/csv-release→staging PR에서 검토하고 staging 브랜치는 Preview로 빌드해. 강사가 준비한 별도 수업 staging Supabase와 production Supabase에 같은 검토된 스키마·소유자 RLS·restrictive AAL2가 있는지 먼저 읽기 전용으로 확인해줘. 기존 운영 DB를 초기화하거나 임의 변경하지 마.
VITE_SUPABASE_URL과 VITE_SUPABASE_PUBLISHABLE_KEY의 Preview/Production 매핑, 실제 build commit·URL·DB 프로젝트 이름을 비밀값 없이 기록해. SNS callback/Redirect URL·SMTP·Naver 서버 어댑터도 환경별로 확인해줘.
검증표에 실패나 미확인이 남으면 완료로 표시하지 마. 통과 후 staging→main PR을 준비하고 main Production 재빌드 후 운영 URL에서 로그인/MFA/CRUD/CSV/계정 분리를 다시 확인해.
Preview 검증이 Production의 같은 환경 검증이라고 단정하지 마. 실패 시 이전 앱 배포 복원과 DB 복구가 다름을 설명하고 릴리스 기록 양식을 채워줘. 강제 push·보호 해제·비밀 커밋 없이 진행해줘.

검증 요청
내 과제 앱의 최종 검증을 실행 가능한 범위에서 진행하고 checks.csv에 근거를 남겨줘.
가상 A/B 계정, 익명, AAL1과 AAL2로 직접 Data API를 검사해. 본인 CRUD 허용, 타인 조회/수정/삭제의 0행 또는 거절과 원본 불변, user_id 위조 추가/변경 거절을 확인해. UI가 숨겨졌다는 이유로 권한 검사를 통과시키지 마.
CSV가 본인 전체 DB 과제만 새 조회하는지, 검색 결과와 무관한지, 한글/인용/CRLF/위험 입력/빈 목록/1,203행을 처리하는지 검사해. AAL1 내보내기 거절, 페이지 실패와 계정 전환에서 다운로드 0회를 확인해.
비밀은 값 대신 존재 여부와 변수 이름만 보고해. git diff --cached, .gitignore, 번들에 secret/service_role/SMTP/OAuth secret이 없는지 점검해. publishable key는 공개 식별값이며 RLS를 대신하지 않음을 명시해.
빌드·PR·배포상태와 실제 URL 기능 검사를 분리해. 로컬 통과, 모의 통과, staging 실제, Production 실제, 미실행을 구분해 결과/시각/commit/검증자를 기록해. 실패·미확인 항목이 남으면 출시 완료라고 쓰지 마.

실습 절차
1. staging Preview와 DB·Auth 설정 대조: 환경표를 채운다. Vercel Production Branch=main을 확인하고 staging은 Preview로 연결한다. Preview의 VITE_ 두 값은 별도 staging Supabase, Production 값은 production Supabase로 지정한다. 각 새 수업 DB를 inspect.sql로 확인하고 검토한 setup.sql→mfa-required.sql을 한 번 적용한다. 환경별 Auth·메일·SNS callback/redirect·Naver 어댑터를 점검한다.
2. feature→staging 검토 후 main 최종 배포: 변경을 커밋·push하고 GitHub PR base=staging, compare=feature/csv-release를 만든다. 동료가 diff·비밀·검사표를 검토하고 병합한다. Vercel staging Preview의 READY와 commit을 확인한 뒤 실제 URL에서 검증한다. 모두 통과하면 PR base=main, compare=staging을 검토·병합한다. main의 새 Production 배포에서 같은 검사를 다시 하고 release-record.md를 완성한다.

완료 확인
환경 변수 변경 뒤 새 빌드를 만든다. 프런트엔드 URL/key 외 비밀값은 VITE_로 만들지 않는다. DB migration은 Vercel 코드 배포와 별도다. 기존 정책이 있으면 SQL을 무작정 재실행하지 않는다.
PR별 base/head·commit·실제 URL·DB 이름·검증자·결과가 남는다. Preview 통과와 Production 재빌드 통과를 별도로 기록한다. 실패하면 배포 완료 표시를 멈추고 원인을 수정한다.

D5-4 과제 앱 3분 발표와 인수인계

구현 요청
완성한 과제 체크 앱의 3분 시연과 인수인계 자료를 만들어줘.
1일차 문제/사용자와 웹 선택→2일차 Git·개선→3일차 DB·Auth→4일차 SNS·MFA→5일차 CSV·검증·배포를 연결해줘.
0:00~0:30 문제/대상, 0:30~1:30 실제 과제 CRUD·검색·CSV, 1:30~2:15 A/B 권한 검증 근거와 MFA, 2:15~2:45 staging/Production URL·commit, 2:45~3:00 한계/다음 기능으로 구성해줘.
실제 확인한 결과와 모의/미확인을 구분하고 비밀키·토큰·TOTP QR/코드·학생 개인정보를 화면/문서에 넣지 마. 발표용 가상 계정을 사용하고 MFA는 청중에게 비밀을 보이지 않게 사전 완료해.
README에는 실행/환경변수 이름/SQL 적용 순서/CSV 열람 범위/테스트 방법/배포·복구·미확인 항목을 적어줘. 동료 피드백 1건을 이슈로 남길 수 있는 양식과 100점 평가표도 포함해줘.

검증 요청
내 과제 앱의 최종 검증을 실행 가능한 범위에서 진행하고 checks.csv에 근거를 남겨줘.
가상 A/B 계정, 익명, AAL1과 AAL2로 직접 Data API를 검사해. 본인 CRUD 허용, 타인 조회/수정/삭제의 0행 또는 거절과 원본 불변, user_id 위조 추가/변경 거절을 확인해. UI가 숨겨졌다는 이유로 권한 검사를 통과시키지 마.
CSV가 본인 전체 DB 과제만 새 조회하는지, 검색 결과와 무관한지, 한글/인용/CRLF/위험 입력/빈 목록/1,203행을 처리하는지 검사해. AAL1 내보내기 거절, 페이지 실패와 계정 전환에서 다운로드 0회를 확인해.
비밀은 값 대신 존재 여부와 변수 이름만 보고해. git diff --cached, .gitignore, 번들에 secret/service_role/SMTP/OAuth secret이 없는지 점검해. publishable key는 공개 식별값이며 RLS를 대신하지 않음을 명시해.
빌드·PR·배포상태와 실제 URL 기능 검사를 분리해. 로컬 통과, 모의 통과, staging 실제, Production 실제, 미실행을 구분해 결과/시각/commit/검증자를 기록해. 실패·미확인 항목이 남으면 출시 완료라고 쓰지 마.

실습 절차
1. 3분 발표·짝 피드백: 요청3으로 발표 원고를 작성하고 발표용 가상 과제3개와 CSV를 준비한다. 청중에게 비밀이 보이지 않게 MFA를 사전 완료한다. 문제/사용자→핵심 동작→권한 검증 근거→두 배포 환경→한계 순서로3분 시연하고 짝에게 개선점1개를 받는다. 실SNS 연결 미완료는 미확인으로 설명한다.
2. 인수인계·릴리스 기록·다음 이슈: README에 실행/환경 변수 이름/SQL 순서/검사 방법/CSV 범위/배포·복구를 정리한다. checks.csv와 release-record.md를 채우고 peer-feedback.md의 개선점 하나를 GitHub 이슈로 남긴다. 최종 main commit·운영URL·발표 원고·가상 CSV·미확인 항목을 제출한다.

완료 확인
저장소/운영 링크·화면 시연·권한 근거·staging/Production commit·다음 개선1개가 있다. QR·OTP·토큰·실제 학생 정보가 발표 화면/CSV/캡처에 없다.
본인이 하지 않은 실SNS·운영 검증을 통과로 표시하지 않는다. 발표용 CSV도 본인 가상 자료만 제출한다. 미커밋 작업·열린 PR·실패 검사·복구 담당자가 분명하다.