다운로드 폴더를 열었을 때 스크린샷, PDF, 압축 파일이 뒤죽박죽 섞여 있는 걸 보면 한숨부터 나옵니다. 하나하나 드래그해서 정리하자니 5분은 걸리고, 며칠 지나면 또 엉망이 되죠. 이런 반복 작업이야말로 Python에게 맡기기 딱 좋은 일입니다. 이 글에서는 설치도 필요 없이, 어질러진 폴더를 확장자별로 자동 정리하는 첫 자동화 스크립트를 처음부터 끝까지 만들어 봅니다.
무엇을 만드나
우리가 만들 스크립트는 이렇게 동작합니다. 이미지, 문서, 압축 같은 분류 폴더를 자동으로 만들고, 폴더 안의 파일들을 확장자에 따라 알맞은 곳으로 옮깁니다.
정리 전 정리 후
test_folder/ test_folder/
├── photo.jpg ├── 이미지/
├── screenshot.PNG │ ├── photo.jpg
├── report.pdf │ └── screenshot.PNG
├── notes.txt ├── 문서/
├── data.csv │ ├── report.pdf
├── archive.zip │ ├── notes.txt
└── song.mp3 │ └── data.csv
├── 압축/
│ └── archive.zip
└── 기타/
└── song.mp3
한 번 만들어 두면 다운로드 폴더든 프로젝트 폴더든, 경로만 바꿔 어디에나 다시 쓸 수 있습니다.
사전 준비
- Python 3.8 이상. 터미널(또는 명령 프롬프트)에서 버전을 확인합니다.
python --version
Python 3.8.0처럼 3.8 이상이 찍히면 됩니다. (python이 안 되면 python3 또는 py로 시도해 보세요.)
- 추가 설치 없음. 이 튜토리얼은 Python에 기본 내장된 표준 라이브러리
pathlib와shutil만 씁니다.pip install이 필요 없다는 뜻입니다. - 연습용 폴더 하나. 실수로 중요한 파일을 옮기지 않도록, 먼저
test_folder라는 빈 폴더를 만들고 아무 파일이나 몇 개 복사해 두세요. 진짜 다운로드 폴더는 스크립트를 충분히 이해한 뒤에 적용합니다.
1단계: 폴더 안의 파일 목록 읽기
자동화의 첫걸음은 "지금 폴더에 뭐가 있는지" 파악하는 것입니다. Python의 pathlib를 쓰면 폴더를 객체처럼 다룰 수 있습니다. iterdir()은 폴더 안의 항목을 하나씩 꺼내 주고, is_file()로 파일과 하위 폴더를 구분합니다.
from pathlib import Path
folder = Path("test_folder")
for item in folder.iterdir():
if item.is_file():
print(item.name, "->", item.suffix)
item.name은 파일 이름(photo.jpg), item.suffix는 확장자(.jpg)입니다. 실행하면 폴더 안 파일들이 이름과 확장자로 나열됩니다. 아직 아무것도 옮기지 않고 "읽기만" 하는 단계라 안전합니다.
2단계: 확장자를 분류로 바꾸는 규칙 만들기
이제 .jpg는 '이미지', .pdf는 '문서'처럼 확장자를 분류로 연결하는 규칙이 필요합니다. 파이썬의 딕셔너리(dictionary) — 이름표와 값을 짝지어 저장하는 자료구조 — 로 표현하면 깔끔합니다.
# 분류 폴더 이름 -> 그 분류에 속하는 확장자들
CATEGORIES = {
"이미지": [".jpg", ".jpeg", ".png", ".gif"],
"문서": [".pdf", ".docx", ".txt", ".xlsx", ".csv"],
"압축": [".zip", ".tar", ".gz"],
}
def find_category(suffix):
"""확장자를 받아 분류 폴더 이름을 돌려준다. 목록에 없으면 '기타'."""
suffix = suffix.lower() # .PNG 와 .png 를 같게 취급
for name, extensions in CATEGORIES.items():
if suffix in extensions:
return name
return "기타"
print(find_category(".PNG")) # 이미지
print(find_category(".hwp")) # 기타 (목록에 없는 확장자)
핵심은 두 가지입니다. suffix.lower()로 확장자를 소문자로 바꿔 .PNG와 .png를 똑같이 다루고, 규칙에 없는 확장자는 무조건 기타로 보내 어떤 파일도 빠뜨리지 않습니다. 새 분류를 넣고 싶으면 CATEGORIES에 한 줄만 추가하면 됩니다.
3단계: 폴더를 만들고 파일 옮기기
분류를 알았으니 실제로 옮길 차례입니다. 폴더 생성은 mkdir, 파일 이동은 shutil.move가 담당합니다.
import shutil
from pathlib import Path
folder = Path("test_folder")
target_dir = folder / "이미지" # test_folder/이미지 경로
target_dir.mkdir(exist_ok=True) # 없으면 만들고, 이미 있으면 그냥 넘어감
source = folder / "photo.jpg"
shutil.move(str(source), str(target_dir / "photo.jpg"))
여기서 exist_ok=True가 중요합니다. 이 옵션이 없으면 폴더가 이미 있을 때 오류가 나서 스크립트가 멈춥니다. shutil.move는 파일을 목적지로 이동시키는데, 경로를 문자열(str(...))로 넘기면 어느 Python 버전에서나 안전합니다.
4단계: 안전장치를 넣어 전체 스크립트로 합치기
이제 조각들을 하나로 모읍니다. 단, 파일 이동은 되돌리기 어렵기 때문에 미리보기 모드(dry-run) 를 넣겠습니다. dry_run=True이면 "무엇을 어디로 옮길지"만 출력하고 실제로는 건드리지 않습니다. 결과를 눈으로 확인한 뒤에야 진짜 이동을 실행합니다.
from pathlib import Path
import shutil
# 분류 폴더 이름 -> 그 분류에 속하는 확장자들
CATEGORIES = {
"이미지": [".jpg", ".jpeg", ".png", ".gif"],
"문서": [".pdf", ".docx", ".txt", ".xlsx", ".csv"],
"압축": [".zip", ".tar", ".gz"],
}
def find_category(suffix):
"""확장자를 받아 분류 폴더 이름을 돌려준다. 목록에 없으면 '기타'."""
suffix = suffix.lower()
for name, extensions in CATEGORIES.items():
if suffix in extensions:
return name
return "기타"
def organize(folder, dry_run=True):
"""folder 안의 파일들을 확장자별 폴더로 정리한다."""
folder = Path(folder)
for item in folder.iterdir():
if not item.is_file():
continue # 하위 폴더는 건너뛴다
category = find_category(item.suffix)
target_dir = folder / category
if dry_run:
print(f"[미리보기] {item.name} -> {category}/")
else:
target_dir.mkdir(exist_ok=True)
shutil.move(str(item), str(target_dir / item.name))
print(f"[이동완료] {item.name} -> {category}/")
if __name__ == "__main__":
# 먼저 미리보기로 확인하고, 이상 없으면 아래 줄의 dry_run=False 로 실행
organize("test_folder", dry_run=True)
# organize("test_folder", dry_run=False)
if __name__ == "__main__":은 "이 파일을 직접 실행할 때만 아래를 돌려라"라는 파이썬 관용구입니다. 처음에는 dry_run=True로 결과를 확인하고, 출력이 예상과 같으면 마지막 줄의 주석(#)을 풀고 dry_run=False로 다시 실행하면 실제 정리가 됩니다.
실행 결과
이 스크립트를 organize.py로 저장하고 실행해 봅니다.
python organize.py
미리보기 모드에서는 이런 출력이 나옵니다. 파일은 아직 그대로입니다.
[미리보기] photo.jpg -> 이미지/
[미리보기] screenshot.PNG -> 이미지/
[미리보기] report.pdf -> 문서/
[미리보기] notes.txt -> 문서/
[미리보기] data.csv -> 문서/
[미리보기] archive.zip -> 압축/
[미리보기] song.mp3 -> 기타/
dry_run=False로 바꿔 다시 실행하면 [이동완료] ... 로 바뀌며, 폴더 안이 이미지/, 문서/, 압축/, 기타/로 깔끔하게 나뉩니다. 처음 만든 스크립트가 손으로 5분 걸리던 일을 1초 만에 끝냅니다. 이게 자동화의 첫 성취감입니다.
응용과 트러블슈팅
같은 이름의 파일이 이미 있으면? shutil.move는 목적지에 같은 이름이 있으면 덮어써 버릴 수 있습니다. 이를 막으려면 옮기기 전에 이름이 겹치는지 확인하고, 겹치면 번호를 붙이는 헬퍼를 씁니다.
from pathlib import Path
def unique_path(target_path):
"""대상 경로가 이미 있으면 name_1, name_2 ... 로 겹치지 않는 경로를 만든다."""
if not target_path.exists():
return target_path
stem, suffix, parent = target_path.stem, target_path.suffix, target_path.parent
n = 1
while True:
candidate = parent / f"{stem}_{n}{suffix}"
if not candidate.exists():
return candidate
n += 1
3단계의 이동 코드를 shutil.move(str(item), str(unique_path(target_dir / item.name)))로 바꾸면 photo.jpg가 이미 있을 때 photo_1.jpg로 안전하게 저장됩니다.
되돌리기가 안 돼요. 파일 이동에는 자동 실행 취소가 없습니다. 그래서 항상 dry_run=True로 먼저 확인하고, 진짜 데이터에 적용하기 전 연습용 폴더에서 검증하는 습관이 중요합니다.
다른 일에도 쓰고 싶어요. 파일 정리는 시작일 뿐입니다. 예를 들어 표준 라이브러리 csv 모듈로 매출 파일을 부서별로 집계하는 것도 몇 줄이면 됩니다.
import csv
totals = {}
with open("sales.csv", encoding="utf-8", newline="") as f:
for row in csv.DictReader(f): # 헤더: 부서,금액
dept = row["부서"]
totals[dept] = totals.get(dept, 0) + int(row["금액"])
print(totals) # 예: {'영업': 150, '개발': 200}
폴더 정리에서 배운 "목록을 읽고 → 규칙을 적용하고 → 결과를 만든다"는 흐름은 CSV 집계, 파일 이름 일괄 변경, 보고서 자동 생성 등 거의 모든 사무 자동화에 똑같이 적용됩니다.
마무리
축하합니다. 방금 여러분은 Python으로 첫 업무 자동화 스크립트를 완성했습니다. 핵심은 화려한 라이브러리가 아니라, 반복되는 일을 발견하고 → 규칙으로 바꾸고 → 코드에 맡기는 사고방식입니다. 손이 자주 가는 일을 하나 골라 "이걸 스크립트로 만들 수 있을까?"라고 물어보는 순간, 여러분의 자동화 여정이 시작됩니다.