Quick Flow
from pathlib import Path
base_dir = Path("data")
file_path = base_dir / "users" / "list.txt"
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text("mina\njin\n", encoding="utf-8")
content = file_path.read_text(encoding="utf-8")Path는 경로 조합·이름·부모 directory를 값처럼 다루고, 실제 read/write는 method 호출에서 일어납니다. 존재 확인 뒤 다시 여는 방식은 파일이 바뀌는 경쟁을 막지 못하므로 실제 작업의 OSError를 처리하고, 외부 파일명은 base와 candidate를 모두 resolve()한 뒤 허용 root 안에 있는지 확인합니다.
기본 흐름
기본형은 Path(...), 경로 조합, 존재 확인, 텍스트 읽기/쓰기, 디렉터리 생성, open()까지 같이 보면 됩니다.
from pathlib import Path
root = Path("data")
config = root / "config.json"
exists = config.exists()
text = config.read_text(encoding="utf-8")
config.write_text(text, encoding="utf-8")
root.mkdir(parents=True, exist_ok=True)
with config.open("r", encoding="utf-8") as file:
...Path 객체에 경로 정보와 파일 시스템 동작이 같이 모여 있기 때문에, 문자열 덧붙이기보다 경로 의도가 더 직접적으로 드러납니다.
경로 조합
경로를 이어 붙이면 Path(...) / "child"다
os.path.join()은 문자열을 계속 이어 붙여야 해서, 읽을 때 경로 조작이 잘 안 보일 수 있습니다.
Path는 경로 자체를 객체로 표현해 줍니다.
from pathlib import Path
root = Path("logs")
log_file = root / "2026" / "app.log"/ 연산자로 경로를 이어 붙이는 문법 덕분에 특히 깊은 경로를 만들 때 가독성이 좋습니다.
상태 확인
존재 확인과 타입 확인도 Path 메서드에서 바로 읽는다
파일 시스템 상태를 볼 때도 별도 문자열 유틸리티로 흩어지지 않고 Path 메서드로 읽을 수 있습니다.
path = Path("config.json")
if path.exists():
print("exists")
if path.is_file():
print("file")즉 "이 경로가 무엇인가"와 "이 경로로 무엇을 할까"가 한 객체 주위에 모입니다.
텍스트 입출력
작은 텍스트 파일은 read_text()와 write_text()가 가장 짧다
작은 텍스트 파일은 open()을 직접 쓰지 않아도 read_text()와 write_text()만으로 충분한 경우가 많습니다.
config_path = Path("config.json")
raw = config_path.read_text(encoding="utf-8")
output = Path("result.txt")
output.write_text("done\n", encoding="utf-8")파일 열기/닫기 패턴보다 훨씬 짧고, "텍스트냐 바이트냐"도 메서드 이름에서 바로 드러납니다.
디렉터리와 파일 객체
저장 전 parent.mkdir()와 더 복잡한 경우의 open()을 같이 본다
파일 저장 전 디렉터리가 없을 수 있다는 점까지 같이 보면 실전 코드가 됩니다.
report = Path("out/reports/daily.txt")
report.parent.mkdir(parents=True, exist_ok=True)
report.write_text("done\n", encoding="utf-8")이 패턴은 리포트 저장, 캐시 파일 저장, 로그 파일 초기 생성에서 자주 보입니다.
JSON, CSV처럼 스트림 단위로 읽고 써야 하거나, with 블록 안에서 파일 객체가 필요한 경우는 결국 path.open()으로 이어집니다.
작은 텍스트 파일은 read_text()와 write_text()로 처리하고, 반복 읽기나 JSON·CSV처럼 파일 객체가 필요한 입출력은 path.open()을 사용합니다.
from pathlib import Path
path = Path("data/users.txt")
with path.open("r", encoding="utf-8") as file:
for line in file:
print(line.strip())즉 "한 번에 전체 문자열을 읽는다"면 read_text(), "파일 객체를 잡고 반복하거나 json/csv 라이브러리에 넘긴다"면 path.open()이라고 보면 됩니다.
주의할 점
텍스트 파일을 다룰 때는 인코딩을 명시하는 편이 안전합니다. 운영체제 기본 인코딩에 기대면 환경마다 결과가 달라질 수 있습니다.
# ❌ 환경 기본 인코딩에 의존
text = Path("note.txt").read_text()
# ✅ 명시적으로 UTF-8 사용
text = Path("note.txt").read_text(encoding="utf-8")
# ❌ 문자열 더하기로 경로를 합치면 구분자와 가독성이 불안정
root = "data"
file_path = root + "/users.txt"
# ✅ 경로 조합은 Path 객체에서 처리
root = Path("data")
file_path = root / "users.txt"외부 입력에서 경로를 만들 때는 문자열의 ..만 검사하지 말고, 기준 경로와 후보 경로를 같은 방식으로 해석한 뒤 containment를 확인합니다.
root = Path("/srv/uploads").resolve()
candidate = (root / user_filename).resolve()
if not candidate.is_relative_to(root):
raise ValueError("path escapes upload root")참고 링크
2 sources