파이썬 파일 이름 일괄 변경 전 dry-run: 기존 파일 충돌을 먼저 검사하는 방법

파일 이름을 바로 바꾸지 말고 원본 이름과 새 이름의 계획표를 만든 뒤, 기존 파일 및 다른 변경 대상과의 충돌을 먼저 검사하세요.

보고서 폴더의 sales 01.csv를 sales_01.csv로 통일하려는 상황을 생각해 보겠습니다. 이미 같은 이름의 파일이 있거나 두 원본이 같은 새 이름을 만들면, 실행 도중 멈추거나 파일을 잃을 수 있습니다. 이 글은 이름 변경 실행이 아니라 실행 전 계획 검증만 다룹니다.

dry-run 계획표에는 무엇을 넣어야 하나요?

원본 이름과 바꿀 이름을 한 쌍으로 기록하고, 변경하지 않을 파일까지 포함한 현재 폴더의 이름 목록과 대조합니다. 여기서는 파일 이름의 ASCII 공백만 밑줄로 바꾸며, 하위 폴더를 재귀 탐색하지 않습니다. 폴더와 심볼릭 링크는 변경 대상에서 제외하지만, 그 이름은 충돌 검사 목록에 포함합니다.

비교 키는 NFC 정규화 후 casefold한 문자열입니다. 이는 서로 다른 표기나 대소문자를 보수적으로 충돌 처리하는 예제의 정책이며, 모든 파일시스템의 이름 비교 규칙을 그대로 재현하는 것은 아닙니다.

원본을 바꾸지 않는 실행 예제

Python 3 표준 라이브러리만 사용합니다. 이 예제는 Windows의 Python 3.14.4에서 실행했습니다. 샘플 파일은 임시 폴더에만 생성되며 rename이나 replace는 호출하지 않습니다. 업무 폴더 경로를 넣기 전에 예제 그대로 실행해 보세요.

from pathlib import Path
from tempfile import TemporaryDirectory
import unicodedata

def name_key(name):
    # 대소문자와 유니코드 표기 차이도 보수적으로 충돌로 취급합니다.
    return unicodedata.normalize("NFC", name).casefold()

def build_plan(folder):
    entries = sorted(Path(folder).iterdir(), key=lambda p: p.name)
    occupied = {name_key(p.name) for p in entries}
    planned = set()
    result = []
    for source in entries:
        if source.is_symlink() or not source.is_file():
            continue
        new_name = source.name.replace(" ", "_")
        if new_name == source.name:
            continue
        key = name_key(new_name)
        if key in occupied or key in planned:
            raise ValueError(f"이름 충돌: {source.name} -> {new_name}")
        planned.add(key)
        result.append((source.name, new_name))
    return result

with TemporaryDirectory() as folder:
    base = Path(folder)
    (base / "sales 01.csv").write_text("sample", encoding="utf-8")
    plan = build_plan(base)
    assert plan == [("sales 01.csv", "sales_01.csv")]
    assert (base / "sales 01.csv").exists()
    assert not (base / "sales_01.csv").exists()
    print("계획:", plan)
    print("원본 유지:", True)

    (base / "sales_01.csv").write_text("existing", encoding="utf-8")
    try:
        build_plan(base)
    except ValueError as error:
        print(error)
    else:
        raise AssertionError("기존 파일 충돌을 감지하지 못했습니다")

assert name_key("REPORT.csv") == name_key("report.csv")

정상 계획과 기존 파일 충돌은 어떻게 보이나요?

직접 실행한 예제의 출력은 다음과 같습니다. 뒤의 충돌 검사는 기존 대상 파일을 추가한 별도 조건입니다. 첫 계획에서 원본 파일이 존재하고 새 이름의 파일은 아직 없다는 것도 assert로 확인합니다.

계획: [('sales 01.csv', 'sales_01.csv')]
원본 유지: True
이름 충돌: sales 01.csv -> sales_01.csv

충돌이 나면 번호를 임의로 붙여 계속 진행하지 않고 계획 생성을 중단합니다. 어떤 원본을 유지하고 어떤 이름을 쓸지는 사용자가 검토할 판단입니다. 변경되지 않는 파일을 검사에서 빼면 sales_01.csv처럼 이미 존재하는 대상을 놓치기 쉽습니다.

실제 이름 변경으로 확장하기 전에 확인할 점

dry-run 이후 다른 프로그램이 파일을 만들거나 지울 수 있습니다. 따라서 사전 검사는 실제 변경 시점의 무충돌을 보장하지 않습니다. 실제 실행은 백업·작업 중 잠금 또는 변경 중지 정책·실행 직전 재검사·성공 항목 기록·부분 실패 복구까지 별도로 설계해야 합니다. 플랫폼에 따라 rename의 기존 대상 처리도 다르므로 이 코드를 단순 반복 rename으로 바꾸어 운영 파일에 적용하지 마세요.

여러 파일의 이름을 서로 맞바꾸는 순환 변경도 이 예제의 지원 대상이 아닙니다. 현재 이름과 겹치면 보수적으로 거부합니다. 경로 권한, 디스크 오류, 숨김 파일 포함 정책과 폴더 내 항목 수가 큰 경우의 메모리 사용도 따로 검토해야 합니다.

자주 묻는 질문

공백 말고 다른 문자도 자동으로 없애나요?

아닙니다. 이 예제는 ASCII 공백 한 종류만 바꿉니다. 문자 제거 규칙을 추가하면 서로 다른 파일이 같은 이름이 될 수 있으므로 별도 입력 사례와 충돌 검사를 추가하세요.

충돌이 없으면 백업 없이 실행해도 되나요?

아닙니다. 계획표는 이름 규칙 검사일 뿐 백업·복구 수단이 아닙니다. 파일 내용을 보존할 복사본과 되돌리기 기록을 먼저 준비해야 합니다.

검사 결과가 비어 있으면 오류인가요?

공백을 바꿀 일반 파일이 없으면 빈 목록이 정상입니다. 접근 권한이나 잘못된 경로로 생기는 예외와 구분하세요.

공식 자료와 다음 단계

업무 자동화 시작 가이드에서 전체 실행 흐름을 확인하세요. 파일 내용을 보존하는 검증은 복사 후 해시로 백업 확인하기, 오류 안내 설계는 예상한 입력 오류만 처리하기로 이어집니다.

작성: 로그 · 2026-10-18 작성 및 공식 문서 확인. AI 보조로 작성했으며 위 임시 폴더 예제는 로컬 실행으로 검증했습니다. 실제 업무 파일의 이름 변경은 실행하지 않았습니다.

댓글

이 블로그의 인기 게시물

API 키 저장 방법: 해싱과 암호화의 차이, Fernet 예제

파이썬 Google Sheets API 연동: 서비스 계정 권한과 읽기·쓰기

Pandas 결측치 처리: isna, fillna, dropna 선택 기준