다운로드 폴더를 열었을 때 스크린샷, 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 --version

Python 3.8.0처럼 3.8 이상이 찍히면 됩니다. (python이 안 되면 python3 또는 py로 시도해 보세요.)

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으로 첫 업무 자동화 스크립트를 완성했습니다. 핵심은 화려한 라이브러리가 아니라, 반복되는 일을 발견하고 → 규칙으로 바꾸고 → 코드에 맡기는 사고방식입니다. 손이 자주 가는 일을 하나 골라 "이걸 스크립트로 만들 수 있을까?"라고 물어보는 순간, 여러분의 자동화 여정이 시작됩니다.