DATA ROOTS · 2026.10.07

CSV·JSON에서
DB로

PostgreSQL로 원리를 이해하고,
TinyDB로 저장된 변화를 직접 확인합니다.

CSV와 JSON에도 데이터는 남습니다. 그렇다면 왜 DB가 필요할까요? 이번에는 PostgreSQL로 데이터 관리의 원리를 이해하고, TinyDB로 직접 추가·검색·수정·삭제합니다. Cursor에서 파일을 열어 저장 결과까지 확인합니다.

01 · 지난주에서 이어지는 질문

구분 기존 학습 이번 주 연결
복습 · W3 API와 JSON JSON은 데이터를 표현하는 형식이지 API나 DB 자체가 아님
복습 · W19 CSV Worker 결과 확인 CSV 입력과 JSON 결과는 프로그램을 종료해도 남음
확장 · W20 인증과 인가 소유자를 확인하는 규칙과 데이터를 저장·검색하는 기능은 별개
NEW PostgreSQL·SQL·키·JOIN 여러 표의 관계를 연결하고 DB가 규칙을 지키게 하기
NEW TinyDB JSON 파일에 저장하면서 CRUD를 제공하는 작은 도구

19주차 Worker나 20주차 로그인 앱을 완성하지 않았어도 시작할 수 있습니다. 같은 담당자·작업 소재를 사용하지만 새 폴더의 독립적인 터미널 프로그램입니다. 로그인·웹 API·Worker는 이번에 실행하지 않습니다.

02 · CSV·JSON·DB는 무엇이 다를까?

CSV는 같은 항목을 가진 여러 행, JSON은 이름과 값으로 이루어진 객체와 배열을 표현하기 좋습니다. DB는 데이터를 저장하고 찾아서 바꾸는 기능을 제공하며, 제품에 따라 관계·제약조건·동시 접근 등도 관리합니다.

아래 두 파일은 같은 작업 3개를 담고 있습니다. 먼저 Cursor에서 나란히 열어보세요.

job_id,title,owner,status
job-001,설문 CSV 검토,mina,대기
job-002,누락값 확인,joon,완료
job-003,월간 리포트 작성,mina,대기
[
  {"job_id":"job-001","title":"설문 CSV 검토","owner":"mina","status":"대기"},
  {"job_id":"job-002","title":"누락값 확인","owner":"joon","status":"완료"},
  {"job_id":"job-003","title":"월간 리포트 작성","owner":"mina","status":"대기"}
]

JSON에서는 배열·객체를 중첩하고 숫자·문자열·불리언 등을 구분할 수 있습니다. CSV는 열의 의미와 값의 타입을 읽는 쪽에서 정해야 합니다. 어느 형식이 무조건 더 좋은 것은 아닙니다.

하고 싶은 일 파일만 직접 다루기 DB 도구를 사용하기
저장하고 다시 읽기 CSV·JSON으로 충분할 수 있음 DB도 가능
mina의 작업만 찾기 파일을 읽고 조건을 검사하는 코드 작성 조회 기능으로 조건 표현
같은 작업 ID 방지 직접 검사 PostgreSQL은 키·제약조건 사용 가능
여러 변경을 함께 확정 별도 처리 필요 PostgreSQL은 트랜잭션 사용 가능

DB를 쓰면 파일이 없어지는 것은 아닙니다. DB도 저장 장치를 사용합니다. 차이는 파일의 확장자보다 그 위에서 제공하는 관리 기능입니다. TinyDB가 PostgreSQL의 기능을 모두 제공하는 것도 아닙니다.

03 · PostgreSQL: 데이터를 표와 관계로 관리하기

모바일에서 넓은 표는 좌우로 밀어 나머지 열을 확인하세요.

PostgreSQL(포스트그레스큐엘, 줄여서 Postgres)은 널리 쓰이는 오픈소스 관계형 DB 관리 시스템입니다. 앱이 DB 서버에 요청하면 서버가 데이터를 조회·변경하고 규칙을 검사합니다. SQL은 이때 사용하는 언어입니다. PostgreSQL은 제품 이름, SQL은 언어 이름입니다.

관계형 모델은 1970년 E. F. Codd의 연구에서 출발했습니다. 저장 위치를 따라가는 절차보다 데이터의 구조와 관계를 중심으로 질의하는 생각이 중요합니다. 오늘은 역사를 외우기보다 작업과 담당자를 두 표로 나누어 봅니다.

