code.gs, index.html 코드 교체

/**
 * 웹 앱 요청이 들어왔을 때 실행되는 함수입니다.
 * index.html 파일을 렌더링하여 반환합니다.
 */
function doGet() {
  return HtmlService.createTemplateFromFile('index')
      .evaluate()
      .setTitle('김해제일고 3학년 10반 학부모 상담 신청');
}

/**
 * HTML 템플릿의 스크립트릿에서 포함된 파일을 로드할 수 있도록 합니다.
 * 예를 들어, include('file')과 같이 사용할 수 있습니다.
 */
function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename)
      .getContent();
}

/**
 * 클라이언트 측에서 호출될 수 있는 함수입니다.
 * 간단한 환영 메시지를 반환합니다. (현재는 사용되지 않음)
 */
function getHelloMessage() {
  return "안녕하세요, Google Apps Script 웹 앱입니다!";
}

/**
 * 클라이언트에서 상담 신청 데이터를 받아 Google Sheet에 기록하는 함수입니다.
 * @param {Object} formData - 클라이언트에서 전송된 폼 데이터 객체
 *   - studentName: 학생 이름
 *   - consultDate: 상담 희망 날짜 (예: "9.8.")
 *   - consultTime: 상담 희망 시간 (예: "19:00-20:00")
 *   - consultContent: 상담 내용
 */
