pharmacy-pos-qr-system/docs/TROUBLESHOOTING-SQLITE-CONNECTION.md

3.4 KiB

트러블슈팅: SQLite "I/O operation on closed file" 에러

발생일

2026-02-27

증상

  • 관리자 페이지에서 회원 검색 시 500 에러 발생
  • 에러 메시지: 조회 실패: I/O operation on closed file.
  • 서버 로그에는 200 OK로 찍히지만 응답 body에 에러 포함

원인

1. SQLite 싱글톤 연결 문제

Flask의 멀티스레드 환경에서 db_manager.get_sqlite_connection()싱글톤 연결을 반환. 한 요청에서 연결을 닫으면 다른 요청에서 "closed file" 에러 발생.

문제 코드:

conn = db_manager.get_sqlite_connection()  # 싱글톤 연결 반환
cursor = conn.cursor()
# ... 작업 ...
# finally에서 conn.close() 호출 시 다른 요청에 영향

2. 존재하지 않는 테이블 참조

product_category_mapping 테이블이 DB에 없는데 쿼리 시도 → SQLite 에러 발생

해결 방법

1. 새 연결 사용 + finally에서 close

conn = None
try:
    conn = db_manager.get_sqlite_connection(new_connection=True)  # 새 연결!
    cursor = conn.cursor()
    # ... 작업 ...
except Exception as e:
    logging.error(f"에러: {e}")
    return jsonify({'success': False, 'message': str(e)}), 500
finally:
    if conn:
        try:
            conn.close()
        except:
            pass

2. 없는 테이블 조회 시 예외 처리

try:
    cursor.execute("SELECT * FROM product_category_mapping WHERE ...")
    # ...
except Exception:
    pass  # 테이블 없으면 무시

수정된 API 목록

API 파일 커밋
/api/members/search app.py 87a56d0
/api/members/history/<id> app.py 87a56d0
/admin/search/user app.py 1414bb1
/admin/search/product app.py 1414bb1
/admin/user/<id> app.py 4691d65, 94a8df6

dbsetup.py 수정사항

get_sqlite_connection() 메서드에 new_connection 파라미터 추가:

def get_sqlite_connection(self, new_connection=False):
    """
    SQLite 연결 반환
    - new_connection=True: 새 연결 생성 (API 요청마다 독립적 연결 필요시)
    - new_connection=False: 기존 싱글톤 연결 반환 (기본값, 하위 호환성)
    """
    if new_connection:
        conn = sqlite3.connect(self.sqlite_path, check_same_thread=False)
        conn.row_factory = sqlite3.Row
        return conn
    
    # 기존 싱글톤 로직
    if self._sqlite_conn is None:
        self._sqlite_conn = sqlite3.connect(self.sqlite_path, check_same_thread=False)
        self._sqlite_conn.row_factory = sqlite3.Row
    return self._sqlite_conn

추가 수정사항

CDN 차단 문제

Edge 브라우저의 Tracking Prevention이 cdnjs.cloudflare.com 차단 → lottie.min.js를 로컬 파일(/static/js/lottie.min.js)로 변경

커밋: 866d10f

교훈

  1. Flask 멀티스레드 환경에서 SQLite 연결은 요청마다 새로 생성해야 안전
  2. API 응답은 HTTP 상태코드로 판단하지 말고 body의 success 필드 확인
  3. 없을 수 있는 테이블/컬럼 조회는 try-except로 감싸기
  4. CDN 의존성은 로컬 fallback 준비

관련 커밋

94a8df6 fix: product_category_mapping 테이블 없을 때 에러 무시
4691d65 fix: /admin/user/<id> SQLite 연결 에러 해결
866d10f fix: lottie CDN을 로컬 파일로 변경
1414bb1 fix: /admin 사이드바 검색 SQLite 연결 에러 해결
87a56d0 fix: /api/members/* SQLite 연결 에러 해결