챕터 5

파일 이름 일괄 변경 — 파이썬으로 PC 폴더, 앱스 스크립트로 드라이브

스캔 파일·사진·증빙 수백 개의 이름을 규칙대로 바꾸는 작업을 자동화합니다. PC 폴더는 파이썬, 구글 드라이브는 앱스 스크립트로 처리하고, 실수로 덮어쓰지 않게 미리보기(드라이런) 단계를 넣는 법을 익힙니다.

4장에서 메일을 보낼 때 테스트 발송부터 거쳤던 것을 기억하실 겁니다. 이름 바꾸기도 같은 원칙으로 갑니다. 한 번 바뀐 파일 이름은 되돌릴 방법이 마땅치 않으니, 바꾸기 전에 "무엇이 무엇으로 바뀔지"를 먼저 화면에 찍어 보는 단계가 이 장의 중심입니다.

이름 바꾸기 자동화가 위험한 이유

영수증 사진 180장이 IMG_0001.JPG, IMG_0002.JPG로 쌓여 있다고 해 봅시다. 이걸 20260901_영수증_001.jpg 식으로 바꾸는 일은 손으로 하면 한 시간이고, 스크립트로 하면 몇 초입니다. 대신 스크립트는 망설이지 않습니다. 규칙이 틀렸으면 틀린 이름이 180개 한꺼번에 생깁니다.

드라이런이라는 안전장치

드라이런(dry run)은 실제로 바꾸지 않고 결과만 보여 주는 실행 방식입니다. 코드 맨 위에 DRY_RUN = True라는 스위치 하나를 두고, 이 값이 True인 동안에는 이름을 출력만 합니다. 출력이 마음에 들면 False로 바꿔 한 번 더 실행합니다. 이 시리즈의 두 예제(파이썬, 앱스 스크립트)는 모두 이 구조입니다.

같은 이름이 이미 있으면 어떻게 될까

OS마다 반응이 다릅니다. 파이썬 공식 문서는 Path.rename()을 이렇게 설명합니다.

"On Unix, if target exists and is a file, it will be replaced silently if the user has permission. On Windows, if target exists, FileExistsError will be raised." (Python 3.14 pathlib 문서)

맥·리눅스에서는 대상 파일이 조용히 덮어써지고, 윈도우에서는 오류가 납니다. 같은 코드를 팀원 PC에서 돌렸더니 한쪽에서만 파일이 사라지는 일이 여기서 생깁니다. 그래서 예제 코드는 OS와 상관없이 target.exists()로 먼저 확인하고, 이미 있으면 건너뜁니다.

파이썬으로 PC 폴더 이름 바꾸기

데이터가 내 PC에 있으니 1장 기준대로 파이썬이 맞습니다. 파이썬은 설치하지 않았다면 Google Colab도 되지만, 이 예제는 내 PC 폴더를 직접 건드리므로 PC에 설치된 파이썬을 기준으로 설명합니다.

AI에게 보낼 요청 프롬프트

파이썬으로 폴더 안 파일 이름을 바꾸는 코드를 써 줘.

  • 대상 폴더: C:/Users/me/Desktop/영수증 (코드 맨 위 변수로 빼 줘)
  • 대상 파일: jpg, png, pdf (대소문자 구분 없이)
  • 새 이름 형식: 파일 수정 날짜(yyyymmdd)_영수증_세 자리 번호.확장자 (예: 20260901_영수증_001.jpg)
  • 번호는 파일 이름 순으로 1부터
  • DRY_RUN 변수가 True면 바꿀 이름만 print하고 실제로는 바꾸지 마
  • 새 이름과 같은 파일이 이미 있으면 덮어쓰지 말고 "건너뜀"이라고 출력
  • pathlib 사용, 초보자가 읽을 수 있게 주석 달아 줘

열 구성에 해당하는 것이 파일명 규칙입니다. 예시 이름을 한 줄 넣어 두면 AI가 형식을 틀리는 일이 크게 줄어듭니다.

받은 코드

from pathlib import Path
from datetime import datetime

FOLDER = Path("C:/Users/me/Desktop/영수증")   # 바꿀 폴더 경로
DRY_RUN = True                                # True면 바꿀 이름만 보여 줌

