[Python 시리즈 6편] 파일과 JSON으로 데이터 읽고 쓰기

English version

5편에서 함수와 모듈로 코드를 나눴다면, 이제는 그 코드가 다룰 외부 데이터를 안정적으로 주고받아야 합니다. 실제 업무 자동화를 하다 보면 파일을 읽어서 처리하고, 결과를 다른 파일로 저장해야 할 때가 많습니다. Python은 표준 라이브러리만으로도 텍스트 파일, CSV, JSON을 쉽게 다룰 수 있습니다. 이번 글에서는 pathlibjson 모듈을 활용해 파일 경로를 안전하게 관리하고 데이터를 읽고 쓰는 방법을 살펴봅니다.

이번 글에서 새로 나오는 용어

  1. pathlib.Path: 문자열 대신 객체로 경로를 다루게 해 주는 표준 라이브러리 도구
  2. 직렬화: 메모리에 있는 데이터를 파일이나 문자열 형태로 변환해 저장하거나 전송하는 과정
  3. JSON Lines: JSON 객체 하나를 한 줄에 기록하는 로그 형식으로 .jsonl 확장자로 많이 사용

핵심 개념

학습 메모

  • 소요 시간: 60분
  • 준비물: 함수·모듈 기반 프로젝트, 기본 자료형·조건문 감각
  • 학습 목표: pathlibjson을 이용해 파일에서 데이터를 읽고 쓰는 루틴 만들기

경로 관리는 Path 객체로, 직렬화는 JSON으로 맡기면 자동화 스크립트가 OS에 덜 의존하게 됩니다.

코드로 따라하기

pathlib로 경로 관리

문자열 대신 Path 객체를 사용하면 운영체제마다 다른 경로 구분자를 신경 쓰지 않아도 됩니다.

from pathlib import Path

data_dir = Path("data")

log_file = data_dir / "logs.txt"
print(log_file)

Path.mkdir(exist_ok=True)는 폴더가 없으면 만들고, 이미 있어도 에러를 내지 않습니다.

텍스트 파일 읽고 쓰기

log_file.write_text("배포 완료\n", encoding="utf-8")

content = log_file.read_text(encoding="utf-8")
print(content)

여러 줄을 조금 더 세밀하게 다루고 싶다면 open() 컨텍스트 매니저를 사용합니다.

with log_file.open("a", encoding="utf-8") as fp:
    fp.write("모니터링 시작\n")

with log_file.open("r", encoding="utf-8") as fp:
    for line in fp:
        print(line.strip())

JSON 읽고 쓰기

JSON은 API, 설정 파일, 로그 등 다양한 곳에서 쓰이는 구조입니다.


config = {
    "slack_webhook": "https://hooks.slack.com/...",
    "channels": ["deploy", "alert"],
}

config_file = data_dir / "config.json"

config_file.write_text(json.dumps(config, ensure_ascii=False, indent=2), encoding="utf-8")

loaded = json.loads(config_file.read_text(encoding="utf-8"))
print(loaded["channels"])

ensure_ascii=False를 설정하면 한글도 깨지지 않고 저장됩니다.

예외 처리 패턴

파일 작업에서는 경로가 없거나 권한 문제가 생길 수 있으므로 예외를 적절히 처리해야 합니다.

try:
    report_text = (data_dir / "report.txt").read_text(encoding="utf-8")
except FileNotFoundError:
    report_text = "보고서 파일이 아직 생성되지 않았습니다."

print(report_text)

이렇게 하면 파일이 없을 때도 프로그램이 중단되지 않고 안내 메시지를 출력할 수 있습니다.

미니 프로젝트: JSON 기반 TODO 저장소

from pathlib import Path

db_file = Path("data/todos.json")

def load_todos() -> list[dict]:
    if not db_file.exists():
        return []
    return json.loads(db_file.read_text(encoding="utf-8"))

def save_todos(todos: list[dict]) -> None:
    db_file.write_text(json.dumps(todos, ensure_ascii=False, indent=2), encoding="utf-8")

def add_todo(title: str):
    todos = load_todos()
    todos.append({"title": title, "done": False})
    save_todos(todos)

add_todo("보고서 초안 작성")
print(load_todos())

이 예제는 파일과 JSON을 함께 다루면서 데이터 저장 흐름을 단단하게 만들 수 있는 기본 틀을 보여 줍니다.

사용자 입력CLI·자동화todo_app.pyadd_todo/load_todospathlib.Path경로/폴더 관리data/todos.jsonJSON 파일다음 스크립트읽어 와 재사용 할 일 추가경로 생성write_text/json.dumpsread_text/json.loads

Path와 JSON 함수를 이렇게 연결해 생각하면, 어디서 경로를 만들고 어디서 직렬화를 담당하는지 명확히 보입니다.

실전 예시: JSON Lines로 배포 로그 작성

API 서버 배포 로그를 JSON Lines(.jsonl) 형식으로 남기면 OpenSearch나 ClickHouse로 바로 적재할 수 있습니다.

📄 JSON Lines란? JSON 개체 하나를 한 줄에 기록하는 방식입니다. 대용량 로그를 스트림으로 처리하거나, 로그 수집기가 한 줄씩 읽어야 할 때 가장 단순한 규격입니다.

from datetime import datetime
from pathlib import Path

log_file = Path("data/deploy.jsonl")

def write_entry(level: str, message: str, **context):
    entry = {
        "timestamp": datetime.utcnow().isoformat(),
        "level": level,
        "message": message,
        **context,
    }
    with log_file.open("a", encoding="utf-8") as fp:
        fp.write(json.dumps(entry, ensure_ascii=False) + "\n")

write_entry("INFO", "배포 완료", service="attendance", version="1.4.2")

이 패턴은 학교 업무 자동화부터 데이터 파이프라인 구축까지 폭넓게 응용할 수 있고, 다음 편에서 다룰 Requests 자동화 예제에서도 바로 API 응답을 기록하는 데 사용합니다.

왜 중요한가

학교 리포트, 동아리 출석부, 간단한 자동화 스크립트 모두 파일을 읽고 쓰는 흐름 위에 올라갑니다. 경로를 안전하게 다루면 실수로 다른 폴더를 지우는 일을 줄일 수 있고, JSON으로 데이터를 저장하면 다른 언어나 도구에서도 쉽게 불러올 수 있습니다.

실습

  • 따라 하기: data 폴더를 만들고 TODO JSON 저장소 예제를 그대로 따라 하며 파일이 생성되는지 확인합니다.
  • 확장하기: JSON Lines 로거를 활용해 level별로 다른 파일에 기록하거나, ensure_ascii 값을 바꿔 한글이 깨지는 상황을 실험합니다.
  • 디버깅: 일부러 없는 경로를 읽어 FileNotFoundError를 만든 뒤 Path.mkdir(exist_ok=True) 또는 예외 처리로 복구합니다.
  • 완료 기준: 텍스트·JSON을 모두 읽고 쓰며 경로를 안전하게 관리하는 함수를 작성해 반복 사용 가능할 때입니다.

마무리

파일과 JSON을 자유롭게 읽고 쓸 수 있으면 자동화 스크립트의 활용 범위가 크게 넓어집니다. 다음 글에서는 requests로 외부 API를 호출하고, 파일 입출력과 결합해 간단한 자동화를 만드는 방법을 다뤄보겠습니다.

💬 댓글

이 글에 대한 의견을 남겨주세요