0Pricing
AI Agents · 강의

오류 처리를 적용한 안전한 파일 작업

파일 존재 여부와 권한을 확인하고 IO 오류를 우아하게 처리합니다.

오류 처리를 적용한 안전한 파일 작업은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

안전한 파일 작업이 중요한 이유

에이전트의 파일 작업은 여러 방식으로 실패할 수 있습니다. 파일이 존재하지 않거나, 에이전트에 권한이 없거나, 경로가 디렉터리이거나, 쓰는 도중 디스크 공간이 부족해질 수 있습니다. 파일 오류가 발생했을 때 충돌하는 에이전트는 불완전한 출력과 손상된 상태를 남깁니다. 적절한 검사와 오류 처리를 포함한 방어적 프로그래밍을 적용하면 에이전트의 복원력을 높일 수 있습니다.

from pathlib import Path

# Unsafe: crashes with FileNotFoundError
# content = Path('missing.txt').read_text()

# Safe: check first
path = Path('config.json')
if path.exists():
    content = path.read_text(encoding='utf-8')
    print('Loaded config')
else:
    print(f'Config not found at {path.resolve()}')
    content = '{}'  # use default

Path.exists() 및 Path.is_file()

파일을 읽기 전에 파일이 존재하고 실제로 일반 파일인지 확인해야 합니다(디렉터리, 디렉터리를 가리키는 심볼릭 링크 또는 특수 파일이 아닌지 확인합니다). Path.exists()는 모든 파일 시스템 객체에 대해 True를 반환하고, Path.is_file()은 일반 파일에 대해서만 True를 반환합니다.

from pathlib import Path

path = Path('data/report.csv')

# Chain of checks
if not path.exists():
    print(f'Not found: {path}')
elif not path.is_file():
    print(f'Not a regular file: {path} (is_dir={path.is_dir()})')
elif path.stat().st_size == 0:
    print(f'File is empty: {path}')
else:
    # Safe to read
    import csv
    with open(path, 'r', encoding='utf-8', newline='') as f:
        reader = csv.DictReader(f)
        rows = list(reader)
    print(f'Read {len(rows)} rows')

os.access() — 권한 확인

os.access(path, mode)는 현재 프로세스에 파일에 대한 지정된 권한이 있는지 확인합니다. 읽기에는 os.R_OK, 쓰기에는 os.W_OK, 실행에는 os.X_OK을 사용합니다. 특정 권한이 필요한 작업을 시도하기 전에 유용하게 사용할 수 있습니다.

import os
from pathlib import Path

def check_file_access(path):
    p = Path(path)

    checks = {
        'exists': p.exists(),
        'is_file': p.is_file(),
        'readable': os.access(p, os.R_OK),
        'writable': os.access(p, os.W_OK),
    }

    for check, result in checks.items():
        status = 'OK' if result else 'FAIL'
        print(f'  {check}: {status}')

    return all(checks.values())

if check_file_access('data/input.json'):
    print('File is accessible')
else:
    print('Access problem — check path and permissions')

FileNotFoundError 처리

존재하지 않는 파일을 열려고 하면 FileNotFoundError(OSError의 하위 클래스)가 발생합니다. 이를 명시적으로 처리하고 예상 경로를 포함한 유용한 오류 메시지를 제공하면 사용자나 운영자가 정확히 무엇이 누락되었는지 알 수 있습니다.

import json
from pathlib import Path

def load_agent_config(config_path='agent_config.json'):
    path = Path(config_path)
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except FileNotFoundError:
        print(f'Config file not found: {path.resolve()}')
        print('Create agent_config.json with your settings')
        print('Example: {"model": "gpt-4o", "max_retries": 3}')
        return {}  # return empty config as default
    except json.JSONDecodeError as e:
        print(f'Invalid JSON in {path}: {e}')
        return {}

# --- demo ---
config = load_agent_config('does_not_exist_config.json')
print(f'Config used: {config}')

PermissionError 처리

프로세스에 파일을 읽거나 쓸 권한이 없으면 PermissionError가 발생합니다. 시스템 파일, 다른 사용자가 소유한 파일 또는 제한적인 권한이 설정된 파일에서 발생할 수 있습니다. 서로 다른 대응이 필요하므로 항상 FileNotFoundError와 별도로 처리해야 합니다.

from pathlib import Path

def read_file_safely(path):
    try:
        return Path(path).read_text(encoding='utf-8')
    except FileNotFoundError:
        print(f'File not found: {path}')
        return None
    except PermissionError:
        import os
        print(f'Permission denied: {path}')
        print(f'File permissions: {oct(Path(path).stat().st_mode)}')
        print(f'Current user: {os.getlogin()}')
        print('Try: chmod +r ' + str(path))
        return None
    except IsADirectoryError:
        print(f'Path is a directory, not a file: {path}')
        return None

