Application/Python

[Python] 04. 파이썬으로 폴더별 파일 자동 분류하기 (확장자별 정리)

devsalix 2026. 6. 12. 10:05
728x90

파이썬으로 폴더별 파일 자동 분류하기 (확장자별 정리)

지금 다운로드 폴더 한번 열어보세요. 영수증 PDF, 카톡에서 받은 사진, 회사에서 보낸 엑셀 양식, 누가 던져준 zip 파일까지 다 뒤죽박죽이시죠.

저도 그래서 큰맘 먹고 정리해보겠다고 마우스 잡았다가, 한 30개쯤 옮기고 나서 그냥 닫았어요. 너무 많아서요.

이걸 파이썬한테 시키면 그냥 끝나거든요. 진짜로요.


파일 자동 분류, 왜 파이썬이 답일까

생각해보면 우리가 폴더 정리할 때 하는 일은 단순해요. "이건 이미지니까 사진 폴더, 이건 PDF니까 문서 폴더". 이 기준만 정해주면 나머지는 기계가 훨씬 잘하는 일이거든요.

마치 우체국 분류기 같은 거예요. 주소(=확장자)만 보고 해당 칸으로 던지는 일.


shutil.move랑 rename은 뭐가 달라요?

저번 글(auto-03)에서 rename()으로 파일 이름 바꾸는 거 해봤잖아요. 사실 rename은 같은 폴더 안에서 이동도 되긴 하는데, 큰 함정이 하나 있어요.

다른 드라이브로는 못 옮겨요. C 드라이브에서 D 드라이브로 rename을 시도하면 OSError가 떠버립니다. 저도 처음에 외장하드로 사진 옮기다가 이걸로 한참 헤맸어요.

그래서 이번엔 shutil.move()를 씁니다. 얘는 드라이브가 달라도 알아서 옮겨주거든요.

import shutil                   # 파일 이동/복사용 표준 라이브러리
from pathlib import Path        # 경로 다루기 (auto-02에서 배운 그거)

src = Path("C:/Users/me/Downloads/사진.jpg")  # 원본
dst = Path("D:/정리함/이미지/사진.jpg")        # 대상
shutil.move(str(src), str(dst))               # 끝

 

str()로 감싼 이유는 옛날 파이썬 버전에서도 안전하게 동작하게 하려는 거예요. 습관처럼 붙여주시면 됩니다.


확장자별 정리의 핵심: 매핑 딕셔너리

이번 글의 진짜 주인공은 이 딕셔너리 하나예요.

CATEGORIES = {
    "이미지": [".jpg", ".jpeg", ".png", ".gif", ".webp"],   # 사진류
    "문서":   [".pdf", ".docx", ".hwp", ".txt"],            # 문서류
    "엑셀":   [".xlsx", ".xls", ".csv"],                    # 표 데이터
    "압축":   [".zip", ".rar", ".7z"],                      # 압축파일
    "영상":   [".mp4", ".mov", ".avi", ".mkv"],             # 동영상
}

 

본인 취향대로 마음껏 바꾸셔도 돼요. "한글 파일은 따로 빼고 싶다" 하시면 새 카테고리 추가하시면 그만이거든요.

이제 확장자를 받으면 어떤 카테고리인지 알려주는 함수를 만들어 볼게요.

def find_category(suffix):
    suffix = suffix.lower()                  # .JPG 같은 대문자 방어
    for cat, exts in CATEGORIES.items():     # 카테고리들을 하나씩 훑으면서
        if suffix in exts:                   # 해당 확장자 리스트에 있으면
            return cat                       # 그 카테고리 이름 반환
    return "기타"                             # 어디에도 없으면 기타

 

suffix.lower() 이거 진짜 중요해요. .JPG.jpg는 파이썬 입장에서 완전 다른 문자열이거든요. 이거 안 해주면 핸드폰에서 받은 .JPG 파일들이 죄다 "기타"로 빠지는 슬픈 일이 생깁니다.


폴더가 없으면 만들어주기

분류해서 옮기려고 했더니 "이미지" 폴더 자체가 아직 없을 수 있잖아요. 그 상태로 move 하면 FileNotFoundError 만나요.

그래서 옮기기 전에 폴더부터 만들어 둡니다.

target_folder = Path("C:/Users/me/Downloads/이미지")
target_folder.mkdir(parents=True, exist_ok=True)

 

옵션 두 개만 기억하시면 돼요.

  • parents=True: 중간 폴더가 없어도 다 만들어줘요
  • exist_ok=True: 이미 있어도 에러 안 내고 그냥 통과해요

이 두 개 안 주면 두 번째 돌릴 때부터 에러 폭탄이에요. 꼭 같이 써주세요.


같은 이름 파일이 있으면?

report.pdf를 문서 폴더로 옮기려는데 거기 똑같이 report.pdf가 있다면? 그냥 덮어써집니다. shutil.move는 윈도우에서도 인정사정 없이 덮어쓰거든요. 모르고 돌리면 소중한 파일이 한 방에 사라져요.

그래서 이름 뒤에 _1, _2 붙여주는 함수를 하나 만들어 둡니다.

def resolve_conflict(target):
    if not target.exists():                          # 같은 이름이 없으면
        return target                                 # 그대로 사용
    stem, suffix, parent = target.stem, target.suffix, target.parent
    i = 1
    while (parent / f"{stem}_{i}{suffix}").exists():  # 빈 번호 찾을 때까지
        i += 1
    return parent / f"{stem}_{i}{suffix}"             # report_1.pdf, report_2.pdf...

 

while True처럼 보여서 처음엔 무서우실 수 있는데, 빈 자리만 찾으면 바로 return으로 빠져나오니까 안전해요.


파이썬 파일 정리 스크립트 전체 코드