사용자 표 users

user_id name
mina 미나
joon 준

작업 표 jobs

job_id title owner_id status
job-001 설문 CSV 검토 mina 대기
job-002 누락값 확인 joon 완료
job-003 월간 리포트 작성 mina 대기

테이블은 표, 행은 작업 한 건, 열은 작업명 같은 항목입니다. 기본키(Primary Key)는 각 행을 구별하는 값으로, 위 예시에서는 users의 user_id와 jobs의 job_id입니다. 기본키는 중복과 NULL을 허용하지 않습니다.

외래키(Foreign Key)는 다른 표의 키를 참조합니다. jobs.owner_id가 users.user_id를 참조하도록 정하면 등록되지 않은 담당자 ID를 넣는 실수를 막을 수 있습니다. 정규화는 중복 저장으로 생기는 불일치를 줄이도록 데이터를 나누고 관계를 정리하는 설계입니다. 미나의 이름을 바꿀 때 모든 작업 행을 수정하지 않고 users의 한 행을 바꾸는 식입니다.

04 · SQL을 읽어보기

이번 SQL은 PostgreSQL 개념 설명용입니다. 설치하거나 TinyDB에 붙여 넣지 않습니다. 실행 전이라 생각하고 결과를 먼저 예상해 보세요.

SELECT job_id, title
FROM jobs
WHERE owner_id = 'mina'
ORDER BY job_id;

SELECT는 가져올 열, FROM은 대상 표, WHERE는 조건, ORDER BY는 정렬 기준입니다. 결과는 job-001 / 설문 CSV 검토, job-003 / 월간 리포트 작성 두 행입니다.

SELECT jobs.job_id, jobs.title, users.name
FROM jobs
JOIN users ON jobs.owner_id = users.user_id
WHERE users.user_id = 'mina'
ORDER BY jobs.job_id;

JOIN은 조건에 맞는 행을 두 표에서 연결해 조회합니다. jobs의 담당자 ID와 users의 사용자 ID가 같은 행을 붙여, 작업명과 사람 이름을 함께 가져옵니다. 원본 표를 하나로 합쳐 저장하는 명령은 아닙니다.

job_id title name
job-001 설문 CSV 검토 미나
job-003 월간 리포트 작성 미나

SQL을 AI에게 작성하게 해도 누구의 데이터인지, 어떤 조건으로 찾는지, 몇 행이 나와야 하는지는 우리가 확인해야 합니다. 담당자 조건을 쓰는 것 자체가 인증은 아니므로, 실제 앱은 W20처럼 요청자의 권한도 확인해야 합니다.

제약조건·인덱스·트랜잭션

기능 작업 관리 예시 알아둘 점
제약조건 같은 job_id 거부, 없는 owner_id 거부 DB에 규칙을 정의해야 검사함
인덱스 담당자별 작업 검색을 돕는 색인 조회에 유리할 수 있지만 저장 공간과 갱신 비용이 듦
트랜잭션 작업 완료 처리와 이력 추가를 함께 확정 중간 실패 시 둘 다 취소하도록 묶음

트랜잭션은 BEGIN으로 시작해 COMMIT으로 확정하거나 ROLLBACK으로 취소합니다. ACID는 원자성(전부 성공 또는 취소), 일관성(정의한 무결성 규칙 유지), 격리성(동시 실행의 간섭 제어), 지속성(확정한 데이터의 보존)을 가리킵니다. 격리 수준과 설정에 따른 차이가 있어 “DB가 모든 업무 오류를 알아서 막는다”는 뜻은 아닙니다.

05 · TinyDB: JSON 파일로 직접 확인하기

TinyDB는 Python에서 사용하는 작은 문서형 DB 도구입니다. JSON 파일에 저장하면서 추가(insert)·검색(search)·수정(update)·삭제(remove) 기능을 제공합니다. DB 서버나 계정 없이 설치할 수 있어 이번 실습에 사용합니다.

비교 PostgreSQL 이번 TinyDB 실습
데이터를 다루는 기본 단위 관계형 테이블의 행 Python 딕셔너리 형태의 문서
요청 방법 SQL Python 함수와 조건식
저장 확인 DB 접속 도구로 조회 Cursor에서 db.json 열기
관계·규칙 JOIN·외래키·고유 제약조건 이번 앱이 필요한 검사 직접 구현
동시 접근·트랜잭션 관련 기능 제공 여러 프로세스·스레드 접근과 ACID 보장을 기대하지 않음