# --- demo ---
import os
print(read_file_safely('does_not_exist.txt'))

os.makedirs('a_directory', exist_ok=True)
print(read_file_safely('a_directory'))

IsADirectoryError 처리

디렉터리를 파일인 것처럼 열려고 하면 IsADirectoryError가 발생합니다. 에이전트가 경로를 잘못 구성했을 때 발생할 수 있습니다. 예를 들어 파일 이름을 추가했는데 해당 이름이 이미 디렉터리로 존재하는 경우입니다. 의미 있는 오류 출력을 제공할 수 있도록 항상 처리해야 합니다.

from pathlib import Path

def safe_write(output_path, content):
    path = Path(output_path)

    # Check the path is not an existing directory
    if path.is_dir():
        raise IsADirectoryError(
            f'Cannot write file: {path} is a directory. '
            f'Use a filename like {path}/output.txt instead.'
        )

    # Ensure parent directory exists
    path.parent.mkdir(parents=True, exist_ok=True)

    try:
        path.write_text(content, encoding='utf-8')
        print(f'Written: {path} ({len(content)} chars)')
    except IsADirectoryError as e:
        print(f'Path error: {e}')
    except PermissionError:
        print(f'Cannot write to {path} — permission denied')

# --- demo ---
import os
safe_write('demo_output/report.txt', 'Agent finished the task.')

os.makedirs('already_a_dir', exist_ok=True)
try:
    safe_write('already_a_dir', 'this will fail')
except IsADirectoryError as e:
    print(f'Rejected: {e}')

tempfile을 사용한 원자적 쓰기

파일에 직접 쓰는 방식은 위험합니다. 에이전트가 쓰기 도중 비정상 종료되면 파일이 일부만 기록된 채 손상된 상태로 남기 때문입니다. 해결 방법은 원자적 쓰기입니다. 먼저 임시 파일에 쓴 다음 최종 경로로 이름을 변경합니다. POSIX 시스템에서 이름 변경은 원자적으로 수행되므로 최종 파일은 이전 버전이나 새 버전 중 하나이며, 일부만 기록된 상태가 되지 않습니다.

import tempfile
import os
import json
from pathlib import Path

