Skip to content

운영유지 리포트 자동화 구축 가이드

작성일: 2026.01.19
대상: PM, 개발팀, 관리자


1. 전체 프로세스 개요 (Process Overview)

이 시스템은 **고객의 요청(슬라이드)**부터 **최종 리포트(엑셀)**까지의 과정을 자동화합니다.

  1. 요청 (Start): 고객이 구글 슬라이드에서 [운영유지 도구] > [현재 슬라이드를 이슈로 등록] 메뉴 클릭.
  2. 생성: 깃허브에 이슈가 자동으로 등록됨 (슬라이드 링크 포함).
  3. 작업: PM이 내용을 다듬고, 개발자가 작업을 진행.
  4. 완료 (End): 개발자가 이슈를 닫으면(Close), 엑셀 리포트가 자동 작성됨.

2. 준비물 (Prerequisites)

  1. Google 계정: 슬라이드 및 스프레드시트 관리 권한
  2. GitHub 계정:
    • Repo Admin 권한
    • Personal Access Token (Classic): 슬라이드에서 이슈를 만들기 위해 필요 (repo 권한 필수)
  3. 문서: 구글 슬라이드(요청용), 구글 스프레드시트(리포트용)

3. 단계별 구축 가이드 (Step-by-Step)

STEP 1. [보내는 쪽] 구글 슬라이드 설정 (고객 요청용)

고객이 메뉴 클릭 한 번으로 이슈를 등록할 수 있게 스크립트를 설정합니다.

  1. 구글 슬라이드 상단 메뉴 확장 프로그램 > Apps Script 클릭.
  2. Code.gs 내용을 지우고 아래 코드를 붙여넣습니다.
/**
* 설정 영역 (이 부분만 수정하세요)
*/
const GITHUB_TOKEN = "ghp_xxxxxxxxxxxx"; // GitHub Personal Access Token
const REPO_OWNER = "your-org-name"; // GitHub 조직명 또는 사용자명
const REPO_NAME = "your-repo-name"; // 레포지토리 이름
/**
* 1. 슬라이드 메뉴 만들기
* 슬라이드를 새로고침하면 상단 메뉴에 [운영유지 도구]가 생깁니다.
*/
function onOpen() {
SlidesApp.getUi()
.createMenu("🛠️ 운영유지 도구")
.addItem("👉 현재 슬라이드를 이슈로 등록", "createIssueFromSlide")
.addToUi();
}
/**
* 2. 이슈 생성 메인 함수
*/
function createIssueFromSlide() {
var ui = SlidesApp.getUi();
// 현재 보고 있는 슬라이드 정보 가져오기
var presentation = SlidesApp.getActivePresentation();
var slide = presentation.getSelection().getCurrentPage();
if (!slide) {
ui.alert("슬라이드를 선택한 상태에서 실행해주세요.");
return;
}
var slideId = slide.getObjectId();
var presentationId = presentation.getId();
var directLink =
"https://docs.google.com/presentation/d/" +
presentationId +
"/edit#slide=id." +
slideId;
// 슬라이드 내의 텍스트들을 긁어모으기 (요약용)
var slideText = "";
var shapes = slide.getShapes();
shapes.forEach(function (shape) {
if (shape.getText()) {
slideText += "- " + shape.getText().asString().trim() + "\n";
}
});
// 깃허브 이슈 제목과 내용 구성
var today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
var issueTitle = "[운영유지] 슬라이드 요청사항 (" + today + ")";
var issueBody =
"### 1. 요청 슬라이드 링크\n" +
"👉 [바로가기](" +
directLink +
")\n\n" +
"### 2. 슬라이드 텍스트 추출 (참고용)\n" +
(slideText ? slideText : "(텍스트 없음)") +
"\n\n" +
"---\n*Generated by Google Slides Automation*";
// 깃허브 API 전송
try {
var response = sendToGitHub(issueTitle, issueBody);
var responseData = JSON.parse(response.getContentText());
// 성공 시 알림 및 생성된 이슈 링크 보여주기
ui.alert(
"✅ 이슈 생성 완료!\n\n이슈 번호: #" +
responseData.number +
"\n(깃허브에서 확인하세요)",
);
} catch (e) {
ui.alert("❌ 오류 발생: " + e.toString());
}
}
/**
* 3. 깃허브 API 호출 헬퍼 함수
*/
function sendToGitHub(title, body) {
var url =
"https://api.github.com/repos/" + REPO_OWNER + "/" + REPO_NAME + "/issues";
var payload = {
title: title,
body: body,
};
var options = {
method: "post",
contentType: "application/json",
headers: {
Authorization: "token " + GITHUB_TOKEN,
},
payload: JSON.stringify(payload),
};
return UrlFetchApp.fetch(url, options);
}
  1. 설정 영역 수정 (상단 3개 변수):

    • GITHUB_TOKEN: GitHub Personal Access Token (repo 권한 필수)
    • REPO_OWNER: GitHub 조직명 또는 사용자명
    • REPO_NAME: 레포지토리 이름
  2. 저장 후 슬라이드를 새로고침합니다.

