본문 바로가기
1일 1 AI 공부

구글앱스스크립트 및 Gemini APT 활용 구클 내 과제 피드백하기

by 구교수 2025. 3. 30.

네, Google Apps Script와 Gemini API를 결합하여 과제 피드백을 자동화하는 상세 단계를 안내해 드리겠습니다. 이 가이드는 과제가 주로 Google Docs 형식이라고 가정하고 작성되었지만, 다른 형식에도 응용 가능합니다.

사전 준비:

  1. Gemini API 키 발급:
    • Google AI Studio 로 이동하여 구글 계정으로 로그인합니다.
    • 새 프로젝트를 생성하거나 기존 프로젝트를 선택합니다.
    • 왼쪽 메뉴에서 "Get API key"를 클릭하여 API 키를 생성하고 안전한 곳에 복사해 둡니다. 이 키는 절대로 외부에 노출되어서는 안 됩니다.
  2. 피드백 받을 과제 폴더 확인:
    • Google Drive에서 Classroom > 클래스룸명 > 과제명 폴더로 이동합니다.
    • 이 폴더의 URL을 보면 folders/ 뒤에 긴 문자열이 있습니다. 이것이 폴더 ID입니다. 이 ID를 복사해 둡니다. (폴더 이름보다 ID를 사용하는 것이 더 정확합니다.)

구현 단계:

1단계: Apps Script 프로젝트 생성 및 기본 설정

  1. 스크립트 편집기 열기:
    • 피드백 결과를 정리할 새 Google 스프레드시트를 하나 만듭니다. (선택 사항이지만 결과 로깅 및 관리에 편리합니다.)
    • 스프레드시트 메뉴에서 도구 > 스크립트 편집기를 엽니다.
    • 또는 Google Drive에서 새로 만들기 > 더보기 > Google Apps Script를 선택하여 독립형 스크립트 프로젝트를 만듭니다.
  2. 프로젝트 이름 지정: 스크립트 편집기 상단의 "제목 없는 프로젝트"를 클릭하여 "과제 자동 피드백" 등 알아보기 쉬운 이름으로 변경합니다.
  3. API 키 안전하게 저장 (Script Properties):
    • 스크립트 편집기 왼쪽 메뉴에서 프로젝트 설정 (톱니바퀴 아이콘)을 클릭합니다.
    • 아래로 스크롤하여 스크립트 속성(Script Properties) 섹션을 찾고 스크립트 속성 추가를 클릭합니다.
    • 속성 이름으로 GEMINI_API_KEY (또는 원하는 이름)를 입력하고, 에 사전 준비에서 복사한 Gemini API 키를 붙여넣습니다. 스크립트 속성 저장을 클릭합니다. 이렇게 하면 코드에 API 키가 직접 노출되지 않아 안전합니다.
  4. 필요한 서비스 활성화:
    • 스크립트 편집기 왼쪽 메뉴에서 서비스 + 버튼을 클릭합니다.
    • Drive API  Google Docs API 를 찾아 선택하고 추가 버튼을 누릅니다. (다른 형식의 파일이라면 해당 API, 예: Slides API, Sheets API 추가)

2단계: Apps Script 코드 작성

스크립트 편집기에 다음 코드를 붙여넣고 필요한 부분을 수정합니다.

// --- 설정 값 ---
// 1. 과제 파일들이 모여있는 구글 드라이브 폴더 ID를 입력하세요.
const ASSIGNMENT_FOLDER_ID = '여기에_과제_폴더_ID_붙여넣기';

// 2. Gemini API 키를 스크립트 속성에서 가져옵니다.
const API_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
// Gemini API 엔드포인트 (텍스트 전용 모델 예시)
const API_ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${API_KEY}`;

// 3. (선택사항) 피드백 결과를 기록할 스프레드시트 설정
const SPREADSHEET_ID = SpreadsheetApp.getActiveSpreadsheet().getId(); // 현재 스프레드시트 사용 시
const LOG_SHEET_NAME = '피드백 로그'; // 결과 기록 시트 이름

// 4. AI에게 전달할 프롬프트 (가장 중요! 구체적으로 작성하세요)
const PROMPT_TEMPLATE = `
당신은 [과목명] 과제를 채점하고 피드백을 제공하는 교사입니다.
다음은 학생이 제출한 과제 내용입니다.