files = sorted(p for p in FOLDER.iterdir() if p.is_file() and p.suffix.lower() in {".jpg", ".png", ".pdf"})
for n, path in enumerate(files, start=1):
    day = datetime.fromtimestamp(path.stat().st_mtime).strftime("%Y%m%d")
    new_name = f"{day}_영수증_{n:03d}{path.suffix.lower()}"
    target = path.with_name(new_name)
    if target.exists():
        print(f"건너뜀(같은 이름 있음): {path.name}")
        continue
    print(f"{path.name}  →  {new_name}")
    if not DRY_RUN:
        path.rename(target)

print(f"{len(files)}개 확인" + (" (미리보기, 실제 변경 없음)" if DRY_RUN else " (변경 완료)"))

sorted()가 있는 이유가 있습니다. 파이썬 문서는 폴더 안 파일 목록의 순서를 보장하지 않는다고 밝히고 있고, 정렬 없이 번호를 매기면 001번과 002번이 뒤죽박죽이 됩니다. {".jpg", ".png", ".pdf"}에 없는 확장자는 대상에서 빠집니다.

실행 결과: 미리보기, 변경, 재실행

가짜 폴더(IMG_0001.JPG, scan2.pdf, 사진.png, memo.txt)를 만들어 세 번 실행한 결과입니다. 먼저 DRY_RUN = True입니다.

IMG_0001.JPG  →  20260901_영수증_001.jpg
scan2.pdf  →  20260903_영수증_002.pdf
사진.png  →  20261002_영수증_003.png
3개 확인 (미리보기, 실제 변경 없음)

마지막 줄에 "실제 변경 없음"이 찍혔으니 파일은 그대로입니다. 이름이 의도대로면 DRY_RUN = False로 고치고 다시 실행합니다.

IMG_0001.JPG  →  20260901_영수증_001.jpg
scan2.pdf  →  20260903_영수증_002.pdf
사진.png  →  20261002_영수증_003.png
3개 확인 (변경 완료)

폴더에는 20260901_영수증_001.jpg, 20260903_영수증_002.pdf, 20261002_영수증_003.png, 그리고 memo.txt가 남습니다. memo.txt는 대상 확장자가 아니라서 건드리지 않았습니다. 확장자가 .JPG에서 .jpg로 바뀐 것도 코드의 .lower() 때문입니다.

같은 코드를 실수로 한 번 더 돌리면 이렇게 됩니다.

건너뜀(같은 이름 있음): 20260901_영수증_001.jpg
건너뜀(같은 이름 있음): 20260903_영수증_002.pdf
건너뜀(같은 이름 있음): 20261002_영수증_003.png
3개 확인 (변경 완료)

이미 새 이름이 된 파일에서 다시 같은 새 이름이 만들어지니, 덮어쓰기 검사가 걸려 모두 건너뜁니다. 마지막 줄의 "변경 완료"는 코드가 끝까지 돌았다는 뜻일 뿐 바뀐 파일이 있다는 뜻이 아니라는 점은 알아 두세요. 실제로 바꾼 개수가 궁금하면 AI에게 "건너뛴 개수와 바꾼 개수를 따로 세서 마지막에 출력해 줘"라고 추가 요청하면 됩니다.

스캔 파일 수백 개를 바꾸기 전에는 폴더 전체를 복사해 둔 사본에서 먼저 DRY_RUN을 False로 돌려 보세요. 원본은 그다음입니다.

오류 수정 대화: FileNotFoundError

이 장에서 가장 자주 만나는 오류는 폴더 경로입니다. 경로를 손으로 옮겨 적다가 영수증을 영수중으로 쓰는 식의 오타가 대표적입니다.

오류 메시지를 그대로 붙여 다시 묻기

위 코드를 실행했더니 아래 오류가 나. 원인과 고친 코드를 알려 줘.

FileNotFoundError: [Errno 2] No such file or directory: 'C:\Users\me\Desktop\영수중'

원인과 고치는 방법

