PyMySQL InterfaceError: (0, ”) 원인과 해결 — Connection Leak 패턴
PyMySQL InterfaceError: (0, ”) 원인과 해결 — Connection Leak 패턴
이 글은 2021-03-04 Python 커뮤니티 도움 요청을 계기로 작성한 원본을 2026년 현재 시점에서 재정리한 것입니다. 코드와 절차는 원본 그대로이며, 원본에 없는 보충 분석은
[분석]블록으로 별도 표기합니다.
2021년, Python 커뮤니티에서 도움 요청이 왔었습니다. API에서 받은 데이터를 DB로 보내주는 코드를 짰는데 아래 에러가 계속 발생한다는 것이었습니다.
pymysql.err.InterfaceError: (0, '')
저도 당시 많이 알지는 못했지만, Python ↔ MySQL 연결 흐름을 함께 짚어가다 보니 원인이 한 줄로 정리됐습니다 — DB Connection을 닫지 않아 발생하는 Connection Leak. 이 글에서는 에러 재현 → 최소 동작 코드 확인 → Connection Leak 원인 진단 → 해결 패턴(try…finally + with 문) → 2026년에 추가로 챙길 것 순서로 정리합니다.
그 과정에서 먼저 PyMySQL 연결 흐름 전체를 같이 짚어봤는데, 순서를 정리해두겠습니다.
1. 에러 재현 — 어떤 상황에서 이 에러가 발생하나
PyMySQL로 MySQL에 접속하는 교과서적인 흐름은 7단계입니다.
pymysql모듈 importpymysql.connect()— host, user, password, db 등 파라미터 지정- Connection 객체에서
cursor()호출 → Cursor 객체 획득 - Cursor 객체의
execute()— SQL 문장을 DB 서버에 전송 fetchall()/fetchone()/fetchmany()— 결과 수신- DML(INSERT / UPDATE / DELETE) 실행 후 Connection의
commit()— 확정 갱신 - Connection의
close()— 연결 종료
7단계를 순서대로 밟으면 정상 동작합니다. 문제는 코드 중간에 예외가 발생하거나, 마지막 close()를 빠뜨린 채 흐름이 끝나는 상황입니다. 아래 코드가 전형적인 위험 패턴입니다.
⚠️ 아래 자격 증명은 모두 더미값입니다. 본인 환경 값으로 바꾸세요.
import pymysql
conn = pymysql.connect(host='localhost', user='tester', password='7890',
db='testdb', charset='utf8')
curs = conn.cursor()
sql = """insert into customer(name, category, region)
values (%s, %s, %s)"""
curs.execute(sql, ('홍길동', 1, '서울'))
conn.commit()
# ❌ conn.close() 누락 → Connection이 열린 채로 남는다
close()가 빠진 채로 반복 실행되면 연결이 쌓이고, 결국 새 연결을 열 수 없는 상태에 도달합니다.

