🍒 구글 스프레드시트 × 그라운드 API 연동 가이드

Fri, 14 Nov 2025 01:48:04 GMT

**🥭 샘플 스프레드 시트 👉🏻 ****[다운로드 ](https://docs.google.com/spreadsheets/d/1sEe8PqzcyBAwWe9Yr7H7pEzNkZrGCxhVew6LUyepbUg/edit?usp=sharing)**

## 📋 목차

1. [개요](https://#%EA%B0%9C%EC%9A%94)

2. [사전 준비](https://#%EC%82%AC%EC%A0%84-%EC%A4%80%EB%B9%84)

3. [버튼으로 일괄 포인트 부여](https://#%EB%B2%84%ED%8A%BC%EC%9C%BC%EB%A1%9C-%EC%9D%BC%EA%B4%84-%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EB%B6%80%EC%97%AC)

4. [문제 해결](https://#%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0)

---

## 개요

구글 스프레드시트의 Apps Script를 사용하여 그라운드 API와 연동하면:

- ✅ 학생 명단에서 직접 포인트 부여

- ✅ 버튼 클릭만으로 일괄 포인트 처리

- ✅ 출석, 과제, 퀴즈 점수를 자동으로 포인트로 변환

- ✅ 진행 상황 실시간 표시

---

## 사전 준비

### 1. 그라운드 API 키 발급

1. [그라운드 웹사이트](https://growndcard.com) 로그인

2. **내 정보** → **API 키 관리** 탭

3. **새 API 키 생성** 클릭

4. API 키 복사 (예: `sk_live_xxx...`)

### 2. 클래스 ID 확인

1. 그라운드 웹사이트 → **클래스 관리**

2. 포인트를 부여할 클래스의 **ID** 복사 (예: `NP0hetJ3wyQKFtRnFeftmPiy8Dl3_2`)

### 3. 학생 번호 확인

- 그라운드에서 각 학생에게 부여된 **번호(code)** 확인

- 스프레드시트의 학생 목록과 매칭

---

## 버튼으로 일괄 포인트 부여

### ✨ 특징

- 여러 학생에게 동시에 포인트 부여

- 선택한 행만 처리 가능

- 진행 상황 실시간 표시

- 상단 메뉴에서 버튼 클릭만으로 실행

### 📝 스프레드시트 구성

| A | B | C | D | E | F |
| --- | --- | --- | --- | --- | --- |
| **선택** | **학생번호** | **이름** | **포인트** | **설명** | **상태** |
| ☑️ | 1 | 김철수 | 10 | 출석 | 대기중 |
| ☑️ | 2 | 이영희 | 10 | 출석 | 대기중 |
| ☐ | 3 | 박민수 | 10 | 출석 | 대기중 |

### 🔧 Apps Script 코드

```
/**
* 그라운드 API 설정
* ⚠️ 실제 API 키와 클래스 ID로 변경하세요!
*/
const GROWND_API_KEY = 'sk_live_your_api_key_here'; // 발급받은 API 키
const GROWND_CLASS_ID = 'your_class_id_here'; // 클래스 ID
const GROWND_API_URL = 'https://growndcard.com/api/v1';

/**
* 메뉴에 커스텀 버튼 추가
*/
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('🎯 그라운드')
.addItem('선택한 학생에게 포인트 부여', 'awardPointsToSelected')
.addItem('전체 학생에게 포인트 부여', 'awardPointsToAll')
.addSeparator()
.addItem('설정', 'showSettings')
.addToUi();
}

/**
* 선택한 학생에게 포인트 부여
*/
function awardPointsToSelected() {
const sheet = SpreadsheetApp.getActiveSheet();
const ui = SpreadsheetApp.getUi();

// 확인 대화상자
const response = ui.alert(
'포인트 부여 확인',
'선택된 학생들에게 포인트를 부여하시겠습니까?',
ui.ButtonSet.YES_NO
);

if (response !== ui.Button.YES) {
return;
}

// 데이터 범위 가져오기 (2행부터 시작, 헤더 제외)
const dataRange = sheet.getRange(2, 1, sheet.getLastRow() - 1, 6);
const data = dataRange.getValues();

let successCount = 0;
let failCount = 0;

// 각 행 처리
for (let i = 0; i < data.length; i++) {
const [checked, studentCode, name, points, description, status] = data[i];

// 체크박스가 선택된 행만 처리
if (checked === true) {
// 상태 업데이트: 처리 중
sheet.getRange(i + 2, 6).setValue('⏳ 처리 중...');
SpreadsheetApp.flush(); // 즉시 화면 업데이트

try {
const result = awardPointToStudent(studentCode, points, description);

if (result.success) {
sheet.getRange(i + 2, 6).setValue(`✅ ${points}P 부여 완료`);
successCount++;
} else {
sheet.getRange(i + 2, 6).setValue(`❌ ${result.error}`);
failCount++;
}
} catch (error) {
sheet.getRange(i + 2, 6).setValue(`❌ ${error.message}`);
failCount++;
}

SpreadsheetApp.flush();
}
}

// 결과 알림
ui.alert(
'포인트 부여 완료',
`성공: ${successCount}명\n실패: ${failCount}명`,
ui.ButtonSet.OK
);
}

/**
* 전체 학생에게 포인트 부여
*/
function awardPointsToAll() {
const sheet = SpreadsheetApp.getActiveSheet();
const ui = SpreadsheetApp.getUi();

// 확인 대화상자
const response = ui.alert(
'전체 포인트 부여 확인',
'전체 학생에게 포인트를 부여하시겠습니까?',
ui.ButtonSet.YES_NO
);

if (response !== ui.Button.YES) {
return;
}

const dataRange = sheet.getRange(2, 2, sheet.getLastRow() - 1, 5); // 체크박스 제외
const data = dataRange.getValues();

let successCount = 0;
let failCount = 0;

for (let i = 0; i < data.length; i++) {
const [studentCode, name, points, description, status] = data[i];

if (studentCode && points) {
sheet.getRange(i + 2, 6).setValue('⏳ 처리 중...');
SpreadsheetApp.flush();

try {
const result = awardPointToStudent(studentCode, points, description);

if (result.success) {
sheet.getRange(i + 2, 6).setValue(`✅ ${points}P 부여 완료`);
successCount++;
} else {
sheet.getRange(i + 2, 6).setValue(`❌ ${result.error}`);
failCount++;
}
} catch (error) {
sheet.getRange(i + 2, 6).setValue(`❌ ${error.message}`);
failCount++;
}

SpreadsheetApp.flush();
}
}

ui.alert(
'포인트 부여 완료',
`성공: ${successCount}명\n실패: ${failCount}명`,
ui.ButtonSet.OK
);
}

/**
* 개별 학생에게 포인트 부여 (내부 함수)
*/
function awardPointToStudent(studentCode, points, description) {
const url = `${GROWND_API_URL}/classes/${GROWND_CLASS_ID}/students/${studentCode}/points`;

const options = {
method: 'post',
contentType: 'application/json',
headers: {
'X-API-Key': GROWND_API_KEY
},
payload: JSON.stringify({
type: 'reward',
points: points,
description: description || '스프레드시트에서 부여',
source: 'google_sheets'
}),
muteHttpExceptions: true
};

try {
const response = UrlFetchApp.fetch(url, options);
const statusCode = response.getResponseCode();
const contentText = response.getContentText();

// HTML 응답 체크 (에러 페이지 방지)
if (contentText.startsWith('<')) {
console.error('HTML 응답:', contentText.substring(0, 200));
return {
success: false,
error: `API 응답 오류 (상태 ${statusCode}). URL을 확인하세요.`
};
}

const result = JSON.parse(contentText);

return result.success
? { success: true }
: { success: false, error: result.error.message };

} catch (error) {
console.error('API 호출 에러:', error);
return { success: false, error: error.message };
}
}

/**
* 설정 대화상자
*/
function showSettings() {
const ui = SpreadsheetApp.getUi();
ui.alert(
'⚙️ API 설정',
`현재 설정:\n\n` +
`API 키: ${GROWND_API_KEY.substring(0, 15)}...\n` +
`클래스 ID: ${GROWND_CLASS_ID}\n\n` +
`설정을 변경하려면 Apps Script 코드를 수정하세요.`,
ui.ButtonSet.OK
);
}
```

### 📖 설정 방법

### 1단계: Apps Script 열기

1. 구글 스프레드시트에서 상단 메뉴: **확장 프로그램** → **Apps Script**

2. 새 창이 열리면 기존 코드 삭제

### 2단계: 코드 복사 및 설정

1. 위의 전체 코드를 복사하여 붙여넣기

2. **중요**: 다음 두 줄을 실제 값으로 수정:

```
const GROWND_API_KEY = 'sk_live_xxx...'; // 발급받은 실제 API 키
const GROWND_CLASS_ID = 'NP0hetJ3wyQKFtRnFeftmPiy8Dl3_2'; // 실제 클래스 ID
```

### 3단계: 저장 및 권한 부여

1. 💾 **저장** 버튼 클릭 (또는 Ctrl+S / Cmd+S)

2. 프로젝트 이름 입력 (예: "그라운드 포인트 관리")

3. Apps Script 탭 닫기

### 4단계: 스프레드시트 구성

1. **스프레드시트를 닫았다가 다시 열기** (새로고침)

2. 잠시 기다리면 상단에 **"🎯 그라운드"** 메뉴가 나타남

3. 스프레드시트에 다음과 같이 구성:

**A열**: 체크박스 삽입

- **삽입** → **체크박스** 클릭

- 각 학생 행에 체크박스 추가

**B열**: 학생번호 (숫자, 예: 1, 2, 3...)
**C열**: 학생이름
**D열**: 부여할 포인트 (숫자)
**E열**: 포인트 설명 (텍스트)
**F열**: 상태 (자동으로 채워짐, 비워두기)

### 5단계: 첫 실행 시 권한 부여

1. **🎯 그라운드** → **선택한 학생에게 포인트 부여** 클릭

2. **권한 검토** 팝업이 나타나면 클릭

3. 본인 구글 계정 선택

4. **고급** 클릭 → **안전하지 않은 페이지로 이동** 클릭

5. **허용** 클릭

### 6단계: 사용하기

1. 포인트를 부여할 학생의 **체크박스 선택**

2. **🎯 그라운드** → **선택한 학생에게 포인트 부여** 클릭

3. 확인 대화상자에서 **예** 클릭

4. 실시간으로 처리 상황 확인

---

## 문제 해결

### ❌ "권한이 없습니다" 오류

**원인**: Apps Script 권한이 부여되지 않음

**해결**:

1. Apps Script 실행 시 **권한 검토** 클릭

2. 본인 구글 계정 선택

3. **고급** → **안전하지 않은 페이지로 이동** 클릭

4. **허용** 클릭

### ❌ "API 키 인증 실패" 오류

**원인**: API 키가 잘못되었거나 만료됨

**해결**:

1. 그라운드 웹사이트에서 API 키 확인

2. Apps Script 코드의 `GROWND_API_KEY` 값 업데이트

3. API 키가 비활성화되지 않았는지 확인

### ❌ "학생을 찾을 수 없습니다" 오류

**원인**: 학생 번호가 잘못됨

**해결**:

1. 그라운드 웹사이트에서 정확한 학생 번호 확인

2. 스프레드시트의 학생 번호와 일치하는지 확인

3. 학생 번호는 **숫자**여야 함 (문자열 X)

4. B열 전체를 선택 → 우클릭 → **서식** → **숫자** → **숫자** 선택

### ❌ "클래스 권한이 없습니다" 오류

**원인**: API 키가 해당 클래스에 접근 권한이 없음

**해결**:

1. 그라운드 웹사이트 → **내 정보** → **API 키 관리**

2. 해당 API 키의 권한에 클래스가 포함되어 있는지 확인

3. 필요시 API 키를 삭제하고 재생성

### ❌ "일일 호출 제한 초과" 오류

**원인**: 하루 500회 API 호출 제한 도달

**해결**:

1. 내일까지 대기 (매일 자정 초기화)

2. 또는 여러 API 키를 생성하여 분산 사용

3. 불필요한 중복 호출 제거

### ❌ 상단에 "🎯 그라운드" 메뉴가 안 보임

**원인**: 코드가 제대로 저장되지 않았거나 새로고침 필요

**해결**:

1. Apps Script에서 코드가 저장되었는지 확인

2. 스프레드시트를 **완전히 닫기**

3. 다시 열기 (최대 30초 대기)

4. 여전히 안 보이면 Apps Script에서 `onOpen` 함수를 **직접 실행**

### ❌ 함수 실행이 너무 느림

**원인**: 많은 학생에게 순차적으로 API 호출

**해결**:

- 학생 수가 많을 경우 10-20명씩 나눠서 처리

- 또는 코드에 배치 처리 추가 (rate limit 방지용 대기 시간 포함)

---

## 📌 활용 팁

### 1️⃣ 출석 체크 시트

| A | B | C | D | E | F |
| --- | --- | --- | --- | --- | --- |
| **선택** | **학생번호** | **이름** | **포인트** | **설명** | **상태** |
| ☑️ | 1 | 김철수 | 5 | 2025-01-15 출석 | |
| ☑️ | 2 | 이영희 | 5 | 2025-01-15 출석 | |
| ☐ | 3 | 박민수 | 0 | 결석 | |

- 출석한 학생만 체크박스 선택

- D열에 출석 포인트 (예: 5점) 입력

- E열에 날짜 + "출석" 입력

- **🎯 그라운드** → **선택한 학생에게 포인트 부여**

### 2️⃣ 퀴즈/과제 점수

| A | B | C | D | E | F |
| --- | --- | --- | --- | --- | --- |
| **선택** | **학생번호** | **이름** | **포인트** | **설명** | **상태** |
| ☑️ | 1 | 김철수 | 20 | 수학 퀴즈 A등급 | |
| ☑️ | 2 | 이영희 | 15 | 수학 퀴즈 B등급 | |
| ☑️ | 3 | 박민수 | 10 | 수학 퀴즈 C등급 | |

- 전체 학생에게 차등 포인트 부여

- **🎯 그라운드** → **전체 학생에게 포인트 부여**

### 3️⃣ 일괄 포인트 부여

전체 학생에게 동일한 포인트를 부여할 때:

- 체크박스 없이 B~E열만 채우기

- **🎯 그라운드** → **전체 학생에게 포인트 부여**

---

## 🎯 마무리

이제 구글 스프레드시트에서 그라운드 포인트를 쉽게 관리할 수 있습니다!

### 추천 사용 시나리오

- ✅ **출석 관리**: 매일 출석 체크 → 버튼 클릭으로 포인트 부여

- ✅ **퀴즈/시험**: 점수 입력 → 등급별 포인트 차등 부여

- ✅ **과제 제출**: 제출 여부 체크 → 일괄 포인트 부여

- ✅ **보너스 포인트**: 특별 활동, 우수 학생 포인트 지급

### 참고

- 📖 API 문서: [https://slashpage.com/grownd/grownd_API](https://slashpage.com/grownd/grownd_API)

---