| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- C
- design pattern
- Heap
- Blockchain
- 파이썬
- 운영체제
- Ethereum
- 네트워크
- IT
- Road to Web3
- Galera Cluster
- Java
- 알고리즘
- 자료구조
- JavaScript
- MySQL
- Spring
- spring webflux
- Algorithm
- OS
- mongoDB
- 디자인 패턴
- JPA
- Data Structure
- react
- redis
- 백준
- 컴퓨터구조
- MSA
- 자바
- Today
- Total
시냅스
Road to Web3 (9) 토큰 vs 네이티브 코인: USDC는 있는데 왜 전송이 안 되죠 본문
Road to Web3 (9) 토큰 vs 네이티브 코인: USDC는 있는데 왜 전송이 안 되죠
ted k 2025. 12. 29. 20:25이 글은 Ethereum 및 EVM 계열을 기준으로 설명합니다.
이번 편의 목표는 딱 하나입니다.
- 잔액이 있는데도 전송이 실패하는 이유를 수수료 모델과 계정 모델로 설명하기
제가 처음 토큰 전송을 해보려다 막혔던 상황이 딱 이거였습니다.
- 지갑에 USDC는 보인다
- 그런데 전송을 누르면 실패하거나 pending 이 오래 간다
- 또는 approve 라는 단계를 요구한다
원인은 거의 항상 하나로 수렴합니다.
- 수수료는 네이티브 코인으로 낸다
그리고 토큰 전송은 사실 단순 송금이 아니라 컨트랙트 호출입니다.
요약
| 구분 | 네이티브 코인 ETH | 토큰, ERC20(e.g. USDC) |
|---|---|---|
| 존재 형태 | 프로토콜 기본 자산 | 컨트랙트가 관리하는 잔고 |
| 수수료 지불 | 본인 자체로 지불 | 네이티브 코인 필요 |
| 전송 방식 | 프로토콜 레벨 잔액 이동 | 컨트랙트 함수 호출 transfer |
| 실패 원인 | 수수료 부족, nonce, 혼잡 | 수수료 부족 + allowance + 컨트랙트 revert |
| 조회 | balanceOf 개념이 프로토콜에 내장 | balanceOf 컨트랙트 호출 |
1) 네이티브 코인과 토큰은 계층이 다르다
네이티브 코인은 체인의 1층 자산입니다.
- 계정 잔액은 프로토콜 상태에 직접 존재
- 전송은 프로토콜 레벨 상태 변경
반면 토큰은 2층 앱 자산입니다.
- 컨트랙트가 내부 매핑으로 잔액을 관리
- 전송은 컨트랙트 코드 실행 결과
이 차이가 모든 UX 문제의 출발점입니다.

