파이썬으로 오래된 파일 자동 삭제·백업하기

다운로드 폴더를 열어 보면 몇 달 전에 받아 놓고 한 번도 열지 않은 설치 파일, 압축을 풀고 나서 방치한 zip, 이름이 견적서_최종_최종2.xlsx인 문서가 뒤엉켜 있습니다. 하나하나 확인하면서 지우려면 매번 20~30분이 걸리고, 그러다 보니 결국 미루게 됩니다. 이 글에서는 오래된 파일 자동 정리를 파이썬으로 처리하는 방법을 다룹니다. 핵심은 “무조건 지우기”가 아니라 압축 백업을 먼저 만들고 나서 지우는 것입니다. 되돌릴 수 있는 구조로 만들어야 마음 놓고 자동화에 맡길 수 있기 때문입니다.

무엇을 기준으로 “오래됐다”고 볼 것인가

파일마다 시간 정보가 여러 개 붙어 있는데, 어느 것을 쓰느냐에 따라 결과가 완전히 달라집니다. 파이썬에서는 Path.stat()으로 세 가지를 읽을 수 있습니다.

속성의미기준으로 쓸 만한가
st_mtime파일 내용이 마지막으로 바뀐 시각적합. 정리 스크립트는 대부분 이 값을 씁니다
st_atime마지막으로 읽은 시각위험. 백신이나 백업 프로그램이 스캔만 해도 갱신될 수 있습니다
st_ctime윈도우는 생성 시각, 리눅스·맥은 메타데이터가 바뀐 시각부적합. OS마다 의미가 달라 크로스 플랫폼 스크립트에 쓰면 안 됩니다

그래서 아래 예제는 전부 st_mtime을 기준으로 삼습니다. 한 가지 알아둘 점은, 파일을 다른 폴더로 복사하면 수정 시각이 복사한 시점으로 바뀔 수 있다는 것입니다. 파이썬으로 옮길 때 원래 시각을 유지하려면 shutil.copy가 아니라 shutil.copy2를 써야 합니다.

1단계 — 지울 목록부터 눈으로 확인하기

제일 먼저 만들 스크립트는 아무것도 지우지 않습니다. 어떤 파일이 대상이 되는지, 용량이 얼마나 확보되는지만 출력합니다. 이 단계를 건너뛰고 바로 삭제 코드를 돌리는 것이 사고가 나는 가장 흔한 경로입니다.

from pathlib import Path
from datetime import datetime, timedelta

TARGET = Path.home() / "Downloads"   # 정리할 폴더
DAYS = 30                            # 며칠 지난 파일을 대상으로 볼지

cutoff = datetime.now() - timedelta(days=DAYS)
total = 0

for f in sorted(TARGET.rglob("*")):
    if not f.is_file():
        continue
    st = f.stat()
    mtime = datetime.fromtimestamp(st.st_mtime)
    if mtime < cutoff:
        print(f"{mtime:%Y-%m-%d}  {st.st_size / 1048576:8.2f} MB  {f.name}")
        total += st.st_size

print()
print(f"{DAYS}일 이상 지난 파일 합계: {total / 1048576:.1f} MB")
삭제 대상 파일 미리보기 실행 결과
먼저 목록만 뽑아본 것입니다. 30일이 지난 3개가 잡혔고 아직 아무것도 지우지 않았습니다.

실행해 보면 날짜·용량·파일명이 줄줄이 나옵니다. 여기서 DAYS 값을 30, 60, 90으로 바꿔 가며 몇 개가 걸리는지 감을 잡으세요. 지우면 안 되는 파일이 목록에 섞여 있다면, 그건 그 폴더가 애초에 자동 정리 대상으로 적절하지 않다는 신호입니다.

2단계 — 삭제 전에 압축 백업 만들기

목록이 마음에 든다면, 다음은 그 파일들을 zip 하나로 묶는 단계입니다. 압축해 두면 원본을 지워도 나중에 꺼낼 수 있고, 이미 압축된 설치 파일이 아닌 이상 용량도 눈에 띄게 줄어듭니다.

import zipfile
from pathlib import Path
from datetime import datetime, timedelta

