테이블 스캐너 (tablescanner)

고아 테이블 탐지, DB 대시보드/최적화, 비대 로그 정리, DB 백업, 테이블 점검을 한 화면에서 관리하는 라이믹스 관리자 도구

현재 버전 v1.5.7 2026-06-17 ~ 2026-07-27 GPLv2
v1.0.0 첫 출시(2026-06-17)부터 v1.5.7까지 약 40일 동안 8번의 버전업이 있었습니다. 처음엔 고아 테이블 스캔·DB 대시보드·최적화·로그 정리·점검 5개 탭짜리 단순한 관리 도구였지만, "새벽 시간대 자동 백업이 실행 안 된다", "방문자가 없어도 정확한 시각에 실행되면 좋겠다" 같은 실사용 요구를 거치며 이중 경로 DB 백업(mysqldump/PHP 순수 덤프 자동 전환) + Gmail 첨부 발송 + Rhymix Queue 비동기 스케줄 탭으로 성장했습니다. 또한 rhymix.org를 통해 실제 사용자(해링밀턴 님, 오이와당근 님)가 남긴 리포트 두 건이 각각 v1.5.6·v1.5.7의 직접적인 계기가 됐습니다 — 둘 다 화면 겉모습만 봐서는 원인을 짐작하기 어려운, 서버 환경 차이에서 비롯된 실제 결함이었습니다.

변화 타임라인

