Devin.KR

백업과 복원 검증

75분 안팎

학습 목표

일관된 백업과 별도 DB 복원의 기준을 정합니다.

개념

백업 파일보다 복원 결과를 봅니다

동아리 도서 대여 DB를 백업했다는 보고만으로는 장애 후 회원의 이력을 되찾을 수 있는지 알기 어렵습니다. 파일이 존재하고 크기가 0보다 커도 잘못된 DB를 백업했거나 키 제약이 빠졌을 수 있습니다. 이번 목표는 H2의 일관된 백업 명령으로 아카이브를 만들고, 체크섬 확인 후 별도 경로에 복원하여 내용·제약·API를 검증하는 것입니다. 원본 DB는 유지합니다. 복원 과정을 재현한 테스트와 복원 후 거부되어야 할 요청까지 성공 기준에 포함합니다.

저장 장치 복사와 DB 백업을 구분합니다

실행 중인 .mv.db를 임의로 복사하면 복사 중 쓰기와 파일 페이지 상태가 섞일 수 있습니다. 이 실습은 활성 JDBC 연결에서 BACKUP TO 명령을 실행합니다. H2가 일관된 백업을 만드는 기능을 사용하며 파일 시스템 복사가 같은 보장을 준다고 설명하지 않습니다. BACKUP은 파일 DB에서 수행하고 메모리 DB를 그대로 파일 백업하는 방식으로 쓰지 않습니다. H2 2.1.214 캐시를 사용하는 실제 테스트로 명령과 Restore 호출을 확인합니다. 다른 DBMS에 이 명령을 그대로 적용하지 않습니다.

기준 시점을 구체적으로 만듭니다

BackupRestoreTest는 새 임시 source DB를 열어 도서·회원·계정·대여·현재 대여·메모·요청 키를 준비합니다. 첫 대여를 커밋한 다음 모든 업무 테이블의 내용을 정렬해 기억하고 백업합니다. 백업이 끝난 뒤 원본 도서 제목을 바꿉니다. 복원 DB의 제목이 백업 시점의 값이고 원본은 바뀐 제목을 유지해야 두 저장소와 기준 시점이 구분됩니다. 변경이 진행 중인 대규모 운영 부하를 재현하는 테스트는 아니지만 일관된 백업 기능의 실제 사용 경로와 복원 내용을 확인합니다.

해시 확인은 복원보다 먼저 합니다

BackupRestore.sha256은 아카이브를 스트림으로 읽어 SHA-256을 계산합니다. restore는 기대 해시와 실제 해시가 같을 때만 대상 디렉터리를 만듭니다. 손상된 파일이면 CHECKSUM_MISMATCH로 실패하고 대상 경로를 만들지도 않습니다. 해시 파일 자체의 출처와 접근 권한은 별도로 신뢰해야 합니다. 누군가 아카이브와 기대 해시를 동시에 바꿀 수 있다면 비교만으로 원본성을 보장할 수 없습니다. 제출 기록에는 원본 파일 이름, 생성 시점, 해시, 보관 위치의 권한을 남기되 백업의 내용이나 자격증명을 붙이지 않습니다.

덮어쓰기를 구조적으로 막습니다

복원 대상 디렉터리가 이미 존재하면 DESTINATION_EXISTS로 거절합니다. 비어 있는 폴더라도 거절하여 담당자가 새 경로를 명시적으로 고르게 합니다. DB 이름은 제한된 소문자·숫자·하이픈 형식만 허용하며 경로 탈출을 피합니다. 이 과제는 격리된 단일 복원 실행을 전제로 합니다. 다른 작성자가 동시에 같은 대상을 선택하는 경쟁까지 보장하는 도구라고 쓰지 않습니다. 실제 복구 실행자는 배타적 작업 공간과 승인된 대상 경로를 준비하고 운영 파일과 혼동하지 않도록 원본·복원 이름을 따로 적습니다.

Restore를 호출하고 새 연결을 엽니다

org.h2.tools.Restore.execute는 아카이브, 대상 디렉터리, 새 DB 이름을 받습니다. 복원 전에 원본 컨텍스트를 닫으며 복원 파일을 사용하는 다른 연결은 없습니다. 복원 뒤 jdbc:h2:file로 새 DB에 연결합니다. 기본값이나 원본 URL로 돌아가면 복원 결과가 아니라 원본 상태를 검사하게 됩니다. 테스트의 source와 restore-dir/restored는 다른 경로입니다. 생성된 파일을 열었다는 사실만으로 완료하지 않고 이후 검사가 모두 끝나야 확인된 복원이라고 기록합니다.

행 수가 같아도 내용은 다를 수 있습니다

스냅샷 비교는 books·members·loans·accounts·loan_notes·active_loans·loan_requests의 모든 컬럼을 정렬한 결과로 수행합니다. 행 수만 비교하면 도서 제목 변경이나 다른 회원의 대여 기록으로 바뀐 오류를 놓칩니다. 계정의 해시도 비교 대상이지만 출력에는 넣지 않습니다. 대여 시각, 현재 대여의 책과 이력 연결, 멱등 요청 키의 저장 결과까지 보존되어야 재시도 계약을 이어갈 수 있습니다. 식별자만 남고 연결 관계가 깨진 복원은 기능을 일부 보여 주더라도 합격으로 판단하지 않습니다.

제약은 실제로 위반해 봅니다