문서형 DB라고 해서 정확성을 포기하는 것은 아닙니다. 제품마다 기능과 보장이 다릅니다. TinyDB로 배우는 것은 CRUD와 파일 저장이며, SQL·JOIN·트랜잭션을 구현하는 실습은 아닙니다.

from tinydb import TinyDB, Query

with TinyDB('data/db.json', encoding='utf-8') as db:
    jobs = db.search(Query().owner == 'mina')

위 코드는 검색 방식의 예시입니다. 전체 실행은 아래 app.py로 진행합니다. sample.json의 배열을 그대로 DB 파일로 사용하는 것이 아니라 처음 한 번 읽어 TinyDB에 넣습니다. 저장 파일은 다음처럼 TinyDB가 관리하는 구조가 됩니다.

{
  "_default": {
    "1": {"job_id":"job-001","title":"설문 CSV 검토","owner":"mina","status":"대기"}
  }
}

_default는 TinyDB 기본 테이블 이름, "1"은 내부 문서 ID입니다. 우리가 정한 job-001과는 다릅니다. 위 그림은 한 건만 발췌한 것이며 실제 최초 실행에는 3건이 들어갑니다. TinyDB의 테이블이라는 이름이 관계형 DB의 관계·제약조건까지 뜻하지는 않습니다.

06 · Cursor에서 작업 목록 관리 만들기

준비와 생성

  1. 새 tinydb-lab 폴더를 만들고 Cursor의 File → Open Folder로 엽니다.
  2. Terminal → New Terminal에서 py --version으로 Python 3.10 이상인지 확인합니다. py가 없다면 python --version을 확인하고 가상환경 생성 명령의 py를 python으로 바꿉니다. 둘 다 없으면 Python 설치 후 Cursor를 다시 엽니다.
  3. Agent에 아래 프롬프트를 붙여 넣고 생성된 파일을 확인합니다.

Python 3.10+와 tinydb==4.9.0으로 터미널 작업 목록 관리 프로그램을 만들어줘.
파일은 app.py, sample.csv, sample.json, requirements.txt, README.md, .gitignore.
DB 서버, 웹 화면, 로그인, Docker, SQL 실행 기능은 넣지 마.

샘플 CSV와 JSON에 동일한 3개 작업을 넣어줘.
필드: job_id, title, owner, status. 모두 문자열.
job-001 / 설문 CSV 검토 / mina / 대기
job-002 / 누락값 확인 / joon / 완료
job-003 / 월간 리포트 작성 / mina / 대기

app.py 위치 기준으로 data/db.json을 사용해.
DB 파일이 없을 때만 sample.json 전체를 검증한 후 TinyDB로 가져와.
CSV는 형식 비교용이며 자동으로 가져오지 마.
기존 DB가 비어 있어도 샘플을 다시 넣지 마. 재실행 시 변경을 유지해.
JSON은 UTF-8, ensure_ascii=False, indent=2로 사람이 읽기 쉽게 저장해.
기존 파일이 깨졌거나 구조·필드·작업 ID가 잘못되면 오류를 알리고 종료해.
자동 삭제·초기화·덮어쓰기로 오류를 숨기지 마.

메뉴: 1 전체 목록, 2 담당자 정확히 검색, 3 작업 추가,
4 상태 수정, 5 작업 삭제, 0 종료.
문자열 앞뒤 공백은 제거하고 빈 항목은 거부해. 상태는 대기/완료만 허용.
job_id 중복은 앱에서 검사해 거부하고 기존 작업을 유지해.
없는 작업 수정·삭제는 안내하고 계속해. 삭제는 대상을 보여주고 yes일 때만 실행해.
검색 결과가 없으면 안내해. 잘못된 메뉴도 다시 선택하게 해.
저장 오류는 성공으로 표시하지 말고 종료해. 종료 시 DB를 닫아줘.
여러 프로그램을 동시에 실행하거나 실행 중 JSON을 직접 수정하지 않도록 안내해.
담당자 검색은 로그인·권한 검사가 아님을 설명해.

