🐍 파이썬 파일입출력 처리와 디렉터리 감시 자동화 방법
⚡ watchdog으로 신규 파일 감지부터 에러 재시도 큐까지 완벽 구현 가이드
파일 처리는 개발에서 빼놓을 수 없는 핵심 요소입니다.
특히 자동화 시스템이나 데이터 파이프라인을 구축할 때는 디렉터리에 새롭게 생성되는 파일을 즉시 감지하고 처리하는 기능이 필요합니다.
수동으로 파일을 옮기거나 실행하는 대신, 프로그램이 알아서 변화에 대응하도록 만든다면 훨씬 효율적이고 안정적인 시스템을 구축할 수 있습니다.
이 글에서는 파이썬의 파일입출력 처리와 함께, watchdog 라이브러리를 활용해 디렉터리를 감시하고 신규 파일만 처리하는 방법을 다루어 보겠습니다.
또한 파일 처리 중 오류가 발생했을 때 단순히 실패로 끝내지 않고, 자동으로 재시도 큐를 만들어 안정적으로 복구하는 전략까지 소개합니다.
실무에 바로 적용할 수 있는 코드 예제와 함께 설명하므로, 파이썬을 활용한 자동화에 관심 있는 분들에게 유용한 자료가 될 것입니다.
📋 목차
📂 파이썬 파일입출력 기본 개념
파이썬에서 파일 입출력은 데이터를 저장하고 불러오는 가장 기본적인 기능입니다.
텍스트 파일, CSV, 로그 파일 등 다양한 포맷을 손쉽게 다룰 수 있으며, 자동화 스크립트나 데이터 처리 파이프라인의 핵심 요소로 활용됩니다.
기본적으로 open() 함수를 통해 파일을 열고, 읽기(read), 쓰기(write), 추가(append) 모드를 지정하여 다루게 됩니다.
예를 들어, 텍스트 파일을 생성하고 내용을 기록하는 방법은 아래와 같습니다.
with 구문을 사용하면 파일을 자동으로 닫아주기 때문에 메모리 누수를 방지할 수 있어 권장됩니다.
# 파일 쓰기 예제
with open("example.txt", "w", encoding="utf-8") as f:
f.write("안녕하세요, 파일 입출력 예제입니다.")
# 파일 읽기 예제
with open("example.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)
위 코드를 실행하면 example.txt 파일이 생성되고, 이후 다시 읽어 내용을 출력할 수 있습니다.
이처럼 파이썬의 파일 입출력은 간단하지만, 안정적인 자동화 시스템을 구축하기 위해서는 더 많은 고려사항이 필요합니다.
📝 자주 쓰이는 파일 모드
| 모드 | 설명 |
|---|---|
| “r” | 읽기 전용 모드 (파일이 존재해야 함) |
| “w” | 쓰기 모드 (기존 파일은 덮어씀) |
| “a” | 추가 모드 (파일 끝에 내용 이어쓰기) |
| “b” | 바이너리 모드 (이미지, 실행파일 처리 시 활용) |
이러한 기본적인 파일 처리 개념을 확실히 이해해야, 뒤에서 설명할 watchdog을 통한 디렉터리 감시와 신규 파일 처리 로직을 원활히 적용할 수 있습니다.
👀 watchdog으로 디렉터리 감시하기
일반적인 파일입출력만으로는 특정 폴더에 새로운 파일이 생성되거나 수정되는 순간을 자동으로 알 수 없습니다.
이럴 때 유용하게 쓸 수 있는 라이브러리가 바로 watchdog입니다.
watchdog은 파이썬에서 디렉터리의 변화를 감지해, 파일 생성, 수정, 삭제 이벤트에 즉각 반응할 수 있도록 도와줍니다.
설치 방법은 간단합니다.
pip을 통해 아래와 같이 설치할 수 있습니다.
pip install watchdog
설치 후에는 Observer와 Handler를 조합하여 디렉터리 이벤트를 감시할 수 있습니다.
아래 코드는 특정 디렉터리를 감시하고, 새로운 파일이 생성되면 알림을 출력하는 간단한 예제입니다.
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class MyHandler(FileSystemEventHandler):
def on_created(self, event):
if not event.is_directory:
print(f"새 파일이 생성되었습니다: {event.src_path}")
if __name__ == "__main__":
path = "./watch_folder"
event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
위 코드에서 on_created() 메서드는 새로운 파일이 생성될 때마다 호출됩니다.
따라서 파일 복사, 업로드, 다운로드 등으로 새 파일이 들어오는 환경에서 자동화된 처리를 쉽게 적용할 수 있습니다.
⚙️ 실무에서 유용한 활용 포인트
- 📂데이터 수집 서버에 업로드되는 로그 파일을 자동 분석
- 📑문서 폴더에 들어오는 신규 PDF 파일을 자동 변환
- 🎵음악 폴더에 추가된 mp3 파일을 자동 정리
이처럼 watchdog을 사용하면 반복적인 수동 작업을 줄이고, 실시간 반응형 파일 처리 시스템을 만들 수 있습니다.
🆕 신규 파일만 처리하는 로직 구현
디렉터리를 감시할 때 모든 이벤트를 무조건 처리하면 불필요한 리소스 소모가 발생합니다.
예를 들어 기존 파일이 단순히 수정되었을 뿐인데 새로운 작업을 실행하면 중복 처리 문제가 생길 수 있습니다.
따라서 실제 업무 환경에서는 신규 파일만 구분하여 처리하는 로직이 반드시 필요합니다.
이를 구현하기 위해서는 파일 생성 이벤트(on_created)를 활용하고, 이미 처리한 파일 목록을 별도로 관리하는 방법을 사용할 수 있습니다.
간단한 경우에는 Python의 set 자료구조에 파일명을 기록해두고 중복 여부를 체크하면 됩니다.
processed_files = set()
class MyHandler(FileSystemEventHandler):
def on_created(self, event):
if not event.is_directory:
filename = event.src_path
if filename not in processed_files:
print(f"신규 파일 처리: {filename}")
processed_files.add(filename)
이 로직을 적용하면 동일한 파일이 여러 번 감지되더라도 최초 한 번만 처리하도록 보장할 수 있습니다.
또한 시스템 종료 시 처리 내역을 로그 파일로 저장해두면, 재실행 후에도 중복 없이 이어갈 수 있습니다.
🔍 고려해야 할 상황
💡 TIP: 대용량 파일이 업로드될 경우, 생성 이벤트가 감지되더라도 파일이 완전히 복사되기 전에 처리하면 오류가 발생할 수 있습니다. 이런 경우에는 파일 크기를 확인하면서 안정적으로 처리 가능한 시점을 기다리는 로직을 추가하는 것이 좋습니다.
예를 들어, 파일 크기를 일정 시간 간격으로 체크해 변동이 없을 때만 “완전히 업로드된 상태”로 간주할 수 있습니다.
이런 안정성 검증 로직을 추가하면 시스템의 신뢰성을 크게 높일 수 있습니다.
✅ 체크리스트
- 🆕처리한 파일을 집합(set) 또는 데이터베이스에 기록했는가?
- 📂파일이 완전히 업로드 완료되었는지 검증하는 로직이 있는가?
- 🔁중복 이벤트 발생 시 불필요한 재처리를 방지했는가?
위와 같은 검증 단계를 통해 단순한 디렉터리 감시 코드를 실무에서도 안전하게 쓸 수 있는 자동화 시스템으로 발전시킬 수 있습니다.
🔄 에러 발생 시 재시도 큐 활용
디렉터리 감시 후 파일을 처리하는 과정에서는 일시적인 네트워크 장애나 파일 잠금, 미완료 업로드 등으로 인해 작업이 실패할 수 있습니다.
실패할 때마다 프로그램을 중단하거나 수동 개입을 기다리면 자동화의 장점이 사라집니다.
이 문제를 해결하려면 재시도 큐를 도입하여 실패 작업을 큐에 넣고, 백오프 전략을 적용해 일정 시간 간격으로 다시 시도하도록 설계해야 합니다.
또한 반복 실패 시에는 데드 레터 큐로 분리해 운영자가 확인할 수 있도록 만드는 것이 좋습니다.
🧱 재시도 큐 설계 원칙
- ⏳지수 백오프 적용: 1초, 2초, 4초처럼 대기 시간을 점진적으로 늘립니다.
- 🧪가역 에러만 재시도: 파일 잠금, 임시 네트워크 오류 등은 재시도하고, 포맷 손상 등 비가역 에러는 즉시 데드 레터 큐로 이동합니다.
- 🗂️상태 저장: 재시도 회수와 마지막 실패 원인을 로그 또는 작은 DB에 기록합니다.
- 🔐중복 방지: 동일 파일이 동시에 큐에 들어오지 않도록 처리중 세트(in-flight set)를 별도로 관리합니다.
import os
import time
import queue
import threading
from dataclasses import dataclass, field
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
MAX_RETRIES = 5
@dataclass(order=True)
class RetryItem:
ready_at: float
path: str = field(compare=False)
attempt: int = field(default=0, compare=False)
reason: str = field(default="", compare=False)
retry_q = queue.PriorityQueue() # (ready_at 기준 우선순위)
dead_letters = []
in_flight = set() # 중복 처리 방지
def process_file(path: str):
# 예시 처리 함수: 파일 크기가 0이면 실패로 가정
size = os.path.getsize(path)
if size == 0:
raise IOError("empty file")
# 실제 처리 로직 자리
print(f"처리 완료: {path} ({size} bytes)")
def schedule_retry(path: str, attempt: int, reason: str):
delay = min(2 ** attempt, 60) # 지수 백오프, 상한 60초
ready_at = time.time() + delay
retry_q.put(RetryItem(ready_at=ready_at, path=path, attempt=attempt, reason=reason))
print(f"재시도 예약: {path} {attempt}회차, {delay}s 후, 사유={reason}")
def worker():
while True:
try:
item = retry_q.get(timeout=1)
except queue.Empty:
continue
now = time.time()
if item.ready_at > now:
time.sleep(item.ready_at - now)
try:
process_file(item.path)
in_flight.discard(item.path)
except Exception as e:
if item.attempt + 1 >= MAX_RETRIES:
dead_letters.append((item.path, str(e)))
in_flight.discard(item.path)
print(f"데드 레터 이동: {item.path}, 이유={e}")
else:
schedule_retry(item.path, item.attempt + 1, str(e))
finally:
retry_q.task_done()
class Handler(FileSystemEventHandler):
def on_created(self, event):
if event.is_directory:
return
path = event.src_path
if path in in_flight:
return
in_flight.add(path)
try:
process_file(path)
in_flight.discard(path)
except Exception as e:
schedule_retry(path, 1, str(e))
if __name__ == "__main__":
# 워커 스레드 시작
t = threading.Thread(target=worker, daemon=True)
t.start()
watch_dir = "./watch_folder"
os.makedirs(watch_dir, exist_ok=True)
observer = Observer()
observer.schedule(Handler(), watch_dir, recursive=False)
observer.start()
print("감시 시작")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
🧭 지수 백오프와 데드 레터 기준
| 항목 | 권장값/설명 |
|---|---|
| 초기 대기시간 | 1~2초로 시작하여 시스템 부하 최소화 |
| 상한(캡) | 60~120초 범위에서 운영 환경에 맞게 조정 |
| 최대 재시도 횟수 | 5회 전후, 이후 데드 레터 큐 이동 |
| 데드 레터 처리 | 별도 폴더 이동 및 관리자 알림(이메일·슬랙) |
💎 핵심 포인트:
재시도 큐는 실패를 늦추는 장치가 아니라, 일시적 장애를 흡수해 전체 파이프라인을 안정화하는 완충지대입니다.
백오프, 최대 시도 횟수, 데드 레터 기준을 명확히 정의해야 운영이 편해집니다.
⚠️ 주의: 큰 파일은 생성 직후 즉시 처리하면 실패 확률이 높습니다.
파일 크기 또는 잠금 상태를 주기적으로 점검해 안정 시점에만 큐에 넣는 전처리 단계를 권장합니다.
💡 실무 적용 예제 코드
앞에서 설명한 파일입출력 처리, watchdog 디렉터리 감시, 신규 파일만 처리, 에러 재시도 큐 개념을 하나로 통합한 실무형 코드 예제를 정리해 보겠습니다.
이 예제는 로그 파일이나 데이터 업로드 폴더를 실시간으로 감시하며, 신규 파일이 들어오면 자동으로 처리하고, 실패할 경우에는 재시도 큐를 통해 안정성을 확보합니다.
import os, time, queue, threading
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
processed = set()
retry_q = queue.PriorityQueue()
def process_file(path):
if os.path.getsize(path) == 0:
raise IOError("파일 크기 0, 실패")
print(f"[처리 성공] {path}")
def schedule_retry(path, attempt):
delay = min(2**attempt, 60)
retry_q.put((time.time() + delay, path, attempt))
print(f"[재시도 예약] {path}, {attempt}회차, {delay}초 후")
def retry_worker():
while True:
try:
ready_at, path, attempt = retry_q.get(timeout=1)
except queue.Empty:
continue
if ready_at > time.time():
time.sleep(ready_at - time.time())
try:
process_file(path)
except Exception as e:
if attempt >= 5:
print(f"[데드 레터 이동] {path}, 이유={e}")
else:
schedule_retry(path, attempt + 1)
retry_q.task_done()
class Handler(FileSystemEventHandler):
def on_created(self, event):
if event.is_directory: return
if event.src_path in processed: return
processed.add(event.src_path)
try:
process_file(event.src_path)
except Exception:
schedule_retry(event.src_path, 1)
if __name__ == "__main__":
threading.Thread(target=retry_worker, daemon=True).start()
folder = "./watch_folder"
os.makedirs(folder, exist_ok=True)
obs = Observer()
obs.schedule(Handler(), folder, recursive=False)
obs.start()
print("[감시 시작]")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
obs.stop()
obs.join()
이 코드는 watchdog이 감지한 신규 파일만 처리하며, 실패할 경우 재시도 큐에 자동으로 등록합니다.
최대 5회까지 재시도한 후에도 실패하면 데드 레터로 분류되어 운영자가 수동 확인할 수 있습니다.
실제 업무 환경에서는 DB 기록, 로그 시스템 연동, 관리자 알림(Slack·메일) 등을 추가하면 완전한 프로덕션급 시스템으로 발전시킬 수 있습니다.
💬 실무에서는 단순 감시 코드에 그치지 않고, 에러 복구 전략과 로그 추적성을 반드시 고려해야 합니다.
이제 단순한 파일 감시를 넘어서, 자동화된 파일 파이프라인을 안정적으로 운영할 수 있는 토대가 마련되었습니다.
❓ 자주 묻는 질문 (FAQ)
watchdog은 어떤 운영체제에서 사용할 수 있나요?
커널 이벤트를 활용하지만, 파이썬 API 사용 방식은 플랫폼에 관계없이 동일합니다.
대량 이벤트가 발생하는 환경에서는 테스트를 통해 적절한 설정값을 조정하는 것이 좋습니다.
신규 파일만 안전하게 처리하려면 무엇을 확인해야 하나요?
처리한 파일 경로를 집합(set)이나 작은 DB에 저장해 중복을 방지합니다.
파일 크기가 일정 시간 동안 변하지 않을 때를 완전 업로드 상태로 간주하면 안정성이 높아집니다.
특정 확장자만 감시하거나 처리하려면 어떻게 하나요?
정규표현식이나 fnmatch를 사용하면 여러 패턴을 간결하게 관리할 수 있습니다.
폴더 단위로 세분화하고 싶다면 감시 경로를 목적별로 분리하는 것이 좋습니다.
on_created와 on_modified 중 무엇을 써야 하나요?
업로드 중간에도 수정 이벤트가 여러 번 발생할 수 있어 on_modified만으로는 중복 처리 위험이 있습니다.
필요 시 on_created로 후보를 등록하고, 크기 안정화 검증 후 최종 처리하는 혼합 접근을 권장합니다.
재시도 큐는 어떻게 설계하는 게 좋나요?
최대 재시도 횟수를 넘기면 데드 레터 큐로 분리해 원인을 기록합니다.
동시에 처리 중인 파일을 추적하는 in-flight 집합으로 중복 등록을 막아야 합니다.
프로그램이 재시작되면 재시도 큐와 처리 이력은 어떻게 유지하나요?
간단한 경우 JSON 파일이나 SQLite를 활용하고, 규모가 커지면 외부 스토리지를 고려합니다.
재기동 시 스냅샷을 읽어 큐를 복원하면 중복과 누락을 줄일 수 있습니다.
하위 폴더까지 모두 감시하려면 어떻게 설정하나요?
폴더 트리가 깊을수록 이벤트 수가 증가하므로 필터링과 백프레셔 전략을 함께 고려해야 합니다.
필요에 따라 상위 폴더는 감시만 하고 실제 처리는 작업 큐에서 균형 있게 분배합니다.
대용량 파일이나 잠금 파일 때문에 처리 실패가 빈번합니다. 어떻게 줄일 수 있나요?
파일을 임시 확장자로 업로드 후 완료 시 최종 이름으로 변경하도록 업로더 규칙을 정하면 더 안전합니다.
처리 함수에서 파일 핸들을 즉시 닫고, 필요 시 공유 읽기 옵션을 검토합니다.
로그 회전이나 배치 업로드처럼 파일이 한꺼번에 들어오면 성능 저하는 없나요?
처리 속도를 추적해 백프레셔 지표를 만들고, 임계치를 넘으면 일시적으로 필터를 강화합니다.
병렬 처리 시에도 동일 파일 중복 처리를 막는 동기화 장치를 유지해야 합니다.
📝 파이썬 파일 감시와 재시도 큐 전략 정리
파이썬에서 파일 입출력 처리를 효율적으로 다루기 위해서는 단순히 open과 read, write 같은 함수만 아는 것에 그치지 않습니다.
운영 환경에서 실시간으로 생성되는 신규 파일을 안정적으로 처리하려면 watchdog 라이브러리를 통해 디렉터리를 감시하고, 불완전한 업로드나 처리 실패 상황에 대비한 재시도 큐 설계를 반드시 고려해야 합니다.
이 글에서는 신규 파일 감지, 중복 방지, 에러 처리 및 복원 전략까지 다루며, 실제 서비스에서 안전하게 활용할 수 있는 핵심 방법을 정리했습니다.
특히 대량의 로그 파일이나 데이터 업로드 환경에서는 감시 이벤트가 폭주할 수 있습니다.
이럴 때는 백프레셔와 큐 시스템을 활용해 시스템 부하를 완화해야 하며, 크기 안정화 검증을 통해 업로드 완료 여부를 확인하는 것이 중요합니다.
또한, 처리 이력과 큐 상태를 디스크나 데이터베이스에 주기적으로 기록해 재기동 후에도 안정성을 유지하는 방식이 권장됩니다.
실무 적용 시에는 환경별 차이를 고려해 테스트를 충분히 진행해야 하며, 여기서 소개한 원칙들을 기반으로 확장하면 더 안정적인 파일 처리 파이프라인을 구축할 수 있습니다.
🏷️ 관련 태그 : 파이썬파일입출력, watchdog, 디렉터리감시, 파일자동처리, 에러재시도큐, 파이썬예제, 파일처리자동화, 로그파일분석, 실시간파일감시, 파이썬프로젝트