본문 바로가기
자기 개발/Python

파이썬(Python) 공부 14편 — 자동화 실전 프로젝트 완전 정복 | 파일정리·이메일·스케줄러·watchdog까지

by conrad 2026. 4. 3.
14 / 15 Python 공부 시리즈 — 자동화 실전 프로젝트
← 13편: 웹 스크래핑 기초 완전 정복 보러 가기
Python 공부 시리즈 · 14편 | 파일 자동화 · 이메일 · 스케줄러 · watchdog · 실전 프로젝트

파이썬(Python) 공부 14편
자동화 실전 프로젝트 완전 정복

파이썬을 배운 진짜 이유가 여기 있습니다. 매일 반복하는 파일 정리, 주기적으로 보내는 이메일, 특정 시간마다 실행해야 하는 작업 — 코드 한 번 짜놓으면 파이썬이 대신 해줍니다. 이번 편에서는 os·shutil로 파일을 다루고, smtplib으로 이메일을 보내고, schedule로 작업을 예약하고, watchdog으로 폴더를 감시하는 방법까지 실전 프로젝트 4개와 함께 정리합니다.

파일·폴더 자동화 이메일 자동 발송 schedule 스케줄러 watchdog 파일 감시 실전 프로젝트 4종

🔧 이번 편에서 만들 실전 프로젝트 4가지

개념 설명보다 실제로 써먹을 수 있는 코드가 먼저입니다. 이번 편에서 만드는 프로젝트는 모두 실무에서 바로 활용할 수 있는 것들로 골랐습니다.

프로젝트 ① 🗂️ 다운로드 폴더 자동 정리기

확장자별로 파일을 자동 분류해 폴더로 이동. os·shutil·pathlib 활용

프로젝트 ② 📧 일일 리포트 이메일 자동 발송

엑셀 데이터를 읽어 HTML 이메일로 자동 발송. smtplib·MIMEText 활용

프로젝트 ③ ⏰ 정해진 시간마다 작업 실행

매일 오전 9시, 매주 월요일 등 원하는 시간에 자동 실행. schedule 활용

프로젝트 ④ 👁️ 폴더 변경 실시간 감지·처리

특정 폴더에 파일이 생기면 자동으로 처리. watchdog 활용

설치 (외부 패키지)

pip install schedule watchdog openpyxl

os·shutil·pathlib·smtplib은 파이썬 표준 라이브러리라 별도 설치 없이 사용 가능합니다.


📁 파일·폴더 자동화 기초 — os·shutil·pathlib

파일 자동화의 3대 도구입니다. os는 경로·디렉터리 조작, shutil은 파일 복사·이동·삭제, pathlib은 객체지향 방식으로 경로를 다루는 현대적인 도구입니다. 세 가지를 조합하면 거의 모든 파일 작업을 자동화할 수 있습니다.

 
file_basics.py — os·shutil·pathlib 핵심 정리
import os, shutil
from pathlib import Path
from datetime import datetime

# ─── pathlib — 현대적 경로 처리 ──────────────
p = Path("C:/Users/me/Downloads")   # Windows
p = Path.home() / "Downloads"       # 홈 폴더 기준 (Mac/Linux/Win 공통)

print(p.exists())          # 존재 여부
print(p.is_dir())          # 폴더인지
print(p.name)             # 'Downloads'
print(p.suffix)           # 확장자 (파일일 때)
print(p.stem)             # 확장자 제외한 파일명
print(p.parent)           # 상위 폴더

# ─── 폴더 내 파일 목록 ───────────────────────
for f in p.iterdir():         # 한 단계만
    print(f.name, f.suffix)

for f in p.rglob("*.pdf"):   # 하위 폴더까지 재귀 검색
    print(f)

# ─── 폴더 생성 ───────────────────────────────
new_dir = p / "이미지"
new_dir.mkdir(exist_ok=True)    # 이미 있어도 오류 없음
(p / "문서" / "2026").mkdir(parents=True, exist_ok=True)  # 중간 폴더도 자동 생성

# ─── 파일 복사·이동·삭제 ─────────────────────
src = p / "report.xlsx"
dst = p / "문서" / "report.xlsx"

