본문 바로가기

미래 먹거리를 위하여

[파이썬 정복하기] 라이브러리 7장 - 데이터 저장하고 관리하기 (맥OS 기준)

OS: MAC

참고: 👉 점프 투 파이썬 - 라이브러리 예제 바로가기

044. 객체를 파일로 저장하고 불러오려면? ― pickle(피클)

- pickle 파일은 사람이 읽을 수 없음 (이진 형식)

- 다른 환경/버전에서 로드 시 호환성 주의

- 신뢰할 수 없는 pickle 파일은 보안상 위험

구분 설명 예시 결과
모듈명 파이썬 객체(리스트, 딕셔너리, 클래스 등)를 
파일로 저장하거나 복원하는 모듈
import pickle - 프로그램의 실행 결과를 파일로 저장
- 머신러닝 모델, 데이터셋 임시 저장
- 세션/설정 값 유지
객체 저장 (직렬화) 객체를
이진 파일로 저장
(dump)
import pickle
data = {'name': 'Yul', 'age': 29, 'job': 'developer'}
with open('data.pkl', 'wb') as f:
   pickle.dump(data, f)
결과값이 출력되지 않음(정상)

# 다만,  data.pkl 파일이 생성되고,
그 안에 data 객체가 저장됨
객체 불러오기 (역직렬화) 저장된 객체를
다시 메모리로 불러오기
(load)
import pickle
with open('data.pkl', 'rb') as f:
      loaded_data = pickle.load(f)
         print(loaded_data)
{'name': 'Yul', 'age': 29, 'job': 'developer'}
비고 파일 모드 요약 'wb'
'rb'
write binary (저장할 때)
read binary (불러올 때)

라이브러리 예제 문제: 

점프 투 파이썬 - 라이브러리 예제 편 7장 44번 문제
점프 투 파이썬 - 라이브러리 예제 편 7장 44번 문제 해결

 

점프 투 파이썬 - 라이브러리 예제 편 7장 44번 문제 해결 및 결과

045. 객체 변경에 따른 오류를 방지하려면? ― copyreg(카피렉)

- pickle로 저장한 객체가 클래스 구조 변경 후에도 안전하게 복원되도록 직렬화 규칙을 등록하는 모듈.

구분 설명 예시 결과
모듈명 pickle로 저장된 객체가 나중에 클래스 구조가 바뀌어도
오류 없이 복원되게 도와주는 모듈
import copyreg - 사용자 정의 클래스 직렬화 시 유용
- 라이브러리 코드나 대형 프로젝트에서 사용
- 유지보수나 버전 관리 시 안정성 확보 가능
사용
목적
클래스 구조(속성, 초기화 방식 등)가
변경되더라도 이전 pickle 데이터를 안전하게 불러오기 위함
- pickle과 함께 사용 시 버전 호환성 보장
기본
구조
타입(type) 의 객체를 저장(직렬화)하고 불러올(역직렬화) 때
어떤 저장 방식(dump_function)복원 방식(load_function) 을 쓸지 지정하라”는 뜻이야.
copyreg.pickle
(type, dump_function, load_function)
# 객체를 직렬화할 거야 (타입, 저장 방식, 불러올 방식)
- 객체 저장 및 불러올 때 사용할 함수등록
- 객체의 직렬화/역직렬화 규칙을 직접 정의 
예시 사용자 정의 클래스(Person)를 
pickle로 저장했다가, 나중에 클래스 구조(예: 속성, 초기화 인자)가 
변경될 경우 오류가 발생한다.

이런 오류를 방지하면서 객체를 안전하게 저장하고 불러오려면 어떻게 해야 할까?
import copyreg, pickle

class Person:
 def __init__(self, name):
  self.name = name
# 저장 규칙 정의
def pickle_person(person):
    return Person, (person.name,)
# copyreg에 등록
copyreg.pickle(Person, pickle_person)
# 객체 생성 및 저장
p = Person('Yul')
with open('person.pkl', 'wb') as f:
     pickle.dump(p, f)
# 파일에서 불러오기
with open('person.pkl', 'rb') as f:
     loaded = pickle.load(f)
# 결과 출력
print(loaded.name)
Yul

라이브러리 예제 문제: (문제가 개 길다. 읽다가 전 내용을 까먹을 수준)

