업로드 실패가 콘텐츠 등록을 취소하지는 않는다
AI Commons의 CMS 내보내기는 먼저 콘텐츠를 등록해 assetId와 uploadUrl을 받고 파일을 올린다. 파일 업로드가 실패해도 앞서 등록한 콘텐츠는 남을 수 있다. 외부 등록과 업로드는 로컬 DB 트랜잭션으로 함께 되돌릴 수 없는 두 단계다.
변경은 용량 부족을 등록 전에 걸러 내고, 등록 후 업로드가 실패하면 받은 ID로 삭제를 시도하도록 했다. 두 처리는 같은 실패를 서로 다른 시점에서 줄인다.
- 파일 크기와 저장 공간 확인
남은 공간 또는 양수인 단일 파일 한도를 넘으면 등록 전에 거절한다.
- 콘텐츠 등록과 파일 업로드
등록 응답에서 얻은 ID와 URL로 파일을 올린다.
- 업로드 실패 시 삭제 시도
등록된 ID를 삭제한 뒤 원래 업로드 오류를 전달한다.
용량 조회 실패는 진행하고 업로드 실패는 그대로 반환한다
로컬 파일의 stat 크기를 CMS의 available, maxUploadFileSize와 대조한다. 초과면 STORAGE_INSUFFICIENT로 거절하고 createContent를 호출하지 않는다. 저장 공간 SDK 조회가 실패하면 warning을 남기고 null을 반환해 검사를 건너뛴다. 조회 장애 때문에 정상 업로드까지 막지 않으려는 선택이다.
단, CMS 서비스 사용 가능 여부를 확인하는 ensureAvailable은 그 try 밖에 있다. 서비스 사용 불가와 SDK 조회 실패가 모두 같은 방식으로 무시되는 것은 아니다.
다음은 실제 업로드 실패 처리다. 삭제 오류가 원래 오류를 가리지 않도록 호출부에서도 rejection을 흡수한다.
try {
await this.uploadFile(uploadUrl, filePath, mimeType);
} catch (uploadError) {
await this.deleteContent(assetId, userId).catch(() => {});
throw uploadError;
} 삭제 wrapper도 SDK 실패를 기록하고 삼킨다. 삭제는 awaited이므로 원래 실패를 전달하기 전 외부 삭제 응답을 기다리는 비용은 남는다.
사전 확인은 공간 예약이 아니고 보상 삭제는 최선의 시도다
용량 검사와 업로드 사이에 다른 요청이 저장 공간을 쓸 수 있다. 네트워크나 CMS 오류도 사전 검사로 막을 수 없다. 삭제마저 실패하면 등록된 콘텐츠가 남을 수 있으며, 업로드 성공 이후의 인코딩 실패나 대기 timeout은 이 catch의 보상 범위 밖이다.
테스트는 충분한 공간, 두 종류의 용량 초과, 조회 결과 null, 업로드 오류와 같은 오류의 재전달을 구분한다. 삭제 wrapper의 사용자 파라미터와 SDK 실패를 삼키는 동작도 확인한다. 파일·SDK·업로드가 mock돼 있으므로 실제 CMS HTTP, 동시 공간 소비나 잔여 콘텐츠 개수를 확인하는 검증은 아니다.
외부 보이스의 보상 삭제처럼 외부 효과를 되돌리는 처리는 실패할 수 있다. 이 내보내기 경로의 추가 선택은 실패 후 정리뿐 아니라 등록 전에 차단할 조건과, 그 조건을 조회하지 못했을 때 계속 진행할 정책을 함께 정한 데 있다.