Claude AI로 영어 단어 퀴즈 웹앱 만들기
Part 3: 로컬 단어 DB 구축과 새로운 시작
📚 시리즈 안내
← 이전 글: [Part 2] Gemini API 연동 시도와 404 오류의 늪
→ 다음 글: [Part 4] 오류 수정과 로그인 검증 완성
🌱 들어가며: 새로운 방향으로!
지난 2편에서 Gemini API의 404 오류와 씨름하다가, 결국 로컬 단어 DB로 방향을 전환하기로 결정했습니다. 사실 이 결정이 오히려 더 좋은 선택이었어요!
이번 편에서는 구글 시트에 단어은행을 만들고, 백엔드를 완전히 새로 작성하는 과정을 다룹니다. 또한 학년 선택을 3단계(초등/중등/고등)로 단순화하고, 로그인 검증 규칙을 추가하는 작업도 함께 진행합니다.
그리고 예상치 못한 권한 오류들도 만나게 되는데요, 하나씩 해결해 나가는 과정을 함께 보시죠!
📸

→ 구글 시트에 단어 데이터가 입력된 화면 캡처
📊 1단계: 단어은행 구글 시트 만들기
먼저 새로운 구글 시트를 만들어 단어 데이터베이스를 구축합니다. Claude가 제안한 구조는 매우 직관적이었어요.
단어은행 시트 구조
| 학년 | 영어단어 | 정답 | 오답1 | 오답2 | 오답3 |
| 초등 | cat | 고양이 | 강아지 | 토끼 | 햄스터 |
| 초등 | apple | 사과 | 바나나 | 오렌지 | 포도 |
| 중등 | adventure | 모험 | 여행 | 휴가 | 산책 |
| 고등 | phenomenon | 현상 | 사건 | 상황 | 조건 |
이 구조의 장점은 오답도 미리 정의해둔다는 점입니다. AI가 실시간으로 생성하는 것보다 교육적으로 더 적절한 오답을 선별할 수 있어요. 예를 들어 "cat"의 오답으로 "강아지, 토끼, 햄스터" 같은 동물 카테고리 단어를 넣으면, 학생들이 의미 있는 구별을 학습할 수 있습니다.
💡 Claude의 팁
각 학년당 최소 15~20개 이상의 단어를 입력하는 것을 추천합니다. 10문제 퀴즈에서 같은 단어가 반복되지 않으려면 충분한 단어 풀이 필요해요!
🎯 2단계: 학년 선택 단순화
기존에는 "초등저학년", "초등고학년", "중등", "고등" 4단계였는데, 이를 더 직관적인 3단계로 변경했습니다.
| 변경 전 (4단계) | 변경 후 (3단계) |
| • 초등저학년 • 초등고학년 • 중등 • 고등 |
• 초등 • 중등 • 고등 |
이렇게 단순화한 이유는:
1. 사용자 경험 개선: 선택지가 적을수록 빠르게 시작 가능
2. 데이터 관리 효율: 3개 카테고리로 단어 분류가 더 명확
3. 점수 체계 명확화: 초등 10점, 중등 30점, 고등 50점으로 구분
⚙️ 3단계: 백엔드 완전 재작성
Gemini API 코드를 모두 걷어내고, 구글 시트 기반의 새로운 백엔드를 작성했습니다.
핵심 변경 사항
// 기존: Gemini API 호출
const response = UrlFetchApp.fetch(GEMINI_URL, options);
// AI가 실시간으로 문제 생성 → 불안정
// 변경: 구글 시트에서 단어 가져오기
const words = getWordsFromSheet(grade);
// 안정적이고 빠른 응답
새로운 퀴즈 생성 로직
새로운 generateQuiz 함수의 동작 방식:
1. 단어은행 시트에서 선택한 학년의 단어만 필터링
2. 필터링된 단어 중 랜덤으로 10개 선택
3. 각 단어의 정답과 오답을 섞어서 4지선다 구성
4. 프론트엔드로 퀴즈 데이터 전송
📸

