파이썬 업무 자동화#3파일 정리 자동화 (이름 일괄 변경)!
목차9개 항목
안녕하세요! Mojangcam입니다 😎
지난 파이썬 업무 자동화 #2 - 웹 크롤링 입문 (requests + BeautifulSoup)에서는 웹에서 데이터를 긁어 CSV로 저장해봤죠. 그런데 그렇게 받은 파일들은 다 어디로 갈까요? 네, 다운로드 폴더죠 😅
한번 열어보세요. 스크린샷, 쇼핑몰 영수증 PDF, 이름 모를 압축파일, 최종_최종2_진짜최종.hwp 까지 수백 개가 뒤섞여 있을 거예요. 손으로 정리하면 30분, 파이썬으로는 1초입니다.
이번 편에서는 확장자별로 폴더를 나눠 담고, 파일 이름에 날짜를 붙여 일괄 변경하는 스크립트를 만들어볼게요. 게다가 오늘 쓰는 도구는 전부 파이썬 내장 기능이라 설치할 게 하나도 없어요! 💡

📌 준비물
- Python 3.10 이상 (3.9에서도 동작하지만 3.10+ 권장)
- 설치할 라이브러리 없음! 🎉 오늘 쓰는
pathlib,shutil,datetime은 전부 파이썬에 기본 탑재예요
# 설치할 게 없어요. 버전만 확인하고 넘어갈게요
python --version
# Python 3.13.5⚠️ 시작 전 딱 하나만 약속해요. 처음 돌릴 땐 반드시 미리보기(dry-run) 모드로 실행하세요. 파일을 옮기는 코드는 잘못 짜면 되돌리기가 정말 번거로워요. 아래 코드에는 안전장치를 기본으로 넣어뒀습니다.
📌 1단계 - pathlib으로 폴더 안 훑어보기
예전엔 os.listdir() 을 썼지만, 요즘은 pathlib 이 훨씬 편해요. 경로를 문자열이 아니라 객체로 다루거든요.
# Python 3.10+
from pathlib import Path
folder = Path.home() / "Downloads" # 홈 폴더 아래 Downloads
for file in folder.iterdir():
if file.is_file():
print(file.name, "|", file.suffix, "|", file.stem)하나씩 뜯어볼게요 💡
- Path.home() : 내 홈 폴더예요. 윈도우면
C:/Users/이름, 맥이면/Users/이름을 알아서 잡아줘요. 경로를 직접 적는 것보다 훨씬 안전해요. - / 연산자 :
Path.home() / "Downloads"처럼 슬래시로 경로를 이어붙여요. 윈도우 역슬래시 때문에 골치 아플 일이 없어져요! - iterdir() : 폴더 바로 아래 항목을 하나씩 돌려줘요.
- is_file() : 폴더가 아니라 파일인지 확인해요. 이걸 안 하면 하위 폴더까지 옮기려다 사고가 나요.
- suffix / stem / name : 각각 확장자(
.pdf), 확장자 뺀 이름(회의록), 전체 이름(회의록.pdf)이에요. 이 셋만 알면 파일 이름 다루기는 끝이에요.
📌 2단계 - 확장자별로 폴더 나눠 담기
확장자와 폴더 이름을 딕셔너리로 짝지어두면, 나중에 규칙을 바꾸기도 쉬워요.
# Python 3.10+
import shutil
CATEGORY = {
"이미지": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
"문서": [".pdf", ".docx", ".txt", ".hwp", ".pptx"],
"압축파일": [".zip", ".7z", ".rar"],
}
def find_category(suffix: str) -> str:
for name, extensions in CATEGORY.items():
if suffix.lower() in extensions:
return name
return "기타"
target_dir = folder / find_category(file.suffix)
target_dir.mkdir(parents=True, exist_ok=True)
shutil.move(file, target_dir / file.name)하나씩 뜯어볼게요 💡
- suffix.lower() : 확장자를 소문자로 바꿔서 비교해요.
사진.JPG처럼 대문자로 된 파일이 의외로 많거든요! - mkdir(parents=True, exist_ok=True) : 폴더를 만들되, 이미 있으면 조용히 넘어가요.
exist_ok=True를 빼면 두 번째 파일부터FileExistsError가 터져요. - shutil.move() : 파일을 옮겨요.
Path.rename()이 아니라 이걸 쓰는 이유가 있는데, 드라이브가 달라도 옮겨지기 때문이에요. C드라이브 다운로드 폴더에서 D드라이브로 옮기는 경우rename()은 에러가 나요. (자세한 건 아래 에러 편에서!)
📌 3단계 - 이름 일괄 변경 + 안전장치
이제 파일 이름 앞에 수정 날짜를 붙여볼게요. 20260722_보고서.pdf 처럼요. 이렇게 해두면 파일 목록이 저절로 시간순 정렬돼서 찾기가 훨씬 편해져요.
# Python 3.10+
from datetime import datetime
def make_new_name(file: Path) -> str:
modified = datetime.fromtimestamp(file.stat().st_mtime)
return f"{modified:%Y%m%d}_{file.name}"
def unique_path(path: Path) -> Path:
"""같은 이름이 있으면 (1), (2)... 를 붙여줘요"""
if not path.exists():
return path
counter = 1
while True:
candidate = path.with_name(f"{path.stem} ({counter}){path.suffix}")
if not candidate.exists():
return candidate
counter += 1하나씩 뜯어볼게요 💡
- file.stat().st_mtime : 파일이 마지막으로 수정된 시각이에요. 숫자(타임스탬프)로 나와서
datetime.fromtimestamp()로 날짜로 바꿔줘요. - f"{modified:%Y%m%d}" : f-string 안에서 바로 날짜 형식을 지정했어요.
20260722형태로 나와요. - unique_path() : 이게 진짜 중요해요. 같은 이름의 파일이 이미 있으면 덮어써서 원본이 사라져요. 그래서 겹칠 땐
(1),(2)를 붙여 피해가게 만들었어요. - with_name() : 경로는 그대로 두고 파일 이름만 바꾼 새 Path를 돌려줘요.
📌 전체 코드 (복붙 OK)
DRY_RUN = True 상태로 먼저 돌려보세요. 화면에 "뭐가 어디로 갈지"만 보여주고 실제로는 아무것도 건드리지 않아요. 결과가 마음에 들면 그때 False 로 바꾸면 됩니다!
# Python 3.10+ / 표준 라이브러리만 사용 (설치 필요 없음)
# 파일명: organize.py
# 실행: python organize.py
import shutil
from datetime import datetime
from pathlib import Path
# ── 설정 ─────────────────────────────────────────
TARGET_DIR = Path.home() / "Downloads" # 정리할 폴더
DRY_RUN = True # True면 '예고'만! 확인 후 False로 바꾸세요
CATEGORY = {
"이미지": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".heic"],
"문서": [".pdf", ".docx", ".doc", ".txt", ".hwp", ".pptx"],
"스프레드시트": [".xlsx", ".xls", ".csv"],
"압축파일": [".zip", ".7z", ".rar", ".tar", ".gz"],
"설치파일": [".exe", ".msi", ".dmg"],
"영상": [".mp4", ".avi", ".mkv", ".mov"],
}
# ─────────────────────────────────────────────────
def find_category(suffix: str) -> str:
"""확장자로 카테고리를 찾아요. 못 찾으면 '기타'"""
for name, extensions in CATEGORY.items():
if suffix.lower() in extensions:
return name
return "기타"
def unique_path(path: Path) -> Path:
"""이름이 겹치면 (1), (2)... 를 붙여 덮어쓰기를 막아요"""
if not path.exists():
return path
counter = 1
while True:
candidate = path.with_name(f"{path.stem} ({counter}){path.suffix}")
if not candidate.exists():
return candidate
counter += 1
def make_new_name(file: Path) -> str:
"""수정 날짜를 앞에 붙인 새 이름 → 20260722_보고서.pdf"""
modified = datetime.fromtimestamp(file.stat().st_mtime)
return f"{modified:%Y%m%d}_{file.name}"
def organize(folder: Path, dry_run: bool = True) -> None:
if not folder.is_dir():
print(f"폴더를 찾을 수 없어요: {folder}")
return
count = 0
for file in list(folder.iterdir()): # list()로 목록을 먼저 확정!
if not file.is_file() or file.name.startswith("."):
continue
category = find_category(file.suffix)
target_dir = folder / category
target = unique_path(target_dir / make_new_name(file))
print(f" {file.name} → {category}/{target.name}")
if not dry_run:
target_dir.mkdir(parents=True, exist_ok=True)
shutil.move(file, target)
count += 1
head = "[미리보기] 실제로는 아무것도 옮기지 않았어요" if dry_run else "[실행 완료]"
print(f"\n{head} · 대상 파일 {count}개")
if __name__ == "__main__":
print(f"정리 대상: {TARGET_DIR}\n")
organize(TARGET_DIR, dry_run=DRY_RUN)💬 코드에서 list(folder.iterdir()) 이 왜 중요한지 짚고 갈게요. iterdir() 은 폴더를 읽어가면서 결과를 돌려주는데, 우리는 그 폴더 안에 새 카테고리 폴더를 계속 만들잖아요? 읽는 중에 폴더 내용이 바뀌면 동작을 장담할 수 없어요. list() 로 감싸서 목록을 먼저 통째로 확정한 다음 작업하면 안전합니다. 실무에서 자주 놓치는 부분이에요!
📌 실행 결과
먼저 DRY_RUN = True 상태로 돌려볼게요.
정리 대상: C:/Users/mojangcam/Downloads
설치파일.exe → 설치파일/20260721_설치파일.exe
보고서.docx → 문서/20260721_보고서.docx
휴가사진.jpg → 이미지/20260721_휴가사진.jpg
무엇.xyz → 기타/20260721_무엇.xyz
회의록.pdf → 문서/20260715_회의록.pdf
영상.mp4 → 영상/20260721_영상.mp4
매출.xlsx → 스프레드시트/20260720_매출.xlsx
[미리보기] 실제로는 아무것도 옮기지 않았어요 · 대상 파일 7개실제로 제 블로그 작업 폴더에 돌려본 화면이에요 👇