shutil.copy2(src, dst)          # 복사 (메타데이터 유지)
shutil.move(str(src), str(dst)) # 이동 (잘라내기+붙여넣기)
src.unlink()                    # 파일 삭제
shutil.rmtree(str(new_dir))     # 폴더 전체 삭제 (주의!)

# ─── 파일 이름 변경·타임스탬프 붙이기 ────────
for f in p.glob("*.png"):
    ts       = datetime.now().strftime("%Y%m%d")
    new_name = f.parent / f"{ts}_{f.name}"
    f.rename(new_name)            # 20260401_photo.png

# ─── 파일 크기·수정 시간 확인 ─────────────────
for f in p.iterdir():
    if f.is_file():
        size  = f.stat().st_size / 1024     # KB
        mtime = datetime.fromtimestamp(f.stat().st_mtime)
        print(f"{f.name:30} {size:.1f}KB  {mtime:%Y-%m-%d}")

📊 프로젝트 ① 다운로드 폴더 자동 정리기

다운로드 폴더에 파일이 쌓이다 보면 어느새 수백 개가 됩니다. 확장자별로 폴더를 나눠 자동으로 정리해 주는 스크립트입니다. 한 번만 실행해도 되고, 나중에 스케줄러와 결합해 매일 자동으로 돌릴 수도 있습니다.

 
organize_downloads.py — 확장자별 자동 분류
import shutil, logging
from pathlib import Path
from datetime import datetime

# ─── 설정 ────────────────────────────────────
DOWNLOAD_DIR = Path.home() / "Downloads"

