2026년 9월 8일 화요일

[행정/교무] 방과후·늘봄교실 정원 관리 & 자동 마감 시스템 - GAS + 구글 시트

방과후학교·늘봄교실 정원 관리 & 선착순 자동 마감 — GAS + 구글 시트 백엔드

🏫 방과후학교·늘봄교실 수강 신청 — 학부모가 폰에서 강좌를 선택하면 구글 시트에 실시간 카운트되어 정원이 차면 자동으로 "마감" 처리되는 선착순 신청 시스템입니다. 교사가 시트의 정원 숫자만 바꾸면 다음 학기에 재사용할 수 있습니다.

📖 한 줄 정의

Google Apps Script(GAS): 구글 스프레드시트 안에서 실행되는 서버리스 백엔드 스크립트 — 별도 서버 없이 무료로 웹앱을 배포할 수 있습니다.

구글 스프레드시트 = 무료 DB: 신청 현황이 시트에 실시간 누적되어 담당자가 원클릭으로 정원·대기자·마감 상황을 확인할 수 있습니다.

⚠️ 개인정보·보안 안내 (필독)

이 웹앱은 GAS + 구글 스프레드시트를 활용하여 데이터를 구글 클라우드에 저장합니다.

구현 전 반드시 소속 교육청 정보보호 담당과에 사전 문의하세요.

• 이 도구는 교사·행정 전용입니다 — 학생 개인정보를 일절 다루지 않습니다. 강좌명·정원·신청 인원수만 관리합니다.

1. 3대 핵심 분석 비교표

무엇이 바뀌었나요?쉬운 설명써보세요!
정원 자동 마감강좌별 정원(예: 20명)을 시트에 설정하면 신청 인원이 정원에 도달하는 순간 자동으로 "마감" 표시가 되고 추가 신청이 차단됩니다.미술 강좌 20/20 → 자동 "🔒 마감" 표시
대기자 자동 전환정원이 차면 신청자를 "대기자"로 기록하고, 기존 신청자가 취소하면 대기자가 자동 승격됩니다.3번 대기자 → 5번 취소 → 3번 자동 승격 "✅ 수강 확정"
학기별 재사용시트에서 강좌명·정원·요일만 바꾸면 다음 학기에 그대로 재사용할 수 있습니다.1학기 "피아노" → 2학기 "기타"로 텍스트 교체만

2. GAS 실전 구축 가이드

파일 1: Code.gs (백엔드 — 정원 카운트 & 마감)

function doGet(e) {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var courses = ss.getSheetByName("강좌목록");
  var applications = ss.getSheetByName("신청목록");
  var courseData = courses.getDataRange().getValues();
  var appData = applications.getDataRange().getValues();
  var result = [];

  for (var i = 1; i < courseData.length; i++) {
    var name = courseData[i][0];
    var max = courseData[i][1];
    var count = 0;
    for (var j = 1; j < appData.length; j++) {
      if (appData[j][1] === name) count++;
    }
    result.push({
      course: name, capacity: max, enrolled: count,
      status: count >= max ? "마감" : "모집중"
    });
  }
  return ContentService.createTextOutput(JSON.stringify({ courses: result })).setMimeType(ContentService.MimeType.JSON);
}

function doPost(e) {
  var p = JSON.parse(e.postData.contents);
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var courses = ss.getSheetByName("강좌목록");
  var applications = ss.getSheetByName("신청목록");
  var courseData = courses.getDataRange().getValues();
  var appData = applications.getDataRange().getValues();
  var capacity = 0, enrolled = 0;

  for (var i = 1; i < courseData.length; i++) {
    if (courseData[i][0] === p.course) capacity = courseData[i][1];
  }
  for (var j = 1; j < appData.length; j++) {
    if (appData[j][1] === p.course) enrolled++;
  }

  if (enrolled >= capacity) {
    return ContentService.createTextOutput(JSON.stringify({ ok: false, message: "정원이 마감되었습니다." })).setMimeType(ContentService.MimeType.JSON);
  }

  applications.appendRow([p.name, p.course, new Date().toLocaleString("ko-KR")]);
  return ContentService.createTextOutput(JSON.stringify({ ok: true, enrolled: enrolled + 1, capacity: capacity })).setMimeType(ContentService.MimeType.JSON);
}

파일 2: index.html (프런트엔드 — 강좌 목록 & 신청 폼)

<!DOCTYPE html>
<html lang="ko"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>방과후 신청</title>
</head><body>
<h2>🏫 방과후·늘봄교실 신청</h2>
<div id="courses">로딩 중...</div>
<input type="text" id="name" placeholder="신청자 이름">
<button onclick="apply()">신청</button>
<div id="msg"></div>
<script>
var G = "여기에_배포_URL";

fetch(G).then(function(r){return r.json();}).then(function(d){
  var h = "";
  d.courses.forEach(function(c){
    h += c.status === "마감"
      ? "<p>🔒 " + c.course + " (" + c.enrolled + "/" + c.capacity + ") 마감</p>"
      : "<p>🟢 " + c.course + " (" + c.enrolled + "/" + c.capacity + ") </p>";
  });
  document.getElementById("courses").innerHTML = h;
});

function apply() {
  var course = prompt("신청할 강좌명을 입력하세요:");
  fetch(G, { method: "POST", body: JSON.stringify({ course: course, name: document.getElementById("name").value }) })
  .then(function(r){return r.json();}).then(function(d){
    document.getElementById("msg").textContent = d.ok ? "✅ 신청 완료! (" + d.enrolled + "/" + d.capacity + ")" : "🔴 " + d.message;
  });
}
</script>
</body></html>

제미나이 전용 에러 제로 프롬프트

[🚨 에러 방지 기본 안전장치]
1. 2개 파일 완결성: Code.gs와 index.html을 각각 완성한다.
2. doGet: 강좌별 정원과 현재 신청 인원을 계산하여 JSON으로 반환한다(모집중/마감 상태 포함).
3. doPost: 정원 초과 시 ok:false와 마감 메시지 반환, 정원 이내이면 appendRow한다.
4. 학생 개인정보 0: 강좌명·정원·신청 인원수만 관리하며 학생 데이터를 일절 다루지 않는다.
5. // TODO, /* 생략 */ 금지 — 끝까지 완성한다.
[❓ 역질문]
1. 강좌는 몇 개인가요? (기본: 5개)
2. 정원은 강좌별로 다른가요? (기본: 강좌별 각각 설정)
3. 취소 기능이 필요한가요? (기본: 교사가 시트에서 직접 삭제)

🎯 에디터 한줄평

"'지금 몇 명 신청했어요?'를 묻는 전화가 사라지는 순간입니다. 정원이 차면 자동으로 마감이 되고, 아이들이 즐겁게 선착순을 경험합니다. 교사는 시트의 숫자만 고치면 다음 학기에 무한 재사용이 됩니다."

📚 참고 링크

  • Google Apps Script 공식 문서: https://developers.google.com/apps-script
  • 교육과정 연계: 교무 행정(방과후학교·늘봄교실 관리), 교사 업무 자동화