실수로 삭제한 티켓 복구

CloudflareBeginner
지금 연습하기

소개

실수로 SQL 을 실행해 티켓이 사라졌지만 애플리케이션 구성은 여전히 올바른 상태입니다. D1 Time Travel 북마크를 저장하고, 합성 레코드 하나를 사용해 삭제 상황을 재현한 다음, 영향을 받지 않은 티켓을 유지하면서 동일한 데이터베이스를 복원합니다.

이 독립적인 복구 실습에서는 삭제해도 되는 원격 데이터베이스 하나와 제공된 읽기 전용 Worker 를 사용합니다. 복원하기 전에 누락된 상태를 확인하고, 복원한 후 애플리케이션 접근이 가능한지 확인합니다.

자신의 학습 계정과 새 VM 을 사용합니다. 먼저 설정 과정에서 Node.js 22.22.0 을 준비한 다음 /home/labex/project/ticket-database에서 프로젝트 로컬 Wrangler 4.131.1 과 평가에 필요한 의존성을 npm install로 설치합니다. 직접 지정된 의존성 버전은 고정되어 있으며, 설치 과정에서 자체 lockfile 이 생성됩니다. 설정 과정에서는 클라우드 로그인이나 평가 대상 데이터베이스 작업을 수행하지 않습니다. 개인 컴퓨터에서는 프로젝트에서 npm install --save-dev wrangler@4.131.1을 실행해 동일한 Wrangler 버전을 설치합니다.

이 실습에서는 D1 Free 허용량 내에서 소량의 합성 레코드를 사용합니다. 기존 계정 사용량도 해당 허용량에 포함됩니다. 구매한 도메인은 필요하지 않습니다. 리소스 삭제와 로그아웃을 모두 확인할 때까지 이 VM 을 유지합니다.

이 VM 을 인증하고 계정 선택

이 단계에서는 새 터미널을 자신의 학습 계정에 연결합니다. Dashboard 에 로그인하는 것만으로는 VM 이 인증되지 않습니다. D1 권한은 데이터베이스 생성, SQL 변경 및 삭제를 허용하고, Workers 권한은 배포를 허용하며, KV 권한은 Wrangler 정리 대상 목록을 확인하는 데 사용됩니다. 인증하기 전에 Background Access 를 포함한 실제 동의 화면을 확인합니다.

준비된 프로젝트로 이동해 고정된 CLI 버전을 확인합니다.

cd /home/labex/project/ticket-database
npx wrangler --version

4.131.1이 출력되어야 합니다. 디바이스 인증을 시작합니다. --device는 브라우저 코드를 표시하고, --browser=false는 사용할 브라우저를 직접 선택할 수 있게 합니다.

npx wrangler login --device --browser=false --scopes account:read user:read d1:write workers_scripts:write workers_kv:write

브라우저에서 표시된 URL 을 열고 현재 코드를 입력한 다음, 학습 계정과 권한을 확인하고 인증을 승인합니다. 터미널에서 성공을 확인할 때까지 기다립니다. 비밀번호나 토큰을 프로젝트 파일에 붙여 넣지 마세요.

npx wrangler whoami --json

loggedIn: true인지 확인한 다음, 계정이 하나만 표시되더라도 계정의 nameid를 확인합니다. 사용할 계정의 ID 를 아래 구성에 복사합니다. 다음 셸 변수는 무작위 바이트 6 개 (16 진수 12 자) 를 사용해 다른 학습자와 이름이 충돌하지 않도록 합니다. here-document 는 JSON 줄 사이의 JSON 을 파일에 기록하며, 내부의 $RUN은 확장됩니다.

$schema 앞의 백슬래시는 이 JSON 키를 그대로 유지하며, $RUN 은 이번 실행의 고유 이름으로 확장됩니다.

RUN=labex-c04-d07-$(openssl rand -hex 6)
cat > wrangler.jsonc <<JSON
{
  "\$schema": "./node_modules/wrangler/config-schema.json",
  "name": "$RUN",
  "account_id": "YOUR_ACCOUNT_ID",
  "main": "src/index.js",
  "compatibility_date": "2026-09-15",
  "workers_dev": true,
  "preview_urls": false
}
JSON

블록을 실행하기 전에 YOUR_ACCOUNT_ID를 실제 계정 ID 로 바꿉니다. RUN을 계속 사용할 수 있도록 이 터미널을 열어 둡니다. name은 이번 실행을 식별하고, account_id는 클라우드 작업에 사용할 계정을 선택합니다. 이 파일은 일반 JSON 이며 JSONC 로도 유효합니다. 이 파일을 작성하는 것만으로는 Worker 가 배포되지 않습니다.

정상 상태의 복원 지점 설정

이 단계에서는 삭제해도 되는 데이터베이스와 제공된 읽기 전용 티켓 API 를 생성합니다. Time Travel은 데이터베이스를 이전 상태로 제자리에서 복원합니다. 대체 데이터베이스를 생성하지 않으며, 선택한 시점 이후에 발생한 변경 사항을 덮어쓸 수 있습니다. 여기서는 방금 생성한 실습 리소스에만 사용합니다.

