기록
Python으로 SQL 간단히 이용해보기 및 Drizzle이란
als982001
2025. 4. 26. 17:08
1. 개요
이번에는 python과 javascript로 SQL을 이용하는 방법을 간단히 배워보았다. 그래서 이를 기록해보려고 한다.
2. Python에서 SQLite 사용하기
2-1. 기본 흐름
# 1. DB 연결
conn = sqlite3.connect("users.db")
# 2. 커서(Cursor) 가져오기
cur = conn.cursor()
# 3. 쿼리 실행
cur.execute("SELECT * FROM users")
# 4. 결과 가져오기
res = cur.execute("SELECT * FROM users")
all_movies = res.fetchall()
# 5. DB 반영
conn.commit()
# 6. DB 연결 종료
conn.close()
- 결과 가져오기
- fetchall(): 결과를 전부 가져옴
- fetchmany(n): 결과를 n개 가져옴
- fetchone(): 결과를 한 개 가져옴
2-2. 테이블 생성 및 데이터 삽입
2-2-1. 테이블 생성
cur.execute("""
CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL
);
""")
2-2-2. 여러 데이터 삽입
# 리스트 형태로 다중 삽입
data = [("lanna", 567), ("bora", 123)]
cur.executemany("INSERT INTO users (username, password) VALUES (?, ?)", data)
# 딕셔너리 형태로 다중 삽입
data = [{"name": "max", "password": 123}]
cur.executemany("INSERT INTO users (username, password) VALUES (:name, :password)", data)
2-3. SQL Injection 주의
def i_change_password(username, new_password):
cur.execute(f"UPDATE users SET password = '{new_password}' WHERE username = '{username}'")
이처럼 문자열을 직접 끼워 넣는 방식은 SQL Injdection 공격 가능성이 있다.
def s_change_password(username, new_password):
cur.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, username))
?를 사용해서 값을 따로 전달하는 것으로 방지할 수 있다.
2-4. Redis를 이용한 캐싱
자주 조회되는 데이터는 매번 DB를 조회하지 않고 Redis에 캐싱하는 것으로 빠르게 가져올 수 있다.
# Redis 연결
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
# 캐싱 로직
def make_expensive_query():
redis_key = "director:movies"
cached_results = r.get(redis_key)
if cached_results:
print("cache hit")
return json.loads(cached_results)
else:
print("cache miss")
res = cur.execute("SELECT COUNT(*), director FROM movies GROUP BY director;")
all_rows = res.fetchall()
r.set(redis_key, json.dumps(all_rows), ex=20) # 캐시 만료 시간 설정 (ex=20초)
return all_rows
- 먼저 Redis에 캐시된 데이터가 있는지 확인
- 없으면 DB에서 쿼리 후 Redis에 저장
- 다음에는 Redis에서 바로 꺼내서 사용
2-5. fetch 관련 주의 사항
- fetchall()을 먼저 쓰면 커서가 끝까지 이동하기에, 이후 fetchmany(), fetchone()을 써도 가져올 데이터가 없다.
- 한 번 가져오면 커서는 앞으로 못 되돌린다. (필요하면 쿼리를 다시 실행해야 함)
2-6. 간단 정리
항목 | 배운 내용 요약 |
SQLite 연결 | connect(), cursor(), execute(), fetch(), commit(), close() |
데이터 삽입 | executemany() 사용 가능 |
SQL Injdection 방지 | ? 파라미터 바인딩 필수 |
Redis 캐싱 | DB 결과를 Redis에 저장하고 재사용 |
커서 주의사항 | fetchall() 이후에는 커서가 끝난다 |
캐시 만료 설정 | set(key, value, ex=초)로 가능 |
2-7. cursor란
cursor(커서)는 DB에 쿼리를 보내고, 결과를 가져오고, 조작하는 역할을 담당하는 객체이다. 2-1 코드에서 conn은 DB와 연결만 하는 것이라면, 커서는 DB에 명령을 날리고 결과를 주고 받는 것을 담당한다.
3. Drizzle
Drizzle은 TypeScript 기반 ORM(Object-Relational Mapping) 라이브러리로, 데이터베이스를 타입 안정성 있게 다룰 수 있도록 도와주는 도구라고 할 수 있다.
- SQL을 직접 작성하는 대신, 코드로 테이블, 쿼리, 관계를 정의하고 조작할 수 있다.
- 기존 ORM(예: Prisma, Sequelize)보다 훨씬 가볍고 직관적이다.
- SQL을 타입 안전하게 만드는 방향에 가깝다.
3-1. Drizzle 특징
특징 | 설명 |
타입 안전성 | TypeScript 타입을 활용해서 쿼리 작성 시 에러를 방지 |
투명한 SQL 생성 | 실제 생성되는 SQL을 쉽게 볼 수 있음 |
얇은 추상화 | SQL을 숨기지 않고, 필요한 경우 직접 제어 가능 |
경량화 | 설치 용량이 가볍고, 실행 속도가 빠름 |
다양한 DB 지원 | PostgreSQL, MySQL, SQLite 등을 지원 |
마이그레이션 지원 | 스키마 변경 관리 도구(drizzle-kit) 제공 |
3-2. Drizzle 사용 흐름
- DB 연결 생성
- 테이블 스키마 정의
- 쿼리 작성 및 실행
- 타입 안정성 유지하면서 결과 활용