어디로 갈지 다 보이죠? 이제 DRY_RUN = False 로 바꾸고 다시 실행하면 진짜로 정리돼요.
다운로드/
├── 기타/20260721_무엇.xyz
├── 문서/20260715_회의록.pdf
├── 문서/20260721_보고서.docx
├── 설치파일/20260721_설치파일.exe
├── 스프레드시트/20260720_매출.xlsx
├── 영상/20260721_영상.mp4
└── 이미지/20260721_휴가사진.jpg
확장자 목록에 없던 .xyz 는 기타 폴더로 잘 들어갔고, 날짜도 파일마다 다르게 붙었죠. 위 실제 실행 화면에서도 똑같아요. CATEGORY 에 .md 를 안 넣어뒀더니 마크다운 파일이 전부 기타 로 빠졌고, .xlsx·.csv 는 스프레드시트 로 정확히 분류됐습니다. 자주 쓰는 확장자는 CATEGORY 에 추가해주면 돼요!
그리고 같은 파일을 또 넣고 실행하면 이렇게 나와요.
회의록.pdf → 문서/20260715_회의록 (1).pdf덮어쓰지 않고 (1) 을 붙여줬어요. unique_path() 가 원본을 지켜준 거예요 🙌
📌 자주 나는 에러
1. FileNotFoundError — 폴더를 찾을 수 없어요
- 원인: 경로가 실제와 달라요. 특히 원드라이브(OneDrive) 백업을 켠 윈도우는 다운로드 폴더가
C:/Users/이름/OneDrive/Downloads로 옮겨가 있는 경우가 많아요. - 해결:
print(Path.home())로 홈 경로를 먼저 찍어보고, 탐색기 주소창의 실제 경로와 맞춰주세요.
2. PermissionError: [Errno 13] Permission denied
- 원인: 옮기려는 파일을 다른 프로그램이 열어둔 상태예요. 엑셀·한글·PDF 뷰어가 대표적이에요.
- 해결: 해당 프로그램을 닫고 다시 실행하세요. 시스템 폴더(
C:/Windows등)를 대상으로 잡은 건 아닌지도 확인하고요.
3. OSError: [Errno 18] Invalid cross-device link
- 원인:
Path.rename()으로 다른 드라이브·파티션에 옮기려 한 경우예요. rename은 같은 디스크 안에서만 동작하거든요. - 해결:
shutil.move()를 쓰세요. 드라이브가 다르면 알아서 복사한 뒤 원본을 지워줘요. 위 전체 코드가 이미shutil.move()를 쓰고 있어요.
4. FileExistsError — 윈도우에서만 나요
- 원인:
Path.rename()은 대상 파일이 이미 있으면 윈도우에서FileExistsError를 냅니다. 반대로 맥·리눅스에선 조용히 덮어써요. 같은 코드가 OS마다 다르게 동작하는 위험한 케이스예요! - 해결: 덮어써도 되면
Path.replace(), 원본을 지키려면 위unique_path()처럼 이름을 바꿔 피하세요.
5. 정리했더니 파일이 사라진 것 같아요 😱
- 원인: 사라진 게 아니라 카테고리 폴더 안으로 들어간 거예요. 그리고 이름 앞에 날짜가 붙어서 못 알아본 것뿐이에요.
- 해결: 탐색기 검색창에 원래 파일 이름 일부를 넣어보세요. 그래서 첫 실행은 꼭
DRY_RUN = True로 하시라는 거예요. 중요한 폴더라면 실행 전 복사본을 하나 떠두는 것도 좋아요.
📌 마무리
오늘은 파이썬 내장 기능만으로 다운로드 폴더를 정리하고 이름까지 일괄 변경해봤어요. 핵심은 세 가지예요 💬
- pathlib 으로 경로를 다루면 윈도우·맥 상관없이 코드가 똑같아져요
- shutil.move 는 드라이브가 달라도 안전하게 옮겨줘요
- dry-run + unique_path 로 실수와 덮어쓰기를 미리 막아요
이 스크립트를 바탕화면에 두고 가끔 한 번씩 실행하면 다운로드 폴더가 늘 깔끔하게 유지돼요. "매번 실행하는 것도 귀찮은데?" 싶으시죠? 그것도 자동화할 수 있어요 — 시리즈 #6에서 예약 실행을 다룰 예정이에요!
다음 편은 파이썬 업무 자동화 #4 - 디스코드 웹훅(Webhook) 알림 보내기 입니다. 스크립트가 다 돌고 나면 내 디스코드로 "정리 완료! 파일 137개" 하고 알림이 오게 만들어볼게요. 기대해주세요 🙌
📚 파이썬 업무 자동화 시리즈
- #1 - 엑셀 자동화 입문 (openpyxl)
- #2 - 웹 크롤링 입문 (requests + BeautifulSoup)
- #3 - 파일 정리 자동화 (이름 일괄 변경) ← 지금 이 글!
- #4 - 디스코드 웹훅(Webhook) 알림 보내기 (예정)
- #5 - ChatGPT API로 업무 자동화 (예정)
- #6 - 예약 실행 자동화 (datetime·schedule) (예정)
- 번외: 파이썬 이메일 자동 발송하기 (smtplib)
연재 · 14편 중 3번째
파이썬 업무 자동화
- #1엑셀 자동화 입문 (openpyxl)!
- #2웹 크롤링 입문 (requests + BeautifulSoup)!
- #3파일 정리 자동화 (이름 일괄 변경)!지금 읽는 글
- #4디스코드 웹훅(Webhook)으로 알림 보내기 (requests)!
- #5로컬 LLM(LM Studio)으로 고객 문의 자동 분류·요약하기! (무료 AI)
- #6예약 실행 자동화! 매일 정해진 시간에 스크립트 돌리기 (schedule·APScheduler)
- #7동적 페이지·로그인 자동화! requests로 안 되는 사이트 긁기 (Selenium)
- #8PDF 자동 처리! 여러 파일 병합·분할부터 비밀번호까지 (pypdf) 📄
- #9마우스·키보드 GUI 자동화! 클릭·타이핑을 파이썬이 대신하게 하기 (PyAutoGUI)
- #10워드(Word) 문서 자동 생성! 엑셀 명단으로 계약서·안내문 100장 뽑기 (python-docx) 📄
- #11개인화 대량 메일 자동 발송! 명단별 첨부파일·발송 로그·재시도까지 (smtplib 실전편) 📬
- #12구글 시트 연동하기! 팀원과 같이 보는 자동 시트 (gspread) 📗
- #13파이썬 자동화 로그·재시도·디스코드 실패 알림 만들기 - 조용히 멈추는 스크립트 잡기
- #14파워포인트(PowerPoint) 보고서 자동 생성! 엑셀 숫자만 바꾸면 발표자료 완성 (python-pptx) 📊