삭제해도 되는 클라우드 데이터베이스를 생성합니다. --binding DB는 애플리케이션 코드에서 사용할 짧은 이름을 지정하고, --update-config는 실제 이름과 UUID 를 wrangler.jsonc에 기록하며, --use-remote=false는 개발을 로컬로 유지합니다.

npx wrangler d1 create "$RUN-db" --binding DB --update-config --use-remote=false

생성된 이름과 ID 를 확인한 다음 저장된 바인딩을 살펴봅니다.

cat wrangler.jsonc

DB 항목에는 이번 실행에서 생성한 데이터베이스의 이름이 있어야 합니다. 바인딩은 코드와 리소스를 연결하도록 구성한 연결 정보입니다. UUID 는 클라우드 데이터베이스를 식별하고, --local은 이 VM 에 별도의 SQLite 데이터베이스를 사용합니다. SQL 명령에는 항상 --local 또는 --remote 중 하나를 포함합니다.

npx wrangler d1 execute DB --remote --file schema.sql
npx wrangler deploy

배포된 URL 을 복사하고 두 합성 티켓을 모두 읽습니다.

URL='YOUR_DEPLOYED_HTTPS_URL'
curl -i "$URL/tickets/1"
curl -i "$URL/tickets/2"

티켓 1(Cannot sign in, open) 은 200 을 반환하고, 티켓 2(Invoice copy, closed) 도 200 을 반환해야 합니다. 배포가 전파되는 중이면 상태 코드와 JSON 이 일치할 때까지 최대 1 분 동안 읽기 요청을 다시 시도합니다.

데이터베이스 정보를 확인한 다음, 현재 북마크를 복구 아티팩트로 저장합니다. 북마크는 데이터베이스 기록에서 특정 위치를 나타내는 불투명한 값입니다. >로 리디렉션하면 JSON 응답이 지정한 파일에 기록됩니다.

npx wrangler d1 info DB
npx wrangler d1 time-travel info DB --json > recovery.json
cat recovery.json

데이터베이스가 프로덕션 백엔드를 사용하고 recovery.json에 비어 있지 않은 bookmark가 있는지 확인합니다. 실습이 끝날 때까지 이 파일을 변경하지 않습니다. 프로덕션 D1 에서는 Time Travel 이 항상 활성화되어 있습니다. Free 요금제는 7 일, Paid 요금제는 30 일 동안 보존합니다. 이번 세션에서 복구하는 데는 몇 분간의 기록만 필요합니다. CLI 도움말에 30 일이 언급되어 있어도 사용 중인 요금제의 보존 기간이 바뀌지는 않습니다.

제한된 실수 삭제 재현

이 단계에서는 이 실습 데이터베이스에서 합성 티켓 1 만 삭제하고 애플리케이션에서 나타나는 증상을 확인합니다. 먼저 wrangler.jsonc를 확인하고 DB가 방금 생성한 데이터베이스의 이름 및 UUID 와 일치하는지 확인합니다. 기존 애플리케이션 데이터베이스에서 실행하지 마세요.

cat wrangler.jsonc
npx wrangler d1 execute DB --remote --command "DELETE FROM tickets WHERE id = 1;"

API 와 영향을 받지 않은 레코드를 읽습니다.

curl -i "$URL/tickets/1"
curl -i "$URL/tickets/2"
npx wrangler d1 execute DB --remote --command "SELECT id, subject, status, source FROM tickets ORDER BY id;"

티켓 1 은 {"error":"not_found"}와 함께 404 를 반환해야 합니다. 티켓 2 는 원래의 200 응답을 계속 반환하고, SQL 쿼리 결과에는 티켓 2 만 포함되어야 합니다. 네트워크 오류나 플랫폼의 404 페이지는 삭제가 발생했다는 증거가 아닙니다.

복원하기 전에 이 단계의 확인을 완료합니다. 이 확인은 실제 행 누락 상태를 검증합니다. 삭제 실패를 확인하지 않고 복원하면 사고 당시의 증거를 확인하지 못하게 됩니다.

기록을 복원하고 애플리케이션 접근 확인

이 단계에서는 저장한 북마크 시점으로 동일한 데이터베이스를 복원합니다. recovery.json을 읽고 정확한 bookmark 문자열을 BOOKMARK에 복사한 다음, 구성된 데이터베이스의 식별자를 다시 확인합니다.

cat recovery.json
BOOKMARK='YOUR_SAVED_BOOKMARK'
cat wrangler.jsonc
npx wrangler d1 time-travel restore DB --bookmark "$BOOKMARK"