def atomic_write_json(file_path, data):
    path = Path(file_path)
    path.parent.mkdir(parents=True, exist_ok=True)

    # Write to temp file in same directory
    tmp_fd, tmp_path = tempfile.mkstemp(
        dir=path.parent,
        prefix='.tmp_',
        suffix='.json'
    )
    try:
        with os.fdopen(tmp_fd, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        # Atomic rename: replaces final file in one operation
        os.replace(tmp_path, path)
        print(f'Atomically wrote: {path}')
    except Exception as e:
        os.unlink(tmp_path)  # clean up temp file on error
        raise

# --- demo ---
atomic_write_json('demo_state/state.json', {'step': 3, 'status': 'running'})
print('File contents:', Path('demo_state/state.json').read_text(encoding='utf-8'))

동시 에이전트를 위한 파일 잠금

여러 에이전트 인스턴스가 동시에 실행되면서 같은 파일에 쓰면 경쟁 조건으로 데이터가 손상됩니다. 동시 쓰기를 방지하려면 fcntl 모듈(Linux/macOS) 또는 플랫폼에 관계없이 사용할 수 있는 filelock 라이브러리로 파일 잠금을 사용하십시오.

from filelock import FileLock, Timeout
import json
from pathlib import Path

COUNTER_FILE = Path('shared_counter.json')
LOCK_FILE = Path('shared_counter.json.lock')

def increment_counter():
    lock = FileLock(str(LOCK_FILE), timeout=10)
    try:
        with lock:
            # Only one process can be here at a time
            if COUNTER_FILE.exists():
                data = json.loads(COUNTER_FILE.read_text())
            else:
                data = {'count': 0}

            data['count'] += 1
            COUNTER_FILE.write_text(
                json.dumps(data, indent=2)
            )
            return data['count']
    except Timeout:
        print('Could not acquire lock within 10 seconds')
        return None

존재 여부를 확인하는 안전한 삭제

존재하지 않는 파일을 삭제하면 FileNotFoundError가 발생합니다. Path.unlink()로 디렉터리를 삭제하면 IsADirectoryError가 발생합니다. 안전하게 삭제하려면 Path.unlink(missing_ok=True)(Python 3.8 이상)를 사용하거나 먼저 존재 여부를 확인하십시오.

from pathlib import Path
import shutil

# Safe file deletion (Python 3.8+)
Path('temp_output.json').unlink(missing_ok=True)

# Safe directory deletion
def safe_remove(path):
    p = Path(path)
    if not p.exists():
        print(f'Already gone: {p}')
        return
    if p.is_file():
        p.unlink()
        print(f'Deleted file: {p}')
    elif p.is_dir():
        shutil.rmtree(p)
        print(f'Deleted directory: {p}')
    else:
        print(f'Unknown file type: {p}')

# Clean up temporary workspace
safe_remove('/tmp/agent_workspace/run_001')

쓰기 전 디스크 공간 확인

대용량 파일(AI 출력, 데이터 세트, 로그)을 쓰는 에이전트는 먼저 사용 가능한 디스크 공간을 확인해야 합니다. shutil.disk_usage(path)는 전체, 사용 중, 여유 바이트 수를 반환합니다. 긴 쓰기 작업을 시작하기 전에 여유 공간이 예상 출력 크기를 초과하는지 확인하십시오.

import shutil
from pathlib import Path

def check_disk_space(output_dir, required_bytes):
    path = Path(output_dir)
    path.mkdir(parents=True, exist_ok=True)

    usage = shutil.disk_usage(path)
    free_gb = usage.free / (1024 ** 3)
    required_gb = required_bytes / (1024 ** 3)

    print(f'Disk free: {free_gb:.2f} GB')
    print(f'Required:  {required_gb:.2f} GB')

    if usage.free < required_bytes * 1.1:  # 10% safety margin
        raise IOError(
            f'Insufficient disk space: '
            f'{free_gb:.2f} GB free, '
            f'{required_gb:.2f} GB required'
        )
    return True

# Before writing a 500 MB dataset
check_disk_space('/tmp/output', 500 * 1024 * 1024)

덮어쓰기 전 백업

에이전트가 기존 파일을 업데이트할 때는 이전 버전의 백업을 보관하는 것이 좋습니다. 새 콘텐츠가 잘못된 경우 롤백할 수 있기 때문입니다. 타임스탬프가 포함된 백업 파일 이름을 사용하고, 디스크 공간이 가득 차지 않도록 백업 수를 제한하십시오.

import shutil
import datetime
from pathlib import Path

def write_with_backup(file_path, content, max_backups=5):
    path = Path(file_path)
    backup_dir = path.parent / '.backups'
    backup_dir.mkdir(exist_ok=True)

    # Backup existing file
    if path.exists():
        timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        backup = backup_dir / f'{path.name}.{timestamp}'
        shutil.copy2(path, backup)
        print(f'Backed up to: {backup}')

    # Write new content
    path.write_text(content, encoding='utf-8')

    # Prune old backups
    backups = sorted(backup_dir.glob(f'{path.name}.*'))
    for old in backups[:-max_backups]:
        old.unlink()
        print(f'Pruned old backup: {old.name}')

# --- demo ---
write_with_backup('demo_notes.txt', 'version 1')
write_with_backup('demo_notes.txt', 'version 2')
print('Backups:', [p.name for p in sorted(Path('.backups').glob('demo_notes.txt.*'))])

빠른 확인: 원자적 쓰기

안전한 파일 쓰기 패턴에 대한 이해도를 확인해 보십시오.

안전한 파일 작업 요약

이제 에이전트가 파일을 방어적으로 처리할 수 있습니다:

  • 접근하기 전에 확인: Path.exists() + Path.is_file() + os.access(path, os.R_OK)
  • FileNotFoundError, PermissionError, IsADirectoryError를 명확한 메시지와 함께 처리
  • 충돌 시 일부만 기록되거나 손상된 파일이 생기지 않도록 원자적 쓰기(임시 파일 + os.replace) 사용
  • 여러 에이전트가 같은 파일에 쓸 때 파일 잠금(filelock 라이브러리) 사용
  • FileNotFoundError 없이 안전하게 삭제하려면 missing_ok=True 사용
  • 대규모 쓰기 전에 디스크 공간을 확인하고, 중요한 파일을 덮어쓰기 전에 백업 보관

자주 묻는 질문

“오류 처리를 적용한 안전한 파일 작업” 강의는 무료인가요?

네 — “오류 처리를 적용한 안전한 파일 작업” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“오류 처리를 적용한 안전한 파일 작업”에서 뭘 배우나요?

파일 존재 여부와 권한을 확인하고 IO 오류를 우아하게 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“오류 처리를 적용한 안전한 파일 작업” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 에이전트 환경에서 파일 읽기 및 쓰기
  2. 디렉터리 탐색 및 파일 찾기
  3. 파일 형식 처리: CSV, JSON, TXT
  4. 오류 처리를 적용한 안전한 파일 작업
← AI Agents(으)로 돌아가기