Devin.KR

AI 개발 · 기본

바이브 코딩 따라하기 - 요청문 그대로 따라 만드는 가계부 앱

저장하고 다시 불러오기 - JSON 파일

json 저장·불러오기, 파일이 없을 때 처리, 한글 깨짐(ensure_ascii·encoding), 임시 폴더에서 실행, 저장 후 다시 읽어 같은지 확인

개발자KR · 원고 갱신

이 장에서 배우는 것

앞 장에서 지출 한 건을 기록하고 화면에 보여 주었다. 그러나 프로그램을 끝내면 실행 중에 담아 둔 기록도 사라진다. 이번에는 기록을 파일에 적고, 그 파일을 다시 읽어 같은 기록이 돌아오는지 확인한다. 화면에 “저장 완료”가 나오는 것만으로 끝내지 않고, 저장한 내용과 읽은 내용을 직접 비교한다.

이번 실습은 이전 파일 없이도 따라 할 수 있다. Python 3.12 이상이 설치된 macOS 또는 Linux에서 main.py 하나로 실행한다. 외부 패키지를 설치하거나 인터넷에 연결할 필요가 없다.

  • 지출 기록을 JSON 파일에 저장하고 다시 불러온다.
  • 파일이 아직 없으면 빈 기록 목록으로 시작한다.
  • 한글을 읽기 쉽게 저장하도록 ensure_ascii와 encoding을 설정한다.
  • 임시 폴더에서 파일을 만들고 실습이 끝나면 정리한다.
  • 저장 전 기록과 다시 읽은 기록이 같은지 검사한다.

이번 장에서 만들 것

프로그램을 종료한 뒤에도 지출을 남기려면 파일 저장이 필요하다. 예를 들어 화면에서 “점심 김밥 5,500원”을 확인했더라도, 그 내용이 실행 중인 프로그램 안에만 있다면 다음 실행에서 꺼낼 수 없다. 파일에 쓰는 과정과 파일에서 읽는 과정을 모두 만들어야 한다.

이번 장에서는 실제 생활 기록을 보관하는 폴더 대신, 실습할 때만 사용하는 임시 폴더를 만든다. 그 안에 expenses.json을 저장하고 다시 읽는다. 프로그램이 끝나면 임시 폴더도 지워진다. 따라서 이번 완성 코드는 저장과 복원을 한 번의 실행 안에서 확인하는 시연이다. 다음 실행까지 기록을 보관하는 용도로 쓰려면 이후에 보관할 폴더를 정해야 한다.

입력은 키보드로 받지 않는다. 날짜가 2026-10-15이고 금액이 5500이며 메모가 “점심 김밥”인 기록 한 건을 코드에 넣는다. 현재 시각이나 임시 폴더 이름을 출력하지 않으므로 실행할 때마다 결과가 같다.

이번 프로그램이 수행할 일과 확인 기준
순서수행할 일확인 기준
1없는 파일을 읽는다빈 목록이 나온다
2정해진 지출을 저장한다파일이 만들어진다
3저장된 글자를 확인한다점심 김밥이 그대로 보인다
4파일을 다시 읽는다처음 기록과 같은 목록이 나온다

따라하기

아래 요청문을 같은 AI 대화에 순서대로 보낸다. 요청문은 그대로 복사해도 된다. AI의 답과 코드 모양은 매번 다를 수 있다. 각 단계의 답 요지는 예상 방향이며, 실제 확인 기준은 실행 결과와 파일 내용이다. 마지막에는 이 장의 완성 코드와 비교한다.

터미널은 명령을 글자로 입력해 프로그램을 실행하는 창이다. 명령은 main.py를 둔 폴더에서 실행한다. main.py가 이미 있다면 다른 폴더에 복사본을 남긴 뒤 시작한다. AI가 파일을 직접 바꾸는 도구를 사용한다면, 적용하기 전에 변경 전후 비교인 diff를 열어 추가하거나 지운 부분을 읽는다.

단계 1. 저장할 기록과 실행 조건을 고정한다

① 보낼 요청문