TARGET = Path.home() / "Downloads"
BACKUP_DIR = Path.home() / "Backup"      # 반드시 TARGET 바깥에 둘 것
DAYS = 30

BACKUP_DIR.mkdir(parents=True, exist_ok=True)
cutoff = datetime.now() - timedelta(days=DAYS)
archive = BACKUP_DIR / f"old-files-{datetime.now():%Y%m%d}.zip"

targets = [
    f for f in TARGET.rglob("*")
    if f.is_file() and datetime.fromtimestamp(f.stat().st_mtime) < cutoff
]

if not targets:
    print("백업할 파일이 없습니다.")
else:
    with zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED) as z:
        for f in targets:
            z.write(f, arcname=str(f.relative_to(TARGET)))
    print(f"{len(targets)}개 파일을 {archive} 로 묶었습니다.")

arcnamerelative_to(TARGET)를 넘기는 이유는, 그렇게 하지 않으면 zip 안에 전체 경로가 그대로 들어가 압축을 풀 때 폴더가 이상하게 겹치기 때문입니다. 백업 폴더를 정리 대상 폴더 바깥에 두는 것도 중요합니다. 안쪽에 두면 다음 실행 때 백업 zip 자신이 정리 대상이 되어 버립니다.

3단계 — 백업과 삭제를 한 번에

두 단계를 합치고 안전장치를 붙인 완성본입니다. DRY_RUN 플래그가 켜져 있으면 목록만 출력하고 끝납니다. 삭제는 os.remove 대신 send2trash 패키지를 써서 휴지통으로 보냅니다. 스크립트가 잘못 동작해도 휴지통에서 되돌릴 수 있기 때문에, 자동 실행을 걸어 둘 스크립트라면 이 방식을 권합니다.

먼저 패키지를 설치합니다. python -m pip install send2trash 한 줄이면 되고, 윈도우·맥·리눅스 모두 지원합니다.

"""오래된 파일 자동 정리: 압축 백업 후 휴지통으로 이동."""
import zipfile
from pathlib import Path
from datetime import datetime, timedelta
from send2trash import send2trash

TARGET = Path.home() / "Downloads"
BACKUP_DIR = Path.home() / "Backup" / "downloads"
DAYS = 30
KEEP_EXT = {".key", ".lic", ".pem"}   # 확장자로 예외 처리
DRY_RUN = True                        # 처음에는 반드시 True로 한 번 돌려볼 것


def find_old_files():
    cutoff = datetime.now() - timedelta(days=DAYS)
    for f in TARGET.rglob("*"):
        if not f.is_file():
            continue
        if f.suffix.lower() in KEEP_EXT:
            continue
        if BACKUP_DIR in f.parents:       # 백업 폴더 자기 자신은 건너뛴다
            continue
        if datetime.fromtimestamp(f.stat().st_mtime) < cutoff:
            yield f


def main():
    targets = list(find_old_files())
    if not targets:
        print("정리할 파일이 없습니다.")
        return

    if DRY_RUN:
        for f in targets:
            print("[예정]", f)
        print(f"총 {len(targets)}개. DRY_RUN을 False로 바꾸면 실제로 실행됩니다.")
        return

    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    archive = BACKUP_DIR / f"{datetime.now():%Y%m%d-%H%M}.zip"

    with zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED) as z:
        for f in targets:
            z.write(f, arcname=str(f.relative_to(TARGET)))

    for f in targets:
        send2trash(str(f))

    print(f"{len(targets)}개를 {archive.name}에 백업하고 휴지통으로 옮겼습니다.")


if __name__ == "__main__":
    main()
압축 백업 후 삭제한 실행 결과
백업 zip을 만든 뒤 삭제한 결과입니다. 최근 파일은 그대로 남아 있습니다.

KEEP_EXT에 넣은 확장자는 아무리 오래돼도 건드리지 않습니다. 인증서나 라이선스 키처럼 “쓸 일은 거의 없지만 없으면 곤란한” 파일을 여기에 넣어 두세요.

보관 기간은 얼마로 잡을까

폴더 성격에 따라 기준이 달라야 합니다. 아래는 출발점으로 삼을 만한 값입니다. 한 달 정도 돌려 보면서 너무 빨리 사라진다 싶으면 늘리고, 여전히 쌓인다 싶으면 줄이는 식으로 맞춰 가면 됩니다.

