엑셀·구글시트 자동화 실험No. 121
구글시트 앱스스크립트로 완료된 행을 보고서에 모으는 방법
결론부터
구글시트에서 완료된 행을 반복해서 모을 때는 앱스스크립트로 원본을 읽고 보고서 전용 시트에 다시 쓰는 구조를 만들 수 있다. 기존 보고서를 갱신하는 방식이므로 중복 누적을 피할 수 있지만 과거 상태는 보존하지 않는다. 사본에서 열 제목과 출력 범위를 확인한 뒤 실행한다.
필터 결과를 계속 붙여 넣을 때 생기는 중복
예를 들어 작업 목록에서 상태가 완료인 행만 모아 검토한다고 가정한다. 어제 복사한 목록 아래에 오늘의 완료 행을 다시 붙이면 같은 작업이 여러 번 들어갈 수 있다. 반대로 지난 보고서를 그대로 두면 완료가 취소된 행도 남는다.
이번 보고서는 현재 상태를 보여 주는 용도다. 실행할 때마다 원본에서 완료 행을 다시 골라 결과를 교체한다. 이력을 쌓거나 완료 시점을 기록하는 작업과는 구분한다.
원본 시트 이름은 작업으로 정한다. A열은 작업번호, B열은 작업명, C열은 상태다. 첫 행의 제목도 이 순서로 입력한다. 출력 시트 이름인 완료보고서는 이 스크립트만 사용하는 공간으로 비워 둔다.
읽기와 쓰기를 묶은 스크립트
사본 구글시트에서 확장 프로그램의 Apps Script를 연다. 다음 코드를 저장하고 rebuildCompleted 함수를 선택해 직접 실행한다. 코드의 행과 열 번호는 설명용 예시 숫자다.
function rebuildCompleted() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const src = ss.getSheetByName('작업');
if (!src) throw new Error('작업 시트가 없다.');
const rows = src.getRange(
1, 1, Math.max(src.getLastRow(), 1), 3
).getValues();
const headers = ['작업번호', '작업명', '상태'];
if (!headers.every((v, i) => rows[0][i] === v)) {
throw new Error('열 제목과 순서를 확인한다.');
}
const selected = rows.slice(1).filter(
r => r[0] !== '' && r[2] === '완료'
);
const output = [headers, ...selected].map(
r => r.map(v =>
typeof v === 'string' && v.startsWith('=')
? "'" + v : v
)
);
const dst = ss.getSheetByName('완료보고서')
|| ss.insertSheet('완료보고서');
const height = Math.max(dst.getLastRow(), output.length);
dst.getRange(1, 1, height, 3).clearContent();
dst.getRange(1, 1, output.length, 3).setValues(output);
}처음 실행할 때 권한 승인이 표시되면 실행 계정과 요청 권한을 확인한다. 이 코드는 현재 스프레드시트의 시트를 읽고 쓰며, 메일 발송이나 외부 전송 기능은 포함하지 않는다.
Google 개발자 문서의 Range 안내는 getValues와 setValues의 배열 구조를 설명한다. 코드는 행마다 셀에 접근하는 대신 범위를 배열로 읽고 결과를 묶어서 쓴다.
등호로 시작하는 문자열 앞에는 작은따옴표를 붙인다. 보고서에 텍스트를 옮길 때 수식으로 해석되는 일을 피하기 위한 처리다. 원본 수식 자체는 복사하지 않고 계산된 값을 읽는다.
주의
파일 사본에서 시험한다. 실행하면 완료보고서의 A열부터 C열까지 기존 내용이 지워지므로 해당 영역에 수동 메모나 보관용 자료를 넣지 않는다.
재실행해도 행이 늘어나지 않는지 확인하기
시험할 때는 완료 행, 대기 행, 작업번호가 빈 행을 준비한다. 완료이면서 작업번호가 있는 행만 결과에 나타나야 한다. 원본을 바꾸지 않고 다시 실행하면 같은 목록이 만들어져야 한다.
완료 상태를 대기로 바꾼 뒤 다시 실행하면 해당 행은 보고서에서 빠져야 한다. 완료 행이 하나도 없을 때는 제목 행만 남아야 한다. 이전 결과가 아래쪽에 남지 않는지도 확인한다.
이 구조는 실행할 때마다 결과를 덧붙이는 중복을 막는다. 다만 원본 자체에 동일한 작업번호가 여러 번 있으면 그 행들은 그대로 출력된다. 작업번호 기준 중복 제거는 별도의 규칙이 필요한 작업이다.
예상되는 변화는 필터 선택, 복사, 이전 결과 정리라는 반복 동작을 함수 실행으로 묶는 것이다. 처리 시간은 기록 수와 시트 상태에 따라 달라지므로 측정하지 않은 절감 시간을 제시하지 않는다.
상태 변경과 실행 시점의 한계, 마무리 점검
상태는 정확히 완료인 값만 선택한다. 앞뒤 공백이 붙거나 다른 표현으로 입력된 행은 빠진다. 공백만 있는 작업번호도 빈 문자열은 아니므로 통과한다. 입력 목록과 작업번호 검증을 원본에서 관리해야 한다.
열 제목을 검사하므로 열을 끼워 넣거나 순서를 바꾸면 실행이 멈춘다. 임의로 다른 열을 읽는 것보다 구조 변경을 먼저 발견하기 위한 장치다. 제목만 같고 내용의 의미가 바뀌는 경우까지 검증하지는 못한다.
결과 시트의 서식은 지우지 않는다. 이전에 셀을 병합하거나 보호해 두었다면 쓰기 단계에서 오류가 날 수 있다. 결과 영역은 병합하지 않은 편집 가능한 범위로 유지한다.
내용을 지운 뒤 쓰는 과정은 하나의 거래처럼 묶여 있지 않다. 쓰기가 실패하면 보고서가 빈 상태로 남을 수 있다. 원본은 보존되므로 오류를 해결한 뒤 다시 생성하되 결과를 반드시 확인한다.
여러 사람이 동시에 실행하거나 예약 실행을 추가하는 경우에는 실행 충돌과 권한을 별도로 설계해야 한다. 우선 직접 실행으로 출력과 누락 조건을 검증한다. 이 보고서는 실행 당시 원본을 읽은 결과이므로, 이후 변경 사항은 다음 실행에서 반영된다.