Python 3.12 이상 표준 라이브러리만 쓰는 main.py를 만들어 줘. 이전 파일 없이 혼자 실행되어야 해. 날짜는 "2026-10-15", 금액은 정수 5500, 메모는 "점심 김밥"인 지출 한 건을 사전으로 만들고 목록에 담아 줘. 키 이름은 date, amount, memo를 써 줘. 사용자 입력이나 현재 시각을 쓰지 말고, python3 main.py로 실행하면 기록을 출력하고 바로 끝나게 해 줘. 아직 파일 저장은 넣지 마.

② AI가 돌려줄 내용의 요지

사전 하나를 목록에 넣고, 목록에서 지출을 꺼내 날짜·금액·메모를 출력하는 코드를 제안할 수 있다. 변수 이름과 출력 방법은 답마다 달라질 수 있다.

사전은 이름표와 값을 짝지어 담는 자료다. 여기서는 amount라는 이름표에 숫자 5500을 넣는다. 목록은 여러 자료를 순서대로 담는 그릇이다. 지금은 지출이 한 건이지만 파일에는 목록 전체를 저장한다.

③ 직접 확인할 것

실행 명령: python3 main.py

날짜, 5500이라는 금액, 점심 김밥이라는 메모가 보이는지 확인한다. 실행 중 입력을 기다리거나 날짜가 오늘 날짜로 바뀌면 요청 조건과 다르다. 코드에서도 금액이 따옴표 없는 정수인지 살핀다. 출력만 보고 숫자와 문자열을 구별하기는 어렵다.

④ 체크포인트

같은 명령을 두 번 실행했을 때 같은 기록이 보이고 프로그램이 바로 끝나면 다음 단계로 간다.

단계 2. 파일 작업을 임시 폴더 안에 둔다

① 보낼 요청문

지금 코드를 바탕으로 tempfile.TemporaryDirectory를 써서 임시 폴더를 만들어 줘. with 구문 안에서 pathlib.Path로 expenses.json의 경로를 만들어 줘. 파일 작업은 모두 그 안에서 하도록 준비하고, 실제 임시 경로는 출력하지 마. 아직 파일을 쓰지 말고 "저장 전 파일 존재: False"를 출력해 줘. 기존 기록 출력은 유지해 줘.

② AI가 돌려줄 내용의 요지

임시 폴더를 만들고, 그 폴더 경로와 expenses.json이라는 이름을 합친다. Path의 exists()로 파일이 있는지 확인하는 코드를 제안할 수 있다.

경로는 파일이 놓일 위치를 나타낸다. Path는 폴더와 파일의 경로를 다루는 표준 라이브러리 도구다. 임시 폴더를 만들었다고 그 안에 expenses.json까지 생기는 것은 아니다. 파일 경로를 준비한 상태와 파일을 쓴 상태는 다르다.

with 구문은 여기서 임시 폴더를 사용하는 범위를 정한다. 이 범위를 빠져나갈 때 폴더와 그 안의 파일을 정리한다. 다음 단계에서 추가할 저장과 불러오기도 모두 이 범위 안에서 호출한다.

③ 직접 확인할 것

실행 명령: python3 main.py

“저장 전 파일 존재: False”가 출력되어야 한다. False는 조건이 참이 아니라는 뜻이다. 여기서는 해당 파일이 없다는 뜻이다. 임시 폴더의 실제 이름은 실행마다 달라질 수 있으므로 결과에 나오지 않아야 한다.

④ 체크포인트

파일 경로가 임시 폴더 안을 가리키고, 파일이 아직 없다는 결과가 나오면 다음 단계로 간다.

단계 3. 파일이 없으면 빈 목록을 돌려준다

① 보낼 요청문

load_expenses(path) 함수를 추가해 줘. 함수는 파일에서 지출 목록을 읽는 역할을 맡아. 먼저 path.exists()로 확인하고, 파일이 없으면 빈 목록 []을 반환해 줘. 파일이 있으면 encoding="utf-8"로 열어 json.load로 읽어 줘. 저장 전의 경로에 이 함수를 호출해서 "처음 불러오기: []"를 출력해 줘. 모든 오류를 빈 목록으로 바꾸는 코드는 넣지 마.

② AI가 돌려줄 내용의 요지

