diff --git a/app/modules/cupang/itemcode.py b/app/modules/cupang/itemcode.py index d5520d1..41385b3 100644 --- a/app/modules/cupang/itemcode.py +++ b/app/modules/cupang/itemcode.py @@ -28,10 +28,13 @@ product_name_snapshot 만 보존한다. from __future__ import annotations +import logging import os import re from typing import Any +logger = logging.getLogger("cupang.itemcode") + # 안전한 SQL 식별자(테이블/컬럼)만 허용 — 인젝션 방지. _IDENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_.]*$") @@ -55,6 +58,7 @@ class ItemcodeReader: self._sql: str | None = None self.enabled = False self.reason = "" + self.last_error = "" # 마지막 조회 오류(진단용, 비밀값 없음) self._configure() def _configure(self) -> None: @@ -130,7 +134,10 @@ class ItemcodeReader: rows = conn.execute( self._sql, {"q": like, "limit": int(limit)} ).fetchall() - except Exception: # noqa: BLE001 — 비밀값 노출 없이 빈 결과. 상세는 서버 로그. + self.last_error = "" + except Exception as exc: # noqa: BLE001 — 모듈을 죽이지 않음. 원인은 로그 + last_error. + self.last_error = f"{type(exc).__name__}: {exc}" + logger.exception("itemcode 조회 실패 (SQL/스키마 확인 필요)") return [] out: list[dict[str, Any]] = [] for r in rows: diff --git a/app/modules/cupang/router.py b/app/modules/cupang/router.py index d97b85b..aeb5a04 100644 --- a/app/modules/cupang/router.py +++ b/app/modules/cupang/router.py @@ -604,11 +604,14 @@ async def product_all( ) -> JSONResponse: """itemcode_db 전체 상품 목록(낱개+세트). 설정 화면 왼쪽 리스트 소스.""" reader = _itemcode(request) + results = reader.list_all() if reader else [] return JSONResponse( { "enabled": bool(reader and reader.enabled), "reason": (reader.reason if reader else "itemcode 리더 미초기화"), - "results": reader.list_all() if reader else [], + "error": (getattr(reader, "last_error", "") if reader else ""), + "count": len(results), + "results": results, } ) diff --git a/app/modules/cupang/templates/cupang/products.html b/app/modules/cupang/templates/cupang/products.html index 88a64a6..2331745 100644 --- a/app/modules/cupang/templates/cupang/products.html +++ b/app/modules/cupang/templates/cupang/products.html @@ -139,7 +139,14 @@ .then(function (r) { return r.json(); }) .then(function (data) { all = (data && data.results) || []; - if (!all.length) { listBox.innerHTML = '

itemcode_db 결과 없음

'; return; } + if (!all.length) { + var msg = "itemcode_db 결과 없음"; + if (data && data.error) { msg += " — 조회 오류: " + esc(data.error); } + else if (data && !data.enabled && data.reason) { msg += " — " + esc(data.reason); } + else { msg += " (테이블이 비었거나 검색 SQL 조건 불일치)"; } + listBox.innerHTML = '

' + msg + '

'; + return; + } render(); }) .catch(function () { listBox.innerHTML = '

목록 로드 실패

'; });