점프 투 파이썬 - 라이브러리 예제 편 7장 45번 문제
점프 투 파이썬 - 라이브러리 예제 편 7장 45번 문제 풀이 및 결과

046. 딕셔너리를 파일로 저장하려면? ― shelve(셸브)

- shelve는 딕셔너리처럼 쓰는 파일 기반 키-값 저장소.

- 키는 str, 값은 피클 가능한(Pickle-able) 파이썬 객체면 OK.

- 용도: 간단한 설정/캐시/작은 데이터의 영속 저장(프로토타입, 스크립트, 크롤러 캐시 등).

- 플랫폼/파이썬 버전 간 데이터 파일 호환 불가할 수 있음. 같은 환경에서 쓰고 읽는 것을 권장.

구분 서명 예시 결과
모듈명 내부적으로 dbm 기반
파일을 사용.
import shelve - 간단한 데이터, 설정값, 캐시 등을 프로그램 종료 후에도 유지 가능
- 딕셔너리처럼 접근
(db['key'] = value)
객체 저장/복원
딕셔너리 형태로
객체를 파일에 저장 및 복원
data = {
    'user': 'sophia',
    'roles': ['admin', 'editor'],
    'prefs': {'theme': 'dark', 'lang': 'ko'},
}

import shelve
with shelve.open('appstate') as db:
    for k, v in data.items():
        db[k] = v     #결과값 출력되지 않음(정상)

with shelve.open('appstate') as db:
    print(db['user'])        # 'sophia'
    print(db['prefs']['lang'])  # 'ko'
sophia
ko
객체 불러오기 (읽기) 저장된 객체를 다시 메모리로 읽어옴.
'r' 모드 사용 가능.
import shelve
with shelve.open('mydata', 'r') as db:
        print(db['user'])
{'name': 'Yul', 'age': 29}
'r': 읽기 전용 읽기 전용, 없으면 에러 shelve.open('cfg', flag='r')  
'n': 새로 생성 항상 새 파일(기존 덮어씀) shelve.open('cfg', flag='n')  
딕셔너리처럼 쓰기 / 읽기 / 삭제   import shelve
with
 shelve.open('mydata') as db:
    db['count'] = db.get('count', 0) + 1
    exists = 'count' in db # 키 존재 확인
    del db['count']                   # 삭제
 
가변객체 주의(writeback)

가변값 안전 업데이트
(재할당 방식)
예상 외 동작 예시 
(writeback=False, 기본)

리스트/딕셔너리 같은 가변 객체는 내부 값만 바꾸면 저장이 안 될 수 있음
→ writeback=True 옵션 사용 또는 다시 할당 필요.
with shelve.open('cart') as db:
    db['items'] = []          # 저장
with shelve.open('cart') as db:
    items = db['items']       # 리스트 객체 로드
    items.append('apple')    # 리스트 변경 (파일반영 X)
    # 해결 1) 다시 할당
    db['items'] = items   # 재할당로 안전 저장
종료 시 자동 저장.
writeback=True 없으면
db['items'] =
db['items']로 다시 할당해야 반영됨
가변객체 주의(writeback)  writeback=True 인 경우 with shelve.open('cart', writeback=True) as db:
    db['items'] = []
with shelve.open('cart', writeback=True) as db:
    db['items'].append('apple')   # 종료 시 디스크 반영
 
파일
닫기/동기화
파일은 반드시 닫아야 반영됨.
with문 사용 시 자동 종료.
수동 저장은 sync() 사용.
with shelve.open('mydata', writeback=True) as db:
    db['k'] = {'x': 1}
    db['k']['x'] = 2
    db.sync()  # 중간 저장(크래시 대비)
데이터 안전 저장
(프로그램 도중 크래시 방지)
파일 모드 요약 'c': 파일이 없으면 생성/
     있으면 열기

'w': 쓰기 전용
import shelve
with shelve.open('mydata', flag='c') as db:
    db['name'] = 'sophia'
    print(db['name'])
# with 블록을 벗어나면 자동 close
sophia
실무 활용 예)
결과
카운터 / 캐시 
결과를 파일에
캐싱(cache)하는 데 유용

“한번 계산(또는 요청)한 결과를 다음에 다시 계산하지 않고 저장된 파일에서 빠르게 꺼내 쓰는 방법


import shelve, time, hashlib

def get_page(url: str) -> str:
    # (예시) 실제 네트워크 호출 대신 흉내만
    time.sleep(0.1)
    return f"<html>fake content of {url}</html>"