v1.5.72026-07-27 · 실사용 버그 리포트
버그 수정: DB 대시보드 테이블명/엔진 빈칸 표시
오이와당근 님이 [DB 대시보드]에서 테이블명·엔진 칸이 비어 나오고 행 수는 항상 0으로 표시된다고 리포트. 원인은 information_schema.tables에서 별칭(AS) 없이 그대로 가져온 table_name/table_rows/engine 컬럼만 문제였고, AS size_mb처럼 명시적으로 별칭을 준 컬럼은 항상 정상이었던 것으로 확인 — MySQL/MariaDB 서버 버전·설정에 따라 별칭 없는 information_schema 컬럼명이 다른 대소문자로 반환될 수 있어 PDO 접근이 어긋난 것. 같은 방식으로 테이블 목록을 얻는 DB 백업 기능도 조용히 빈 덤프를 만들었을 가능성이 있어 함께 수정.
v1.5.62026-07-26 · 실사용 버그 리포트
버그 수정: "테이블 점검 실행" 버튼이 오류로 죽는 문제
해링밀턴 님 리포트: [테이블 점검] 실행 버튼을 누르면 Call to a member function closeCursor() on bool 오류로 점검이 중단됨. 테이블별로 CHECK TABLE을 순차 실행하는 루프에서 특정 테이블 쿼리가 실패하면 코어 DB::query()가 내부 상태를 비정상적으로 남기는 경우가 있는 것으로 추정 — 정확한 코어 버전별 재현은 확인 못했지만, 테이블 하나의 실패가 전체 점검을 중단시켜서는 안 되므로 테이블별 호출을 try/catch로 감싸 나머지 테이블 점검은 계속 진행하도록 수정.
v1.5.52026-07-25 · 최종 검토
보안 강화: XE 부트스트랩 가드 누락 + 크론 키 비교 방식
PHP 파일 5개 전부 상단에 if(!defined('__XE__')) exit(); 가드가 없었던 것을 발견 — 다른 BSplus 모듈과 달리 이 모듈만 누락. 라이믹스 루트 .htaccessmodules/ 안의 일반 PHP 직접 실행까지는 막지 않는 구조라 전부 추가. 외부 크론 엔드포인트의 cron_key 비교도 !==에서 타이밍 공격에 안전한 hash_equals()로 교체.
v1.5.42026-07-25 · 실사용 버그 리포트
버그 수정: 애드온 소유 테이블이 "고아 테이블"로 오판되어 DROP 가능하던 문제
"고아 테이블 스캐너"가 modules/*/schemas/*.xml만 훑어 소유 모듈을 판단하는 구조라, 애드온(addons/)이 만든 테이블은 항상 "고아"로 잘못 분류됨. 실사용 중 lef_moved_files(구버전 애드온 lua_external_file 소유 — bsplus_external_storage의 이동 기록 가져오기 기능이 아직 참조)가 고아 테이블로 표시되어 DROP 버튼이 노출되는 것을 발견. 알려진 애드온 소유 테이블 예외 목록을 추가해 고아 판정에서 제외하고 별도 "애드온 소유 테이블" 섹션으로 분리 표시(삭제 버튼 없음).
v1.5.32026-06-20 · 신규 기능
Rhymix Queue 비동기 연동 + 외부 크론 엔드포인트 + 잠금 없는 백업 옵션
기존 트리거 방식 자동 백업은 방문자가 있어야만 실행되어 새벽 시간대 백업이 누락되는 문제 — Queue::addTaskAtInterval() 등록 + 외부 webcron(cron-job.org 등) 분당 1회 호출로 방문자 없이도 정확한 시각에 실행. 고유 키 기반 외부 크론 URL(procTablescannerCronRun) 신규 추가, 재발급 버튼 포함. mysqldump에 --single-transaction/--skip-lock-tables 옵션을 추가해 백업 중 서비스 락 대기 리스크 제거. 백업 경로가 files/attach/ 하위면 외부 노출 위험을 빨간 경고로 안내.
v1.5.22026-06-20 · 버그 수정
예약 자동 백업이 설정 시각에 실행되지 않던 문제 + 이메일/목록 UX 개선
트리거 조건이 "현재 시각 == 설정 시각" 정확히 일치 방식이라, 설정 시각(예: 새벽 3시)에 방문자가 없으면 영원히 실행되지 않던 구조 — "오늘 예약 시각이 지났고 + 오늘 아직 백업 안 했으면 실행"으로 변경해 그 이후 첫 방문 시 자동 실행되도록 수정. 백업 파일 목록에 이메일 발송 결과(발송/실패/용량초과)와 수동/예약 구분 뱃지 추가, 이메일 동기 발송으로 전환, 첨부 최대 크기 50MB → 2048MB로 확대.
v1.5.02026-06-19 · 신규 기능
DB 백업 탭 신설 + "정상 테이블 120개 미생성 오판" 치명적 버그 수정
즉시 백업, 이중 저장 경로(기본+추가 드라이브), mysqldump/PHP 순수 덤프 자동 전환, gzip 압축, 자동 백업 스케줄, 보관 기간 정리, 이메일 첨부 발송을 갖춘 백업 탭 신설. 같은 버전에서 발견된 치명적 버그: 코어 DB::query()addPrefixes()FROM information_schema.tablesFROM xe_information_schema.tables로 잘못 변환해 쿼리가 실패, 스키마 정의된 정상 테이블 120개 전부가 "미생성"으로 오판되던 문제 — SQL 키워드를 소문자로 작성해 대문자만 매칭하는 addPrefixes()를 우회하도록 수정.
v1.0.02026-06-17 · 첫 출시
고아 테이블 스캐너 / DB 대시보드 / 최적화 / 비대 로그 정리 / 테이블 점검
5개 탭으로 첫 출시. 스키마 기반 고아 테이블 탐지 + DROP, 라이믹스 PDO API 기반 구현, 비대 로그 자동 정리 스케줄(트리거 방식).

전체 패치리스트

v1.5.72026-07-27
버그 수정: DB 대시보드 테이블명/엔진 빈칸 표시 (오이와당근 님 실사용 리포트)
  • 증상: [DB 대시보드] 탭 "크기 상위 10개 테이블"/"파편화 상위 5개 테이블" 목록의 테이블명·엔진 칸이 비어 나오고 행 수는 항상 0. 크기(MB)/낭비 공간/낭비 비율 등 나머지 컬럼은 정상 표시.
  • 원인: information_schema.tables에서 별칭 없이 그대로 셀렉트한 table_name/table_rows/engine 컬럼만 문제였고, AS size_mb처럼 명시적으로 별칭을 준 컬럼은 항상 정상 — 서버 버전·설정에 따라 별칭 없는 information_schema 컬럼명이 다른 대소문자로 반환될 수 있어 PDO::FETCH_OBJ/배열 키 접근이 대소문자 불일치로 비었던 것.
  • model.php/admin.controller.php/class.php 전체 8곳에서 table_name/table_rows/engine에 자기 별칭(table_name as table_name 등)을 명시적으로 추가 — 서버/버전에 관계없이 항상 쿼리에 작성한 그대로의 컬럼명이 반환되도록 함.
  • 같은 방식으로 테이블 목록을 얻는 DB 백업 기능(runBackup())도 같은 서버 환경에서는 조용히 빈 덤프를 만들었을 가능성이 있어 함께 수정됨.
v1.5.62026-07-26
버그 수정: "테이블 점검 실행" 버튼이 오류로 죽는 문제 (해링밀턴 님 실사용 리포트)
  • 증상: [테이블 점검] 탭에서 실행 버튼을 누르면 Call to a member function closeCursor() on bool 오류가 발생하며 점검이 실패함.
  • 원인 추정: 테이블별로 CHECK TABLE을 순차 실행하는 루프에서, 특정 테이블(손상됐거나 접근 권한/엔진 문제가 있는 테이블)에 대한 쿼리가 실패하면 코어 DB::query()가 내부 상태를 비정상적으로 남기는 경우가 있어 다음 테이블 처리 시 죽는 것으로 보임. 정확한 코어 버전별 재현은 확인 못했지만, 테이블 하나의 실패가 전체 점검을 중단시켜서는 안 됨.
  • 테이블별 CHECK TABLE 호출을 try/catch로 감싸 특정 테이블에서 예외가 나도 그 테이블만 "오류" 결과로 기록하고 나머지 테이블 점검은 계속 진행하도록 변경.
v1.5.52026-07-25
최종 검토: 보안 강화 2건
  • PHP 파일 5개 전부 상단에 if(!defined('__XE__')) exit(); 가드 누락 — 다른 BSplus 모듈들과 달리 이 모듈만 없었음. 라이믹스 루트 .htaccessmodules/ 안의 일반 .php 직접 실행까지는 막아주지 않는 구조라(bsplus_external_storage vendor 이슈 때 확인된 것과 동일한 사정) 부트스트랩 없이 직접 실행될 위험을 막기 위해 5개 파일 모두 추가.
  • procTablescannerCronRun(외부 스케줄러 전용 크론 엔드포인트)의 cron_key 비교를 !==에서 hash_equals()로 교체 — 타이밍 공격에 안전한 상수 시간 비교로 강화. 키 생성 자체는 이미 random_bytes(16)으로 안전했음.
  • 참고: review_module의 "액션 파일 불일치" 경고 16건은 admin 전용 액션을 admin 파일에 분리하는 라이믹스의 정확한 관례로, 실제 문제 아닌 것으로 확인(known false-positive).
v1.5.42026-07-25
버그 수정: 애드온 소유 테이블이 "고아 테이블"로 오판되어 DROP 가능하던 문제
  • 문제: "고아 테이블 스캐너"가 modules/*/schemas/*.xml만 훑어서 테이블 소유 모듈을 판단하는데, 애드온(addons/)이 만든 테이블은 이 방식으로는 절대 찾을 수 없음 — 실제로 주인이 있는데도 "schema 없음, 모듈 불명 고아 테이블"로 잘못 분류되어 DROP 버튼이 노출됨.
  • 실사용 중 발견된 사례: lef_moved_files(구버전 애드온 lua_external_file이 만든 이동 기록 테이블)가 고아 테이블로 표시됨. bsplus_external_storage 모듈의 "구버전 애드온 이동 기록 가져오기" 기능이 아직 이 테이블을 참조하므로, 여기서 DROP하면 예전 이동 기록을 영영 가져올 수 없게 됨.
  • scanTables()에 알려진 애드온 소유 테이블 예외 목록($addon_tables) 추가 — 고아 판정에서 제외하고 새 "애드온 소유 테이블" 섹션에 별도 표시(삭제 버튼 없음, 정보 제공용).
v1.5.32026-06-20
신규 기능: Rhymix Queue 비동기 백업 + 외부 크론 엔드포인트
  • 추가: 기존 트리거 방식은 방문자가 있어야만 실행되어 새벽 시간대 자동 백업이 누락되는 문제 — Queue::addTaskAtInterval() 등록 + 외부 webcron(cron-job.org 등) 분당 1회 호출로 방문자 없이도 설정 시각에 정확히 자동 백업 실행. 백업 예약 설정 저장 시 자동으로 Queue 태스크 등록/해제 시 취소, 등록 여부를 백업 탭 상태표에 실시간 표시.
  • Queue가 등록된 경우 트리거 방식 triggerAutoBackup()이 즉시 종료되도록 해 동일 시각 이중 실행 방지.
  • 추가: 고유 키 기반 외부 크론 URL 엔드포인트(procTablescannerCronRun) — 백업 탭 최초 접속 시 크론 키 자동 생성, 재발급 버튼 제공.
  • mysqldump 잠금 없는 백업 옵션 추가: --single-transaction(InnoDB 일관성 스냅샷, 테이블 락 없음), --skip-lock-tables, --routines --triggers.
  • 백업 경로가 files/attach/ 하위인 경우 빨간 경고(Nginx 환경에서 .htaccess 차단이 동작하지 않아 SQL 덤프 외부 노출 위험) 표시, 웹 루트 하위(비attach)는 주황 주의, 웹 루트 외부는 안전 표시.
v1.5.22026-06-20
버그 수정: 예약 자동 백업 미실행 + 이메일/목록 UX 개선
  • 원인: 트리거 조건이 "현재 시각 == 설정 시각" 정확히 일치 방식이었음 → 설정 시각(예: 새벽 3시)에 방문자가 없으면 영원히 실행 안 됨.
  • 수정: "오늘 예약 시각이 지났고 + 오늘 아직 백업 안 했으면 실행" 방식으로 변경 → 설정 시각 이후 첫 페이지 요청(일반 방문자 포함)에서 자동 실행, 로컬(XAMPP) 환경도 관리자가 오전에 접속하기만 하면 당일 백업 완료.
  • 추가: 백업 파일 목록에 이메일 발송 결과(✓ 발송/✗ 실패/용량초과/-) 열, 수동/예약 구분 뱃지 열 추가. 백업 탭 상태표에 "마지막 이메일" 행 추가.
  • 이메일 발송을 send(true) 동기 전송으로 변경(Queue 환경에서도 즉시 발송 보장), setFrom() 추가, 본문 타입 text/plain 명시.
  • 이메일 첨부 최대 크기 50MB → 2048MB(2GB)로 확대, Gmail/Outlook SMTP 25MB 서버 제한 안내 문구 추가.
v1.5.02026-06-19
신규 기능: DB 백업 탭 + 치명적 버그 수정 3건
  • 추가: 즉시 백업 버튼, 이중 저장 경로(기본+추가 드라이브/NAS 동시 저장), mysqldump 우선 사용 + 쉘 실행 불가 호스팅은 PHP 순수 덤프로 자동 전환, gzip 압축, 자동 백업 스케줄, 보관 기간 초과 파일 자동 삭제, 이메일 첨부 발송, 백업 디렉토리 .htaccess 자동 생성, DB 비밀번호는 임시 .cnf 파일로 커맨드라인 노출 없이 처리.
  • 치명적 버그 수정: 정상 테이블 120개가 "미생성" 목록에 표시되던 문제. 원인은 코어 DB::query()addPrefixes()FROM information_schema.tablesFROM xe_information_schema.tables로 잘못 변환해 쿼리가 실패, 스키마 정의 테이블 전체가 "missing" 판정되던 것 — SQL 키워드를 소문자(from, table 등)로 작성해 대문자 키워드만 매칭하는 addPrefixes()를 우회하도록 수정.
  • deleted="true" 마이그레이션 삭제 마커가 붙은 테이블을 필터링하지 않아 "미생성" 오탐이 나던 문제, 스키마 XML 파일 하나에 테이블이 여러 개면 preg_match()가 첫 번째만 인식하던 문제(→ preg_match_all()로 교체), 최적화 탭에서 {number_format(...)}가 라이믹스 v1 템플릿 파서에서 함수 호출을 지원하지 않아 리터럴 문자열로 출력되던 문제(→ 모델에서 미리 포맷 후 table_rows_fmt 키로 전달) 모두 수정.
  • 구형 XE API($oDB->_query()/_fetch()) 전면 교체 → $oDB->query($sql, $params) + fetchAll(PDO::FETCH_OBJ), _XE_PATH_RX_BASEDIR 상수 교체.
  • UI 개선: 탭 활성 표시가 라이믹스 v1 파서에서 출력 안 되던 {@삼항연산자} 문법 대신 <!--@if--> 인라인 조건으로 교체, 스캔 탭 정상/코어 테이블 섹션 기본 접힘 처리로 이상 항목만 즉시 확인 가능하도록 개선.
v1.0.02026-06-17
첫 출시
  • 핵심 기능: 고아 테이블 스캐너 / DB 대시보드 / 테이블 최적화 / 비대 로그 정리 / 테이블 점검 5개 탭.
  • 스키마(schemas/*.xml) 기반 고아 테이블 탐지 + DROP 기능.
  • 라이믹스 PDO API 기반 구현, 비대 로그(세션/카운터 로그 등) 자동 정리 스케줄(트리거 방식).

지원 환경

PHP7.4 이상 (Rhymix 공식 최소 요구사항), 8.2 이상 권장. 상한선 없음.
Rhymix2.0.0 이상 권장.
DBMySQL/MariaDB — DB 대시보드/최적화/점검 탭이 information_schema.tables 기반이라, 셸 접근이 제한된 공유 호스팅에서도 동작. mysqldump 미지원 환경은 백업이 PHP 순수 덤프로 자동 전환.
라이선스GPLv2 (Rhymix 코어 및 다른 BSplus 모듈과 동일)