[학생 과제 내용 시작]
{{STUDENT_TEXT}}
[학생 과제 내용 끝]

아래 평가 기준(루브릭)을 바탕으로 학생의 과제에 대해 구체적인 피드백을 작성해 주세요.

[평가 기준 시작]
- 항목 1: [구체적인 기준 설명]
- 항목 2: [구체적인 기준 설명]
- 항목 3: [구체적인 기준 설명]
[평가 기준 끝]

피드백 형식:
1.  **잘한 점:** (2-3가지 구체적인 예시 포함)
2.  **개선할 점:** (2-3가지 구체적인 예시 및 개선 방안 제시)
3.  **총평:** (간결하게 요약)

한국어로 작성해 주세요.
`;

// --- 메인 함수 ---
function generateFeedbackForAllAssignments() {
  const folder = DriveApp.getFolderById(ASSIGNMENT_FOLDER_ID);
  // Google Docs 파일만 가져오기 (필요시 MimeType 변경)
  const files = folder.getFilesByType(MimeType.GOOGLE_DOCS);

  // (선택사항) 로그 시트 준비
  const ss = SpreadsheetApp.getActiveSpreadsheet(); // 독립형 스크립트 사용 시 SpreadsheetApp.openById(SPREADSHEET_ID) 사용
  let sheet = ss.getSheetByName(LOG_SHEET_NAME);
  if (!sheet) {
    sheet = ss.insertSheet(LOG_SHEET_NAME);
    sheet.appendRow(['파일명', '학생 이름 (추정)', '과제 내용 (요약)', 'AI 피드백', '오류']); // 헤더 추가
  }

  while (files.hasNext()) {
    const file = files.next();
    const fileName = file.getName();
    const fileId = file.getId();
    let studentName = extractStudentName(fileName); // 파일명에서 학생 이름 추출 시도
    let feedback = '';
    let errorMsg = '';
    let docContent = '';

    try {
      // 1. 문서 내용 추출
      docContent = DocumentApp.openById(fileId).getBody().getText();

      if (!docContent.trim()) {
        errorMsg = '문서 내용이 비어있습니다.';
        Logger.log(`${fileName}: 문서 내용 비어있음`);
      } else {
        // 2. 프롬프트 생성
        const prompt = PROMPT_TEMPLATE.replace('{{STUDENT_TEXT}}', docContent);

        // 3. Gemini API 호출
        const requestBody = {
          "contents": [{
            "parts": [{
              "text": prompt
            }]
          }],
          // 필요시 추가 파라미터 설정 (temperature, topK, topP 등)
          // "generationConfig": {
          //   "temperature": 0.7,
          //   "topK": 40
          // }
        };

        const options = {
          'method': 'post',
          'contentType': 'application/json',
          'payload': JSON.stringify(requestBody),
          'muteHttpExceptions': true // API 오류 시 스크립트 중단 방지
        };

        const response = UrlFetchApp.fetch(API_ENDPOINT, options);
        const responseCode = response.getResponseCode();
        const responseBody = response.getContentText();

        if (responseCode === 200) {
          const jsonResponse = JSON.parse(responseBody);
          // 응답 구조 확인 필요 (API 변경 가능성 있음)
          if (jsonResponse.candidates && jsonResponse.candidates.length > 0 &&
              jsonResponse.candidates[0].content && jsonResponse.candidates[0].content.parts &&
              jsonResponse.candidates[0].content.parts.length > 0) {
            feedback = jsonResponse.candidates[0].content.parts[0].text.trim();
            Logger.log(`${fileName}: 피드백 생성 완료`);

            // 4. 피드백 전달 (옵션 선택)
            // 옵션 A: 문서에 댓글로 추가
             addCommentToDoc(fileId, feedback);

            // 옵션 B: (주석 처리됨) 이메일로 보내기 등 다른 작업 추가 가능

          } else {
            errorMsg = 'API 응답에서 피드백 내용을 찾을 수 없습니다. 응답: ' + responseBody;
            Logger.log(`${fileName}: 피드백 추출 오류 - ${errorMsg}`);
          }
        } else {
          errorMsg = `API 오류 발생 (코드: ${responseCode}): ${responseBody}`;
          Logger.log(`${fileName}: API 오류 - ${errorMsg}`);
        }
      }
    } catch (e) {
      errorMsg = `스크립트 실행 중 오류: ${e.message} \n Stack: ${e.stack}`;
      Logger.log(`${fileName}: 스크립트 오류 - ${errorMsg}`);
    }

    // (선택사항) 로그 시트에 결과 기록
    sheet.appendRow([
      fileName,
      studentName,
      docContent.substring(0, 100) + (docContent.length > 100 ? '...' : ''), // 내용 앞부분만 로깅
      feedback,
      errorMsg
    ]);

    // API 과부하 방지를 위한 잠시 대기 (필요시 조절)
    Utilities.sleep(1500); // 1.5초 대기
  }

  Logger.log('모든 과제 피드백 생성 작업 완료');
  SpreadsheetApp.getUi().alert('자동 피드백 생성 및 로그 기록이 완료되었습니다.');
}

// --- 보조 함수 ---

/**
 * Google 문서에 댓글을 추가하는 함수
 * @param {string} docId 댓글을 추가할 문서 ID
 * @param {string} commentText 추가할 댓글 내용
 */
function addCommentToDoc(docId, commentText) {
  try {
    const doc = DocumentApp.openById(docId);
    // 문서 최상단에 댓글 추가 (선택 범위를 지정하여 특정 부분에 달 수도 있음)
    doc.getBody().editAsText().insertText(0, ''); // 댓글 앵커를 위한 빈 텍스트 삽입 (필요 없을 수도 있음)
    doc.addComment(commentText); // 문서 자체에 댓글 추가
    Logger.log(`문서(${docId})에 댓글 추가 완료`);
  } catch (e) {
    Logger.log(`문서(${docId}) 댓글 추가 오류: ${e.message}`);
    // 로그 시트에도 기록되므로 여기서는 로깅만 수행
  }
}

/**
 * 파일 이름에서 학생 이름을 추출하는 함수 (샘플 로직)
 * 클래스룸 파일명 규칙에 맞게 수정 필요 (예: '과제명 - 학생이름.docx')
 * @param {string} fileName 파일 이름
 * @returns {string} 추출된 학생 이름 또는 빈 문자열
 */
function extractStudentName(fileName) {
  // 예시: "과제 제목 - 홍길동.docx" 같은 형식일 경우
  const parts = fileName.split(' - ');
  if (parts.length > 1) {
    // 마지막 부분에서 확장자 제거
    const namePart = parts[parts.length - 1];
    const dotIndex = namePart.lastIndexOf('.');
    if (dotIndex > 0) {
      return namePart.substring(0, dotIndex).trim();
    } else {
      return namePart.trim();
    }
  }
  // 다른 형식의 파일 이름 규칙이 있다면 여기에 로직 추가
  return ''; // 추출 실패 시
}
Use code with caution.JavaScript

3단계: 코드 수정 및 설정 값 입력

  1. ASSIGNMENT_FOLDER_ID: 코드 상단의 여기에_과제_폴더_ID_붙여넣기 부분을 사전 준비에서 복사한 실제 폴더 ID로 교체합니다.
  2. PROMPT_TEMPLATE: 이 부분이 가장 중요합니다!
    • [과목명]을 실제 과목명으로 바꾸세요.
    • [평가 기준 시작] [평가 기준 끝] 사이에 실제 사용하는 평가 기준(루브릭)이나 핵심 질문들을 명확하고 구체적으로 넣어주세요. AI는 이 기준을 바탕으로 피드백을 생성합니다.
    • 피드백 형식을 원하는 대로 수정할 수 있습니다. (예: 점수 포함, 특정 부분 강조 등)
    • 테스트를 통해 프롬프트를 계속 개선해야 좋은 품질의 피드백을 얻을 수 있습니다.
  3. (선택사항) 로그 시트 설정:
    • 독립형 스크립트를 사용한다면 SPREADSHEET_ID에 대상 스프레드시트 ID를 직접 입력해야 합니다.
    • LOG_SHEET_NAME을 원하는 시트 이름으로 변경할 수 있습니다.
  4. (선택사항) 피드백 전달 방식 결정:
    • 기본 코드는 addCommentToDoc 함수를 호출하여 각 학생의 Google 문서에 직접 댓글을 답니다. 이것이 가장 일반적입니다.
    • 만약 로그 시트에만 기록하고 싶다면 addCommentToDoc(fileId, feedback); 라인을 주석 처리(// 추가)하거나 삭제하세요.
  5. extractStudentName 함수: 클래스룸에서 생성되는 파일 이름 규칙에 맞게 학생 이름을 추출하는 로직을 수정해야 할 수 있습니다. (현재는 ' - ' 뒤의 텍스트를 이름으로 간주하는 예시)

4단계: 스크립트 실행 및 권한 부여

  1. 스크립트 편집기 상단 메뉴에서 generateFeedbackForAllAssignments 함수를 선택합니다.
  2. 실행 (▶︎ 아이콘) 버튼을 클릭합니다.
  3. 최초 실행 시 권한 요청:
    • 스크립트가 Google Drive, Google Docs, 외부 서비스(Gemini API), Google Sheets 등에 접근할 수 있도록 권한을 부여해야 합니다.
    • 권한 검토 버튼을 클릭하고, 스크립트를 실행할 구글 계정을 선택합니다.
    • "Google에서 이 앱을 확인하지 않았습니다."라는 경고가 나올 수 있습니다. 고급을 클릭하고 (프로젝트 이름)(으)로 이동 (안전하지 않음)을 선택하여 진행합니다. (본인이 작성한 스크립트이므로 안전합니다.)
    • 요청하는 모든 권한에 대해 허용 버튼을 클릭합니다.

5단계: 실행 결과 확인 및 피드백 검토

  1. 스크립트 실행이 완료될 때까지 기다립니다. (학생 수에 따라 시간이 걸릴 수 있습니다. Apps Script 실행 시간 제한에 유의하세요.)
  2. 로그 시트 확인: 스프레드시트의 피드백 로그 시트에 각 파일별 처리 결과(파일명, 학생 이름, 피드백 내용, 오류 여부)가 기록됩니다. 오류가 발생했다면 오류 열의 내용을 확인하여 원인을 파악합니다.
  3. 학생 문서 확인: addCommentToDoc 함수를 사용했다면, 각 학생의 Google 문서 파일에 AI가 생성한 피드백 댓글이 추가되었는지 확인합니다.
  4. AI 피드백 검토 및 수정: 매우 중요! AI가 생성한 피드백은 초안일 뿐입니다. 반드시 로그 시트나 학생 문서의 댓글을 교사가 직접 검토하고 필요한 부분을 수정해야 합니다. AI는 문맥을 잘못 이해하거나 부적절한 피드백을 생성할 수 있습니다.

추가 팁 및 고려사항:

  • 테스트: 처음에는 1~2개의 파일만 있는 테스트 폴더를 만들어 스크립트가 정상 작동하는지, 프롬프트가 원하는 대로 피드백을 생성하는지 충분히 테스트하세요.
  • 실행 시간 제한: 학생 수가 매우 많으면 Apps Script의 실행 시간 제한(일반 계정 6분, Workspace 계정 30분)을 초과할 수 있습니다. 이 경우, 스크립트를 여러 번 실행하거나, 특정 개수만 처리하고 다음 실행을 예약하는 등의 로직이 필요할 수 있습니다. Utilities.sleep()을 사용하여 API 호출 사이에 적절한 지연 시간을 주는 것이 좋습니다.
  • 오류 처리: 제공된 코드는 기본적인 오류 처리를 포함하지만, 실제 운영 시에는 더 견고한 오류 처리 로직(예: 특정 횟수 재시도)을 추가하는 것이 좋습니다.
  • 비용: Gemini API는 사용량에 따라 비용이 발생할 수 있습니다. Google AI Studio에서 가격 정책과 무료 사용량을 확인하세요.
  • 프롬프트 개선: 피드백 품질은 프롬프트에 크게 좌우됩니다. 다양한 프롬프트를 시도하고, 좋은 예시와 나쁜 예시를 프롬프트에 포함하는 등 '프롬프트 엔지니어링'에 노력을 기울이세요.

이 가이드가 구글 클래스룸 과제 피드백 자동화 시스템 구축에 도움이 되기를 바랍니다! 막히는 부분이 있다면 언제든지 다시 질문해주세요.

'1일 1 AI 공부' 카테고리의 다른 글

1min.AI 평생구독권 구입!! (블랙프라이데이 행사중!)  (1) 2024.11.24
미드저니 sref 사용해보기  (1) 2024.09.04
Claude AI System Prompts 공개  (2) 2024.08.31
Ideogram 2.0 출시  (0) 2024.08.23
Flux Lora the Explorer  (0) 2024.08.19