파일 유무를 먼저 확인하고, 없는 경우에만 빈 목록을 반환하는 함수를 제안할 수 있다. 파일이 있으면 읽기 모드로 열어 JSON 내용을 Python 자료로 바꾼다.

함수는 이름을 붙여 다시 사용할 수 있도록 묶은 작업이다. load_expenses는 경로를 받아 그곳의 기록을 돌려준다. 처음 실행에서는 파일이 없는 것이 자연스러우므로 빈 목록을 돌려주도록 정한다.

JSON은 자료를 일정한 규칙의 글자로 표현하는 형식이다. 이 예제의 목록, 사전, 문자열, 정수는 JSON으로 저장했다가 다시 읽기 알맞다. json.load는 열린 파일에서 그 글자를 읽어 Python 자료로 바꾼다.

③ 직접 확인할 것

실행 명령: python3 main.py

“처음 불러오기: []”가 나오고 파일을 찾을 수 없다는 오류 없이 끝나야 한다. 코드에서는 파일이 없을 때 처리하는 부분이 파일을 여는 부분보다 앞에 있는지 확인한다. 파일을 먼저 열면 뒤의 존재 확인까지 도달하지 못한다.

④ 체크포인트

없는 파일을 읽어도 빈 목록으로 시작하면 다음 단계로 간다. 파일 내용이 잘못된 경우까지 빈 목록으로 바꾸지는 않는다.

불러오기는 파일이 없으면 빈 목록을 돌려주고 파일이 있으면 JSON을 읽는다

단계 4. 한글을 읽기 쉽게 JSON으로 저장한다

① 보낼 요청문

save_expenses(path, expenses) 함수를 추가해 줘. path.open("w", encoding="utf-8")로 열고 json.dump에 ensure_ascii=False, indent=2를 전달해 줘. 정해진 기록 목록을 임시 폴더의 expenses.json에 저장해 줘. 저장 뒤에는 "저장 후 파일 존재: True"와 "저장 파일 내용:"을 출력하고 파일의 글자를 보여 줘. 파일을 다시 글자로 읽을 때도 encoding="utf-8"을 지정해 줘.

② AI가 돌려줄 내용의 요지

저장 함수를 만들고 json.dump로 목록을 파일에 쓴다. 한글을 그대로 보이게 하는 설정과 들여쓰기 설정을 함께 넣는 코드를 제안할 수 있다.

encoding은 글자를 파일의 바이트로 바꾸거나 바이트를 글자로 읽는 규칙이다. 바이트는 파일에 저장되는 데이터의 단위다. 쓰는 쪽과 읽는 쪽을 모두 utf-8로 맞춘다. 컴퓨터의 기본 설정에 맡기지 않고 코드에 적어 두는 것이다.

ensure_ascii=False는 JSON 안에서 한글을 “점심 김밥”처럼 보이게 한다. 기본 설정에서는 한글이 역슬래시와 u로 시작하는 표현으로 저장될 수 있다. 그 표현 자체가 한글 손상을 뜻하지는 않는다. encoding은 글자와 바이트 사이의 규칙이고, ensure_ascii는 JSON 글자 안의 표현 방식이다. 둘은 맡은 일이 다르다.

indent=2는 들여쓰기를 두 칸으로 만들어 사람이 파일을 읽기 쉽게 한다. 저장한 기록의 뜻이나 금액을 바꾸는 설정은 아니다.

③ 직접 확인할 것

실행 명령: python3 main.py

“저장 후 파일 존재: True”가 나오고, 파일 내용에 “점심 김밥”이 그대로 보여야 한다. True는 조건이 참이라는 뜻이다. 금액은 5500으로 보이고, 날짜와 메모에는 큰따옴표가 붙는다. diff에서도 파일을 여는 모든 위치에 utf-8이 들어갔는지 살핀다.

④ 체크포인트

파일이 만들어지고 한글을 읽을 수 있으면 다음 단계로 간다. 이 시점에는 아직 저장 내용이 원래 기록과 같은지까지 확인하지 않았다.

단계 5. 저장한 파일을 다시 읽어 비교한다

① 보낼 요청문

