
엑셀이나 구글 시트로 데이터를 다루다 보면 어느 순간 “이 값, 분명 전에 봤는데?” 하는 순간이 찾아옵니다. 특히 구글 폼 응답을 자동으로 모으는 시트를 사용한다면 중복 데이터는 피할 수 없는 문제입니다. 같은 사용자가 여러 번 제출하거나, 운영자가 잘못 복사·붙여 넣기를 하거나, 다른 시트에서 불러오면서 키 값이 겹치는 일이 생기죠. 처음에는 수작업으로 ‘데이터 → 중복 항목 제거’를 눌러 정리하지만, 점점 번거롭고 놓치는 일이 많아집니다. 이럴 때 Google Apps Script를 활용하면 클릭 한 번으로 자동 정리가 가능합니다. 게다가 버튼까지 시트에 만들어 두면, 개발을 모르는 동료도 클릭 한 번으로 중복 데이터를 없앨 수 있습니다. 오늘은 그 과정을 단계별로 정리해 보겠습니다.
1) 중복 기준을 먼저 정하기 – ‘같은 데이터’의 정의가 정확해야 자동화가 안전하다
자동화의 첫걸음은 코드를 짜는 게 아닙니다. 무엇을 중복으로 판단할지 명확히 정의하는 일이 먼저예요. 많은 초보자들이 “중복 데이터 제거 스크립트”를 바로 검색해 붙여 넣지만, 기준이 애매하면 완전히 다른 결과가 나올 수 있습니다. 예를 들어 “이름이 같으면 같은 고객이다”라고 단순히 정해 버리면, 성이 같은 다른 사람까지 모두 지워질 수도 있죠. 반대로 “모든 열이 완전히 일치할 때만 중복으로 친다”라고 하면, 공백 하나만 달라도 중복으로 인식되지 않아 의미가 없습니다. 즉, 스크립트의 정확도는 중복의 정의에서 결정됩니다.
중복 기준은 데이터의 종류에 따라 다르게 잡아야 합니다. 아래 예시를 보세요.
- 폼 응답 시트: 응답자가 입력하는 이메일 주소를 기준으로 합니다. 같은 이메일이 두 번 들어왔다면 같은 사람의 중복 응답일 가능성이 높습니다.
- 주문·거래 데이터: 주문번호 또는 결제 ID가 중복되는 경우만 제거해야 합니다. 이름이나 날짜만 같다고 해서 같은 주문이라고 볼 수는 없죠.
- 회원 관리 시트: 회원 ID 또는 이름+연락처 조합을 키로 사용합니다. 단순히 이름만 보면 ‘홍길동’이 전국에 수십 명일 수 있습니다.
- 프로젝트·업무 기록: 날짜+업무명을 합쳐서 하나의 고유한 키로 만듭니다.
이처럼 중복 기준은 “유일해야 하고, 변하지 않아야 하며, 사람이 인위적으로 바꾸지 않는 데이터”를 선택해야 합니다. 이메일, 주문번호, ID, 타임스탬프 같은 값들이 여기에 속합니다. 반면 이름, 금액, 지역명 등은 언제든 바뀔 수 있기 때문에 기준으로 쓰기에 위험합니다.
그다음으로 중요한 건 헤더 행과 데이터 영역을 구분하는 것입니다. 대부분의 시트는 1행이 제목이고 2행부터 실제 데이터가 들어갑니다. 만약 코드에서 이 구분을 하지 않으면, ‘이메일’이라는 글자 자체를 중복으로 인식해 첫 행이 지워지는 참사가 벌어집니다. 그래서 항상 코드 안에는 const startRow = 2;처럼 시작 행을 명시해야 합니다.
또한 데이터 구조가 복잡할수록 중복의 형태도 다양합니다. 예를 들어 고객 데이터는 이런 식으로 겹칠 수 있습니다.
A열(이름) | B열(이메일) | C열(날짜)
------------------------------------
김하늘 | sky@naver.com | 2024-10-21
김하늘 | sky@naver.com | 2024-10-22 ← 날짜만 다름 (중복 아님)
김하늘 | blue@naver.com | 2024-10-21 ← 이메일 다름 (다른 사람)
첫 번째와 두 번째 행은 이메일이 같지만 날짜가 다르기 때문에, ‘응답’ 기준에서는 중복이 아닙니다. 그러나 ‘회원’ 기준에서는 같은 사람으로 볼 수도 있죠. 이렇듯 무엇을 구분하려는 시트인지에 따라 중복의 정의가 달라집니다. 그래서 스크립트로 넘어가기 전, 다음의 질문에 스스로 답해 보는 게 좋습니다.
- 이 시트는 무엇을 관리하나요? (예: 고객, 주문, 프로젝트, 설문)
- 이 데이터 중 반드시 고유해야 하는 값은 무엇인가요?
- 같은 값이 여러 번 나올 때는 어떤 행을 남기고 어떤 행을 지워야 하나요?
이 세 가지 질문이 명확해지면, 스크립트가 단순해집니다. 예를 들어 “이메일이 같으면 중복이며, 첫 번째만 남긴다”라고 결정했다면, 코드는 단 몇 줄이면 충분합니다. 반면 “이름+이메일이 같을 때만 중복으로 본다”는 규칙이라면, 코드를 조금 더 세밀하게 조정해야겠죠.
마지막으로, 중복 기준은 문서 맨 위나 별도의 셀에 주석처럼 남겨 두는 것을 추천합니다. 예: “중복 기준: B열(이메일) / 시작 행: 2행 / 중복 시: 아래쪽 행 삭제” 이렇게 시트에 메모를 남겨 두면, 나중에 다른 사람이 코드를 수정하거나 버튼을 누를 때 실수를 줄일 수 있습니다.
즉, ‘무엇을 지울지’보다 ‘무엇을 남길지’를 먼저 정하는 게 핵심이에요. 이 기준이 확실해야 나중에 자동화가 아무리 자주 돌아도 두려움이 없습니다. 그리고 바로 그 기준 위에서 Apps Script가 진짜 ‘도움이 되는 자동화’로 작동하기 시작합니다.
2) 기본 코드 작성 – 같은 이메일이 있으면 아래쪽 행 삭제
먼저 구글 시트 상단 메뉴에서 확장 프로그램 → Apps Script를 클릭합니다. 새 창이 열리면 아래 코드를 붙여 넣습니다.
function removeDuplicateEmails() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const startRow = 2; // 데이터 시작 행
const keyCol = 2; // 중복 기준 열 (B열)
const lastRow = sheet.getLastRow();
if (lastRow < startRow) return;
const values = sheet.getRange(startRow, 1, lastRow - startRow + 1, sheet.getLastColumn()).getValues();
const seen = {};
for (let i = values.length - 1; i >= 0; i--) {
const key = values[i][keyCol - 1];
if (!key) continue;
if (seen[key]) {
sheet.deleteRow(i + startRow);
} else {
seen[key] = true;
}
}
}
이 스크립트는 단순하지만 강력합니다. B열(이메일)에 같은 값이 있을 경우, 가장 마지막 행부터 거꾸로 올라가며 중복된 행을 삭제합니다. 거꾸로 삭제하는 이유는 위에서부터 지우면 행 번호가 당겨져서 잘못된 행이 지워질 수 있기 때문입니다.
Tip: 이메일 대신 주문번호나 이름을 기준으로 하고 싶다면 keyCol = 2 부분을 해당 열 번호로 바꾸면 됩니다. 예를 들어 이름(A열)을 기준으로 하면 keyCol = 1.
3) “지우기 전에 확인하고 싶어요!” – 중복 표시용 버전
바로 지우는 게 부담스럽다면 “중복”이라고 표시만 해 주는 버전을 사용할 수도 있습니다. 이 버전은 마지막 열에 ‘중복’이라는 표시를 추가합니다.
function markDuplicates() {
const sheet = SpreadsheetApp.getActiveSheet();
const startRow = 2;
const keyCol = 2; // 기준 열: B열
const markCol = sheet.getLastColumn() + 1;
const lastRow = sheet.getLastRow();
if (lastRow < startRow) return;
const values = sheet.getRange(startRow, 1, lastRow - startRow + 1, sheet.getLastColumn()).getValues();
const seen = {};
const marks = [];
for (let i = 0; i < values.length; i++) {
const key = values[i][keyCol - 1];
if (!key) {
marks.push([""]);
continue;
}
if (seen[key]) {
marks.push(["중복"]);
} else {
seen[key] = true;
marks.push([""]);
}
}
sheet.getRange(startRow, markCol, marks.length, 1).setValues(marks);
sheet.getRange(1, markCol).setValue("중복여부");
}
이 코드로 실행하면 원본 데이터는 그대로 유지되고, 오른쪽 끝 열에 “중복” 표시가 생깁니다. 확인 후 직접 삭제하거나, 필터 기능을 이용해 중복만 걸러 지울 수도 있습니다.
4) 두 개 이상의 열을 기준으로 중복 제거하기
때로는 이메일 하나만으로는 중복 여부를 판단하기 어렵습니다. 예를 들어 같은 이름의 다른 고객, 같은 이메일을 쓰는 가족 구성원처럼요. 이럴 때는 두열을 결합해 하나의 고유 키로 만들어 비교할 수 있습니다.
function removeDuplicateByTwoCols() {
const sheet = SpreadsheetApp.getActiveSheet();
const startRow = 2;
const lastRow = sheet.getLastRow();
if (lastRow < startRow) return;
const data = sheet.getRange(startRow, 1, lastRow - startRow + 1, sheet.getLastColumn()).getValues();
const seen = {};
for (let i = data.length - 1; i >= 0; i--) {
const name = data[i][0]; // A열
const email = data[i][1]; // B열
const key = name + '|' + email;
if (!name && !email) continue;
if (seen[key]) {
sheet.deleteRow(i + startRow);
} else {
seen[key] = true;
}
}
}
이 스크립트는 A열과 B열을 합쳐 하나의 키로 인식합니다. 즉, 같은 이름과 이메일을 가진 행만 중복으로 간주하고 삭제합니다. 이 방식을 사용하면 “이름이 같은데 다른 이메일을 가진 사람”은 그대로 남게 되어 훨씬 안전합니다.
5) 클릭 한 번으로 실행하기 – 버튼 만들기
이제 코드를 완성했으니 버튼을 만들어 봅시다. 시트 안에 버튼을 추가해 두면, 코드 편집기를 몰라도 누구나 클릭 한 번으로 실행할 수 있습니다.
- 상단 메뉴 → 삽입 → 그리기 → 새로 만들기를 클릭
- 네모 상자를 그린 뒤 “중복 제거”라는 글자를 적습니다.
- 저장 후 시트에 삽입합니다.
- 도형 오른쪽 상단 점 3개 → 스크립트 할당 클릭
- 스크립트 이름(예:
removeDuplicateEmails)을 입력
이제 버튼을 클릭하면 바로 실행됩니다. 버튼을 두 개 만들어 “표시용(markDuplicates)”과 “삭제용(removeDuplicateEmails)”으로 나누면 더욱 안전합니다.
6) 자동으로 실행하기 – 시간 기반 트리거
매번 버튼을 누르기 번거롭다면 자동화할 수도 있습니다. 예를 들어 매일 새벽 3시에 자동으로 중복을 제거하도록 트리거를 걸어 두는 겁니다.
- Apps Script 창 왼쪽의 시계 아이콘(트리거)을 클릭합니다.
- 트리거 추가 버튼 클릭
- 실행할 함수 선택:
removeDuplicateEmails - 이벤트 유형: 시간 기반
- 주기: 매일 / 오전 3시 등으로 지정 후 저장
이렇게 하면 매일 새벽 자동으로 중복 데이터가 정리되어 깔끔한 상태를 유지합니다. 단, 혹시라도 오류로 정상 데이터가 삭제될 수 있으므로, 실행 전 시트를 한 번 복제해 백업을 만들어 두는 게 좋습니다.
7) 실무 팁: 삭제 대신 “휴지통 시트”로 옮기기
데이터가 중요한 시트라면 바로 삭제하기보다 “휴지통 시트”로 옮겨두는 것이 좋습니다. 이렇게 하면 실수로 잘못된 행이 지워져도 언제든 복원할 수 있습니다.
function moveDuplicateToTrash() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getActiveSheet();
const trash = ss.getSheetByName("_trash") || ss.insertSheet("_trash");
const startRow = 2;
const keyCol = 2;
const lastRow = sheet.getLastRow();
if (lastRow < startRow) return;
const numCols = sheet.getLastColumn();
const data = sheet.getRange(startRow, 1, lastRow - startRow + 1, numCols).getValues();
const seen = {};
for (let i = data.length - 1; i >= 0; i--) {
const key = data[i][keyCol - 1];
if (!key) continue;
if (seen[key]) {
trash.appendRow(data[i]);
sheet.deleteRow(i + startRow);
} else {
seen[key] = true;
}
}
}
“_trash”라는 이름의 시트가 자동으로 생성되며, 중복된 데이터는 그쪽으로 이동합니다. 이 방식은 협업 중인 팀에도 안전하고, 실수로 중요한 데이터가 지워지는 사고를 방지할 수 있습니다.
결론: 중복 없는 시트는 관리의 기본
Apps Script는 코드를 잘 몰라도 구글 시트를 강력한 자동화 도구로 만들어 줍니다. 중복 제거 자동화는 그중에서도 가장 활용도가 높은 기능입니다. 단 한 번 세팅해 두면, 매일 클릭 한 번 혹은 자동 트리거로 데이터를 깨끗하게 유지할 수 있습니다.
오늘 다룬 내용을 요약하면 다음과 같습니다.
- 중복 기준 열을 명확히 정한다 (예: 이메일, 주문번호)
- 코드 작성 → 버튼 연결로 누구나 실행 가능하게 만든다
- 자동 실행 트리거로 정기적으로 정리한다
- 실수 방지를 위해 표시용/삭제용/휴지통 버전을 나눈다
데이터 정리는 누군가가 “한 번은 꼭 해야 하는 일”이지만, Apps Script를 이용하면 “자동으로 계속되는 일”로 바꿀 수 있습니다. 작은 자동화한 줄이, 매달 몇 시간을 절약해 줍니다. 오늘 한 번 시트에 버튼을 만들어 보세요. 그 버튼 하나가, 여러분의 데이터 품질을 완전히 바꿔줄 겁니다.