확인한 환경과 범위

Windows · Python 3.12.4. 본문의 첫 예제를 실행하여 표준 출력과 예상 결과가 일치함을 확인했습니다. 연습문제의 모든 입력을 검증한 것은 아닙니다.

이 글의 순서 4개 단락

파일이 Python 스크립트 옆에 있어도 Path("data.txt")가 항상 그 파일을 가리키지는 않습니다. 상대 경로는 현재 작업 폴더를 기준으로 해석되기 때문입니다. 작업 폴더에 같은 이름의 다른 파일이 있으면 오류 없이 엉뚱한 내용을 읽을 수도 있습니다.

이 실습은 Python 3.12를 사용합니다. Path.cwd()로 작업 폴더를 확인하고, 스크립트 옆의 파일을 읽어야 할 때는 Path(__file__).resolve().parent를 기준으로 삼습니다.

같은 스크립트를 세 가지 상황에서 실행하기

다음 예제는 전용 임시 폴더에 스크립트와 데이터 파일을 직접 만듭니다. runpy.run_path()로 같은 스크립트 파일을 실행하면서 작업 폴더를 바꾸고, 마지막에는 원래 작업 폴더로 돌아갑니다. 임시 폴더와 그 안의 샘플은 사용 후 정리됩니다. 따로 준비할 파일은 없습니다.

import os
import runpy
from pathlib import Path
from tempfile import TemporaryDirectory

script = '''\
from pathlib import Path

root = Path(__file__).resolve().parent.parent
relative = Path("data.txt")
script_file = Path(__file__).resolve().parent / "data.txt"

print("작업 폴더:", Path.cwd().relative_to(root).as_posix())
print("상대 경로 확인:", relative.resolve().relative_to(root).as_posix())
print("스크립트 기준 경로 확인:", script_file.relative_to(root).as_posix())

for label, path in [("상대 경로", relative), ("스크립트 기준", script_file)]:
    try:
        print(f"{label}: {path.read_text(encoding='utf-8').strip()}")
    except FileNotFoundError:
        print(f"{label}: FileNotFoundError")
'''

original = Path.cwd()
with TemporaryDirectory() as temporary:
    root = Path(temporary).resolve()
    lesson = root / "lesson"
    other = root / "other"
    lesson.mkdir()
    other.mkdir()
    script_path = lesson / "check_path.py"
    script_path.write_text(script, encoding="utf-8")
    (lesson / "data.txt").write_text("lesson file", encoding="utf-8")

    try:
        print("[lesson에서 실행]")
        os.chdir(lesson)
        runpy.run_path(str(script_path), run_name="__main__")

        print("[other에서 실행: 파일 없음]")
        os.chdir(other)
        runpy.run_path(str(script_path), run_name="__main__")

        print("[other에서 실행: 동명 파일 있음]")
        (other / "data.txt").write_text("other file", encoding="utf-8")
        runpy.run_path(str(script_path), run_name="__main__")
    finally:
        os.chdir(original)

Python 파일이 있는데 FileNotFoundError가 나는 이유: 작업 폴더 비교 실습 — 첫 예제의 실제 실행 결과

입력은 없으며 예상 출력은 다음과 같습니다. 임시 폴더의 절대 경로는 실행마다 달라지므로, 경로는 그 폴더를 기준으로 표시합니다.

[lesson에서 실행]
작업 폴더: lesson
상대 경로 확인: lesson/data.txt
스크립트 기준 경로 확인: lesson/data.txt
상대 경로: lesson file
스크립트 기준: lesson file
[other에서 실행: 파일 없음]
작업 폴더: other
상대 경로 확인: other/data.txt
스크립트 기준 경로 확인: lesson/data.txt
상대 경로: FileNotFoundError
스크립트 기준: lesson file
[other에서 실행: 동명 파일 있음]
작업 폴더: other
상대 경로 확인: other/data.txt
스크립트 기준 경로 확인: lesson/data.txt
상대 경로: other file
스크립트 기준: lesson file

왜 실행 위치에 따라 결과가 달라질까?