저장 뒤 load_expenses(path)를 다시 호출해서 loaded라는 별도 변수에 담아 줘. loaded와 원래 expenses를 ==로 비교해 줘. 다르면 RuntimeError("저장 전후 기록이 다릅니다.")를 일으키고, 같을 때만 "저장 전후 일치: True"를 출력해 줘. 다시 읽은 목록의 건수와 각 기록도 출력해 줘. 기록 출력 형식은 "2026-10-15 | 5,500원 | 점심 김밥"으로 맞춰 줘.

② AI가 돌려줄 내용의 요지

파일에서 새로 읽은 목록을 원래 목록과 비교한다. 서로 다르면 실행을 오류로 끝내고, 같으면 건수와 복원한 기록을 출력하는 코드를 제안할 수 있다.

==는 두 값이 같은지 비교한다. 이 예제에서는 목록에 담긴 사전의 내용까지 비교한다. 단순히 파일이 생겼는지 확인하는 것보다 검사 범위가 넓어진다. 파일이 있어도 빈 목록을 저장했다면 이 비교를 통과하지 못한다.

원래 expenses를 불러온 값으로 덮어쓰지 않는 이유도 있다. 비교할 원본을 남겨야 저장 전후가 같은지 판단할 수 있기 때문이다. loaded = expenses라고 쓰는 것도 파일을 읽는 일이 아니므로 검사가 되지 않는다.

③ 직접 확인할 것

실행 명령: python3 main.py

“다시 읽은 건수: 1”, 정해진 기록 한 줄, “저장 전후 일치: True”가 나와야 한다. 출력에 5,500이라고 쉼표가 붙어도 파일 속 금액은 정수 5500이어야 한다. 쉼표는 화면에 보여 주기 위한 꾸밈이다.

④ 체크포인트

비교 대상 한쪽이 원래 목록이고 다른 쪽이 파일에서 읽은 목록인지 확인하면 다음 단계로 간다.

원래 목록과 파일에서 다시 읽은 목록을 비교해야 저장 전후의 일치를 확인할 수 있다

단계 6. 실패하는 경우와 전체 변경을 확인한다

① 보낼 요청문

지금 코드의 저장·불러오기 흐름을 검토해 줘. 파일 없음 처리가 먼저인지, 읽기와 쓰기가 모두 utf-8인지, 한글 저장에 ensure_ascii=False를 썼는지, 비교가 파일에서 읽은 값으로 이루어지는지 설명해 줘. 비교 검사가 실제로 작동하는지 확인할 수 있도록, 저장 호출에만 빈 목록을 넣는 일시적인 변경과 복구 방법을 알려 줘. 마지막 완성본에는 원래 기록을 저장하도록 해 줘.

② AI가 돌려줄 내용의 요지

저장 호출을 잠시 save_expenses(path, [])로 바꾸면 원래 기록과 다시 읽은 빈 목록이 달라져 오류가 나는지 확인할 수 있다. 확인 뒤에는 save_expenses(path, expenses)로 되돌리라고 제안할 수 있다.

③ 직접 확인할 것

정상 확인 명령: python3 -W error -m py_compile main.py

정상 실행 명령: python3 main.py

첫 명령은 코드를 실행하지 않고 문법을 검사하며, 경고도 오류로 취급한다. 통과하면 보통 출력이 없다. 이어서 두 번째 명령으로 실제 파일 저장과 불러오기를 확인한다.

그다음 저장 호출에만 빈 목록을 넣어 한 번 실행한다. 파일에는 빈 목록이 저장되지만 원본에는 한 건이 남아 있으므로 “저장 전후 기록이 다릅니다.”라는 오류가 나야 한다. 오류 안내에 나오는 파일 경로나 줄 번호는 환경에 따라 다를 수 있다.

확인이 끝나면 저장 호출을 원래대로 복구한다. diff에서 시험용 변경이 남아 있지 않은지 보고 두 명령을 다시 실행한다. 문법 검사와 정상 실행을 모두 통과한 파일을 완성본으로 남긴다.

④ 체크포인트

일부러 다르게 저장했을 때 검사가 실패하고, 복구한 뒤에는 아래 실행 결과와 같으면 실습을 마친다. AI의 “문제없다”는 답 대신 실행과 비교로 확인한 상태다.

완성 코드

아래 내용을 main.py에 저장한다. 이 파일에는 지출 기록을 만드는 부분까지 포함되어 있으므로 앞 장의 코드나 미리 준비한 JSON 파일이 필요 없다.