def cached_get(url: str) -> str:
    key = hashlib.sha1(url.encode()).hexdigest()
    with shelve.open('cache') as db:
        if key in db:
            return db[key] #이미 저장된 결과면 그대로 반환
        html = get_page(url)
        db[key] = html  # 새로 계산한 결과를 캐시에 저장
        return html

print(cached_get('https://example.com'))
print(cached_get('https://example.com')) 
# 두 번째는 캐시 히트
fake content of https://example.com
fake content of https://example.com
(두 줄 다 같지만,
첫 번째는 실제 연산(딜레이 0.1초 포함)
두 번째는 캐시에서 즉시 반환되어 빠름.)
비교 (pickle vs shelve) pickle은 “파일 하나에 객체 하나”를 저장, shelve는 “딕셔너리처럼 여러 개를 키로 나눠 저장”. - pickle → 한 객체 덤프/로드
- shelve → 여러 객체를 키별로 관리
shelve는 내부적으로 pickle을 사용하지만, 사용법은 더 단순하고 직관적

라이브러리 예제 문제: 

점프 투 파이썬 - 라이브러리 예제 편 7장 46번 문제
점프 투 파이썬 - 라이브러리 예제 편 7장 46번 문제 풀이 및 결과

047. 블로그 데이터를 저장하려면? ― sqlite3(시퀄라이트)

- 데이터를 구조적으로, 안전하게, 여러 개의 테이블로 연결해서 저장.

- 블로그/메모 앱/데스크톱 유틸처럼 단일 파일에 구조적 저장 필요 시 사용

  • 여러 INSERT → executemany() + 한 번에 commit()
  • 적절한 INDEX (검색 조건/정렬에 쓰는 컬럼)
  • 대량 작업 전후: PRAGMA journal_mode=WAL 고려, 앱 시작 시 1회 설정

 

구분 설명 예시 결과
모듈명 파이썬 표준 라이브러리의
임베디드 SQL 데이터베이스.
단일 파일(.db)에 테이블/인덱스/트랜잭션을 지원.
import sqlite3 - 앱과 함께 배포 쉬움
- 설치 없이 사용 가능
- SQL로 구조적 질의/정렬/필터
DB 연결 데이터베이스 파일을 열거나 없으면 생성.
메모리 전용은 ':memory:'.
conn = sqlite3.connect('blog.db') blog.db 파일 연결(없으면 생성)
테이블 생성 블로그 글/태그/댓글 등 스키마 정의. IF NOT EXISTS 권장. conn.execute("""CREATE TABLE IF NOT EXISTS posts( id INTEGER PRIMARY KEY, title TEXT NOT NULL, body TEXT, author TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP );""") posts 테이블 생성
데이터 추가(INSERT) **바인딩 파라미터(?)**로 안전하게 삽입. conn.execute("INSERT INTO posts(title, body, author) VALUES(?, ?, ?)", ("첫 글", "내용", "sophia"))conn.commit() 1행 추가, 커밋 후 저장
데이터 조회(SELECT) SELECT ... FROM 테이블명..
조건/정렬/페이지네이션.
fetchall() 조회된 모든 데이터
rows = conn.execute("SELECT id, title FROM posts ORDER BY id DESC LIMIT 5").fetchall() 최근 5건의 (id, title) 목록
단건 조회 fetchone()으로 하나만. post = conn.execute("SELECT * FROM posts WHERE id=?", (1,)).fetchone() id=1인 행 또는 None
수정
(UPDATE)
UPDATE 테이블명 SET ... 
조건에 맞는 행 갱신.
conn.execute("UPDATE posts SET title=? WHERE id=?", ("제목 수정", 1)); conn.commit() 대상 행의 제목 변경
삭제
(DELETE)
DELETE FROM 테이블명 ...
조건 삭제.
conn.execute("DELETE FROM posts WHERE id=?", (1,)); conn.commit() id=1 행 삭제
다중 삽입
(executemany)
여러 행을 한 번에 삽입. conn.executemany("INSERT INTO posts(title, body, author) VALUES(?, ?, ?)", [("둘째 글","...", "sophia"), ("셋째 글","...", "sophia")]); conn.commit() 2행 이상 일괄 추가
데이터 저장 conn.commit() INSERT 쿼리문으로 데이터를 입력 시 conn.commit()으로 파이썬 종료해야 데이터 저장됨 # 커밋 없이 close()를 호출하면 변경된 내용이 모두 사라진다
사전(dict)처럼 읽기 row_factory로 컬럼명을 키로 접근. conn.row_factory = sqlite3.Row post = conn.execute("SELECT * FROM posts LIMIT 1").fetchone()
print(post["title"])
title 키로 접근 가능
인덱스 자주 조회하는 컬럼에 인덱스 생성. conn.execute("CREATE INDEX IF NOT EXISTS idx_posts_author ON posts(author)") author 조건 검색 빨라짐
UPSERT 고유 제약 위반 시 갱신. conn.execute("""INSERT INTO posts(id, title, body, author) VALUES(?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET title=excluded.title""", (1, "새제목", "본문", "sophia")); conn.commit() id가 있으면 업데이트, 없으면 삽입
핵심 API
& 사용 패턴




