Google Spreadsheet를 데이터베이스로 활용하기

1. 개요

교우관계 분석 시스템에서는 별도의 데이터베이스 서버 없이 Google Spreadsheet를 활용하여 데이터를 저장하고 관리합니다. 이는 다음과 같은 장점이 있습니다:

  • 무료로 사용 가능한 클라우드 저장소
  • 간단한 CRUD 작업 구현 가능
  • 스프레드시트 UI를 통한 쉬운 데이터 확인 및 관리
  • Apps Script를 통한 서버리스 백엔드 구현

2. 스프레드시트 구조

2.1 시트 구성

스프레드시트는 두 개의 시트로 구성됩니다:

  1. 학생 정보 시트

    • 열 구조:
      • A열: 학생 이름(번호 순서대로)
  2. 설문 응답 시트

    • 열 구조:
      • A열: 타임스탬프(설문 응답 시간)
      • B열: 응답자 이름
      • C열: 관계도 데이터 (좌표값)
      • D~E열: 우리반에서 칭찬하고 싶은 친구 데이터 (이름, 이유)
      • F~G열: 우리반에서 상대하기 어려운 친구 데이터 (이름, 이유)
      • H~I열: 다른 반에서 관계가 좋은 친구 데이터 (이름, 이유)
      • J~K열: 다른 반에서 관계가 불편한 친구 데이터 (이름, 이유)
      • L열: 학교생활 고민 사항
      • M열: 선생님께 하고 싶은 말

3. Apps Script 구현

3.1 기본 설정

// Code.gs
const sheetID = "YOUR_SPREADSHEET_ID";
const studentSheetName = "학생정보";
const surveySheetName = "설문응답";

function doGet(e) {
  const action = e.parameter.action;

  switch (action) {
    case "getStudentList":
      return getStudentList();
    case "getAnalysisData":
      return getAnalysisData();
    default:
      return ContentService.createTextOutput(
        JSON.stringify({ status: "error", message: "Invalid action" })
      ).setMimeType(ContentService.MimeType.JSON);
  }
}

3.2 데이터 조회 함수

function getStudentList() {
  try {
    const sheet =
      SpreadsheetApp.openById(sheetID).getSheetByName(studentSheetName);
    const data = sheet.getDataRange().getValues();
    // 헤더 제외하고 학생 데이터만 추출
    const students = data.slice(1).map((row) => row[0]);

    return ContentService.createTextOutput(
      JSON.stringify({
        status: "success",
        data: students,
      })
    ).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: error.toString(),
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }
}

function getAnalysisData() {
  try {
    const sheet =
      SpreadsheetApp.openById(sheetID).getSheetByName(surveySheetName);
    const data = sheet.getDataRange().getValues();

    return ContentService.createTextOutput(
      JSON.stringify({
        status: "success",
        data: data.slice(1), // 헤더 제외
      })
    ).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: error.toString(),
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }
}

3.3 데이터 저장 함수

function doPost(e) {
  try {
    const data = JSON.parse(e.postData.contents);
    const sheet =
      SpreadsheetApp.openById(sheetID).getSheetByName(surveySheetName);

    // 설문 응답 데이터 구성
    const rowData = [
      data.userName,
      new Date(),
      formatPositionData(data.studentPositions),
      data.praise.name,
      data.praise.reason,
      data.difficult.name,
      data.difficult.reason,
      data.otherclass.friendlyName,
      data.otherclass.friendlyReason,
      data.otherclass.badName,
      data.otherclass.badReason,
      data.concern,
      data.teacherMessage,
    ];

    // 데이터 저장
    sheet.appendRow(rowData);

    return ContentService.createTextOutput(
      JSON.stringify({ status: "success" })
    ).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: error.toString(),
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }
}

// 위치 데이터 포맷팅 함수
function formatPositionData(positions) {
  return positions
    .map((p) => `${p.name}:(${p.position.x},${p.position.y})`)
    .join("\n");
}

4. 웹 앱과의 연동

4.1 API 호출 설정

const SCRIPT_URL = "YOUR_APPS_SCRIPT_URL";

4.2 데이터 요청 구현

async function fetchStudentList() {
  try {
    const response = await fetch(`${SCRIPT_URL}?action=getStudentList`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const result = await response.json();

    if (result.status === "success") {
      return result.data;
    } else {
      throw new Error(result.message);
    }
  } catch (error) {
    console.error("Error:", error);
    throw error;
  }
}

4.3 데이터 저장 구현

async function submitSurvey(surveyData) {
  try {
    const response = await fetch(SCRIPT_URL, {
      method: "POST",
      mode: "no-cors",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(surveyData),
    });
    return true;
  } catch (error) {
    console.error("Error:", error);
    throw error;
  }
}

5. 보안 및 권한 설정

  1. Apps Script 배포 시 설정:

    • 실행 대상: ‘모든 사용자’
    • 액세스 권한: ‘누구나’
  2. 스프레드시트 권한:

    • Apps Script에 읽기/쓰기 권한 부여

6. 주의사항

  1. 데이터 검증

    • 클라이언트에서 기본적인 데이터 검증 수행
    • 서버 사이드에서도 데이터 유효성 검사 필요
  2. 에러 처리

    • 네트워크 오류, 데이터 형식 오류 등 고려
    • 사용자에게 적절한 피드백 제공
  3. 성능 고려사항

    • 대량의 데이터 처리 시 시간 소요
    • 적절한 데이터 청소 및 관리 필요

마치며

Google Spreadsheet를 데이터베이스로 활용하면 간단한 웹 애플리케이션을 서버리스로 구현할 수 있습니다. 특히 교육 현장에서 사용되는 도구의 경우, 익숙한 스프레드시트 인터페이스를 통해 데이터를 쉽게 관리할 수 있다는 장점이 있습니다.