import json
import tempfile
from pathlib import Path


def load_expenses(path):
    if not path.exists():
        return []
    with path.open("r", encoding="utf-8") as file:
        return json.load(file)


def save_expenses(path, expenses):
    with path.open("w", encoding="utf-8") as file:
        json.dump(expenses, file, ensure_ascii=False, indent=2)


def main():
    expenses = [
        {
            "date": "2026-10-15",
            "amount": 5500,
            "memo": "점심 김밥",
        }
    ]

    with tempfile.TemporaryDirectory() as temp_dir:
        path = Path(temp_dir) / "expenses.json"

        print(f"저장 전 파일 존재: {path.exists()}")
        print(f"처음 불러오기: {load_expenses(path)}")

        save_expenses(path, expenses)

        print(f"저장 후 파일 존재: {path.exists()}")
        print("저장 파일 내용:")
        print(path.read_text(encoding="utf-8"))

        loaded = load_expenses(path)
        same = loaded == expenses
        if not same:
            raise RuntimeError("저장 전후 기록이 다릅니다.")

        print(f"다시 읽은 건수: {len(loaded)}")
        for expense in loaded:
            print(
                f"{expense['date']} | "
                f"{expense['amount']:,}원 | "
                f"{expense['memo']}"
            )
        print(f"저장 전후 일치: {same}")


if __name__ == "__main__":
    main()

줄별 해설

맨 위의 import 세 줄은 필요한 표준 라이브러리를 가져온다. json은 자료와 JSON 사이의 변환을, tempfile은 임시 폴더 생성을, Path는 경로와 파일 접근을 맡는다. 별도 설치는 필요 없다.

load_expenses 안의 첫 조건은 파일이 없는지 확인한다. 없다면 return []로 빈 목록을 돌려주고 그 함수의 실행을 끝낸다. 파일이 있을 때만 읽기 모드인 "r"로 연다. with 안의 json.load(file)은 파일에서 읽은 내용을 반환한다. with 범위를 벗어나면 파일은 닫힌다.

save_expenses에서는 쓰기 모드인 "w"를 사용한다. 이 모드는 기존 파일이 있으면 내용을 비우고 새로 쓴다. 이번 코드는 기록을 하나씩 파일 뒤에 붙이는 방식이 아니라, 현재 목록 전체로 파일을 교체하는 방식이다. 이어지는 json.dump는 첫 인자인 expenses를 두 번째 인자인 file에 쓴다.

main은 이번 시연의 순서를 묶은 함수다. expenses에는 사전 한 개가 든 목록을 넣는다. 날짜는 날짜 모양의 문자열이고, amount만 정수다. JSON 저장을 위해 별도의 날짜 자료로 바꾸지 않는다.

TemporaryDirectory가 만든 폴더 이름은 temp_dir에 들어간다. Path(temp_dir) / "expenses.json"의 슬래시는 여기서 폴더 경로와 파일 이름을 잇는 역할이다. 문자열을 직접 이어 붙이지 않아도 된다.

처음 두 출력은 파일이 없다는 상태와 빈 목록을 확인한다. 그다음 save_expenses를 호출한다. 저장 함수의 with가 끝나 파일이 닫힌 뒤에 파일 존재 여부와 실제 글자를 읽어 출력한다. read_text는 JSON을 해석하지 않고 파일 전체를 문자열로 읽는다. 파일 모양을 눈으로 확인하기 위한 호출이다.

loaded = load_expenses(path)는 파일의 JSON을 다시 목록으로 읽는 부분이다. same에는 두 목록의 비교 결과가 들어간다. 다르면 raise로 오류를 발생시킨다. 같지 않은 상태에서 성공 메시지가 나오지 않도록 하는 검사다.

len(loaded)는 목록에 든 지출 건수를 센다. 반복문인 for는 목록에서 기록을 하나씩 꺼낸다. 출력 문자열 안의 amount 뒤에 붙은 :,는 금액에 세 자리마다 쉼표를 표시한다. 파일에 저장된 숫자는 바꾸지 않는다.

