PostgreSQL 기준으로 작성되었습니다.
conn은 데이터베이스와의 연결 자체를 담당하는 객체입니다.
self.conn = connection.Connection().postgresql_connection()
DB와의 통로 역할만 하며, SQL을 직접 실행하는 기능은 없습니다.
cursor는 conn을 통해 실제 SQL을 실행하는 객체입니다.
conn = 은행 입장 (문)
cursor = 창구 직원 (실제 업무 처리)
with self.conn.cursor() as cursor:
cursor.execute("SELECT * FROM users")
result = cursor.fetchall()
| 메서드 | 설명 |
|---|---|
cursor.execute(sql) | SQL 1개 실행 |
cursor.executemany(sql, list) | SQL 여러 개 실행 |
cursor.fetchone() | 결과 1행 반환 |
cursor.fetchmany(n) | 결과 n행 반환 |
cursor.fetchall() | 결과 전체 반환 |
cursor.close() | 커서 닫기 |
self.conn.execute("SELECT * FROM users") # ❌ 불가능
self.conn.cursor().execute("SELECT * FROM users") # ✅ 가능
cursor는 SQL 실행 후 결과를 내부에 임시 저장합니다.
DB 서버 cursor 내부
┌─────────┐ ┌──────────────┐
│ 100만 행 │ →SQL→ │ 결과 임시 저장 │ → fetchone()으로 하나씩
└─────────┘ └──────────────┘
cursor.execute("SELECT * FROM users")
cursor.fetchone() # 1행 반환
cursor.fetchmany(100) # 100행 반환
cursor.fetchall() # 전체 반환
하나의 conn에서 여러 cursor를 동시에 운용할 수 있습니다.
cursor1 = self.conn.cursor()
cursor2 = self.conn.cursor()
cursor1.execute("SELECT * FROM users") # 독립 실행
cursor2.execute("SELECT * FROM orders") # 서로 영향 없음
result1 = cursor1.fetchall()
result2 = cursor2.fetchall()
cursor 단위로 작업하고, conn으로 commit / rollback 합니다.
with self.conn.cursor() as cursor:
try:
cursor.execute("INSERT INTO users VALUES (%s)", ("Alice",))
cursor.execute("INSERT INTO orders VALUES (%s)", (1,))
self.conn.commit() # 둘 다 성공 시 저장
except:
self.conn.rollback() # 하나라도 실패 시 전부 취소
한 번에 모든 데이터를 가져오지 않고 필요한 만큼만 가져올 수 있습니다.
cursor.execute("SELECT * FROM users") # 100만 행 조회
cursor.fetchone() # ✅ 1행만 메모리에 올림
cursor.fetchmany(100) # ✅ 100행만 메모리에 올림
cursor.fetchall() # ⚠️ 100만 행 전부 메모리에 올림
with 블록이 끝나면 자동으로 cursor.close()가 호출됩니다.
# with 없이
cursor = self.conn.cursor()
cursor.execute("SELECT * FROM users")
result = cursor.fetchall()
cursor.close() # 직접 닫아야 함 ⚠️
# with 사용
with self.conn.cursor() as cursor:
cursor.execute("SELECT * FROM users")
result = cursor.fetchall()
# 자동으로 cursor.close() 호출 ✅
cursor와 conn의 with 종료 시 동작이 다릅니다.
| 구문 | 종료 시 |
|---|---|
with self.conn.cursor() as cursor | cursor.close() 만 호출 |
with self.conn as conn | conn.close() 호출 ⚠️ |
self.conn = connection.Connection().postgresql_connection()
with self.conn.cursor() as cursor:
cursor.execute("SELECT * FROM users")
# cursor만 닫힘, conn은 유지 ✅
print(self.conn.closed) # 0 = 열림 ✅
self.conn을 with로 직접 감싸면 블록 종료 시 연결이 닫혀 재사용이 불가능합니다.
with self.conn as conn: # ⚠️ 블록 끝나면 self.conn이 닫힘
pass
# 이후 self.conn 재사용 불가! ❌
self.conn을 여러 메서드에서 재사용하는 경우, cursor만 with로 관리하는 것이 좋습니다.
class UserService:
def __init__(self):
self.conn = connection.Connection().postgresql_connection()
def get_users(self):
with self.conn.cursor() as cursor: # cursor만 with로 관리
cursor.execute("SELECT * FROM users")
return cursor.fetchall()
# cursor 닫힘, conn 유지 → 재사용 가능 ✅
def insert_user(self, name):
with self.conn.cursor() as cursor: # 같은 conn 재사용
cursor.execute("INSERT INTO users VALUES (%s)", (name,))
self.conn.commit()
def close(self):
self.conn.close() # 모든 작업 종료 후 명시적으로 닫기
conn 생성
│
├── cursor 생성 (with)
│ ├── execute() : SQL 실행
│ ├── fetch() : 결과 가져오기
│ └── close() : 자동 호출 (with 종료 시)
│
├── commit() / rollback() : conn이 담당
│
└── conn.close() : 모든 작업 종료 후 명시적 호출
| conn | cursor | |
|---|---|---|
| 역할 | DB 연결 유지 | SQL 실행 |
| 생성 | 한 번만 | 작업마다 |
| with 종료 시 | 연결 닫힘 ⚠️ | cursor만 닫힘 ✅ |
| SQL 실행 | ❌ | ✅ |