이 명령은 데이터를 덮어쓰고 진행 중인 쿼리를 취소한다는 경고를 표시합니다. 삭제해도 되는 이 데이터베이스만 확인합니다. 복원이 성공했다는 메시지와 실행 취소용 북마크가 출력되어야 합니다. 데이터베이스를 다시 만들거나, 스키마를 다시 가져오거나, 누락된 행을 삽입하지 마세요. 그런 작업은 복구 기법을 우회하게 됩니다.

복원된 데이터베이스와 기존 애플리케이션 바인딩을 조회합니다.

npx wrangler d1 execute DB --remote --command "SELECT id, subject, status, source FROM tickets ORDER BY id;"
curl -i "$URL/tickets/1"
curl -i "$URL/tickets/2"

원래의 두 행과 두 API 응답이 모두 다시 반환되어야 합니다. Worker 는 여전히 같은 UUID 를 가리키며 대체 바인딩이 필요하지 않습니다. 복원 중 읽기가 잠시 불가능하면 읽기를 다시 시도합니다. 복원 대신 새 시드로 조용히 대체하지 마세요.

Dashboard 에서 정확한 D1 데이터베이스를 열고 ID 가 변경되지 않았는지 확인한 다음, 읽기 전용 화면에서 복원된 티켓을 확인합니다. 이 확인은 복구된 데이터와 리소스를 연결합니다. SQL/API 결과는 기능이 정상임을 보여 주는 증거입니다. 계정의 Audit Logs에서 Resource ID를 이 데이터베이스 UUID 로 필터링하고 복구 시점을 포함하는 시간 범위를 선택합니다. 이벤트 시간을 클릭하여 세부 정보를 열고 Resource를 펼친 다음 type: database.time_travel.restore를 확인합니다. 리소스 ID 와 성공한 작업이 복원 출력과 일치하는지 확인합니다. 감사 기록이 표시되기까지 시간이 걸릴 수 있으므로, 목록이 일시적으로 비어 있다고 해서 실패로 판단하지 마세요. 데이터 확인은 복구된 상태를 증명하고, 실제 복원 출력과 감사 이벤트는 복구 작업이 수행되었음을 기록합니다.

동일한 D1 데이터베이스에 복원된 티켓

이 예시는 동일한 데이터베이스에서 복구한 두 개의 원래 티켓을 보여 줍니다. 생성된 이름은 이 예시 실행을 식별하므로 실제 이름과 UUID 는 다릅니다. 스크린샷은 복원된 행을 보여 줍니다. SQL/API 검사는 접근 복구를 확인하고, 실제 Time Travel 명령 출력과 일치하는 감사 이벤트는 복구 작업을 확인합니다.

Time Travel 감사 이벤트

이벤트 제목에는 일반 작업 이름인 create가 표시됩니다. 펼친 Resource 섹션의 type: database.time_travel.restore가 복구 작업을 나타냅니다. 성공 결과, 정확한 데이터베이스 UUID, 시간을 확인하세요. 여기에 표시된 값은 이 예시 실행의 값입니다.

삭제해도 되는 리소스 삭제

이 단계에서는 VM 이 아직 인증된 상태에서 이 실습의 리소스만 삭제합니다. 먼저 모든 기능 확인을 완료합니다. 삭제 확인이 끝날 때까지 구성 파일을 유지합니다.

npx wrangler delete

이번 실행의 구성에 있는 Worker 이름만 삭제하는지 확인합니다.

npx wrangler d1 delete DB

확인 프롬프트를 읽고 이번 실행에서 생성한 데이터베이스만 삭제하는지 확인합니다. 그런 다음 데이터베이스 목록을 표시합니다.

npx wrangler d1 list --json

기록해 둔 데이터베이스 이름과 UUID 가 성공적인 응답에 없어야 합니다. 다른 리소스는 남아 있을 수 있습니다. 인증 또는 네트워크 오류가 발생했다면 결과를 확정할 수 없습니다. 접근 문제를 해결하고 로그인이 유지된 상태에서 다시 조회한 다음 계속합니다. 이 단계의 확인은 로그인 상태에서 실행합니다.

이 VM 의 인증 종료

이 단계에서는 독립적인 삭제 확인이 통과한 후에만 인증을 종료합니다. 로그아웃하면 이 VM 에 저장된 Wrangler 인증 정보가 제거됩니다. VM 을 닫는 것만으로는 클라우드 정리가 완료되지 않습니다.

npx wrangler logout
npx wrangler whoami --json

loggedIn: false가 출력되어야 합니다. 인증되지 않은 이 쿼리는 0 이 아닌 종료 코드를 반환할 수 있습니다. 구조화된 응답에 로그아웃 상태가 명시된 경우에만 정상적인 결과입니다. 확인을 완료한 다음 실습 환경을 종료합니다.

요약

실수로 티켓을 삭제한 상황을 복구하는 방법을 실습했습니다. 확인 가능한 데이터베이스 결과를 점검하고, 선택한 계정과 로컬 상태를 명확히 유지했으며, 로그아웃하기 전에 삭제해도 되는 리소스를 제거했습니다.