시냅스

Road to Web3 (11) 실패하는 트랜잭션도 비용이 든다: revert와 운영비 본문

Road To Web3/Blockchain

Road to Web3 (11) 실패하는 트랜잭션도 비용이 든다: revert와 운영비

ted k 2026. 1. 1. 10:09
이 글은 Ethereum 및 EVM 계열을 기준으로 설명합니다.

 

이번 편의 목적입니다.

  • 실패가 왜 비용을 만드는가
  • 그 비용을 제품과 운영 정책으로 어떻게 흡수할 것인가
  • 재시도 상태 머신을 어떻게 잡을 것인가

 

제가 처음 온체인 결제를 바라봤을 때 들었던 생각입니다.

  • 실패는 롤백이니 비용도 0이다
  • 실패는 예외 케이스라서 UX 로 숨기면 된다
  • 재시도는 그냥 다시 보내면 된다

하지만 체인에서는 실패가 정상입니다.
그리고 실패는 운영비로 곧바로 변환됩니다.


 

요약

질문 결론
왜 실패가 돈이 드나 실행 자원을 이미 소비했기 때문
누가 돈을 내나 트랜잭션을 제출한 주소
무엇이 가장 비싼 실패인가 out of gas, 잘못된 수수료, 무한 재시도
제품이 해야 할 일 실패를 상태로 만들고, 사용자에게 선택지를 준다
운영이 해야 할 일 재시도 정책, 알림, 회수 가능한 제공 설계

 

1) 실패 비용의 구조

트랜잭션은 실행입니다.
실패해도 실행은 했다면 가스는 소비됩니다. 

실패가 비용이 되는 이유를 3줄로 줄이면

  • 노드들이 내 트랜잭션을 실행한다
  • 실행 중 연산과 스토리지를 건드렸다
  • 상태는 롤백되지만, 실행 자원 소비는 되돌릴 수 없다
  • 그리고 이 실행은 내 지갑만 하는 게 아니라, 블록에 포함된 순간부터 모든 검증 노드가 같은 결과를 재현해야 한다
  • 즉 실패 트랜잭션도 네트워크 입장에서는 처리해야 할 작업이며, 공짜로 두면 스팸이 무한정 늘어난다
  • 그래서 상태 변경은 롤백되더라도, 검증과 실행에 든 비용은 가스로 정산되고 트랜잭션 제출자가 부담한다

그래서 revert 는 롤백이면서 동시에 비용 청구입니다.

롤백은 데이터의 되돌림으로, 실행 시간의 되감기가 아닙니다.

 

2) 실패 유형을 제품 관점으로 분류하기

실패는 기술 원인이 아니라 제품 행동으로 분류하는 편이 빠릅니다.

A. 사용자가 해결 가능한 실패

  • 수수료 부족 네이티브 코인 부족
  • 수수료 설정 낮음
  • 네트워크 혼잡으로 pending 길어짐
  • nonce 줄막힘

이건 UX 로 안내하면 해결됩니다.

B. 사용자가 해결하기 어려운 실패

  • 컨트랙트 정책 revert
    • e.g. 블랙리스트 pause
  • allowance 부족 approve 필요
  • 상태 경합 이미 처리됨, 마감됨, 재고 없음

이건 사용자에게 설명 가능한 문장으로 번역해야 합니다.

C. 운영이 책임져야 하는 실패

  • RPC 장애 타임아웃
  • 인덱서 지연
  • 시퀀서 이슈 L2
  • 서명 검증 로직 버그

이건 사용자 탓으로 돌리면 제품이 망합니다.


 

3) 실패를 숨기지 말고 상태 머신으로 만든다

web2 결제 시스템에서

  • 승인
  • 매입
  • 정산
  • 환불

이 분리가 있는 이유는 실패가 존재하기 때문입니다.

온체인도 비슷합니다.
특히 결제형 UX 라면 상태 머신이 필수입니다.

권장 상태 예시

  • created
  • signed
  • submitted
  • pending
  • included
  • confirmed
  • finalized
  • failed_revert
  • failed_dropped
  • failed_replaced
  • canceled

여기서 핵심은

  • 성공보다 실패 상태가 더 많아야 정상이다
  • 실패 상태마다 다음 액션이 다르다


 

4) 재시도 전략: 그냥 다시 보내기가 아니다

재시도는 비용이 드는 행위입니다.
그래서 재시도는 정책이어야 합니다.

재시도 3원칙

1. 멱등성 키로 중복 처리를 막는다
2. 실패 원인별로 재시도 방법을 바꾼다
3. 사용자의 의도 없는 자동 재시도는 제한한다

 

참고: 수수료 올려 retry는 언제 가능한가
수수료를 올려 같은 nonce로 대체하는 retry replacement 는 대부분 pending 단계에서만 의미가 있습니다. 블록에 포함되면 nonce가 소비되므로 같은 nonce로 다시 보낼 수 없습니다. 성공 포함 뒤에는 다음 nonce로 새 트랜잭션을 보내는 문제로 바뀌고, revert로 실패했더라도 nonce는 소비되니 원인을 수정한 뒤 다음 nonce로 재시도해야 합니다. 다만 포함된 것처럼 보였다가 reorg로 빠지면 다시 pending처럼 돌아갈 수 있어, 확정 정책이 필요합니다.

 

실패 원인별 추천 액션