첫 실행에서는 작업 폴더와 스크립트가 있는 폴더가 모두 lesson이므로 두 경로가 같은 파일을 가리킵니다. 두 번째 실행에서는 작업 폴더가 other로 바뀌어 상대 경로가 other/data.txt를 찾습니다. 그 파일은 아직 없으므로 FileNotFoundError가 발생하지만, 스크립트 기준 경로는 여전히 lesson/data.txt를 읽습니다.

세 번째 실행에서는 other/data.txt를 만들었기 때문에 상대 경로도 성공합니다. 하지만 읽은 내용은 other file입니다. 예외가 없다는 사실만으로 의도한 파일을 읽었다고 판단할 수는 없습니다.

예제의 script 문자열이 세 번 실행되는 같은 스크립트의 내용입니다. Path.cwd()는 현재 작업 폴더를 반환하고, relative.resolve()는 상대 경로가 실제로 가리키는 절대 경로를 확인합니다. 표시할 때는 relative_to(root)와 as_posix()로 임시 폴더 아래의 경로만 일정한 형태로 보여 줍니다. 일반적인 문제 해결에서는 print(Path.cwd())와 print(Path("data.txt").resolve())로 전체 경로를 직접 확인해도 됩니다. Python 3.12 pathlib 문서

경로와 파일명을 점검하기

사용자가 실행한 폴더의 입력 파일을 읽는 프로그램이라면 작업 폴더 기준 상대 경로가 적절할 수 있습니다. 스크립트와 함께 놓인 데이터 파일을 읽어야 한다면 Path(__file__).resolve().parent / "data.txt"를 사용할 수 있습니다. resolve()는 경로를 절대 경로로 만들고 심볼릭 링크를 해석하며, parent는 부모 폴더를 가리킵니다. Python 3.12 pathlib 문서: Path.resolve

출력된 경로가 의도한 위치인지 확인한 다음 파일명과 확장자도 살펴보세요. data.txt.txt처럼 확장자가 두 번 붙었거나 이름에 오타가 있을 수 있습니다. 대소문자를 구별하는 파일 시스템에서는 Data.txt와 data.txt가 다릅니다. Windows 탐색기에서 확장자가 숨겨져 있다면 확장자 표시를 켜고 확인하세요. ~/data.txt의 ~는 자동으로 홈 폴더가 되지 않으므로 필요하면 Path("~/data.txt").expanduser()를 사용합니다.

__file__은 파일에서 실행하는 일반적인 스크립트나 파일 기반 모듈에서 유용합니다. 이 예제에서는 runpy.run_path()가 실행 대상 스크립트의 __file__을 설정합니다. 대화형 인터프리터나 노트북 셀에서는 정의되지 않을 수 있고, 일부 로더나 패키징 환경에서는 실제 데이터 위치와 맞지 않을 수 있습니다. 그런 환경에서는 Path.cwd()를 확인하고 데이터 위치를 명시적으로 지정하세요. Python 3.12 문서: module.file, runpy.run_path

이 예제는 파일이 없는 경우만 처리합니다. 권한 부족이나 UTF-8이 아닌 파일을 읽는 경우에는 다른 오류가 발생할 수 있습니다. 임시 폴더를 만들거나 파일을 읽고 쓸 권한도 필요합니다. os.chdir()는 프로세스 전체의 작업 폴더를 바꾸므로 여러 작업이 동시에 실행되는 프로그램에서는 주의해야 합니다. Python 3.12 tempfile 문서

작은 연습과 풀이

other/data.txt가 있는 세 번째 상황에서 lesson/data.txt의 이름을 Data.txt로 바꾼다면 두 읽기 결과는 어떻게 될까요?

상대 경로는 계속 other/data.txt를 읽으므로 other file을 출력합니다. 대소문자를 구별하는 파일 시스템에서는 스크립트 기준 경로가 lesson/data.txt를 찾지 못해 FileNotFoundError를 출력합니다. 대소문자를 구별하지 않는 파일 시스템에서는 바뀐 이름의 파일을 찾을 수 있으므로 결과가 다를 수 있습니다.

CHECK THE SOURCE

참고한 공식 문서

기능 설명은 위 문서를 참고했으며, 예제의 입력·결과와 한계는 본문에 따로 기록했습니다.

한 줄씩 이해하고, 차곡차곡 기록합니다.이 글의 오류 제보하기 ↗