자, 이제 다 모아볼게요. 이게 메인입니다.

from pathlib import Path
import shutil

DOWNLOADS = Path.home() / "Downloads"   # 본인 다운로드 폴더 자동 인식
DRY_RUN = True                          # 일단 미리보기만 (auto-03에서 배운 그 패턴)

CATEGORIES = {
    "이미지": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
    "문서":   [".pdf", ".docx", ".hwp", ".txt"],
    "엑셀":   [".xlsx", ".xls", ".csv"],
    "압축":   [".zip", ".rar", ".7z"],
    "영상":   [".mp4", ".mov", ".avi", ".mkv"],
}

def find_category(suffix):
    suffix = suffix.lower()
    for cat, exts in CATEGORIES.items():
        if suffix in exts:
            return cat
    return "기타"

def resolve_conflict(target):
    if not target.exists():
        return target
    stem, suffix, parent = target.stem, target.suffix, target.parent
    i = 1
    while (parent / f"{stem}_{i}{suffix}").exists():
        i += 1
    return parent / f"{stem}_{i}{suffix}"

count = 0
for file in DOWNLOADS.iterdir():        # 다운로드 폴더 안의 모든 항목
    if file.is_dir():                    # 폴더면 건드리지 않기 (중요!)
        continue
    category = find_category(file.suffix)
    target_folder = DOWNLOADS / category
    target_folder.mkdir(exist_ok=True)
    target = resolve_conflict(target_folder / file.name)
    if DRY_RUN:
        print(f"[미리보기] {file.name} → {category}/{target.name}")
    else:
        shutil.move(str(file), str(target))
        print(f"[이동] {file.name} → {category}/{target.name}")
    count += 1

print(f"\n총 {count}개 처리됨 (DRY_RUN={DRY_RUN})")

 

실행 결과는 대충 이렇게 나와요.

[미리보기] 회의록.pdf → 문서/회의록.pdf
[미리보기] 강아지.jpg → 이미지/강아지.jpg
[미리보기] 매출표.xlsx → 엑셀/매출표.xlsx
[미리보기] 강아지.jpg → 이미지/강아지_1.jpg
...
총 47개 처리됨 (DRY_RUN=True)

 

여기서 꼭 짚고 싶은 부분이 if file.is_dir(): continue예요. 이미 만들어진 "이미지" 폴더를 또 분류하려고 하면 폴더 안의 폴더 안의 폴더… 카오스가 펼쳐집니다. 폴더는 무조건 무시하세요.

미리보기 결과 보시고 마음에 드시면 DRY_RUN = False로 바꿔서 진짜 실행하시면 돼요.


보너스: 사진을 촬영월별로 정리

확장자별 분류만으로는 부족하실 수도 있어요. 사진이 1000장 있는데 다 "이미지" 폴더에 들어가면 그것도 카오스잖아요.

수정 시간 기준으로 월별 폴더에 넣어주는 짧은 스크립트입니다.

from pathlib import Path
from datetime import datetime
import shutil

PHOTOS = Path(r"C:\사진정리\원본")              # r" " 붙이면 역슬래시 신경 안 써도 돼요

for file in PHOTOS.iterdir():
    if not file.is_file():
        continue
    mtime = datetime.fromtimestamp(file.stat().st_mtime)  # 수정 시간
    folder = PHOTOS / mtime.strftime("%Y-%m")              # "2025-03" 같은 폴더명
    folder.mkdir(exist_ok=True)
    shutil.move(str(file), str(folder / file.name))

 

stat().st_mtime이 파일 수정 시간이에요. 카메라가 기록한 실제 촬영 시간(EXIF)이랑은 살짝 다를 수 있는데, 그건 외부 라이브러리가 필요해서 다음 기회에 다룰게요.


자주 만나는 에러들

에러 메시지 원인 해결
FileNotFoundError 대상 폴더가 없음 mkdir(exist_ok=True) 먼저
PermissionError 파일이 다른 프로그램에서 열려 있음 엑셀이나 한글 같은 거 닫고 다시
OSError [WinError 17] rename으로 다른 드라이브 이동 shutil.move 쓰기
NameError: shutil import 빼먹음 import shutil 추가

특히 두 번째, 엑셀 파일 열어둔 채로 돌리면 백퍼 에러나요. 저도 한참 헤맸던 부분이에요.


실습 과제

  1. 본인 다운로드 폴더 경로로 위 메인 스크립트 돌려보세요. DRY_RUN = True 상태로요.
  2. 출력 결과 쭉 보시고, 분류가 마음에 들면 False로 바꿔서 진짜 실행.
  3. 본인이 자주 받는 확장자(예: .psd, .ai) 있으면 CATEGORIES에 추가해서 한번 더 돌려보세요.

처음엔 무서우실 수 있는데, dry-run으로 미리 확인하는 습관만 들이시면 사고날 일 거의 없어요.


입문편을 마치며

여기까지 따라오셨다면 파일이랑 폴더 다루는 건 이제 거의 다 익히신 거예요. 경로 다루기(auto-02), 이름 일괄 변경(auto-03), 그리고 오늘 한 확장자별 정리까지.

다운로드 폴더 들어갈 때마다 "아 정리해야 하는데" 하던 그 스트레스, 일주일에 한 번 스크립트 한 번 돌리는 걸로 끝낼 수 있어요.

다음 글부터는 진짜 직장인 자동화의 꽃, 엑셀로 들어갑니다. openpyxl이라는 라이브러리로 셀에 값 넣고, 색칠하고, 시트 통째로 만드는 것까지 해볼 거예요. 솔직히 여기서부터가 진짜 재밌어지는 구간이거든요.


 


제 글이 도움이 되셨다면 댓글 & 공감 부탁드려요 😀

 

 
728x90