연결/종료 & 컨텍스트 매니저 import sqlite3
from pathlib import Path

db_path = Path("blog.db")
with sqlite3.connect(db_path) as conn:          
  # with: 자동 commit/rollback + close
    conn.execute("PRAGMA foreign_keys = ON")
                              # FK(외국어) 사용 시 권장
SQLite는 기본적으로 외래키 검사 기능이 꺼져 있으니까, 외래키(FK)를 쓰는 테이블을 만든다면
PRAGMA foreign_keys = ON 을 꼭 켜줘야 한다.
스키마 설계(블로그 최소 예시)

# 스키마(schema)
= 데이터베이스의 구조(Structure)
= 어떤 테이블이 있고, 그 안에 어떤 칼럼이 있고, 서로 어떻게 연결되는가”를 정의한 설계도
with sqlite3.connect("blog.db") as conn:
    conn.executescript("""
    PRAGMA foreign_keys = ON;
    CREATE TABLE IF NOT EXISTS posts(
        id INTEGER PRIMARY KEY#프라이머리키
        title TEXT NOT NULL,
        body  TEXT,
        author TEXT,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP
    );
    CREATE TABLE IF NOT EXISTS comments(
        id INTEGER PRIMARY KEY,
        post_id INTEGER NOT NULL,
        body TEXT NOT NULL,
        author TEXT,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY(post_id) REFERENCES posts(id) ON DELETE CASCADE);
    CREATE INDEX IF NOT EXISTS idx_comments_post ON comments(post_id);
    """)
출력값은 없지만 내부에 결과가 생김.(정상)

1. posts 테이블
   ㄴid
   ㄴtitle
   ㄴbody
   ㄴauthor
   ㄴcreated_at

2. comments 테이블
   ㄴid
   ㄴpost_id
   ㄴbody
   ㄴauthor
   ㄴcreated_at

3. 인덱스

   ㄴidx_comments_post

CRUD(크러드) 빠른 흐름
= 데이터를 만들고, 읽고, 고치고, 지우는 모든 기본 동작
with sqlite3.connect("blog.db") as conn:
    # Create(데이터 추가)
    conn.execute("INSERT INTO posts(title, body, author) VALUES(?, ?, ?)",
                 ("SQLite 시작하기", "내용...", "sophia"))
    # Read(데이터 조회)
    rows = conn.execute("SELECT id, title FROM posts ORDER BY id DESC").fetchall()
    # Update(데이터 수정)
    conn.execute("UPDATE posts SET title=? WHERE id=?", ("수정된 제목", rows[0][0]))
    # Delete(데이터 삭제)
    conn.execute("DELETE FROM posts WHERE id=?", (rows[-1][0],))
웹·앱 서비스의 뼈대는 결국 CRUD(크러드).
페이지네이션 & 검색
= 데이터를 효율적으로 불러오는 기술
= 데이터베이스에 쌓인 수많은 글 중에서, 사용자가 보고 싶은 일부만 빠르고 정확하게 보여주는 기술
page, page_size, q = 1, 10, "%SQLite%"
offset = (page-1)*page_size

with sqlite3.connect("blog.db") as conn:
     posts = conn.execute("""
            SELECT id, title, author, created_at
            FROM posts
            WHERE title LIKE ? OR body LIKE ?
            ORDER BY created_at DESC
            LIMIT ? OFFSET ?
    """, (q, q, page_size, offset)).fetchall()