→ Apps Script 편집기에서 generateQuiz 함수가 보이는 화면 캡처
🔐 4단계: 로그인 검증 규칙 추가
보안과 데이터 일관성을 위해 로그인 입력값 검증 기능을 추가했습니다.
검증 규칙
| 항목 | 규칙 | 예시 |
| 아이디 | 영어(대소문자) + 숫자만 가능 | ✅ student123 ❌ 학생123 |
| 비밀번호 | 숫자만 가능 | ✅ 1234 ❌ pass1234 |
이중 검증 구조
Claude는 프론트엔드와 백엔드 양쪽 모두에서 검증하는 구조를 제안했습니다. 이유는:
• 프론트엔드 검증: 사용자에게 즉각적인 피드백 제공
• 백엔드 검증: API 직접 호출 시도도 차단 (보안)
// 정규식 검증 코드
const idRegex = /^[a-zA-Z0-9]+$/; // 아이디
const pwRegex = /^[0-9]+$/; // 비밀번호
🚫 5단계: 권한 오류와의 싸움
백엔드를 완성하고 테스트하려는데, 예상치 못한 오류들이 연달아 발생했습니다.
오류 1: HTML 파일을 찾을 수 없음
❌ 오류 메시지
Exception: index(이)라는 이름의 HTML 파일을 찾을 수 없습니다.
원인: Apps Script 프로젝트에 index.html 파일이 없거나, 파일 이름이 다른 경우
해결: Apps Script 편집기에서 + 버튼 → HTML 선택 → 이름을 "index"로 입력
오류 2: 구글 시트 접근 권한 없음
❌ 오류 메시지
You do not have permission to access the requested document
원인: 구글 시트가 Apps Script와 공유되지 않음
해결 방법:
1. 각 구글 시트 열기 (계정, 랭킹, 기록, 단어은행)
2. 우측 상단 "공유" 버튼 클릭
3. "링크가 있는 모든 사용자" 또는 본인 이메일 추가
4. 권한: "편집자"로 설정
📸 [스크린샷 3] 구글 시트 공유 설정 화면

→ 시트 공유 버튼을 클릭하고 편집자 권한을 설정하는 화면 캡처
오류 3: "이 앱은 확인되지 않았습니다" 경고
Apps Script를 처음 실행할 때 이 경고가 나타날 수 있습니다. 본인이 만든 스크립트이므로 안전하게 진행하면 됩니다.
1. "권한 검토" 버튼 클릭
2. Google 계정 선택
3. 좌측 하단 "고급" 클릭
4. "(프로젝트 이름)(으)로 이동" 클릭
5. "허용" 클릭
📸




→ "고급" 버튼이 보이는 화면
🧪 6단계: 테스트 함수로 검증하기
Claude는 각 기능별로 테스트 함수를 제공했습니다. 이를 통해 문제를 빠르게 진단할 수 있었어요.
| 함수명 | 용도 |
| testSheetConnection() | 4개 구글 시트 연결 상태 확인 |
| testWordBank() | 단어은행에서 데이터 가져오기 테스트 |
| testLogin() | 로그인 검증 로직 테스트 |
| testGenerateQuiz() | 퀴즈 생성 기능 테스트 |
✅ testSheetConnection 정상 결과
✅ 계정 시트: 계정
✅ 랭킹 시트: 랭킹
✅ 기록 시트: 기록
✅ 단어은행 시트: 단어은행
💡 이번 편에서 배운 점
• 유연한 방향 전환: API가 안 되면 대안을 찾으면 됩니다. 오히려 더 나은 결과가 나올 수도 있어요.
• 테스트 함수의 중요성: 각 기능별 테스트 함수가 있으면 문제 진단이 훨씬 빨라집니다.
• 권한 설정 확인: Google 서비스를 연동할 때는 공유/권한 설정을 꼭 확인하세요.
• 이중 검증 패턴: 프론트엔드 + 백엔드 양쪽에서 검증하면 보안과 UX 모두 챙길 수 있습니다.
📌 다음 편 예고
다음 편에서는 로그인이 안 되는 문제를 해결합니다. 계정 시트에 데이터가 있는데도 "일치하지 않음"이 뜨는 원인을 찾고, 랭킹 중복 문제도 함께 수정합니다.
➡️ 다음 글: [Part 4] 오류 수정과 로그인 검증 완성
📋 이번 편 요약
• 단어은행 구글 시트 생성 (학년|영어단어|정답|오답1|오답2|오답3)
• 학년 선택 4단계 → 3단계 (초등/중등/고등) 단순화
• 로그인 검증 규칙 추가 (아이디: 영어+숫자, 비밀번호: 숫자만)
• 권한 오류 해결 (HTML 파일, 시트 공유 설정)
• 테스트 함수로 각 기능 검증
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
방향을 바꾸는 것도 전진입니다! 🌱
이 글이 도움이 되셨다면 공감과 댓글 부탁드립니다! 🙏
'실전 프로젝트' 카테고리의 다른 글
| Claude AI로 영어 단어 퀴즈 웹 앱 만들기 [6편] (0) | 2026.01.01 |
|---|---|
| Claude AI로 영어 단어 퀴즈 웹 앱 만들기 [5편] (0) | 2025.12.22 |
| Claude AI로 영어 단어 퀴즈 웹 앱 만들기 [4편] (0) | 2025.12.20 |
| Claude AI로 영어 단어 퀴즈 웹 앱 만들기 [2편] (0) | 2025.12.20 |
| Claude AI로 영어 단어 퀴즈 웹 앱 만들기 [1편] (0) | 2025.12.19 |