짧은 답
파일이 있는지 확인하려면 표준 라이브러리의 os.path.isfile()을 쓰세요. 경로가 실제로 존재하는 일반 파일을 가리키면 True, 아니면 False를 돌려줘요. 경로가 없다고 에러를 내는 일은 없습니다.
경로는 프로그램이 실행되는 폴더(현재 작업 디렉터리)를 기준으로 해석돼요. /home/ada/notes.txt나 C:\Users\Ada\notes.txt 같은 절대 경로를 넘기면 예외입니다.
이 페이지의 나머지 부분에서는 같은 질문을 던지는 다른 방법들, 그 방법들의 차이, 그리고 아예 묻지 말아야 하는 경우를 다뤄요.
os.path.exists vs isfile vs isdir
os.path 모듈에는 서로 관련된 확인 함수가 세 개 있고, 각각 조금씩 다른 질문에 답해요:
| 함수 | 경로가 이럴 때 True를 돌려줌 |
|---|---|
os.path.exists(p) | 존재하는 무엇이든: 파일, 디렉터리, 정상 동작하는 심볼릭 링크 |
os.path.isfile(p) | 존재하는 일반 파일 |
os.path.isdir(p) | 존재하는 디렉터리 |
디렉터리가 끼는 순간 차이가 드러납니다:
출력:
reports True False True
reports/summary.txt True True False
reports/old.txt False False False
reports는 폴더인데도 os.path.exists("reports")는 True예요. 코드가 그 경로를 파일로 읽으려는 참이라면 exists는 잘못된 확인입니다. 디렉터리도 통과해 버리고, 그다음 open("reports")가 실패하거든요(Linux와 macOS에서는 IsADirectoryError, Windows에서는 PermissionError). 파일을 뜻할 때는 isfile, 폴더를 뜻할 때는 isdir을 고르세요.
이 세 os.path 함수에 대해 알아둘 점 두 가지:
- 심볼릭 링크는 따라가요. 실제 파일을 가리키는 링크는 파일로 치고, 깨진 링크(대상이 사라진 링크)는 세 함수 모두에서
False가 나옵니다. - 파이썬이 어떤 폴더 안을 들여다볼 권한이 없으면, 확인 함수는
PermissionError를 내지 않고False를 돌려줘요. 그러니False는 엄밀히 "존재하지 않음"이 아니라 "찾을 수 없거나 접근할 수 없음"이라는 뜻이에요.
pathlib으로 확인하기
pathlib은 표준 라이브러리에 있는 객체 지향 경로 API이고, 요즘 새로 작성하는 코드는 대부분 이걸 씁니다. Path 객체에는 같은 세 가지 확인이 메서드로 들어 있어요:
Path.exists(), Path.is_file(), Path.is_dir()는 os.path.exists, isfile, isdir과 똑같이 동작해요. / 연산자는 운영체제에 맞는 구분자로 경로 조각을 이어 붙이기 때문에, folder / "summary.txt"는 Windows, macOS, Linux 어디서나 동작합니다. Path 객체로 읽고 쓰는 방법은 파일 처리에서 볼 수 있어요.
어느 쪽을 써야 할까요? 코드의 나머지 부분에서 이미 Path 객체를 주고받고 있다면 pathlib을 계속 쓰세요. 평범한 문자열과 os 함수로 작업하고 있다면 os.path.isfile도 똑같이 올바릅니다. 두 방법의 답은 같아요.
먼저 확인하는 게 잘못된 선택일 수 있는 이유
파일이 있는지 확인하는 가장 흔한 이유는 그다음에 그 파일을 열기 위해서예요:
import os
if os.path.isfile("config.txt"):
with open("config.txt") as f: # 이 시점엔 파일이 사라졌을 수도 있다
settings = f.read()
else:
settings = ""
여기엔 틈이 있어요. isfile() 호출과 open() 호출 사이에 다른 프로그램이나 다른 스레드, 혹은 사용자가 파일을 지우거나 이름을 바꿀 수 있습니다. 확인 결과는 True였는데 열기는 여전히 실패하고, 프로그램은 결국 FileNotFoundError로 죽어요. 이런 틈을 경쟁 상태(race condition), 또는 "확인 시점과 사용 시점의 차이(time of check to time of use)"라고 부릅니다. 내 노트북에서는 드물지만, 여러 프로세스가 한 폴더를 같이 쓰는 바쁜 서버에서는 훨씬 자주 일어나요.
해결책은 따로 확인하는 단계를 없애고 open() 자체가 확인이 되게 하는 거예요. 파일이 없으면 파이썬이 FileNotFoundError를 발생시키고, 그걸 except에서 처리하면 됩니다:
출력:
theme=dark
(no settings file, using defaults)
이제 작업이 두 개가 아니라 하나라서 경쟁할 틈 자체가 없어요. 파이썬 커뮤니티에서는 이 스타일을 EAFP, 즉 "허락보다 용서를 구하는 게 쉽다(easier to ask forgiveness than permission)"라고 불러요. 반대로 먼저 확인하는 스타일은 LBYL, "뛰기 전에 살펴라(look before you leap)"입니다. 파일을 여는 순간의 정답을 아는 건 운영체제뿐이라서, 파일 작업에서 파이썬은 EAFP 쪽으로 기울어요.
잘못될 수 있는 게 FileNotFoundError만은 아니에요. 디렉터리인 경로를 열면 IsADirectoryError(Windows에서는 PermissionError)가, 읽을 권한이 없는 파일을 열면 PermissionError가 발생합니다. 셋 다 OSError의 하위 클래스라서 except OSError: 하나로 "열 수 없음" 경우를 한꺼번에 잡을 수 있어요. 특정 에러 하나만 잡을지 에러 계열 전체를 잡을지 고르는 방법은 예외 페이지에서 다룹니다.
그냥 확인만 해도 되는 경우
먼저 확인하는 게 늘 틀린 건 아니에요. 답만 필요하고 바로 다음 줄에서 그 파일로 뭔가를 할 게 아니라면 올바른 도구입니다:
- 어떤 메시지를 보여줄지 고를 때("저장된 게임 3개를 찾았어요")
- 긴 작업을 시작하기 전에 사용자가 입력한 경로를 검증할 때
- 이미 끝난 작업을 건너뛸 때(답이 낡았더라도 작업을 한 번 더 하는 비용뿐일 때)
경험칙은 이래요. 파일을 열거나 읽거나 쓰거나 지울 거라면 try 안에서 하세요. 그냥 알고 싶기만 하다면 isfile()이나 Path.is_file()로 충분해요.
파일이 없을 때만 새로 만들기
반대 질문도 나와요. "이 파일을 쓰되, 이미 있는 파일은 절대 덮어쓰지 마라." 뻔한 방법에는 앞에서와 같은 경쟁 상태가 있습니다:
if not os.path.exists("report.txt"):
with open("report.txt", "w") as f: # 다른 프로세스가 먼저 만들 수도 있다
f.write("new report\n")
모드 "x"(배타적 생성)는 두 단계를 한 번에 처리해요. 파일을 만들거나, 그 이름이 이미 쓰이고 있으면 FileExistsError를 발생시킵니다:
출력:
attempt 1: created report.txt
attempt 2: report.txt already exists, left it alone
폴더라면 os.makedirs(path, exist_ok=True)와 Path(path).mkdir(parents=True, exist_ok=True)가 디렉터리와 없는 상위 디렉터리까지 만들어 주고, 이미 있으면 아무것도 하지 않아요. isdir()을 먼저 부를 필요가 없습니다.
흔한 실수
- 파일을 뜻하면서
exists()를 쓰기. 같은 이름의 디렉터리도 확인을 통과해요.isfile()이나Path.is_file()을 쓰세요. - 상대 경로의 기준을 잊기.
"data.txt"는 현재 작업 디렉터리에서 찾아요. 그건 프로그램을 실행한 위치이지, 스크립트가 들어 있는 폴더라는 보장은 없어요. 스크립트 옆에 있는 파일을 찾으려면Path(__file__).parent에서 경로를 만드세요. - 확인한 다음에 열기. 그 사이에 파일이 바뀔 수 있어요.
try안에서 열고FileNotFoundError를 처리하세요. - 모든 예외를 잡기.
open()을except Exception:으로 감싸면 파일을 읽는 코드의 오타와 버그까지 숨겨져요.FileNotFoundError나OSError를 잡으세요.
자주 묻는 질문
파이썬에서 파일이 있는지 어떻게 확인하나요?
os를 import하고 os.path.isfile("data.txt")를 호출하세요. 실제로 존재하는 일반 파일일 때만 True를 돌려줘요. pathlib에서는 Path("data.txt").is_file()입니다. 같은 이름의 디렉터리도 있는 것으로 치고 싶다면 os.path.exists()나 Path.exists()를 쓰세요.
os.path.exists와 os.path.isfile은 뭐가 다른가요?
os.path.exists(p)는 그 경로에 뭔가 있기만 하면 True예요. 파일, 디렉터리, 실제 대상을 가리키는 심볼릭 링크 모두 해당합니다. os.path.isfile(p)는 일반 파일일 때만 True라서 디렉터리에는 False를 돌려줘요. 디렉터리용 짝은 os.path.isdir(p)입니다.
파일을 열기 전에 존재 여부를 먼저 확인해야 하나요?
보통은 아니에요. 확인과 open() 호출 사이에 파일이 지워지거나 새로 생길 수 있어서, 확인해 봤자 아무것도 보장되지 않아요. try 안에서 파일을 바로 열고 except에서 FileNotFoundError를 처리하세요. 따로 존재 여부만 확인하는 게 맞는 경우는 답만 필요할 때예요. 예를 들어 어떤 메시지를 보여줄지 정할 때가 그렇습니다.
파일이 없을 때만 새로 만들려면 어떻게 하나요?
모드 "x"로 여세요: open("report.txt", "x"). 파이썬이 파일을 만들고, 이미 뭔가 있으면 FileExistsError를 발생시켜요. 확인과 생성이 한 단계에서 일어나니 다른 프로그램이 그 사이에 끼어들 틈이 없습니다.