# LIMIT: 몇 개씩 가져올지
# OFFSET: 어디서부터 가져올지

# 파라미터 바인딩(?)
WHERE title LIKE ?
→ 안전하게 변수값을 SQL에 연결하는 방법 (SQL Injection 방지)

트랜잭션(여러 작업을 하나로)
= 여러 개의 데이터 작업을 하나의 덩어리(묶음) 로 묶어서,
전부 성공하거나 전부 실패하게 만드는 기능.
with sqlite3.connect("blog.db") as conn:
  try:
        conn.execute("BEGIN")  #트랜잭션 시작
        # 새 글 작성
     post_id = conn.execute("""
            INSERT INTO posts(title, body, author)
            VALUES(?, ?, ?)
     """, ("트랜잭션 글", "본문 내용", "sophia")).lastrowid

        # 댓글 추가
    conn.execute("""
        INSERT INTO comments(post_id, body, author)
            VALUES(?, ?, ?)
        """, (post_id, "첫 댓글", "sophia"))

      conn.commit()  # 모든 작업 성공 → 확정(저장)
  except:
      conn.rollback()  # 중간에 에러 발생 → 모두 되돌림
       raise
트랜잭션의 4대 특징
(ACID)
= 데이터의 신뢰성을 보장하는 마지막 방패

 

🔎 LIKE 검색 자세히 보기

검색어 의미 매칭 예시
'python' 정확히 "python"인 경우만 "python" ✅ "python3" ❌
'%python%' "python"이 포함된 경우 "파이썬과 sqlite", "python blog" ✅
'python%' "python"으로 시작 "python tutorial" ✅ "my python note" ❌
'%python' "python"으로 끝남 "learn python" ✅ "python guide" ❌

주의사항 체크리스트

  • 항상 바인딩 파라미터(?) 사용 → SQL 인젝션 예방
  • 트랜잭션 단위로 commit()/rollback() 관리 (컨텍스트 매니저 권장)
  • '스키마 변경(마이그레이션)'은 버전 관리 필요 (예: alembic 대체 스크립트 자체 구현)
  • 동시 쓰기 많다면 성능/락 대기 발생 가능 → 소규모/단일 인스턴스에 적합
  • sqlite3.Row로 컬럼명 접근을 표준화하면 코드 가독성↑
  • 외래키 쓸 때 PRAGMA foreign_keys = ON 잊지 않기

라이브러리 예제 문제: 

점프 투 파이썬 - 라이브러리 예제 편 7장 47번 문제
점프 투 파이썬 - 라이브러리 예제 편 7장 47번 문제 풀이 및 결과1

47번은 SQLite 도구까지 학습한다.

DB Broswer for SQLite’를 내려받아 설치(바로가기) → 실행

맥 OS용 GUI 도구

아 졸라 헷갈린다.

풀이에 내용이 계속 추가되길래 단순하게 풀이 방식을 올렸는데 거기서 끝이 아니였다. 

결국은 저자의 의도대로 코드를 작성해야한다.

 

점프 투 파이썬 - 라이브러리 예제 편 7장 47번 문제 풀이 및 결과2

 

다시 SQLite로 돌아와서  Open Database  blog.db 선택

점프 투 파이썬 - 라이브러리 예제 편 7장 47번 - SQLite 활용1

 

애플바 상단의 Edit → modify Table → id 의 AI(Auto Increment) 선택 → PRIMARY KEY 우측에 ("id" AUTOINCREMENT) 추가됨

점프 투 파이썬 - 라이브러리 예제 편 7장 47번 - SQLite 활용2

 

SQLite 상단의 Execute SQL 선택 → 데이터 입력 

INSERT INTO blog (subject, content, date)
VALUES ('자동 증가', 'id값이 자동 증가되어 입력됩니다.', '20190831'); ▶ 실행(Execute all) 버튼선택 → 하단의 메세지 확인

점프 투 파이썬 - 라이브러리 예제 편 7장 47번 - SQLite 활용3

 

상단 탭 Browse Data 선택 → blog 내용 확인 → id 자동 증가 + 데이터 저장 완료

점프 투 파이썬 - 라이브러리 예제 편 7장 47번 - SQLite 활용4

 

나머지는 예시에서 다뤘으니.. 오늘은 여기까지하자.

이렇게 지겨운 내용은 처음이다.

아오 멀미나.