마지막 조건은 이 파일을 직접 실행했을 때 main을 호출하도록 한다. 프로그램 실행이 끝나며 임시 폴더의 with 범위도 종료되고, 이번에 만든 JSON 파일도 함께 정리된다.

실행 결과

main.py가 있는 폴더의 터미널에서 아래 명령을 실행한다. 명령은 입력 안내이므로 인용으로 표시한다.

python3 -W error -m py_compile main.py

python3 main.py

문법 검사는 출력 없이 끝나고, 프로그램은 다음 내용을 출력한다. 임시 폴더의 실제 경로는 포함하지 않는다.

저장 전 파일 존재: False
처음 불러오기: []
저장 후 파일 존재: True
저장 파일 내용:
[
  {
    "date": "2026-10-15",
    "amount": 5500,
    "memo": "점심 김밥"
  }
]
다시 읽은 건수: 1
2026-10-15 | 5,500원 | 점심 김밥
저장 전후 일치: True

명령을 다시 실행해도 처음에는 False와 빈 목록이 나온다. 이전 실행의 파일을 잃어버린 오류가 아니라 새 임시 폴더에서 시작하기 때문이다. 실제로 읽은 파일의 내용까지 같다는 것은 마지막 True로 확인한다.

실무에서 자주 틀리는 것

파일을 열고 나서 없는지 확인한다

파일이 없으면 open을 호출하는 순간 오류가 난다. 아래 잘못된 순서는 존재 여부를 확인하기 전에 읽기를 시도한다. 다음 두 예시는 완성 코드의 해당 부분을 설명하기 위한 비교다.

틀린 순서: with path.open("r", encoding="utf-8") as file: 안에서 if not path.exists():를 검사한다.

고친 순서: 먼저 if not path.exists():에서 return []을 실행하고, 그다음 with path.open("r", encoding="utf-8") as file:로 연다.

파일 없음은 처음 실행에서 예상한 상황이다. 반면 파일 내용이 망가져 읽지 못하는 상황은 원인을 확인해야 한다. 모든 오류를 한꺼번에 빈 목록으로 바꾸면 두 상황을 구별하기 어렵다.

한글 표현 설정과 파일 인코딩을 하나로 생각한다

ensure_ascii=False만 넣었다고 파일을 읽고 쓰는 규칙까지 정해지는 것은 아니다. 읽기와 쓰기에 encoding을 명시하고, JSON의 한글 표현도 별도로 정한다.

부족한 코드: with path.open("w") as file: 안에서 json.dump(expenses, file, ensure_ascii=False)를 호출한다.

고친 코드: with path.open("w", encoding="utf-8") as file: 안에서 json.dump(expenses, file, ensure_ascii=False, indent=2)를 호출한다. 읽을 때도 encoding="utf-8"을 쓴다.

한글이 역슬래시 표현으로 보이더라도 다시 읽으면 같은 문자열일 수 있다. 눈에 보이는 파일 모양과 복원한 값의 일치는 따로 확인한다.

저장 후 원래 변수만 다시 출력한다

화면에 원래 목록이 보인다고 파일 저장이 검증되는 것은 아니다. 파일에 어떤 값이 들어갔는지는 다시 읽어야 확인할 수 있다.

틀린 코드: loaded = expenses 다음에 same = loaded == expenses로 비교한다.

고친 코드: loaded = load_expenses(path) 다음에 same = loaded == expenses로 비교한다.

틀린 코드는 두 변수에 같은 목록을 가리키게 했으므로 저장 파일에 문제가 있어도 비교를 통과한다. 단계 6처럼 일부러 빈 목록을 저장해 보는 확인이 이런 실수를 찾는 데 도움이 된다.

기존 JSON 뒤에 목록을 계속 붙인다

파일을 덧붙이기 모드인 "a"로 열고 json.dump를 반복하면 목록 표현이 연달아 붙을 수 있다. 이것은 하나의 JSON 자료로 읽을 수 있는 형태가 아니다.

틀린 코드: with path.open("a", encoding="utf-8") as file: 안에서 목록을 json.dump로 저장한다.

고친 코드: with path.open("w", encoding="utf-8") as file: 안에서 현재 목록 전체를 json.dump로 저장한다.