FOLDER_MAP = {
    "이미지"  : [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp"],
    "동영상"  : [".mp4", ".mov", ".avi", ".mkv", ".wmv"],
    "음악"    : [".mp3", ".wav", ".flac", ".aac"],
    "문서"    : [".pdf", ".docx", ".doc", ".txt", ".hwp", ".pptx"],
    "스프레드시트": [".xlsx", ".xls", ".csv"],
    "압축파일": [".zip", ".rar", ".7z", ".tar", ".gz"],
    "설치파일": [".exe", ".msi", ".dmg", ".pkg"],
    "코드"    : [".py", ".js", ".html", ".css", ".json", ".sql"],
}

# ─── 로깅 설정 ───────────────────────────────
logging.basicConfig(
    filename="organizer.log",
    level=logging.INFO,
    format="%(asctime)s %(message)s",
    encoding="utf-8",
)

def get_target_folder(suffix: str) -> str:
    """확장자 → 대상 폴더명 반환"""
    for folder, exts in FOLDER_MAP.items():
        if suffix.lower() in exts:
            return folder
    return "기타"

def organize():
    moved = 0
    for file in DOWNLOAD_DIR.iterdir():
        if not file.is_file():
            continue                       # 폴더 건너뜀

        target_folder = DOWNLOAD_DIR / get_target_folder(file.suffix)
        target_folder.mkdir(exist_ok=True)

        dest = target_folder / file.name
        if dest.exists():               # 같은 이름 파일 있으면 타임스탬프 추가
            ts   = datetime.now().strftime("%H%M%S")
            dest = target_folder / f"{file.stem}_{ts}{file.suffix}"

        shutil.move(str(file), str(dest))
        logging.info(f"이동: {file.name} → {target_folder.name}/")
        moved += 1

    print(f"정리 완료: {moved}개 파일 이동됨")
    logging.info(f"=== 정리 완료: {moved}개 ===")

if __name__ == "__main__":
    organize()

💌 프로젝트 ② 이메일 자동 발송 — smtplib

smtplib은 파이썬 표준 라이브러리로 이메일을 보내는 모듈입니다. Gmail을 사용한다면 앱 비밀번호를 먼저 발급받아야 합니다. (구글 계정 → 보안 → 2단계 인증 활성화 → 앱 비밀번호 생성)

파이썬 자동화 이메일 업무 자동화 ▲ 파이썬으로 이메일 자동 발송을 구현하면 일일 리포트, 알림, 정기 공지 등 반복 업무를 완전 자동화할 수 있다. smtplib과 email 표준 모듈 조합으로 텍스트부터 HTML 이메일, 첨부 파일까지 모두 처리 가능하다. (출처: Unsplash / 참고 이미지)
 
send_email.py — HTML 이메일 + 첨부파일 발송
import smtplib, os
from email.mime.multipart import MIMEMultipart
from email.mime.text      import MIMEText
from email.mime.base      import MIMEBase
from email               import encoders
from pathlib             import Path

# ─── 계정 설정 (환경변수 권장) ───────────────
SMTP_HOST = "smtp.gmail.com"
SMTP_PORT = 587
SENDER    = os.environ.get("EMAIL_USER", "your@gmail.com")
PASSWORD  = os.environ.get("EMAIL_PASS", "앱비밀번호16자리")

def send_report(
    to      : str | list,
    subject : str,
    body_html: str,
    attachments: list[Path] = None,
):
    """HTML 이메일 + 선택적 첨부파일 발송"""
    msg = MIMEMultipart("alternative" if not attachments else "mixed")
    msg["From"]    = SENDER
    msg["To"]      = to if isinstance(to, str) else ", ".join(to)
    msg["Subject"] = subject

    msg.attach(MIMEText(body_html, "html", "utf-8"))

    # 첨부파일 처리
    for fp in (attachments or []):
        part = MIMEBase("application", "octet-stream")
        part.set_payload(Path(fp).read_bytes())
        encoders.encode_base64(part)
        part.add_header("Content-Disposition", f'attachment; filename="{Path(fp).name}"')
        msg.attach(part)

    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
        server.starttls()              # TLS 암호화
        server.login(SENDER, PASSWORD)
        server.sendmail(SENDER, to, msg.as_string())
    print(f"발송 완료 → {to}")

# ─── 사용 예시 ───────────────────────────────
if __name__ == "__main__":
    html_body = """
    <h2>📊 일일 판매 리포트</h2>
    <p>오늘 날짜: <b>2026-04-01</b></p>
    <table border="1" cellpadding="8">
        <tr><th>팀</th><th>판매수</th><th>매출</th></tr>
        <tr><td>영업1팀</td><td>142</td><td>21,300,000원</td></tr>
        <tr><td>영업2팀</td><td>98</td><td>14,700,000원</td></tr>
    </table>
    <p>※ 본 메일은 자동 발송입니다.</p>
    """
    send_report(
        to         = "team@company.com",
        subject    = "[자동] 일일 판매 리포트 - 2026-04-01",
        body_html  = html_body,
        attachments = ["sales_report.xlsx"],   # 첨부파일 (없으면 생략)
    )
💡
보안 중요 비밀번호를 코드에 직접 넣지 말 것

이메일 계정 정보를 코드에 직접 하드코딩하면 GitHub 등에 올렸을 때 노출될 수 있습니다.

  • 환경변수 사용: os.environ.get("EMAIL_PASS") 방식 권장
  • .env 파일 활용: python-dotenv 라이브러리로 .env 파일에서 로드
  • .gitignore에 추가: .env, secrets.py 등 민감 파일은 반드시 Git 제외

프로젝트 ③ 스케줄러 — schedule로 작업 예약

schedule 라이브러리는 cron보다 훨씬 직관적인 문법으로 작업을 예약할 수 있습니다. every().day.at("09:00")처럼 영어 문장을 읽듯이 스케줄을 작성합니다.

 
scheduler.py — schedule 라이브러리 활용
import schedule, time, logging
from datetime import datetime

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s %(message)s")

# ─── 실행할 작업 함수들 ────────────────────────
def daily_report():
    logging.info("일일 리포트 이메일 발송 시작")
    # send_report(...)  ← 위에서 만든 함수 호출

def organize_downloads():
    logging.info("다운로드 폴더 정리 시작")
    # organize()  ← 프로젝트 ① 함수 호출

def backup_files():
    logging.info("주간 백업 시작")

def health_check():
    print(f"[{datetime.now():%H:%M}] 스케줄러 정상 동작 중")

# ─── 스케줄 등록 ─────────────────────────────
schedule.every().day.at("09:00").do(daily_report)          # 매일 오전 9시
schedule.every().day.at("18:30").do(organize_downloads)    # 매일 오후 6시 30분
schedule.every(30).minutes.do(health_check)               # 30분마다
schedule.every().monday.at("08:00").do(backup_files)       # 매주 월요일 8시
schedule.every(10).seconds.do(health_check)                # 테스트용: 10초마다

# 특정 날짜·요일 선택지
# schedule.every().hour.do(job)         → 매시 정각
# schedule.every().wednesday.do(job)    → 매주 수요일
# schedule.every(2).weeks.do(job)       → 2주마다
# schedule.every().day.at("12:00").do(job).tag("lunch")  → 태그 붙이기

# ─── 실행 루프 ───────────────────────────────
print("스케줄러 시작 — Ctrl+C로 종료")
while True:
    schedule.run_pending()    # 대기 중인 작업 실행
    time.sleep(1)             # 1초마다 체크
백그라운드 실행 팁 컴퓨터를 꺼도 실행되게 하려면

schedule은 파이썬 프로세스가 실행 중일 때만 작동합니다. 컴퓨터를 켜두거나 서버에서 실행해야 합니다.

  • Windows: 작업 스케줄러(Task Scheduler)에 파이썬 스크립트 등록
  • Mac / Linux: crontab으로 등록 — crontab -e
  • 서버 운영: nohup python scheduler.py & 또는 systemd 서비스 등록
  • APScheduler: 더 강력한 스케줄링이 필요하면 APScheduler 라이브러리 활용

👁 프로젝트 ④ watchdog — 폴더 실시간 감시

watchdog은 파일 시스템의 변화(생성·수정·삭제·이동)를 실시간으로 감지하는 라이브러리입니다. 특정 폴더에 파일이 생기는 순간 바로 처리 로직을 실행할 수 있습니다. 스케줄러가 "정해진 시간에" 실행한다면, watchdog은 "파일이 생기는 순간" 즉시 반응합니다.

 
watch_folder.py — 폴더 변경 실시간 감지·처리
import time, shutil, logging
from pathlib           import Path
from watchdog.observers import Observer
from watchdog.events   import FileSystemEventHandler, FileCreatedEvent

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s [%(levelname)s] %(message)s")

class AutoProcessor(FileSystemEventHandler):
    """감시 폴더에 파일이 생기면 자동 처리"""

    def __init__(self, output_dir: Path):
        self.output_dir = output_dir
        self.output_dir.mkdir(exist_ok=True)

    def on_created(self, event: FileCreatedEvent):
        if event.is_directory:
            return

        src = Path(event.src_path)
        logging.info(f"새 파일 감지: {src.name}")

        time.sleep(0.5)    # 파일 쓰기 완료 대기

        if src.suffix.lower() in (".csv", ".xlsx"):
            self._process_data_file(src)
        elif src.suffix.lower() in (".jpg", ".png", ".jpeg"):
            self._process_image(src)
        else:
            logging.info(f"처리 규칙 없음: {src.suffix}")

    def _process_data_file(self, src: Path):
        # 데이터 파일 → 처리 후 output 폴더로 이동
        shutil.copy2(src, self.output_dir / src.name)
        logging.info(f"데이터 처리 완료: {src.name} → {self.output_dir.name}/")

    def _process_image(self, src: Path):
        # 이미지 파일 → 이름에 타임스탬프 붙여서 저장
        from datetime import datetime
        ts  = datetime.now().strftime("%Y%m%d_%H%M%S")
        dst = self.output_dir / f"{ts}_{src.name}"
        shutil.copy2(src, dst)
        logging.info(f"이미지 처리 완료: {dst.name}")

    def on_deleted(self, event):
        if not event.is_directory:
            logging.info(f"파일 삭제됨: {Path(event.src_path).name}")

# ─── 감시 시작 ───────────────────────────────
WATCH_DIR  = Path("./inbox")     # 감시할 폴더
OUTPUT_DIR = Path("./processed")  # 처리 결과 저장 폴더
WATCH_DIR.mkdir(exist_ok=True)

event_handler = AutoProcessor(OUTPUT_DIR)
observer      = Observer()
observer.schedule(event_handler, str(WATCH_DIR), recursive=False)
observer.start()

print(f"감시 시작: {WATCH_DIR.resolve()}  (Ctrl+C 로 종료)")
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    observer.stop()
observer.join()

🥇 모두 합치기 — 통합 자동화 시스템

위에서 만든 네 가지 기능을 하나로 묶으면 실무 수준의 자동화 시스템이 됩니다. 스케줄러가 매일 정해진 시간에 파일을 정리하고 리포트를 이메일로 보내고, watchdog이 중요 폴더를 항상 감시합니다.

 
main_automation.py — 통합 자동화 진입점
import schedule, time, threading, logging
from pathlib           import Path
from watchdog.observers import Observer
# from organize_downloads import organize
# from send_email        import send_report
# from watch_folder      import AutoProcessor

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s %(message)s")

def run_scheduler():
    """스케줄러 — 별도 스레드에서 실행"""
    schedule.every().day.at("09:00").do(lambda: logging.info("리포트 발송"))
    schedule.every().day.at("18:00").do(lambda: logging.info("폴더 정리"))
    while True:
        schedule.run_pending()
        time.sleep(30)

def run_watchdog():
    """watchdog — 별도 스레드에서 실행"""
    from watchdog.events import LoggingEventHandler
    handler  = LoggingEventHandler()
    observer = Observer()
    observer.schedule(handler, "./inbox", recursive=False)
    observer.start()
    try:
        while True: time.sleep(1)
    except Exception: observer.stop()
    observer.join()

if __name__ == "__main__":
    # 각 기능을 별도 스레드로 동시 실행
    threading.Thread(target=run_scheduler, daemon=True).start()
    threading.Thread(target=run_watchdog,  daemon=True).start()

    print("자동화 시스템 가동 — Ctrl+C 로 종료")
    try:
        while True: time.sleep(1)
    except KeyboardInterrupt:
        print("\n종료")

📝 14편 실습 문제

실습 1 — 파일 정리기 확장
  • 프로젝트 ①의 다운로드 정리기를 확장해 날짜별 하위 폴더를 추가로 생성 (이미지/2026-04/ 형식)
  • 30일 이상 된 파일은 오래된파일/ 폴더로 이동
  • 처리 결과를 organize_log.txt에 기록
실습 2 — 스케줄러 + 이메일 통합
  • 매일 오전 8시에 현재 날짜·요일·날씨(또는 임의 메시지)를 이메일로 자동 발송
  • 발송 성공/실패 여부를 email_log.csv에 날짜·시간과 함께 기록
  • 실제 발송 테스트는 자신의 이메일로 먼저 확인
14편 핵심 요약
  • pathlib 핵심: Path.home() / iterdir() / glob() / mkdir(parents=True) / stat()
  • shutil 핵심: copy2() — 복사 / move() — 이동 / rmtree() — 폴더 삭제 (주의)
  • 프로젝트 ①: 확장자별 FOLDER_MAP → get_target_folder() → shutil.move() + 충돌 시 타임스탬프
  • smtplib 이메일: MIMEMultipart + MIMEText(html) + MIMEBase(첨부) → starttls() → login() → sendmail()
  • 보안: 비밀번호는 환경변수·.env로 관리 / .gitignore 등록 필수
  • schedule: every().day.at("09:00").do(함수) / run_pending() + time.sleep(1) 루프
  • watchdog: FileSystemEventHandler 상속 → on_created/on_deleted/on_modified 오버라이드 → Observer 등록
  • 통합: threading.Thread로 스케줄러 + watchdog 동시 실행
다음 편 예고 15편 (최종편) — 파이썬 학습 마무리 & 다음 스텝

가상환경 관리 심화 / 타입 힌트 / 테스트 코드 / 이후 학습 로드맵

🐍

※ 본 포스팅은 Python 3.10 이상 환경에서 테스트된 학습용 콘텐츠입니다. 이메일 자동 발송 시 Gmail 앱 비밀번호 및 보안 설정을 반드시 확인하세요. 파일 삭제(shutil.rmtree) 및 이동 작업은 되돌릴 수 없으므로 테스트 폴더에서 먼저 검증 후 사용하시기 바랍니다.