2) USDC 전송은 단순 송금이 아니라 컨트랙트 호출이다
USDC를 전송한다는 말은 이런 행위입니다.
- to 는 USDC 컨트랙트 주소
- data 는 transfer(recipient, amount) 호출 인코딩
- value 는 보통 0
즉, ETH 전송은 잔액 이동이고
USDC 전송은 컨트랙트 함수 호출입니다.
그래서 실패 원인도 컨트랙트 로직 범주까지 확장됩니다.
3) USDC는 있는데 왜 전송이 안 되나
가장 많은 원인 7가지를 정리하면 이렇습니다.
1. 수수료로 낼 네이티브 코인이 없다
USDC 전송 트랜잭션도 가스를 씁니다.
그 가스비는 ETH 같은 네이티브 코인으로 냅니다.
- 지갑에 USDC 100 있어도
- ETH 0 이면
- USDC 전송 트랜잭션 자체를 실행할 수 없습니다
이게 가장 흔한 원인입니다.
2. 네트워크가 다르다, 체인이 다르다
USDC는 체인마다 다른 컨트랙트 주소입니다.
- Ethereum mainnet 의 USDC
- Base 의 USDC
- Arbitrum 의 USDC
이들은 이름은 같아도 다른 자산처럼 동작합니다.
지갑에서 보이는 USDC가 어느 네트워크에 있는지부터 확인해야 합니다.
3. 토큰 컨트랙트 정책으로 revert
토큰은 컨트랙트라서 정책이 있습니다.
- 블랙리스트
- 일시 중지 pause
- 전송 제한
이런 정책에 걸리면 트랜잭션은 revert 로 실패할 수 있습니다.
4. allowance 승인 단계가 필요하다
USDC를 상대 서비스가 내 잔고에서 가져가게 하려면 approve 가 필요합니다.
- 내가 직접 transfer 하는 건 approve 불필요
- 서비스가 transferFrom 으로 가져가려면 approve 필요
즉, 결제형 UX 에서 approve 가 튀어나오는 겁니다.
5. nonce 문제로 pending 또는 replaced
계정의 nonce가 어긋나면 전송이 늦어질 수 있습니다.
- 낮은 수수료로 보낸 tx가 pending 으로 막혀있으면
- 그 뒤 nonce tx는 같이 줄이 막힌다
그래서 pending 정리가 필요할 때가 있습니다.
6. 수수료 설정이 낮아서 오래 대기
baseFee 와 tip 설정이 낮으면 블록에 못 들어갑니다.
지갑은 보통 자동으로 조정하지만, 수동 설정은 실수하기 쉽습니다.
7. 수신자 주소 실수 또는 잘못된 체인
토큰은 주소가 맞더라도, 상대가 그 체인을 지원하지 않으면 사실상 분실처럼 됩니다.
예: 거래소 입금 주소에 다른 체인에서 전송
블록체인에서 취소와 회수는 기본값이 아닙니다.
4) 결제 시스템 비유로 다시 보기
USDC는 있는데 왜 전송이 안 되냐를 web2 결제 언어로 바꾸면 이렇습니다.
- USDC 잔액은 상품권 잔액
- 수수료 ETH 는 택배비
- transfer 는 내가 직접 보내는 택배 접수
- transferFrom 결제는 가맹점이 내 상품권 잔액에서 차감하는 모델
- approve 는 자동이체 권한 등록
상품권이 있어도 택배비가 없으면 발송을 못 하듯,
토큰이 있어도 수수료용 네이티브 코인이 없으면 전송이 안 됩니다.

5) 실패 시나리오별 체크리스트
| 증상 | 흔한 원인 | 확인 순서 |
|---|---|---|
| 즉시 실패 | ETH 수수료 부족, revert | ETH 잔액, 시뮬레이션 에러 |
| pending 오래 | 수수료 낮음, nonce 줄막힘 | 가스 제안값, pending tx |
| replaced | 같은 nonce로 더 높은 수수료 tx | 지갑 히스토리 |
| 보냈는데 상대가 못 받음 | 체인 불일치, 주소 네트워크 불일치 | 네트워크, 입금 안내 |
6) 체크리스트
1) 전송 자산과 수수료 자산이 다르다는 문장을 UI 에 박아둔다
2) 네트워크 선택을 사용자가 틀리기 어렵게 만든다
3) 토큰 전송은 컨트랙트 호출이라 revert 이유를 노출할 수 있으면 노출한다
4) 결제형이면 approve 와 transferFrom 모델을 명확히 구분한다
5) allowance 는 최소 권한 원칙으로 설정하고 무한 승인 UX 를 경계한다
6) pending 상태 머신을 구현하고 timeout 과 재시도 정책을 둔다
7) 입금 주소는 체인까지 포함해 검증한다
8) 최소 수수료 잔액 부족 시 안내와 onramp 동선을 제공한다
9) 운영자는 receipt 와 event 를 기반으로 상태를 추적한다
결론
- 토큰은 컨트랙트가 관리하는 2층 자산이고, 전송은 컨트랙트 호출이다
- 가스비는 네이티브 코인으로 내기 때문에, 토큰만 있으면 전송이 막힌다
- approve 와 transferFrom 은 결제형 UX 에서 자연스럽게 등장한다
- 체인 불일치는 실무에서 가장 비싼 실수다
'Road To Web3 > Blockchain' 카테고리의 다른 글
| Road to Web3 (10) 스마트컨트랙트는 DB 트랜잭션 함수다 (0) | 2025.12.29 |
|---|---|
| Road to Web3 (8) 가스비의 실체: 가스 사용량 곱하기 가스 가격 (0) | 2025.12.29 |
| Road to Web3 (7) 서명 2종류: 메시지 서명 vs 트랜잭션 서명 (0) | 2025.12.28 |
| Road to Web3 (6) 지갑은 키 관리 + 서명기: 주소/개인키/공개키 (0) | 2025.12.28 |
| Road to Web3 (5) 온체인 vs 오프체인: 임의 처리가 아니라 합의 경계 (0) | 2025.12.28 |