실패 유형 원인 추천 액션
pending 오래 수수료 낮음 수수료 상향 같은 nonce로 replacement
dropped 전파 부족, 노드 정책 재제출 또는 다른 RPC로 브로드캐스트
replaced 더 높은 수수료 tx에 의해 대체됨 기존 tx를 포기하고 최신 tx 추적
revert 조건 불충족 재시도 금지, 원인 안내, 입력 수정
out of gas gasLimit 부족 estimateGas 후 재제출, 버퍼 확대
RPC timeout 인프라 같은 tx hash 재조회, 재제출은 신중

 

참고: web2 결제 retry와 공통점과 차이점
web2 결제에서도 retry는 정책입니다. 공통점은 멱등성입니다. 결제 승인 요청은 네트워크 타임아웃이나 중간 장애로 중복 전송이 흔하니, 상점 주문번호 또는 idempotency key로 같은 결제를 중복 청구하지 않게 막습니다. 온체인도 동일하게, 같은 사용자 액션이 여러 번 제출되더라도 서비스 제공과 과금이 한 번만 일어나도록 멱등성 키와 상태 머신이 필요합니다.

차이점은 재시도 단위와 비용 모델입니다. web2에서는 보통 같은 요청을 다시 보내되, 같은 결제 승인인지 확인한 뒤 재전송을 조심스럽게 합니다. 반면 온체인은 계정 nonce가 순서를 강제하므로 pending이 막혀 있으면 뒤 트랜잭션이 줄줄이 막히고, 재시도는 동일 nonce로 수수료를 올려 대체 replacement 하는 형태가 자주 등장합니다. 또한 온체인은 실패 재시도 자체가 가스비로 즉시 비용화되며, 블록 포함 이후에도 재구성 reorg 같은 사후 변수까지 고려해야 합니다. 그래서 결제 retry가 네트워크 신뢰성 문제를 다루는 것이라면, 온체인 retry는 네트워크 신뢰성에 더해 순서와 수수료 시장까지 함께 다루는 문제입니다.

 


 

5) 누가 비용을 부담할 것인가

제품 설계에서 가장 민감한 질문입니다.

모델 1. 사용자가 가스를 낸다

  • 대부분의 지갑 UX
  • 사용자가 실패 비용을 직접 체감
  • 단점: 실패 UX 가 곧 제품 UX

모델 2. 서비스가 가스를 스폰서한다

  • 메타 트랜잭션, relayer
  • 서비스가 실패 비용을 부담
  • 장점: 유저에게 부드러운 UX
  • 단점: 스팸 방어와 리스크 관리가 필요

이 경우 운영 관점의 체크리스트가 더 중요해집니다.

  • 레이트리밋
  • 리플레이 방지 nonce
  • 사전 시뮬레이션으로 실패 제거
  • 쿼터, 과금, 차단

 

6) 왜 이게 운영비로 직결되는가

실패 비용은 단순 가스가 아닙니다.
운영비는 이렇게 구성됩니다.

  • 가스비 자체
  • RPC 비용
  • 인덱서 비용
  • CS 문의 처리 비용
  • 재시도 트래픽과 큐 비용
  • 금액 미수금 리스크 오프체인 선제공일 때

web2 결제에서 승인 실패율이 올라가면

  • CS 폭증
  • 전환율 하락
  • 재시도 폭증

이 일과 거의 동일한 일이 체인에서도 벌어집니다.
다만 체인은 실패가 돈을 태우는 방식으로 더 노골적입니다.


 

7) web2 결제로 번역한 UX 패턴

패턴 A. 승인 완료 화면을 포함 화면으로 착각하지 않기

  • 서명 완료, 제출 완료는 승인에 가깝다
  • 확정은 정산에 가깝다
  • 중간 상태를 UX 로 드러내야 한다

패턴 B. 실패 시 사용자에게 3가지 버튼을 준다

  • 수수료 올리고 재시도
  • 취소하고 나가기
  • 원인 보기

패턴 C. 선제공은 회수 가능한 형태로 제공하기

  • 기능 토큰을 잠깐만 열어준다
  • API 크레딧을 보류 상태로 잡는다
  • 확정 후 커밋한다

 

8) 체크리스트

1) 실패를 예외가 아니라 정상 상태로 모델링한다
2) 사용자 해결 가능 실패와 운영 실패를 분리한다
3) revert 는 재시도 금지, 원인 안내를 기본값으로 둔다
4) out of gas 는 estimateGas 와 버퍼로 최우선 제거한다
5) pending 은 timeout 과 replacement 정책을 갖는다
6) dropped 는 전파 전략과 멀티 RPC 를 준비한다
7) 멱등성 키로 중복 제공과 중복 과금을 막는다
8) 서비스 스폰서 모델이면 스팸 방어가 먼저다
9) 실패율을 메트릭으로 보고 알림을 건다
10) explorer 링크와 tx 상태 화면을 제품에 넣는다
11) 인덱서 지연과 reorg 를 전제로 UX 를 설계한다
12) 결제 서비스처럼 승인과 정산을 분리한다


 

결론

  • 실패는 롤백이지만 비용이 0이 아니다
  • 실패를 상태 머신으로 만들면 UX 와 운영이 단단해진다
  • 재시도는 비용이므로 정책으로 다뤄야 한다
  • 가스 스폰서는 강력하지만, 스팸 방어가 필수다

 

Comments