폴더권장 기준백업
다운로드30일압축 보관 후 삭제
스크린샷 · 화면 캡처60일압축 보관 후 삭제
프로그램 로그 파일14일보통 불필요, 바로 삭제
빌드 산출물 · 임시 폴더7일불필요
프로젝트 작업 폴더기간 기준 금지자동화 대상에서 제외

마지막 줄이 중요합니다. 작업 중인 프로젝트 폴더는 몇 달간 손대지 않다가 갑자기 다시 여는 일이 흔합니다. 시간만으로 판단하면 안 되는 폴더는 아예 자동화 대상에서 빼는 편이 낫습니다.

자동 실행으로 걸어두기

스크립트를 만들어 놓고 매번 직접 실행해야 한다면 결국 안 하게 됩니다. 윈도우는 작업 스케줄러, 맥·리눅스는 cron에 등록해서 주 1회 정도 돌게 해 두세요. 명령줄로 등록하는 쪽이 빠릅니다.

:: 윈도우 — 매주 일요일 오전 9시 실행, 결과는 로그 파일에 누적
schtasks /Create /TN "old-file-cleanup" /SC WEEKLY /D SUN /ST 09:00 ^
  /TR "cmd /c python C:\scripts\cleanup.py >> C:\scripts\cleanup.log 2>&1"

:: 등록 확인 / 지금 바로 한 번 실행 / 삭제
schtasks /Query  /TN "old-file-cleanup"
schtasks /Run    /TN "old-file-cleanup"
schtasks /Delete /TN "old-file-cleanup" /F
# 맥 · 리눅스 — crontab -e 로 열어서 아래 한 줄 추가
0 9 * * 0 /usr/bin/python3 /home/user/scripts/cleanup.py >> /home/user/scripts/cleanup.log 2>&1

로그를 파일로 남기는 것을 잊지 마세요. 자동 실행은 화면에 아무것도 보여주지 않기 때문에, 로그가 없으면 스크립트가 조용히 실패하고 있어도 몇 달간 모를 수 있습니다. 등록한 뒤에는 한 번 수동으로 실행해서 로그 파일이 실제로 생기는지 확인하세요.

주의할 점

  • 클라우드 동기화 폴더는 대상에서 빼세요. 원드라이브·구글 드라이브·드롭박스 폴더에서 지우면 다른 기기와 웹에서도 함께 사라집니다.
  • 온라인 전용(자리표시자) 파일에 주의하세요. 로컬에 내용이 없는 파일을 스크립트가 건드리면 전체 다운로드가 시작될 수 있습니다.
  • 시스템 폴더는 절대 지정하지 마세요. TARGET에 윈도우 설치 폴더나 사용자 폴더 최상위를 넣으면 복구가 어려운 사고로 이어집니다. 대상은 항상 구체적인 하위 폴더로 지정하세요.
  • 첫 실행은 반드시 DRY_RUN으로. 폴더나 기간을 바꿀 때마다 다시 한 번 확인하는 습관을 들이세요.
  • 백업 zip도 언젠가는 쌓입니다. 백업 폴더 자체에 “1년 지난 zip은 삭제” 같은 규칙을 하나 더 걸어 두면 무한정 늘어나지 않습니다.

자동 삭제 스크립트를 처음 돌릴 때는 실제 폴더 대신 테스트용 폴더를 만들어서 파일 몇 개를 넣고 확인해 보세요. 파일의 수정 시각은 os.utime으로 임의로 바꿀 수 있어서, 며칠을 기다리지 않고도 동작을 검증할 수 있습니다.

마무리

전부 한 번에 만들 필요는 없습니다. 오늘은 1단계 스크립트만 복사해서 다운로드 폴더에 돌려 보세요. 실행에 10초도 걸리지 않고, 아무것도 지우지 않으면서 몇 GB가 잠들어 있는지 바로 보여줍니다. 그 숫자를 확인하고 나면 나머지 단계를 붙일 이유가 생깁니다.

다음 글에서는 마우스에 손이 가는 시간을 줄여 주는 개발자를 위한 단축키 모음(윈도우/맥)을 정리해 보겠습니다.

댓글 남기기