문제점
구글 폼으로 제출 받은 파일들은 하나의 폴더에 모인다.
제출자가 많을 경우 추가적인 파일 분류가 필요할 수 있다.
구글 폼 이용하시면 양식 제출로 파일 업로드도 가능하다는 사실 알고 계실거예요.
하지만 여러 설문을 통해 받은 파일들은 구글 드라이브의 한 폴더에 일괄적으로 쌓이기 때문에 나중에 따로 파일을 정리해야해서 불편했던 경험이 있으실 것 같아요.
오늘은 설문 양식을 제출 받으면 업로드된 파일이 자동으로 폴더 별로 분류되도록 자동화하는 기능을 알아보겠습니다.
예시 보기
회사에서 연말 정산 자료 제출을 구글 폼으로 받는 상황을 가정해 보겠습니다. 만약 위 양식을 토대로 연말 정산 관련 자료를 받을 경우 발생할 수 있는 예상 문제를 살펴볼까요?
위와 같이 구글 폼으로 파일을 받을 경우 두 가지 문제점이 있습니다.
첫 번째, 파일이 하나의 폴더에 일괄적으로 저장되므로 부서별 분류가 필요할 경우 직접 분류해야 한다.
두 번째, 수정본 파일을 거듭 보냈을 경우 파일 관리에 어려움이 있다.
사실 위 예시와 같이 제출자 수가 적을 경우에는 크게 문제가 되지 않을 수도 있습니다. 하지만 제출자가 수십 명 이상이거나, 한 사람 당 파일을 여러 개 올리는 상황이라면? 파일 정리 시간이 기하급수적으로 늘어날 수 있겠죠?
오늘은 두 가지 문제점 중 첫 번째로 파일을 분류하는 작업을 진행해보도록 하겠습니다.
문제 해결 방법
Apps Script(앱스 스크립트) 활용으로 파일 자동 분류하기
Apps Script를 사용하면 누군가 구글 폼 양식을 제출할 때마다 특별한 동작을 자동으로 수행하도록 설정이 가능해요.
그래서 먼저, “어떤 동작을 수행하게 할 것인가”, 와 “언제 수행하게 할 것인가” 이 두 가지를 설정하는 방법에 대해서 설명드릴게요.
먼저 Apps Script를 실행해 볼까요?
구글폼 오른쪽 상단 더보기(⋮) 버튼 클릭 후 Apps Script 실행
앱 스크립트 프로젝트의 이름을 변경해준 뒤 코드 작성란에 필요한 로직을 작성합니다.
코드 작성란에 구체적으로 “어떤 동작을 수행하게 할 것인가” 에 대한 내용을 적는다고 보시면 됩니다.
동작 확인을 위한 예시 코드 실습하기
본격적인 코드 작성에 앞서 예시 코드를 통해 Apps Script의 동작 방식을 이해해 봅시다. 코드 작성이 어렵게 느껴지지 않도록 상세하게 설명드릴게요.
예시 코드 살펴보기
function myFunction(e) {
const response = e.response; // 제출된 응답 정보 가져오기
const itemResponses = response.getItemResponses(); // 질의 응답에 대한 정보만 선별하기
Logger.log(itemResponses) // 질의 응답 기록하기
// 질의응답 하나하나 꺼내기
for (let i = 0; i < itemResponses.length; i++) {
const question = itemResponses[i].getItem().getTitle(); // 질문 제목 선택
const answer = itemResponses[i].getResponse(); // 응답 내용 선택
Logger.log(question + ": " + answer); // 질문: 응답 형태로 기록하기
}
}
⚠️ 괄호, 띄어쓰기 등 정확하게 일치하도록 똑같이 복사해서 사용해주세요.
Apps Script가 어떻게 동작하는지 확인하기 위해 예시 코드를 작성해 봅시다.
위 코드는 설문 양식이 제출되면 "해당 양식의 질문과 답변을 로그에 기록하도록 지시하는 코드" 입니다.
먼저 동작 확인을 위해 기존에 적혀있는 코드는 싹 지워준 뒤 위 코드를 복사붙여넣기 합니다.
복사한 뒤 Ctrl + S 또는 저장하기 아이콘을 눌러 저장합니다.
복사붙여넣기 결과는 아래와 같습니다.
트리거 등록하기
트리거는 방아쇠, 기폭 장치 등을 의미하죠? 편집기에서 작성한 코드를 “언제” 동작하게 할지 정해주는 곳이예요. 우리는 “설문 양식을 제출할 때” 동작하게 해야겠죠?
왼쪽 사이드바에 마우스를 가져간 후 트리거(시계 모양 아이콘)를 클릭합니다.
오른쪽 하단에 트리거 추가 버튼을 클릭해 새 트리거를 만듭니다.
함수 선택, 이벤트 소스, 이벤트 유형을 사진과 동일하게 맞춰서 "저장" 클릭하시면 새 트리거가 생성됩니다.
저장 클릭 시 이렇게 동의를 구하는 페이지가 뜨는데 "Allow" 해주시면 됩니다.
아무래도 자동화 프로그램이 하나의 봇이기 때문에 이 봇이 내 구글 폼을 들여다 보는 것에는 동의가 필요한 것 같습니다.
이제 예시 코드에 대한 트리거 설정까지 완료되었습니다. 구글 폼 양식을 제출하면 어떤 일이 일어나는지 살펴볼까요?
설문 양식 제출하기
이제 앱 스크립트가 잘 동작하는지 확인해 볼 시간입니다. 기존에 만들어 둔 양식의 링크를 들어가 실제 설문에 임하는 것처럼 임의로 작성해서 제출해 봅니다.
제 경우 이름: 왕수박 / 부서: 시설팀으로 하고 연말정산 자료를 제출해 보았습니다.
결과 확인하기
앱 스크립트로 다시 돌아오신 뒤 왼쪽 사이드바에서 “실행”을 클릭합니다.
여기서는 트리거가 발생한 직후 기록된 내용들을 확인할 수 있는 곳입니다. 일반적으로 “로그” 라고 부릅니다.
만약 코드에 오류가 있을 경우 이 곳에서 그 오류 내역을 확인할 수 있습니다.
예시 코드로 구성한대로 제출된 질의응답이 로그에 잘 기록되었습니다.
본격적인 파일 자동화 코드 작성하기
코드 작성을 위한 함수 이해하기
코드 작성에 앞서 함수가 뭔지 애매하신 분들을 위해 함수와 구성 요소에 대해 간단히 설명 드리겠습니다.
함수는 어떤 값을 집어넣으면 미리 정해둔 동작, 알고리즘을 거쳐 새로운 결과를 내주는 도구를 의미합니다.
function myFunction(e) {}
- function myFunction : myFunction이라는 이름을 가진 함수를 만들겠다는 의미입니다.
- (e) : 함수에 들어갈 요소를 의미합니다. 함수 실행에 필요한 요소라고 생각해주세요.
- {} : 중괄호 안에 구체적인 동작(코드)을 작성합니다. 중괄호 안에 내용이 실제로 동작하게 될 로직입니다.
함수는 흔히 “믹서기”와 비교합니다. “믹서기”가 함수라면 “갈아버린다.” 라는 동작은 함수의 로직이라 볼 수 있습니다.
만약 믹서기에 당근을 넣는다면 “당근”은 함수에 필요한 요소로 볼 수 있습니다.
위 코드 예시에 적용해 본다면 아래와 같이 표현할 수 있겠습니다.
// 먼저 함수를 정의합니다.
function 믹서기(재료) {
return 재료를 "갈아버린다."
}
// 정의한 함수를 실행합니다.
const 당근주스 = 믹서기(당근)
정리하자면 앞으로 우리가 할 일은 "파일을 받으면, 그 파일을 부서별로 자동 분류한다." 라는 작업을 만들텐데 이러한 작업이 수행될 수 있도록 "함수" 라는 형태를 가지고 구현해야 하므로, 함수에 대해 먼저 정리해 보았습니다.
시나리오 작성하기
양식이 제출되었을 경우 어떻게 연말 정산 파일이 분류되도록 할지에 대한 구체적인 계획을 세워야 합니다. 여러 접근 방법이 있을 수 있겠지만 저는 아래와 같이 수행할 예정입니다.
1. 설문 양식을 제출하면 myFunction이라는 함수가 실행됩니다.
2. 함수는 아래 동작을 수행합니다.
a. 제출된 양식을 가져옵니다.
b. 제출된 양식에서 부서 정보를 확인합니다.
c. 제출된 양식에서 업로드된 파일 정보(ID)를 확인합니다.
d. 확인된 정보를 토대로 파일을 사전에 생성해 둔 해당 부서 폴더로 이동시킵니다.
e. 이동 시킨 뒤 해당 파일이 해당 부서 폴더로 잘 이동되었다는 기록을 남깁니다.
3. “실행” 란에 접속해서 트리거가 잘 수행되었는지 확인합니다.
파일 자동 정리 구현하기
저장할 폴더 만들기
먼저 구글 드라이브에 부서별 폴더를 미리 생성해 둡니다.
저의 경우 구글 폼 내에서 “연말정산 자료 제출” 이란 이름으로 업로드 항목을 만들어 뒀었기 때문에 이와 동일한 이름의 폴더가 이미 생성되어 있습니다. 그래서 해당 폴더에 부서별 폴더를 생성했습니다.
아마 이전 예시 코드를 따라오셨던 분들이라면 이전에 업로드된 파일들이 이미 있으실텐데 전부 삭제해주신 뒤 폴더를 생성해주시면 되겠습니다.
여기서부터 중요합니다. 이제 생성한 각 폴더의 ID를 알아내야 합니다. 구글 드라이브는 생성되는 모든 폴더와 파일에 대한 ID를 가지고 있습니다.
위 사진을 보시면 저의 경우 시설팀 폴더로 들어간 뒤 URL 창을 보면 ID를 확인할 수 있습니다.
전체 주소가 아니라 “folders/” 이후에 적힌 “1QiF-b0WK…” 이렇게 쭈욱 적힌 내용이 ID에 해당합니다. 이 정보를 어디에 미리 복사해 두세요.
코드 작성하기
이제 위 시나리오대로 코드를 작성해 보겠습니다. 사실상 코딩이기 때문에 코딩에 익숙하지 않으시다면 어렵게 느껴지실 수도 있지만 차근차근 잘 설명해 보도록 하겠습니다.
또한 요즘은 ChatGPT를 이용하면 얼마든지 코드를 쉽게 작성할 수 있기 때문에 향후 기능 추가 시 ChatGPT의 도움을 받으시는 걸 추천드립니다.
그렇지만 아무래도 막연히 코드를 복사하는 것 보다는 코드를 보는 게 조금이라도 더 익숙해 지는게 좋겠죠?
function myFunction(e) {
// 0. 부서 폴더 ID 정보를 기록합니다.
const teamFolderIds = {
'총무팀': '1Y0sqeOLfJJxZr8Vhv5v6G5MtzCAeCpOl',
'재무팀': '1miHQoTTE7HMK_Whodyb4B34Jwstka_tb',
'인사팀': '1lGSLTMRAwR-rHwauGQJ4jHQckIYpAbYg',
'시설팀': '1QiF-b9WKBkeC6SVAorZsX4tJIWFd27Q5',
} // ex) teamFolderIds['시설팀'] = '1QiF-b9WKBkeC6SVAorZsX4tJIWFd27Q5'
// a. 제출된 양식 가져오기
const response = e.response;
/** itemResponses에는 양식 내 각 항목에 대한 질의응답 정보들이 들어있습니다.*/
const itemResponses = response.getItemResponses();
// b. 제출된 양식에서 부서 정보 가져오기
/**
* 부서에 대한 질문 항목은 전체 양식에서 두 번째에 위치했었습니다.
* 프로그래밍의 순서 개념은 보통 0에서 시작하므로 두 번째는 1에 해당합니다.
* itemResponses에서 [1]을 붙이는 것은 두 번째 항목 정보를 가져오겠다는 의미입니다.
*/
const team = itemResponses[1].getResponse(); // 부서 정보를 GET, ex) '시설팀'
const teamFolderId = teamFolderIds[team] // 해당하는 부서의 폴더 ID를 획득
const teamFolder = DriveApp.getFolderById(teamFolderId); // 폴더 ID로 폴더에 접근
// c. 제출된 양식에서 업로드된 파일 정보(ID) 가져오기
/**
* 업로드 항목의 응답 정보는 아래와 같은 리스트 형태로 가져옵니다.
* ex) ['1qLbpwAkhQ5YT4o41c0FvMsI0Kc589rI2', '1fxcl1PlaOz9nvsEr4FkYhhGul6vZ0VcK',...]
* 쉼표로 구분된 각각의 문자는 각 파일의 ID에 해당합니다.
*/
const fileIdList = itemResponses[2].getResponse(); // 업로드 파일 정보 GET (세 번째 항목)
fileIdList.forEach((id) => { // 리스트에서 파일 ID 하나씩 꺼내기
const file = DriveApp.getFileById(id); // ID를 통해 파일에 접근
const fileName = file.getName(); // 파일 이름 획득, 기록에 남기기 위해
// d. 해당 부서 폴더로 파일 이동
file.moveTo(teamFolder) // 파일을 지정한 부서 폴더로 이동
// e. 성공 기록 남기기
Logger.log(`성공: ${fileName} -> ${teamFolder}`); // 파일 이동이 완료되었음을 기록
})
}
전체 코드는 위와 같습니다. 각 코드 한 줄의 기능을 주석으로 설명해 두었으니 참조 바랍니다. 바로 적용해보고 싶으신 분들은 위 전체 코드를 복사하셔서 붙여 넣으신 뒤 사용하시면 되겠습니다.
그러면 코드를 시나리오에 맞춰 찬찬히 한 번 살펴보겠습니다.
⚠️ 체크사항
아래 코드는 전체 코드의 일부이므로 복붙하면 올바로 동작하지 않습니다.
슬래시 두 개(//) 또는 /** */ 이것 안에 적힌 내용은 “주석”이라 부르며 내용 설명에 해당합니다.
코드는 전체적으로 위에서 아래로 순차적으로 동작한다는 점을 기억해 주세요.
function myFunction(e) {
// 0. 부서 폴더 ID 정보를 기록합니다.
const teamFolderIds = {
'총무팀': '1Y0sqeOLfJJxZr8Vhv5v6G5MtzCAeCpOl', // ID 교체할 것
'재무팀': '1miHQoTTE7HMK_Whodyb4B34Jwstka_tb', // ID 교체할 것
'인사팀': '1lGSLTMRAwR-rHwauGQJ4jHQckIYpAbYg', // ID 교체할 것
'시설팀': '1QiF-b9WKBkeC6SVAorZsX4tJIWFd27Q5', // ID 교체할 것
} // ex) teamFolderIds['시설팀'] = '1QiF-b9WKBkeC6SVAorZsX4tJIWFd27Q5'
📌 코드 설명
myFunction이라는 이름의 함수 작성을 시작합니다.
teamFolderIds는 우리가 생성한 부서별 폴더의 ID를 기록해두는 곳입니다.
기존 ID는 지우고 여러분이 생성한 폴더의 ID를 넣어주세요.
반드시 위 예시처럼 ID를 작은따옴표(’)로 감싸주신 뒤 마지막에 쉼표(,)를 꼭 넣어주세요.
코드가 하나라도 틀릴 경우 코드 오류가 발생할 수 있습니다.
// a. 제출된 양식 가져오기
const response = e.response;
/** itemResponses에는 양식 내 각 항목에 대한 질의응답 정보들이 들어있습니다.*/
const itemResponses = response.getItemResponses();
// b. 제출된 양식에서 부서 정보 가져오기
/**
* 부서에 대한 질문 항목은 전체 양식에서 두 번째에 위치했었습니다.
* 프로그래밍의 순서 개념은 보통 0에서 시작하므로 두 번째는 1에 해당합니다.
* itemResponses에서 [1]을 붙이는 것은 두 번째 항목 정보를 가져오겠다는 의미입니다.
*/
const team = itemResponses[1].getResponse(); // 부서 정보를 GET, ex) '시설팀'
const teamFolderId = teamFolderIds[team] // 해당하는 부서의 폴더 ID를 획득
const teamFolder = DriveApp.getFolderById(teamFolderId); // 폴더 ID로 폴더에 접근
📌 코드 설명
제출된 양식에서 부서 정보를 가져온 뒤 해당 부서의 폴더 ID를 가져오는 코드입니다.
자세한 사항은 코드 내 주석을 따라와 주세요.
// c. 제출된 양식에서 업로드된 파일 정보(ID) 가져오기
/**
* 업로드 항목의 응답 정보는 아래와 같은 리스트 형태로 가져옵니다.
* ex) ['1qLbpwAkhQ5YT4o41c0FvMsI0Kc589rI2', '1fxcl1PlaOz9nvsEr4FkYhhGul6vZ0VcK',...]
* 쉼표로 구분된 각각의 문자는 각 파일의 ID에 해당합니다.
*/
const fileIdList = itemResponses[2].getResponse(); // 업로드 파일 정보 GET (세 번째 항목)
fileIdList.forEach((id) => { // 리스트에서 파일 ID 하나씩 꺼내기
const file = DriveApp.getFileById(id); // ID를 통해 파일에 접근
const fileName = file.getName(); // 파일 이름 획득, 기록에 남기기 위해
// d. 해당 부서 폴더로 파일 이동
file.moveTo(teamFolder) // 파일을 지정한 부서 폴더로 이동
// e. 성공 기록 남기기
Logger.log(`성공: ${fileName} -> ${teamFolder}`); // 파일 이동이 완료되었음을 기록
})
📌 코드 설명
양식 제출 시 업로드된 파일 정보를 가져옵니다.
파일 정보를 GET하면 파일 ID를 얻을 수 있는데, 파일이 여러 개일 수 있기 때문에 리스트라고 부르는 대괄호 안에 담겨져 나옵니다.
리스트 안에 있는 파일 ID를 하나씩 꺼내면서 해당 부서 폴더로 파일을 이동시켜 줍니다.
마지막으로 이동이 성공했다면 로그에 기록을 남깁니다.
트리거 재등록하기
코드 작성을 완료하셨다면 권한 관련 이슈가 있어 트리거를 다시 등록해야 합니다.
기존에 등록된 트리거는 삭제하고, 다시 생성해 줍니다.
트리거 등록 과정에서 위와 같은 오류를 만난다면 하단에 표시된 곳(Allow)을 클릭해서 권한을 다시 등록해주시면 됩니다.
결과 보기
재무팀의 왕찐만두 사원이 연말정산자료 2개를 포함하여 구글 폼 양식을 제출한 결과 파일이 재무팀 폴더로 파일이 잘 이동한 것을 확인할 수 있습니다. 추가로 앱 스크립트의 “실행” 에서 성공했을 경우 기록했던 로그도 확인해 볼 수 있습니다.
마무리하며
앱 스크립트를 사용하려면 사실 자바스크립트(JavaScript)라는 프로그래밍 언어에 대한 약간의 지식이 있으면 유리할 수 있습니다. 하지만 프로그래밍 지식이 없더라도 오늘 글을 통해서 자동화 생성 흐름과 코드 해석을 잘 따라와주셨다면 이후 ChatGPT나 기타 생성형 AI로 앱 스크립트 코드 생성 요청을 해서도 얼마든지 만들 수 있습니다.
많은 분들이 앱 스크립트를 사용해서 더 많은 업무 자동화를 이뤄낼 수 있었으면 좋겠습니다 😊
'일상 속 개발 탐구' 카테고리의 다른 글
맥북으로 개발자처럼 사진 영상 편집하기 (3) - 유튜브 동영상 저장 및 편집하기 (0) | 2025.02.07 |
---|---|
맥북으로 개발자처럼 사진 영상 편집하기 (2) - 사진 편집하기 (0) | 2025.01.15 |
맥북으로 개발자처럼 사진 영상 편집하기 (1) - 터미널과 친해지기 (0) | 2025.01.15 |