README에 아래 명령과 검증 순서를 제공해:
py -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe app.py

검증: 최초 3건, mina 검색 2건, job-004 추가 후 4건,
job-004 완료로 수정 후 재실행해도 유지, 중복 ID 거부,
삭제 취소 시 유지, yes로 삭제 후 재실행해도 복원되지 않음.
.gitignore에는 .venv/, __pycache__/, data/를 넣어줘.
TinyDB는 PostgreSQL의 SQL·JOIN·제약조건·트랜잭션 대체품이 아니라는 설명도 넣어줘.

실행

py -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe app.py

위에서부터 한 줄씩 실행합니다. 가상환경 활성화는 필요하지 않습니다. 터미널 하나에서 메뉴 번호를 입력합니다. 브라우저는 열지 않습니다. 재실행은 마지막 명령만 사용합니다. 처음 설치에는 인터넷이 필요합니다.

막히면 완성 예제 ZIP을 내려받아 압축을 풀고, app.py가 있는 폴더를 Cursor에서 열어 같은 명령을 실행하세요. 실행 안내 · Python 코드 · CSV 샘플 · JSON 샘플

07 · 파일을 열어 결과 검증하기

순서 직접 할 일 예상 결과
1 sample.csv와 sample.json 비교 후 실행 같은 3건, data/db.json 생성
2 메뉴 1, 메뉴 2에서 mina 검색 전체 3건, mina는 2건
3 메뉴 3: job-004 / 결과 확인 / mina / 대기 전체 4건, JSON에 새 작업 표시
4 메뉴 4: job-004 → 완료 JSON의 상태 변경
5 메뉴 0으로 종료 후 다시 실행 총 4건, 완료 상태 유지
6 job-004를 다시 추가 중복 거부, 기존 작업 보존
7 메뉴 5에서 job-004 삭제 질문에 no 삭제 취소, 4건 유지
8 다시 삭제하고 yes 입력, 재실행 3건, 삭제 작업 복원 안 됨
9 없는 ID 수정·삭제, 잘못된 상태·메뉴 입력 오류 안내 후 메뉴 계속

Cursor 파일 탐색기에서 data/db.json을 열어 읽기만 합니다. 프로그램 실행 중 파일을 직접 편집하거나 두 프로그램을 동시에 실행하지 않습니다. 업데이트가 안 보이면 탭을 닫았다가 다시 여세요.

중복 ID를 막은 주체는 누구일까요? 이번에는 app.py의 contains 검사입니다. PostgreSQL의 기본키처럼 DB 자체가 강제하는 제약조건이 아닙니다. 또한 mina 검색은 단순 필터이며 인증된 사용자만 조회하게 만든 기능이 아닙니다.

막혔을 때

증상 확인할 것
app.py 또는 requirements.txt가 없음 Cursor 폴더와 터미널 위치 확인
No module named tinydb 설치와 실행 모두 .venv의 python.exe 사용
sample.json을 바꿨는데 DB가 그대로임 최초 생성 때만 가져오는 것이 정상
db.json이 비었는데 샘플이 안 들어옴 기존 DB는 비어 있어도 유지하는 것이 정상
JSON 파일 오류 종료 후 백업하고 파일 내용 확인. 자동 초기화하지 않음
처음부터 다시 실습하고 싶음 새 폴더에 ZIP을 다시 풀어 실행

08 · 어떤 저장소를 선택할까?

작은 표를 주고받는다면 CSV, 구조가 있는 데이터를 주고받는다면 JSON이 편리합니다. 소규모 로컬 프로그램의 문서 CRUD에는 TinyDB처럼 가벼운 도구를 쓸 수 있습니다. 여러 사용자의 데이터 관계·동시 수정·트랜잭션을 관리해야 한다면 PostgreSQL 같은 DB의 기능을 검토합니다.

오늘의 완료 상태는 로컬 TinyDB 작업 목록 관리 프로그램입니다. PostgreSQL 서버·로그인 연동·클라우드 DB를 만든 것은 아닙니다. 다음 주는 “작업에 PDF나 사진을 첨부한다면 파일과 메타데이터를 어디에 둘까?”라는 질문으로 스토리지를 살펴봅니다. 파일 저장과 DB의 메타데이터 관리 역할을 나누는 관점입니다.

공식 자료

← 20주차 로그인과 권한 · 전체 학습 노트