메시지 그대로 "그 이름의 파일이나 폴더가 없다"는 뜻입니다. 확인할 곳은 세 군데입니다.

  • 철자와 공백 폴더 이름의 오타, 끝에 붙은 공백을 확인하세요. 탐색기 주소창에서 경로를 복사해 붙이는 편이 안전합니다.
  • 역슬래시(\) 윈도우에서 복사한 C:\Users\me를 그대로 따옴표 안에 넣으면 \U 같은 조합이 특수문자로 해석되어 다른 오류가 납니다. 슬래시(/)로 바꾸거나 Path(r"C:\Users\me\Desktop\영수증")처럼 따옴표 앞에 r을 붙이세요.
  • 실행 위치 "영수증"처럼 짧은 상대경로를 쓰면, 파이썬이 파일 위치가 아니라 현재 작업 폴더를 기준으로 찾습니다. 더블클릭으로 실행하면 이 기준이 달라져 같은 코드가 어느 날은 되고 어느 날은 안 됩니다. 절대경로를 쓰는 습관이 가장 확실합니다.

예제 코드가 맨 위에 FOLDER 한 줄만 두는 이유도 이것입니다. 고칠 곳이 한 군데여야 오류 때 찾기 쉽습니다.

파일 이름에 넣으면 안 되는 것들

규칙을 정할 때 이름에 거래처명이나 시각을 넣고 싶어지는데, 여기에 함정이 있습니다.

윈도우 금지 문자와 길이 제한

항목 내용 출처
금지 문자 `< > : " / \ ? *` 9개, 제어문자도 금지
이름 끝 공백이나 마침표로 끝내지 말 것 같은 문서
예약어 CON, PRN, AUX, NUL, COM1~COM9, LPT1~LPT9 같은 문서
경로 길이 드라이브 문자부터 합쳐 260자(MAX_PATH) Microsoft Learn 경로 길이 제한

"(주)A/B상사"나 "10:30"처럼 슬래시와 콜론이 든 거래처명·시각은 이름에 그대로 못 씁니다. AI에게 요청할 때 "금지 문자는 밑줄(_)로 바꿔 줘"를 한 줄 넣으면 됩니다. 경로 260자는 한글 폴더를 깊게 파 두는 사무실에서 생각보다 빨리 닿습니다. 윈도우 10 1607 이후에는 레지스트리 설정으로 긴 경로를 켤 수 있지만, 앱에 따라 적용되지 않을 수 있고 회사 PC는 IT 정책이 막기도 합니다.

맥에서 받은 파일의 한글 자소 분리

맥은 한글 파일명을 자소가 분리된 형태(NFD)로 저장합니다. "가"가 한 글자(U+AC00)가 아니라 "ㄱ"과 "ㅏ" 두 글자(U+1100, U+1161)로 들어 있는 식입니다. 윈도우·리눅스·웹은 합쳐진 형태(NFC)를 씁니다. 그래서 맥에서 만든 압축 파일을 윈도우에서 풀면 이름의 한글이 자음·모음으로 갈라져 보이고, 눈으로는 같은 이름인데 == 비교가 False가 나오기도 합니다.

이런 증상이 생기면 AI에게 "파일 이름이 자소 분리돼 있어, 합쳐 줘"라고 말하면 보통 이런 한 줄이 돌아옵니다. 코드 위쪽에 import unicodedata와 함께 넣는 형태입니다.

new_name = unicodedata.normalize("NFC", path.name)

이 한 줄은 따로 실행해 확인했습니다. 자소가 분리된 '영수증_001.pdf'(16글자로 셈)를 넣으면 보통 글자 11자짜리 이름으로 돌아오고, 눈으로 보는 이름과 똑같다고 비교됩니다. 다만 위 예제 코드에는 넣지 않았으니, 맥 파일을 자주 받는 환경이라면 이 장의 코드에서 new_name을 만드는 부분과 함께 AI에게 보여 주고 "이름을 NFC로 정규화하도록 고쳐 줘"라고 요청해 시험해 보세요.

구글 드라이브 폴더는 앱스 스크립트로

파일이 드라이브에 있다면 PC에 내려받을 필요가 없습니다. 앱스 스크립트의 DriveApp(구글 드라이브를 다루는 내장 서비스)으로 바로 이름을 바꿉니다. 쓰는 방법은 3장과 같습니다. 구글 시트나 script.google.com에서 새 프로젝트를 열어 코드를 붙여 넣습니다.

폴더 ID 찾기와 요청 프롬프트

드라이브에서 대상 폴더를 열고 주소창을 보면 folders/ 뒤에 긴 문자열이 있습니다. 그것이 폴더 ID입니다. 이어서 ChatGPT나 Claude에 이렇게 요청합니다.