function processConsultationRequest(formData) {
  // 스크립트가 연결된 활성 스프레드시트를 가져옵니다.
  // 특정 스프레드시트 ID로 열려면:
  // const spreadsheet = SpreadsheetApp.openById('YOUR_SPREADSHEET_ID_HERE');
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();

  // 데이터를 기록할 시트 이름을 지정합니다. (여기서는 '시트1'로 가정)
  const sheetName = '시트1'; // <-- 이 부분을 실제 시트 이름으로 변경하세요!
  const sheet = spreadsheet.getSheetByName(sheetName);

  if (!sheet) {
    throw new Error('"' + sheetName + '" 시트를 찾을 수 없습니다. 시트 이름을 확인해주세요.');
  }

  // 1. 날짜에 따라 열 인덱스 결정
  let columnIndex; // Google Sheet의 B열은 인덱스 2, C열은 3 등
  switch (formData.consultDate) {
    case '9.8.':
      columnIndex = 2; // B열
      break;
    case '9.9.':
      columnIndex = 3; // C열
      break;
    case '9.10.':
      columnIndex = 4; // D열
      break;
    case '9.11.':
      columnIndex = 5; // E열
      break;
    case '9.12.':
      columnIndex = 6; // F열
      break;
    default:
      // 유효하지 않은 날짜가 넘어온 경우 에러 처리
      Logger.log('유효하지 않은 상담 날짜: ' + formData.consultDate);
      throw new Error('유효하지 않은 상담 날짜입니다.');
  }

  // 2. 시간에 따라 행 인덱스 결정
  let rowIndex; // Google Sheet의 3행은 인덱스 3, 4행은 4
  switch (formData.consultTime) {
    case '19:00-20:00':
      rowIndex = 3;
      break;
    case '20:00-21:00':
      rowIndex = 4;
      break;
    default:
      // 유효하지 않은 시간이 넘어온 경우 에러 처리
      Logger.log('유효하지 않은 상담 시간: ' + formData.consultTime);
      throw new Error('유효하지 않은 상담 시간입니다.');
  }

  // 3. 해당 셀에 학생 이름 입력
  const targetCell = sheet.getRange(rowIndex, columnIndex);

  // 셀이 비어있는지 확인하여 중복 신청 방지
  // 이미 값이 있다면 (예: 다른 학생 이름이 있다면) 중복 신청으로 간주
  if (targetCell.getValue().toString().trim() !== '') {
    const existingStudent = targetCell.getValue();
    Logger.log(`[중복 신청] ${formData.consultDate} ${formData.consultTime} 시간은 이미 ${existingStudent} 학생이 신청했습니다.`);
    throw new Error(`죄송합니다. ${formData.consultDate} ${formData.consultTime} 시간은 이미 신청되었습니다. 다른 시간을 선택해주세요.`);
  }

  // 셀에 학생 이름 기록
  targetCell.setValue(formData.studentName);

  // 선택 사항: 상담 내용을 별도의 시트나 로그에 기록
  Logger.log('--- 새로운 상담 신청 ---');
  Logger.log('학생 이름: ' + formData.studentName);
  Logger.log('희망 날짜: ' + formData.consultDate);
  Logger.log('희망 시간: ' + formData.consultTime);
  Logger.log('상담 내용: ' + formData.consultContent);
  Logger.log('------------------------');

  // 성공 메시지 반환 (클라이언트의 onSuccess 핸들러로 전달됨)
  return '상담 신청이 성공적으로 접수되었습니다.';
}
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      body {
        font-family: Arial, sans-serif;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: flex-start;
        min-height: 100vh;
        margin: 0;
        padding-top: 30px;
        padding-bottom: 50px; /* 하단 여백 추가 */
        background-color: #f4f4f4;
      }
      h1 {
        color: #333;
        margin-bottom: 20px;
      }
      .greeting-box {
        background-color: #e0f7fa;
        border: 1px solid #b2ebf2;
        border-radius: 8px;
        padding: 20px;
        margin-bottom: 30px;
        text-align: center;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        max-width: 600px;
        width: 90%;
      }
      .greeting-box p {
        margin: 0;
        font-size: 1.1em;
        color: #00796b;
        line-height: 1.5;
      }
      table {
        width: 80%;
        max-width: 800px; /* 표 최대 너비 설정 */
        border-collapse: collapse;
        margin-top: 20px;
        background-color: white;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
      }
      th, td {
        border: 1px solid #ddd;
        padding: 12px 8px;
        text-align: center;
      }
      th {
        background-color: #f2f2f2;
        font-weight: bold;
        color: #333;
      }
      tr:nth-child(even) {
        background-color: #f9f9f9;
      }
      th:first-child, td:first-child {
        width: 15%;
      }

      /* 상담 신청 섹션 스타일 */
      .application-section {
        background-color: #ffffff;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        padding: 30px;
        margin-top: 40px;
        width: 80%;
        max-width: 600px;
      }
      .application-section h2 {
        color: #333;
        text-align: center;
        margin-bottom: 25px;
      }
      .form-group {
        margin-bottom: 20px;
      }
      .form-group label {
        display: block;
        margin-bottom: 8px;
        font-weight: bold;
        color: #555;
      }
      .form-group input[type="text"],
      .form-group select,
      .form-group textarea {
        width: calc(100% - 20px); /* 패딩 고려 */
        padding: 10px;
        border: 1px solid #ccc;
        border-radius: 5px;
        font-size: 1em;
        box-sizing: border-box; /* 패딩이 너비에 포함되도록 */
      }
      .form-group input[type="radio"] {
        margin-right: 5px;
      }
      .form-group .radio-option {
        margin-right: 20px;
        display: inline-block;
      }
      .form-group textarea {
        resize: vertical; /* 세로 크기 조절 가능 */
        min-height: 80px;
      }

      .submit-button {
        display: block;
        width: 100%;
        padding: 12px 20px;
        font-size: 1.1em;
        cursor: pointer;
        background-color: #007bff; /* 파란색 버튼 */
        color: white;
        border: none;
        border-radius: 5px;
        transition: background-color 0.3s ease, transform 0.1s ease;
        margin-top: 30px;
      }
      .submit-button:hover {
        background-color: #0056b3; /* 호버 시 색상 */
        transform: translateY(-2px); /* 약간 위로 떠오르는 효과 */
      }
      .submit-button:active {
        transform: translateY(0); /* 클릭 시 효과 */
      }
      .submit-button.submitted {
        background-color: #28a745; /* 전송 완료 시 초록색 */
        cursor: default;
      }
    </style>
  </head>
  <body>
    <h1>김해제일고 3학년 10반 학부모 상담 신청</h1>

    <div class="greeting-box">
      <p>안녕하세요. 상담 날짜와 시간을 입력하여 신청해주세요.</p>
    </div>

    <table>
      <thead>
        <tr>
          <th></th>
          <th>월</th>
          <th>화</th>
          <th>수</th>
          <th>목</th>
          <th>금</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>학생명</td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
        </tr>
        <tr>
          <td>19:00-20:00</td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
        </tr>
        <tr>
          <td>20:00-21:00</td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
        </tr>
      </tbody>
    </table>

    <div class="application-section">
      <h2>상담 신청</h2>

      <div class="form-group">
        <label for="studentName">1. 학생 이름을 적어주세요</label>
        <input type="text" id="studentName" name="studentName" required>
      </div>

      <div class="form-group">
        <label for="consultDate">2. 상담 희망 날짜를 택해주세요</label>
        <select id="consultDate" name="consultDate" required>
          <option value="">날짜 선택</option>
          <option value="9.8.">9.8.</option>
          <option value="9.9.">9.9.</option>
          <option value="9.10.">9.10.</option>
          <option value="9.11.">9.11.</option>
          <option value="9.12.">9.12.</option>
        </select>
      </div>

      <div class="form-group">
        <label>3. 상담 희망 시간을 택해주세요</label>
        <div class="radio-group">
          <label class="radio-option">
            <input type="radio" name="consultTime" value="19:00-20:00" required> 19:00-20:00
          </label>
          <label class="radio-option">
            <input type="radio" name="consultTime" value="20:00-21:00"> 20:00-21:00
          </label>
        </div>
      </div>

      <div class="form-group">
        <label for="consultContent">4. 상담 시 상의하실 내용을 간략히 적어주세요</label>
        <textarea id="consultContent" name="consultContent" placeholder="30자 이내로만 작성부탁드립니다" maxlength="30"></textarea>
      </div>

      <button type="button" class="submit-button" id="submitBtn" onclick="submitForm()">전송</button>
      <div id="submitMessage" style="text-align: center; color: green; margin-top: 15px;"></div>
    </div>

    <script>
      // 폼 데이터를 Apps Script 백엔드로 전송하는 함수
      function submitForm() {
        const studentName = document.getElementById('studentName').value;
        const consultDate = document.getElementById('consultDate').value;
        const consultTime = document.querySelector('input[name="consultTime"]:checked');
        const consultContent = document.getElementById('consultContent').value;
        const submitBtn = document.getElementById('submitBtn');
        const submitMessage = document.getElementById('submitMessage');

        // 간단한 유효성 검사
        if (!studentName || !consultDate || !consultTime) {
          alert('학생 이름, 희망 날짜, 희망 시간을 모두 입력해주세요.');
          return;
        }

        const formData = {
          studentName: studentName,
          consultDate: consultDate,
          consultTime: consultTime.value,
          consultContent: consultContent
        };

        // 전송 버튼 비활성화 및 텍스트 변경
        submitBtn.disabled = true;
        submitBtn.innerText = '전송 중...';
        submitBtn.classList.remove('submitted'); // 혹시 모를 재전송 대비

        // Apps Script 함수 호출
        google.script.run
          .withSuccessHandler(onFormSuccess)
          .withFailureHandler(onFormFailure)
          .processConsultationRequest(formData); // 백엔드에 보낼 함수
      }

      function onFormSuccess(response) {
        const submitBtn = document.getElementById('submitBtn');
        const submitMessage = document.getElementById('submitMessage');

        submitBtn.innerText = '전송 완료';
        submitBtn.classList.add('submitted'); // 전송 완료 스타일 적용
        submitBtn.disabled = false; // 다시 활성화 (새로운 신청을 위해)

        // 폼 필드 초기화
        document.getElementById('studentName').value = '';
        document.getElementById('consultDate').value = '';
        const radioButtons = document.querySelectorAll('input[name="consultTime"]');
        radioButtons.forEach(radio => radio.checked = false);
        document.getElementById('consultContent').value = '';

        submitMessage.innerText = response || '상담 신청이 성공적으로 접수되었습니다.';
        submitMessage.style.color = 'green';

        // 3초 후 메시지 사라지게
        setTimeout(() => {
          submitMessage.innerText = '';
          submitBtn.innerText = '전송'; // 버튼 텍스트 원래대로
          submitBtn.classList.remove('submitted');
        }, 3000);
      }

      function onFormFailure(error) {
        const submitBtn = document.getElementById('submitBtn');
        const submitMessage = document.getElementById('submitMessage');

        submitBtn.disabled = false;
        submitBtn.innerText = '전송'; // 에러 발생 시 버튼 텍스트 원상 복구
        submitBtn.classList.remove('submitted');

        submitMessage.innerText = '상담 신청 중 오류가 발생했습니다: ' + error.message;
        submitMessage.style.color = 'red';

        console.error("Form Submission Error:", error);
      }
    </script>
  </body>
</html>


코멘트

댓글 남기기

Cha's Record에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기