이번 방식에서 "w"는 파일 전체를 다시 쓰는 선택이다. 임시 폴더에서 연습하는 동안에도 쓰기 모드와 저장 대상을 diff로 확인하는 습관을 들인다.

한눈에 보기

저장과 불러오기에 쓰는 도구의 역할
도구 또는 설정역할이번 확인 방법
json.dumpPython 자료를 JSON으로 파일에 쓴다저장 파일 내용을 출력한다
json.load파일의 JSON을 Python 자료로 읽는다loaded에 별도로 담는다
path.exists()경로가 존재하는지 확인한다저장 전후 False와 True를 본다
encoding="utf-8"파일의 글자 변환 규칙을 정한다읽기와 쓰기에 모두 지정한다
ensure_ascii=FalseJSON에 한글을 그대로 표시한다점심 김밥이 보이는지 확인한다
TemporaryDirectory실습 파일을 둘 임시 폴더를 만든다범위 안에서 쓰고 읽는다
loaded == expenses저장 전후 내용이 같은지 비교한다일부러 다르게 저장해 실패도 본다

이번 코드에서 확인한 것은 파일 없음 처리, 한글을 포함한 저장과 복원, 저장 전후 비교다. 전원이 꺼지는 순간의 저장 보호나 동시에 여러 프로그램이 파일을 바꾸는 상황은 다루지 않았다. 다음에는 복원한 기록에도 정해진 분류를 붙일 수 있도록 입력 규칙을 더한다.

연습 문제

  1. 고정 입력 목록에 날짜가 "2026-10-16", 금액이 정수 1800, 메모가 "버스"인 기록을 추가한다. 다시 읽은 건수와 기록 출력이 어떻게 바뀌는지 확인한다.
  2. ensure_ascii=False를 잠시 ensure_ascii=True로 바꾼다. 저장 파일에서 한글이 보이는 방식과 저장 전후 비교 결과를 관찰한 뒤 원래대로 복구한다.
  3. 저장 호출만 save_expenses(path, [])로 바꾼다. 파일 존재 여부는 참인데 비교가 실패하는 이유를 설명하고 원래 호출로 복구한다.
  4. load_expenses의 파일 없음 처리를 잠시 지운다. 정상 코드와 어느 지점에서 실행 결과가 달라지는지 확인하고 복구한다.

정답과 해설

  1. expenses의 첫 사전 다음에 같은 키를 가진 사전을 하나 더 넣는다. 다시 읽은 건수는 2가 되고 “2026-10-16 | 1,800원 | 버스”가 기존 기록 다음 줄에 나온다. 목록 전체를 저장하므로 저장 전후 일치는 True다. 파일 안의 두 번째 금액에는 쉼표가 붙지 않는다.
  2. 파일 내용 출력에서 한글이 역슬래시와 u로 시작하는 표현으로 바뀐다. json.load는 그 표현을 다시 한글 문자열로 읽으므로 마지막 기록 출력은 같고 저장 전후 일치도 True다. 사람이 파일을 읽기 쉽게 하려던 실습 조건에 맞춰 False로 복구한다.
  3. 빈 목록도 정상적인 JSON이므로 파일 생성과 읽기는 성공한다. 하지만 원래 expenses에는 한 건이 있고 loaded에는 기록이 없으므로 비교에서 오류가 발생한다. 이 결과는 파일 존재 확인만으로 내용의 정확성을 판단할 수 없음을 보여 준다. 호출을 복구한 뒤 문법 검사와 정상 실행을 다시 확인한다.
  4. 처음 불러오기를 실행할 때 파일이 아직 없으므로 FileNotFoundError가 발생한다. 저장 호출까지 도달하지 못한다. if not path.exists():와 return []을 파일을 여는 줄 앞에 복구하면 빈 목록으로 시작하고 나머지 시연이 이어진다.
오탈자·오류 제보 비공개로 접수되어 원고 수정에 반영됩니다

이메일 등 개인정보는 받지 않습니다. 답변이 필요한 질문은 아래 댓글을 이용해 주세요.

READER FEEDBACK

질문·의견

내용에 관한 질문이나 더 나은 설명을 위한 의견을 남겨 주세요. 오탈자는 위의 제보 양식이 더 빨리 반영됩니다. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.