Apps Script는 Google Workspace 애플리케이션(Google Drive, Calendar, Sheets, Docs, Gmail 등)을 자동화하고 커스터마이즈할 수 있는 JavaScript 기반의 클라우드 스크립트 언어입니다.
Apps Script는 엑셀의 VBA와 유사한 도구로, 반복 작업을 자동화하고 구글 워크스페이스와의 연동을 통해 번거로운 업무를 간소화할 수 있는 강력한 기능을 제공합니다. 
앱스 스크립트를 활용한 다양한 사례가 있지만, 막상 적용하려고 하니 감이 잘 잡히지 않더라고요.
설명이 미흡한 부분이 많지만 '이런 경우도 활용할 수 있군~' 정도로 가볍게 읽어주시면 감사하겠습니다!
1. 담당자 자동 메일 발송
교사는 구글 클래스룸을 통해 학생들에게 문서 사본을 배포하거나 제출받을 수 있지만,
동일한 문서가 아닌 경우 또는 교사나 학생이 아닌 외부 업체와 협업할 때, 수작업으로 내용을 확인해야 하는 번거로움이 있습니다.
이럴 때, 구글의 확장 프로그램인 Apps Script를 활용하면 더욱 효율적으로 업무를 처리할 수 있답니다!
[확장 프로그램] - [Apps Script]를 실행하여 아래 코드를 입력하고,
저장한 뒤 [실행] 버튼을 누르시면
// 메뉴 추가
function onOpen() {
var ui = SlidesApp.getUi();
ui.createMenu('작업')
.addItem('제출', 'submitSlide')
.addToUi();
}
// 제출 함수
function submitSlide() {
var email = "example@naver.com"; // 검토자가 받을 이메일
var slide = SlidesApp.getActivePresentation();
var slideTitle = slide.getName(); // 슬라이드 제목 가져오기
var slideId = slide.getId(); // 프레젠테이션 ID 가져오기
var slideUrl = "https://docs.google.com/presentation/d/" + slideId;
try {
// 이메일 제목과 내용 설정
var subject = slideTitle + " 제출";
var body = `
슬라이드 제목: ${slideTitle}\n
슬라이드 링크: ${slideUrl}
`;
// 이메일 전송
GmailApp.sendEmail(email, subject, body);
// 알림 표시
SlidesApp.getUi().alert("담당자에게 이메일 알람이 전송되었습니다.\n감사합니다! 👏");
} catch (e) {
SlidesApp.getUi().alert("오류가 발생했습니다: " + e.message);
}
}
JavaScript
복사
아래와 같이 새로운 '제출'이라는 메뉴가 생긴 것을 확인할 수 있습니다!
만약 해당 문서를 변형 없이 공유하고 싶다면, 아래와 같이 사본을 전달해 보세요!
원본 공유 링크 중 edit
copy로 수정하면 끝!
공유받은 사용자는 '사본 만들기'를 통해 앱스 스크립트 파일 및 기능도 함께 복사할 수 있습니다.
2. 공유 활동지
스프레드시트는 기본적으로 [데이터] - [시트 및 범위 보호] - [범위 수정 권한] - [맞춤 - 수정자 추가]를 통해 영역별 접근 권한을 부여할 수 있습니다.
그러나 공유된 사용자가 많을 경우에 권한을 수동 부여하는 작업은 상당히 번거롭습니다.
이런 경우에도 반복 구조를 적절히 활용해 계정이 명시되어 있는 열과 행을 사용자에게 입력받아 한 번에 '보호된 시트 및 범위' 설정을 할 수 있습니다.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('보호 범위 일괄 생성')
.addItem('시작', 'protectRowsBasedOnGoogleAccount')
.addItem('삭제', 'removeAllProtections')
.addToUi();
}
function protectRowsBasedOnGoogleAccount() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); // 현재 활성화된 시트 사용
// 사용자로부터 열 알파벳과 시작하는 행 번호 입력받기
var columnLetter = Browser.inputBox('구글 계정이 들어 있는 열 알파벳을 입력하세요. (예: B)').toUpperCase(); // 열 알파벳을 대문자로 변환
var startRow = parseInt(Browser.inputBox('시작하는 행 번호를 입력하세요. (예: 2)')); // 시작하는 행 번호
// 열 알파벳을 숫자로 변환
var columnNumber = columnLetterToNumber(columnLetter);
if (isNaN(columnNumber) || isNaN(startRow)) {
SpreadsheetApp.getUi().alert('유효한 열 알파벳과 숫자를 입력하세요.');
return;
}
// 특정 구글 계정에 따라 행별로 보호 설정 적용
var range = sheet.getDataRange(); // 시트 전체 범위를 가져옵니다
var values = range.getValues(); // 시트의 데이터를 가져옵니다
for (var i = startRow - 1; i < values.length; i++) { // 사용자가 입력한 행 번호부터 시작
var email = values[i][columnNumber - 1]; // 사용자가 입력한 열 번호에 해당하는 값을 가져옵니다
if (email) { // 구글 계정이 있는 경우에만 진행
var rowRange = sheet.getRange(i + 1, 1, 1, sheet.getLastColumn()); // 해당 행의 전체 범위를 가져옵니다
var rowProtection = rowRange.protect().setDescription('Protected row ' + (i + 1)); // 보호 범위를 설정하고 설명 추가
rowProtection.addEditor(email); // 해당 이메일 사용자를 편집자로 추가
rowProtection.removeEditors(rowProtection.getEditors().filter(function(user) {
return user.getEmail() != email;
})); // 다른 편집자를 제거
if (rowProtection.canDomainEdit()) {
rowProtection.setDomainEdit(false); // 도메인 내의 다른 사용자가 편집하지 못하도록 설정
}
}
}
}
function removeAllProtections() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); // 현재 활성화된 시트 사용
// 시트에서 모든 보호 조치 제거
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
// 시트 보호 제거
var sheetProtection = sheet.getProtections(SpreadsheetApp.ProtectionType.SHEET);
for (var i = 0; i < sheetProtection.length; i++) {
var protection = sheetProtection[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
// 열 알파벳을 숫자로 변환하는 함수
function columnLetterToNumber(columnLetter) {
var columnNumber = 0;
var length = columnLetter.length;
for (var i = 0; i < length; i++) {
columnNumber *= 26;
columnNumber += columnLetter.charCodeAt(i) - ('A'.charCodeAt(0) - 1);
}
return columnNumber;
}
JavaScript
복사
3. 구글 스프레드 시트 비밀번호 설정
엑셀과 다르게, 구글 스프레드시트에서는 기본적으로 문서의 읽기/수정 시 비밀번호를 설정하는 기능을 제공하지 않습니다. 협업, 클라우드 기반 환경이라는 명확한 장점 때문에 스프레드시트를 사용하시는 분들도 이 부분 때문에 보안이 우려되는 경우가 많았을 텐데요, 저 또한 학생 상담 이력이나 생활기록부, 반 편성 등 민감한 정보를 다룰 때 외부의 접근으로부터 문서를 보호하는 방법이 없을까 고민이 되더라고요.
완벽하진 않지만, 앱스 스크립트를 활용해서 이 문제를 해결하는 방법을 살펴보도록 하겠습니다.
function protectSheet() {
const PASSWORD = "1234"; // 비밀번호 설정 (예:1234)
const ui = SpreadsheetApp.getUi();
while (true) {
// 비밀번호 입력 요청
let result = ui.prompt("비밀번호 확인", "비밀번호를 입력하세요.\n", ui.ButtonSet.OK_CANCEL);
let button = result.getSelectedButton();
let input = result.getResponseText();
// 사용자가 OK 버튼을 누르고 비밀번호가 일치할 경우
if (button == ui.Button.OK && input === PASSWORD) {
ui.alert("Google Sheet 사용이 가능합니다.");
break;
}
// 사용자가 취소 버튼을 누를 경우
if (button == ui.Button.CANCEL) {
ui.alert("작업이 취소되었습니다.");
break;
}
// 비밀번호가 틀린 경우
ui.alert("비밀번호가 일치하지 않습니다. 다시 시도해 주세요.");
}
}
JavaScript
복사
처음에는 위와 같이 단순하게 설계하였으나, 팝업창 뒤로 시트의 내용 일부가 노출되는 문제를 확인했습니다.
따라서,
1) HTML Service를 사용하여 별도의 HTML 팝업창을 구현
2) 사용자가 비밀번호를 입력하기 전에 민감한 시트를 ‘숨김’으로 처리하여 정보 노출 방지
하도록 시나리오를 수정해야겠다는 아이디어를 얻었습니다. (thanks to GPT...)
저는 'protect'라는 빈 시트를 하나 생성해 두고,
스프레드시트를 열 때 자동으로 이 시트를 제외한 모든 시트를 ‘숨김’으로 처리한 뒤
사용자가 비밀번호를 옳게 입력하면 자동으로 숨겨진 시트가 복원되도록 설정하였습니다.
function onOpen() {
hideSheets(); // 스프레드시트를 열 때 시트를 숨김
const ui = SpreadsheetApp.getUi();
let isAuthorized = false; // 비밀번호 인증 여부
// 비밀번호가 맞을 때까지 반복
while (!isAuthorized) {
const response = ui.prompt('비밀번호 확인', '비밀번호를 입력하세요:', ui.ButtonSet.OK);
if (response.getSelectedButton() === ui.Button.OK) {
const password = response.getResponseText();
if (password === "1234") { // 비밀번호 설정
unhideSheets(); // 비밀번호가 맞으면 숨겨진 시트를 보임
ui.alert('비밀번호가 확인되었습니다. 숨겨진 시트를 복원합니다.');
isAuthorized = true; // 루프 종료
} else {
ui.alert('비밀번호가 일치하지 않습니다. 다시 입력해 주세요.'); // 비밀번호가 틀리면 경고
}
} else {
ui.alert('비밀번호를 입력해야 시트를 사용할 수 있습니다.'); // 취소 방지
}
}
}
// 특정 시트를 제외하고 숨기기
function hideSheets() {
const sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
sheets.forEach(sheet => {
if (sheet.getName() !== "protect") { // 숨기지 않을 시트 이름 설정
sheet.hideSheet();
}
});
}
// 숨겨진 시트를 다시 보이기
function unhideSheets() {
const sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
sheets.forEach(sheet => {
sheet.showSheet();
});
}
JavaScript
복사
다소 엉성하지만, 학생들의 개인 정보가 포함된 시트를 열기 전에 해당 기능을 걸어두면 좋습니다. (정확한 보안을 위해서는 역시 공유자를 따로 설정하거나, 엑셀에서 비밀번호를 거는 방법이 가장 안전할 것 같습니다…ㅎㅎ)
4. 교복 검사 시트지 월별 생성
저희 학교는 공통적으로 조례시간마다 교복 검사를 실시해서 생활교육위원회를 개최하는데요, 체크 리스트를 매월 생성하고 또 날짜별로 열을 수정하는 것이 번거롭다고 생각했습니다. 하지만, 이제는 이런 반복적인 업무는 앱스 스크립트를 활용해서 간편하게 해결할 수 있습니다!
우선 3월 시트만 하나만 양식을 구상하여 생성한 뒤,
첫 번째 날짜(기준 셀)와 요일을 다음과 같은 양식으로 입력합니다.
토요일과 일요일을 제외하고 새로운 열을 추가하기 위해,
위와 같이 함수를 적용한 뒤, 이후 셀은 일괄 생성하시면 됩니다.
함수 입력
(기준셀) =TEXT(DATE(2025,3,4),"M/D (aaa)")
(기준 셀 이후)
=TEXT(WORKDAY(DATEVALUE(LEFT(F1,FIND(" ",F1)-1)),1),"M/D (aaa)")
앱스 스크립트에서 아래 코드를 실행시키면, 자동으로 기준 셀을 월별로 변경하면서 월별 시트를 한 번에 생성해 줍니다!
function copyAndModifySheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName('3월'); // 3월 시트
var months = ['4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
for (var i = 0; i < months.length; i++) {
var month = months[i];
// 시트 복제
var newSheet = sourceSheet.copyTo(ss).setName(month);
// E1 셀에 대한 함수 수정
newSheet.getRange('E1').setFormula('=TEXT(DATE(2025,' + (i + 4) + ',1),"M/D (aaa)")');
// F1부터 X1까지 자동 채우기 및 날짜 수정
for (var j = 6; j <= 24; j++) { // F1 (6)부터 X1 (24)까지
var formula = '=TEXT(WORKDAY(DATEVALUE(LEFT(E1,FIND(" ",E1)-1)),' + (j - 5) + '),"M/D (aaa)")';
newSheet.getRange(1, j).setFormula(formula);
}
}
}
JavaScript
복사
앱스 스크립트를 잘 활용하면 확실히 반복되는 업무를 자동화하는 데 많은 도움을 받을 수 있을 것 같습니다.
다만, 실제 본인의 업무에 어떻게 녹여낼지는 더 고민하고, 그 과정에 익숙해지는 것이 필요할 것 같아요.
학교 현장에서 반복되는 수많은 불필요한 반복 노동들…
이제는 앱스 스크립트로 탈출합시다 ! 