2. 최소 동작 코드 — connect → execute → commit → close
원인 진단 전에, 비교 기준이 되는 정상 흐름 코드를 먼저 확인합니다.
⚠️ 아래 자격 증명은 모두 더미값입니다. 본인 환경 값으로 반드시 바꾸세요. (Python 3.6+ / PyMySQL 0.9.0+ / MySQL 5.7+ 기준. 환경 버전은
pip show pymysql로 확인하세요.)
import pymysql
conn = pymysql.connect(host='localhost', user='tester', password='7890',
db='testdb', charset='utf8')
curs = conn.cursor() # 1. DB의 문을 열다
sql = """insert into customer(name,category,region)
values (%s, %s, %s)"""
curs.execute(sql, ('홍길동', 1, '서울')) # 2. 작업을 한다.
curs.execute(sql, ('이연수', 2, '서울')) # 2. 작업을 한다.
conn.commit() # 3. 저장한다.
conn.close() # 4. DB의 문을 닫는다.
코드가 정상 흐름으로 끝날 때는 마지막 conn.close()가 실행되므로 문제없습니다. 문제는 중간 어딘가에서 예외가 발생해 close() 줄에 도달하지 못할 때입니다.
3. 왜 InterfaceError가 발생하나 — Connection Leak
핵심 진단은 한 줄입니다. DB의 문을 닫지 않아서 발생한 에러이며, 이 현상을 Connection Leak이라 부릅니다.
“SQL Connection을 열고 프로그램 중간에서 에러가 발생하면, Connection은 그대로 열려 있는 상태로 있을 수 있다. 이렇게 오픈되어 있는 Connection이 증가하면, 나중에 새로운 Connection을 오픈할 수 없게 되는데, 이를 Connection Leak이라 부른다. 이러한 Connection Leak을 막기 위하여 … try… finally 블록을 사용하여 finally에서 항상 Connection을 Close 해야한다.”
2026년 관점에서 조금 더 풀어보면 이렇습니다.
[분석] 한 프로세스에서
connect()만 반복하고close()를 호출하지 않으면, MySQL 서버 측에서 허용하는 동시 연결 수(max_connections)의 한계에 결국 도달하게 됩니다. 이 한계를 넘으면 새 연결 요청이 거부되면서InterfaceError로 이어집니다.max_connections기본값은 MySQL 배포판·버전에 따라 다르므로 수치를 단정하기보다는, 연결을 열었으면 반드시 닫는 습관을 코드 구조로 보장하는 편이 중요합니다.
4. 해결 패턴 — try…finally + with 문
예외가 발생하더라도 conn.close()가 반드시 실행되도록 보장하려면 try...finally 블록을 사용합니다. Python 공식 문서에 따르면 finally 절은 예외 발생 여부와 무관하게 항상 실행됩니다(docs.python.org — try 문).
⚠️ 아래 자격 증명도 더미값입니다. 본인 환경 값으로 반드시 바꾸세요.
아래 예시는 INSERT와 SELECT 두 동작을 하나의 try...finally 블록 안에 묶어 conn.close()를 구조적으로 보장하는 패턴입니다.
import pymysql
conn = pymysql.connect(host='localhost', user='tester', password='7890',
db='testdb', charset='utf8')
try:
# INSERT
with conn.cursor() as curs:
sql = "insert into customer(name,category,region) values (%s, %s, %s)"
curs.execute(sql, ('이광수', 1, '서울'))
conn.commit()
# SELECT
with conn.cursor() as curs:
sql = "select * FROM customer"
curs.execute(sql)
rs = curs.fetchall()
for row in rs:
print(row)
finally:
conn.close()
핵심 포인트 3가지입니다.
finally는 예외 발생 여부와 무관하게 실행됩니다 —try블록 어디서 예외가 터져도finally의conn.close()는 반드시 호출됩니다. Connection Leak을 구조적으로 차단하는 방식입니다.with conn.cursor() as curs:— 블록을 벗어나는 순간 커서 리소스가 자동으로 해제됩니다. 커서를 따로 닫지 않아도 됩니다.- INSERT와 SELECT를 별도 커서로 분리한 이유 — 두 동작의 커서 라이프사이클을 명확히 분리해, 한 커서의 결과 세트가 다른 동작에 영향을 주지 않도록 하기 위해서입니다.
5. 2026년 회고 — 지금이라면 추가로 챙길 것
이 섹션은 2021년 원본에 없는 내용입니다. §1~§4가 원문 코드와 팩트를 다룬다면, 여기서는 2026년 현재 시점에서 돌아보며 드는 분석 의견을 덧붙입니다.
with 컨텍스트 매니저로 Connection까지 자동 close
with conn.cursor() as curs: 패턴은 원본에서도 이미 사용했습니다. 한 발 더 나아가, Connection 자체도 컨텍스트 매니저로 사용할 수 있습니다. PyMySQL 1.0.0 이상에서는 with pymysql.connect(...) as conn: 형태가 지원되어 finally: conn.close() 없이도 블록 종료 시 자동으로 닫힙니다. 다만 이 패턴을 도입하기 전에 반드시 pip show pymysql로 본인 환경의 버전을 먼저 확인해두는 것이 좋습니다.
연결 풀(Connection Pool)·ORM은 이 글 범위 밖
여러 요청을 동시에 처리하는 환경에서는 매번 connect()를 새로 호출하는 대신, 연결을 미리 만들어 재사용하는 Connection Pool 방식이 쓰입니다. SQLAlchemy·DBUtils 같은 라이브러리가 이 역할을 담당합니다. 그 내용은 이 글의 범위 밖이므로 키워드만 남겨둡니다.
마치며
DB의 문을 열고 작업 후에 저장하고 닫으세요. 2021년 답글에서 핵심은 이 한 줄이었고, 2026년 지금도 변하지 않았습니다. try...finally 패턴 몇 줄로 conn.close()를 구조적으로 보장해두면, 예외 흐름에서도 Connection이 방치되지 않습니다. PyMySQL을 처음 다루신다면, 연결을 여는 코드를 작성하는 순간부터 닫는 코드도 함께 써두는 습관을 권합니다.
참고 출처