업로드 복구 실험이 실패했는데, 실제 녹화 파일에는 문제가 없었습니다. 공용 개발 DB에 통합 테스트가 만든 가짜 실패 행 1,121개가 남아 있었고, 제품의 스위퍼가 존재하지 않는 파일을 계속 처리하려 했습니다. 테스트의 잔여 데이터가 서비스의 실패처럼 보인 상황이었습니다.

종료 시 삭제를 추가하면 정상 실행 뒤에는 깨끗해집니다. 하지만 지금 검증하려는 대상이 바로 프로세스 종료와 복구였습니다. 정리 코드가 반드시 실행된다는 전제에 다시 기대지 않으려고, 저는 쓰기 대상부터 테스트 전용 DB로 분리했습니다. 그리고 그 DB를 비워도 되는지 판단하는 소유 표식을 추가했습니다. 구현과 반례 검증은 AI와 협업했고 PR #61로 병합됐습니다.

연결을 닫는 것과 데이터를 지우는 것은 달랐습니다

기존 테스트는 종료할 때 DB 연결을 닫았지만, 생성한 상태 행은 남겼습니다. 테스트를 정상 종료할 때마다 지우도록 바꾸면 평상시에는 깨끗해 보일 수 있습니다. 그러나 강제 종료나 정리 실패가 발생하면 같은 오염이 다시 생깁니다. 기동 시 청소를 추가하는 방법도 문제가 있습니다. 공용 DB의 행 중 무엇이 가짜인지 구분해 삭제해야 하기 때문입니다.

후보 해결하는 부분 남는 문제
종료 시 자기 행 삭제 정상 종료의 잔여 데이터 강제 종료 시 오염 재발
공용 DB 기동 시 청소 과거 잔여 데이터 회수 실제 개발 데이터와 구분해야 함
전용 DB와 소유 표식 쓰기·정리 대상 자체를 분리 생성 권한과 이행 절차 필요

전용 DB 패턴 자체는 업로드 테스트에 이미 있었습니다. 문제는 store와 upload가 같은 부트스트랩을 쓰지 않아 공용 DB로 들어갈 경로가 남아 있다는 점이었습니다. 초기화를 한 곳으로 모으고 PG_TEST_DB로 지정한 대상만 받도록 통일했습니다. 격리 규칙을 테스트마다 복제하면 다음 진입점에서 다시 빠질 수 있어서입니다.

테이블 모양으로 소유자를 추측했다가 틀렸습니다

초기 판정은 테이블 개수를 보고 “테스트 DB처럼 보이는가”를 확인하는 방식이었습니다. 이 조건은 업무 DB가 우연히 같은 구조를 가지는 순간 무너집니다. 실제로 미끼 DB의 데이터가 삭제되는 반례가 확인됐습니다. 스키마 모양은 사용 목적을 증명하지 못했습니다.

수정한 기준은 pokeclip_testdb_marker라는 빈 마커 테이블입니다. 새 DB를 생성한 때에만 표식을 만듭니다. 이미 존재하는 DB에 표식이 없으면 사용과 정리를 모두 거부합니다. 기존 DB에 먼저 마커를 넣고 “이제 내 것”이라고 판단하면 보호 장치가 아무 의미가 없어집니다. 이 구분이 변경의 핵심입니다.

입력·상태 부트스트랩의 결정
공용 DB와 테스트 DB 이름이 같음 시작 거부
기준 DSN의 DB 이름이 비어 있음 시작 거부
기존 DB에 표식 없음 사용·정리 거부
새 테스트 DB 생성할 때 표식도 생성
표식이 있는 테스트 DB 정해진 테스트 초기화 경로 사용

PR #61의 변경과 테스트에서는 동일 이름, 빈 이름, 표식 없는 기존 DB를 거부하고 새 DB는 정상 생성하는지 확인했습니다. 이 검증의 중심은 발견 당시 1,121개였던 잔여 행을 빨리 지우는 데 있지 않습니다. 업무 스키마와 모양이 같아도 표식이 없으면 건드리지 않는가가, 초기 테이블 개수 판정과 수정한 판정을 가르는 반례였습니다.

격리한 뒤에도 동시 실행은 별도 문제입니다

명시적 표식을 붙여도 같은 DB 이름을 두 테스트 실행이 함께 쓰면 서로의 데이터를 지울 수 있습니다. 그래서 실행별 고유 DB 이름 규약을 남겼습니다. 표식은 테스트가 사용할 권한이 있는 대상을 구분하지만, 두 실행을 서로 격리하는 이름까지 대신 만들어 주지는 않습니다.

DB를 생성하려면 테스트 계정에 CREATEDB 권한도 필요합니다. PostgreSQL의 CREATE DATABASE 문서가 설명하는 생성 권한과 실제 테스트 환경 설정을 맞춰야 합니다. 기존 테스트 DB에는 일회성 이행이 필요하며, 표식이 없다는 이유로 발견한 DB를 자동으로 비우는 방식은 선택하지 않았습니다.

강제 종료돼도 남을 장소가 달라졌습니다

테스트가 다시 중간에 죽더라도 잔여 행은 전용 DB에 남습니다. 공용 개발 DB의 스위퍼가 가짜 파일을 처리하게 만드는 경로를 먼저 끊은 것입니다. 변경과 반례 요약에는 전용 DB 생성뿐 아니라 미끼 DB를 삭제했던 실패도 함께 남겼습니다.

이 문제를 종료 정리 코드만으로 고쳤다면 정상 종료 시험에서는 통과했을 것입니다. 그러나 필요한 보장은 실패 뒤의 청소 성공보다, 실패해도 다른 실행의 데이터에 손대지 않는 경계였습니다. 테스트 DB를 만들 때만 소유 표식을 만들고, 기존 DB의 소유권은 추측하지 않는 규칙으로 그 경계를 옮겼습니다.


이 글은 PokeClip의 개발·검증 기록을 2026년 10월 15일 기준으로 정리한 CASE 17입니다. 전체 기술 기록에서 연결되는 결정을 함께 읽을 수 있습니다.