실패해도 정리하는 데이터
90분 안팎
학습 목표
자신이 만든 데이터만 정리하고 타 실행 데이터를 보존합니다.
개념
정리는 실패 경로의 기능입니다
검증이 통과한 마지막 줄에서만 삭제하면 단언 실패 때 데이터가 남습니다. 다음 실행이 중복 가입으로 실패하면 처음 제품 결함 대신 준비 오류를 조사하게 됩니다. 정리 코드는 성공과 실패를 모두 지나는 finally에 배치합니다. 준비 단계도 try 내부에 둡니다. 두 계정을 준비하다 두 번째 준비가 실패한 경우에도 먼저 만든 계정은 정리 대상입니다.
이번 로컬 실습의 fixture.cjs는 withFixture(store, runId, ids, verify)를 제공합니다. store는 비동기 create와 remove를 가진 제공 메모리 저장소입니다. 이 저장소는 정리 제어 흐름을 빠르게 확인하기 위한 모델이며 실제 SQL 삭제는 미션의 Java 검사에서 추가합니다. 학습자는 finally의 TODO를 완성하고 생성 성공 후 owned에 등록하는 위치를 유지합니다.
소유 목록의 의미입니다
owned에는 해당 fixture가 생성에 성공한 이메일만 넣습니다. 입력 ids 전부를 처음부터 소유 목록으로 쓰면 아직 생성하지 않은 값까지 삭제할 수 있습니다. 기존 행이 같은 이메일을 갖고 준비가 중복으로 거절된 상황에서는 그 행을 지워서는 안 됩니다. 준비 계획과 실제 생성 기록을 별도 목록으로 관리합니다. 서버가 다른 ID를 발급한다면 요청 값 대신 성공 응답의 ID를 검증해 기록해야 합니다.
정리할 때 전체 테이블 삭제나 이메일 접두사 삭제를 쓰지 않습니다. mine이라는 문자열이 겹치거나 비슷한 이름을 쓴 다른 실행이 피해를 볼 수 있습니다. store.remove에는 정확한 이메일을 전달합니다. 실제 DB에서는 WHERE email=?에 값을 바인딩하고, 사용자와 자식 프로필의 관계가 있다면 자식부터 지웁니다. 외래키 오류는 삭제 순서나 참조 관계가 잘못되었는지 확인하는 신호입니다.
준비 실패를 주입합니다
제공 prepare 테스트는 mine.a 생성 뒤 mine.b 생성에서 prepare failed를 던집니다. reject가 발생해도 첫 행은 finally에서 삭제되어야 합니다. 테스트는 실행 전부터 있던 seed@example.test와 other.a@example.test의 집합이 그대로 남는지 확인합니다. 자기 잔여 행 0만 확인하면 전체 삭제 결함도 통과할 수 있으므로 타 실행과 기존 행 보존을 함께 단언합니다.
create를 await하지 않고 owned에 등록하면 저장 완료와 정리가 경합할 수 있습니다. 정리 함수가 먼저 실행한 뒤 생성이 늦게 끝나면 잔여 데이터가 생깁니다. Promise 거절도 원래 try 범위를 벗어날 수 있습니다. 비동기 준비는 완료를 기다린 뒤 소유 기록을 추가합니다. 이 순서가 저장소의 생성 완료 계약과 맞는지 확인하고 성공을 추측하지 않습니다.
검증 실패와 원래 오류입니다
제공 verify 테스트는 두 계정을 준비한 후 verify failed를 던집니다. withFixture가 오류를 삼키고 성공 값을 반환하면 assert.rejects가 실패합니다. 정리를 했다는 이유로 원래 제품 검증 실패를 없애면 보고가 거짓 성공이 됩니다. try에서 verify를 await하고 finally를 실행한 뒤 오류가 호출자에게 전파되게 합니다. finally 안에 return을 쓰면 앞선 반환이나 오류를 덮을 수 있으므로 사용하지 않습니다.
삭제도 실패할 수 있는 실제 저장소에서는 원래 단언 오류와 정리 오류를 둘 다 기록해야 합니다. 한 삭제가 실패하면 나머지 삭제를 시도할지 정책을 정하고, 실패한 정확한 ID 목록을 후속 정리 작업에 넘깁니다. 단순 finally만으로 오류 두 개가 자동 보존되지는 않습니다. 이 레슨의 제공 remove는 실패하지 않으며 삭제 실패의 합성 오류 집계는 평가 범위 밖입니다. 한계를 README에 남겨 다음 작업과 구분합니다.
테스트 실행 메시지를 읽습니다
node --test fixture.test.cjs를 실행하면 success, prepare, verify, 두 실행 독립성, 잘못된 식별자 검사가 실행됩니다. starter는 생성 데이터가 남아 deepEqual에서 기대 집합과 실제 집합이 다르다고 나옵니다. diff의 추가 행이 mine.a인지 mine.b인지 보고 실패 단계와 연결합니다. 문법 오류가 있으면 단언에 도달하지 않으므로 테스트 실패와 실행 불가를 나누어 처리합니다.
Expected rejection 또는 Missing expected rejection에 해당하는 메시지는 실패가 기대된 호출이 정상 종료했다는 뜻입니다. catch에서 오류를 무시했는지, verify를 await하지 않았는지 먼저 살펴봅니다. 삭제 테스트를 지우거나 실행 환경 문제로 단정하지 않습니다. 실제 비동기 호출의 완료 시점과 오류 전파 경로가 fixture의 책임이며 데이터 정리와 함께 검증되어야 합니다.
중첩 실행으로 타 실행을 보호합니다
두 실행 독립성 테스트는 mine fixture 안에서 second fixture를 실행합니다. second가 정리된 뒤에도 mine 계정은 남아 있어야 하며 mine까지 종료되면 원래 두 계정만 남습니다. 단순히 마지막에 행 수 2인지만 확인하면 다른 계정을 삭제하고 자기 행을 남긴 실수를 놓칩니다. 중간 소유 행 존재와 마지막 정확한 집합을 함께 확인하면 정리 범위 오류를 더 좁혀 찾습니다.
실제 병렬 요청을 이 중첩 검사로 증명하지는 않습니다. 이 실습은 비동기 함수의 순차 완료와 이름 공간 독립성을 관찰합니다. DB 잠금, 네트워크 단절, 저장 성공 후 응답 유실은 추가 설계가 필요합니다. 응답이 없어 생성 여부를 모를 때는 기록 가능한 요청 식별자와 조회 절차를 마련하고 타 실행을 건드리지 않는 범위로 복구합니다. 애매한 상태에서 전체 삭제로 해결하지 않습니다.
완료 증거의 범위를 씁니다
미션에서는 동일한 실패 위치를 Java API 준비에도 적용합니다. 한 계정 생성 후 준비 실패, 두 계정 생성 후 검증 실패를 주입하고 finally에서 정확한 이메일을 SQL로 삭제합니다. 실패 직후 자기 이메일이 0행인지 조회하고 기존 및 다른 실행의 정렬된 이메일 집합을 비교합니다. 여기서 생성 성공 뒤 목록 등록과 finally 진입이라는 제어 흐름은 같지만 관찰 대상은 실제 H2 행입니다.
리뷰 자료에는 실패 유형, 준비 성공 ID 목록, 정리 전후 조회, 보존할 기준 집합을 적습니다. 정리 후 초록색 결과만 남기면 원래 실패 위치를 설명하기 어렵습니다. 정리 검사가 통과했다는 증거와 제품 검사가 실패했다는 증거를 함께 보존합니다. node:test의 도구 옵션 전반은 서재 장으로 보내고 이 레슨에서는 소유 범위와 실패 제어 흐름을 실제로 구현하는 데 집중합니다.
따라하기
자기 목록만 제거합니다
독립적으로 실행하여 결과를 비교합니다.
const rows=new Set(['seed','other','mine']);for(const id of ['mine'])rows.delete(id);console.log([...rows].sort().join(' '));실행 결과
other seed
준비 실패도 finally를 지납니다
독립적으로 실행하여 결과를 비교합니다.
const owned=[];try{owned.push('mine.a');throw Error('prepare failed');}catch(e){console.log(e.message);}finally{console.log('cleanup',owned.join(' '));}실행 결과
prepare failed cleanup mine.a
로컬 정리 검사를 실행합니다
qa-cleanup 루트에서 실행하고 diff의 추가 mine 행을 읽습니다. fixture.cjs의 finally를 완성한 뒤 다섯 테스트가 통과하는지 확인합니다.
bash check.sh확인 문제
실습
fixture.cjs의 finally를 완성합니다. 성공·준비 실패·검증 실패 뒤 생성 성공 ID만 정리하고 기준 두 계정을 보존합니다. 테스트 파일은 유지합니다.
실행 명령
bash check.sh
기대 결과
Node 5개 실패 0
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- 실행 순서에 따라 결과가 달라지는 테스트를 확인하는 방법을 설명해 주시면 됩니다.