구글 앱스 스크립트로 드라이브 폴더 안 파일 이름을 바꾸는 코드를 써 줘.

  • 폴더 ID는 코드 맨 위 상수 FOLDER_ID로 빼 줘
  • 새 이름 형식: 파일 생성일(yyyyMMdd, 한국 시간)_계약서_세 자리 번호.원래 확장자 (예: 20260910_계약서_001.pdf)
  • DRY_RUN 상수가 true면 Logger.log로 바꿀 이름만 보여 주고 실제로는 바꾸지 마
  • 마지막에 몇 개를 처리했는지 로그로 알려 줘

받은 코드

// 드라이브 폴더 주소 끝부분(folders/ 뒤)을 FOLDER_ID에 넣습니다.
const FOLDER_ID = '여기에_폴더_ID';
const DRY_RUN = true;

function renameDriveFiles() {
  const files = DriveApp.getFolderById(FOLDER_ID).getFiles();
  let n = 0;
  while (files.hasNext()) {
    const file = files.next();
    const oldName = file.getName();
    const day = Utilities.formatDate(file.getDateCreated(), 'Asia/Seoul', 'yyyyMMdd');
    const ext = oldName.includes('.') ? oldName.slice(oldName.lastIndexOf('.')) : '';
    n++;
    const newName = day + '_계약서_' + String(n).padStart(3, '0') + ext;
    Logger.log(oldName + ' → ' + newName);
    if (!DRY_RUN) file.setName(newName);
  }
  Logger.log(n + '개 ' + (DRY_RUN ? '미리보기' : '변경 완료'));
}

실행하면 처음에는 권한 승인 창이 뜹니다. 이름 변경에는 드라이브 전체 접근 권한이 필요하므로, 승인 화면의 범위를 읽고 본인 계정이 맞는지 확인한 뒤 허용하세요. 로그는 아래쪽 실행 로그 창에 나옵니다.

실행 결과

DRY_RUN이 true인 동안은 같은 형식의 로그만 찍히고 파일은 그대로입니다. 로그가 맞으면 false로 바꿔 다시 실행합니다. 앱스 스크립트 모의 환경에서 파일 두 개(스캔 1.pdf, 확장자 없는 contract)로 돌려 본 결과는 이렇습니다.

스캔 1.pdf → 20260910_계약서_001.pdf
contract → 20260912_계약서_002
2개 변경 완료

확장자가 없는 파일은 ext가 빈 문자열이라 번호까지만 붙습니다. 이 코드는 파이썬 예제와 달리 같은 이름이 있는지 검사하지 않습니다. 번호가 이름에 들어가 겹칠 일은 드물지만, 같은 폴더에 한 번 더 돌리면 이미 바뀐 파일에 새 번호가 매겨지니 실행은 한 번만 하세요. 번호 순서가 날짜순이어야 한다면 로그에서 먼저 확인하는 것이 안전합니다.

파일이 아주 많을 때

앱스 스크립트는 한 번 실행에 6분이 한도입니다(구글 공식 할당량 문서). 수백 개 안팎이면 대개 충분하지만, 수천 개를 한 번에 바꾸다 Exceeded maximum execution time이 뜨면 AI에게 오류를 그대로 붙여 "이어서 처리하도록 나눠 줘"라고 요청하세요. getContinuationToken()으로 중단 지점을 저장하는 방식이 나오는데, 이 토큰은 일반적으로 1주일간 유효합니다(구글 공식 문서). 건당 처리 시간은 공식 수치가 없으니 먼저 30~50개로 시험해서 직접 재 보는 편이 정확합니다.

정리

이름 바꾸기 자동화의 핵심은 속도가 아니라 순서입니다. DRY_RUN으로 미리 보고, 덮어쓰기 검사로 막고, 복사본에서 시험한 뒤 원본에 적용하세요. PC 폴더는 파이썬(pathlib), 드라이브 폴더는 앱스 스크립트(DriveApp)가 맡습니다.

다음 챕터에서는 '엑셀·CSV 여러 파일 합치고 정리하기 — 파이썬 판다스 입문'을 다루겠습니다. 지점별로 흩어진 파일을 하나로 합치고 한글 인코딩 오류를 고치는 과정을 따라 합니다.