복원 후 같은 도서 PK 삽입, 같은 이메일의 회원 삽입, 존재하지 않는 도서를 참조하는 대여 삽입, 대여 이력과 책이 맞지 않는 현재 대여 삽입을 시도합니다. 각각 데이터 무결성 예외가 나야 합니다. INFORMATION_SCHEMA에 제약 이름이 보인다는 사실보다 실제 쓰기를 거절하는지가 더 직접적인 근거입니다. 일부러 실패하는 SQL은 격리된 복원 DB에서 실행하고 기존 내용이 손상되지 않는지 확인합니다. 모든 제약을 제거하고 데이터만 넣은 덤프는 이 검사에서 걸립니다.

API에서 정상과 거부를 확인합니다

복원된 DB로 컨트롤러와 보안 필터를 새로 만들고 도서 제목·대여 상태를 조회합니다. 익명 /me는 401, 다른 회원의 대여 상세는 404, 일반 회원의 도서 수정은 403, CSRF 없는 대여도 403이어야 합니다. 로그인은 새 세션으로 수행합니다. 원본 프로세스의 메모리 세션까지 백업되는 것은 아니므로 오래된 쿠키 실패를 데이터 손실이라고 판단하지 않습니다. 권한 경계가 빠진 복원은 조회만 잘되어도 안전한 서비스 복구가 아닙니다.

재시도와 동시성도 이어서 봅니다

백업 전에 사용한 요청 키를 같은 회원과 같은 도서로 재전송하면 기존 대여 ID와 Location이 나옵니다. 이 결과는 새 대여를 만든 것이 아니라 저장된 멱등 결과를 재생한 것입니다. 아직 대여되지 않은 책에는 두 회원의 경쟁 요청을 동시에 시작하여 성공 하나와 충돌 하나, 현재 대여 한 건을 확인합니다. 이 복원 검사는 시작 게이트를 사용한 실행 사례이며 이전 모듈의 강제 경합 회귀 테스트도 함께 유지합니다. 임의의 모든 운영 경합을 증명했다고 쓰지 않습니다.

데이터 손실과 복구 시간을 설명합니다

원본의 백업 이후 제목 변경이 복원 DB에 없다는 것은 예상된 손실 경계입니다. RPO는 허용하는 데이터 손실 범위이고 RTO는 복구 완료까지 허용하는 시간 목표입니다. 이 과제는 운영 목표 숫자를 임의로 만들지 않습니다. 실제 조직의 목표와 백업 주기를 합의하고 생성·보관·복원·API 점검의 시간을 따로 측정해야 합니다. 해시가 맞았다는 사실만으로 RTO를 만족했다고 쓸 수 없습니다. 테스트 실행 시간은 실습 규모에서의 관측이며 큰 운영 DB의 복구 시간 예측으로 사용하지 않습니다.

starter 실패를 안전 경계에서 고칩니다

미션 starter에는 앞 모듈 완성 코드와 새 검사들이 들어 있습니다. 체크섬 비교가 빠져 checksumMismatchDoesNotCreateDestination에서 기대한 예외가 다르게 나오거나 대상이 만들어집니다. Restore 오류만 기다리지 말고 복원 전 검증을 추가합니다. 요청 ID 검증과 지표 집계 TODO도 함께 완성한 뒤 bash check.sh로 누적 Java와 관측 과제를 실행합니다. H2 명령의 자세한 권한과 옵션은 공식 BACKUP 문서를 확인하며, 보관된 파일이 실제로 읽히는지는 이번 복원 테스트로 증명합니다.

따라하기

미션의 복원 경계를 찾습니다

backend-m10-observe-restore-mission starter에서 검사를 실행합니다. 이 과제는 앞 단계 전체 프로젝트를 이어받았습니다. checksumMismatchDoesNotCreateDestination이 어떤 경로를 보호하는지 읽습니다.

./mvnw test

복원 전 무결성을 검사합니다

BackupRestore.restore의 TODO에 sha256(archive)와 expected 비교를 추가합니다. 다르면 IllegalArgumentException의 CHECKSUM_MISMATCH로 끝내고 대상 경로 생성은 뒤에 둡니다. 기존 대상 거절도 유지합니다.

실제 백업과 별도 복원을 검증합니다

전체 미션의 요청 ID TODO도 완성하고 Maven 검사를 실행합니다. BackupRestoreTest는 임시 파일 DB에 BACKUP TO와 Restore.execute를 실제 실행하며 복원 후 내용을 대조합니다.

./mvnw test
python3 report.py

누적 결과를 대조합니다

solution에서 직접 실행한 보고서입니다. 172개에는 앞 단계 161개, 요청 추적 8개, 복원 3개가 포함됩니다. 지표 하네스는 bash check.sh에서 별도로 실행합니다.

python3 report.py

실행 결과

Tests=172
Failures=0
Errors=0
Skipped=0

복구 증거를 묶습니다

CHECKSUM_MISMATCH·DESTINATION_EXISTS·전체 내용·PK/FK/UNIQUE·조회·권한·CSRF·키 재생·동시 대여의 근거를 README-M10.md 기준으로 작성합니다. 원본 제목이 유지되고 복원 제목은 백업 시점이라는 차이를 설명합니다.

bash check.sh

확인 문제

실습

미션 starter의 BackupRestore 체크섬 TODO와 요청 ID·지표 TODO를 완성합니다. bash check.sh는 실제 H2 BACKUP·별도 Restore·전체 내용·PK/FK/UNIQUE·복원 DB API 권한·CSRF·키 재생·동시 대여·원본 불변과 이전 회귀 검사를 실행합니다. 검사를 바꾸지 않고 복원 기록과 손실 경계를 제출합니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

누적 Java 172개와 지표 집계 8개·Java 관측 1개가 실패 없이 통과합니다.

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 실행 환경에서 API 오류를 추적하는 순서를 설명합니다.