사용 방법

  1. 슬라이드 상단 메뉴에 [🛠️ 운영유지 도구] 메뉴가 생성됩니다.
  2. 요청할 슬라이드를 선택한 상태에서 [👉 현재 슬라이드를 이슈로 등록] 클릭.
  3. 슬라이드 링크 + 텍스트가 포함된 GitHub 이슈가 자동 생성됩니다.

STEP 2. [받는 쪽] 구글 스프레드시트 설정 (리포트용)

  1. 새 스프레드시트를 만들고 시트 이름을 **전체 진행내역**으로 변경합니다.
  2. **1행(헤더)**을 아래 순서대로 작성합니다. (A열 ~ N열)

Apps Script 설정

확장 프로그램 > Apps Script 클릭 후 아래 코드 붙여넣기.

function doPost(e) {
var sheetName = "전체 진행내역";
try {
var data = JSON.parse(e.postData.contents);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sheetName);
if (!sheet) {
sheet = ss.getSheets()[0];
}
// 날짜 예쁘게 바꾸기 (2026-01-19 -> 1월, 19일)
// 날짜 객체를 반환하도록 수정해서 분기 계산에 씀
function parseDateObj(dateStr) {
if (!dateStr) return null;
try {
return new Date(dateStr);
} catch (err) {
return null;
}
}
var createdDate = parseDateObj(data.created_at);
var closedDate = parseDateObj(data.closed_at);
// 엑셀에 적을 텍스트 ("1월", "19일")
var createdMonth = createdDate ? createdDate.getMonth() + 1 + "" : "-";
var createdDay = createdDate ? createdDate.getDate() + "" : "-";
var closedMonth = closedDate ? closedDate.getMonth() + 1 + "" : "-";
var closedDay = closedDate ? closedDate.getDate() + "" : "-";
// 1. 분기 계산 (작업월 기준)
var quarter = "-";
if (closedDate) {
var m = closedDate.getMonth() + 1;
quarter = Math.ceil(m / 3) + "분기"; // 1~3월=1분기, 4~6월=2분기...
}
// 2. 번호 자동 매기기
// 현재 시트의 행 개수를 세어서 번호를 만듭니다. (헤더가 1줄 있다고 가정)
// 만약 데이터가 하나도 없으면 1번, 있으면 그 다음 번호
var lastRow = sheet.getLastRow();
var newNo = lastRow - 1;
if (newNo < 1) newNo = 1; // 혹시 계산이 꼬이면 1번부터
// 링크 + 댓글 내용
var resolutionText = data.html_url + "\n\n[조치내역]\n" + data.resolution;
// 3. 헤더에 맞춘 정위치 배열
var rowData = [
newNo, // A: 번호 (1, 2, 3... 자동부여)
createdMonth, // B: 요청월
createdDay, // C: 요청일
"", // D: 요청자
data.title, // E: 요청사항
resolutionText, // F: 진행상황
quarter, // G: 분기 구분
closedMonth, // H: 작업월
closedDay, // I: 작업일
data.labels, // J: 이슈 구분 (라벨)
"", // K: 이슈 항목 (빈칸)
"완료", // L: 진행 단계
"", // M: 소요시간
data.assignee, // N: 담당자
];
sheet.appendRow(rowData);
return ContentService.createTextOutput(
JSON.stringify({ success: true })
).setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(
JSON.stringify({ success: false, error: err.toString() })
).setMimeType(ContentService.MimeType.JSON);
}
}