시냅스

Road to Web3 (10) 스마트컨트랙트는 DB 트랜잭션 함수다 본문

Road To Web3/Blockchain

Road to Web3 (10) 스마트컨트랙트는 DB 트랜잭션 함수다

ted k 2025. 12. 29. 20:36
이 글은 Ethereum 및 EVM 계열을 기준으로 설명합니다.

 

이번 편은 웹 백엔드 개발자가 가장 빨리 감을 잡는 비유를 사용합니다.

  • 스마트컨트랙트 함수 호출 = DB 트랜잭션 함수 호출
  • require = 사전 조건 검사
  • revert = 롤백

이 비유가 완벽하진 않지만, 초반에 뇌를 붙잡는 데는 매우 유효합니다.

 

제가 처음 스마트컨트랙트를 접할 때 했던 오해는 이렇습니다.

  • 컨트랙트는 마법 같은 자동화 서버다
  • 실패하면 아무 일도 안 일어난다
  • 트랜잭션은 값만 보내는 단순 송금이다

하지만 EVM에서 컨트랙트는 더 현실적입니다.

  • 상태를 가진 프로그램
  • 입력 calldata 를 받아 실행
  • 실행 결과로 상태를 변경하거나, 실패하면 롤백
  • 실행 비용은 가스로 청구

 

요약

웹 백엔드 EVM
API 호출 트랜잭션 또는 call
DB 트랜잭션 begin commit rollback 컨트랙트 실행 성공 또는 revert
사전 검증 validation require
예외 throw revert
트랜잭션 로그 테이블 이벤트 로그 event
DB 락 전역 직렬화 nonce 와 블록 순서
외부 시스템 오라클 브리지 인덱서

 

1) 컨트랙트는 상태를 가진 프로그램이다

컨트랙트는 코드와 상태가 같이 붙어 있습니다.

  • 코드: EVM 바이트코드
  • 상태: storage 슬롯 값들

web2로 비유하면

  • 코드: 배포된 서비스 바이너리
  • 상태: DB 테이블

컨트랙트 주소는 서비스 엔드포인트이자, 그 상태를 품은 저장소 네임스페이스입니다.


 

2) 컨트랙트 호출은 DB 트랜잭션 함수 호출과 닮았다

EVM에서 상태를 바꾸는 호출은 결국 트랜잭션입니다.

  • 트랜잭션이 블록에 포함되고
  • 모든 노드가 그 트랜잭션을 실행하고
  • 동일한 상태 변경을 재현해야 합니다

이 구조 때문에 컨트랙트 함수는 DB 트랜잭션 함수처럼 봐도 됩니다.

예를 들어 ERC20 transfer 는 이런 함수입니다.

  • balances[from] 감소
  • balances[to] 증가
  • Transfer 로그 기록

web2로 번역하면

  • update accounts set balance = balance - amount where id = from
  • update accounts set balance = balance + amount where id = to
  • insert into audit_log

단, EVM에는 트랜잭션 격리 수준을 선택하는 옵션이 없습니다.
체인이 정한 순서대로 전역 직렬로 실행됩니다.


 

3) require 는 사전 조건 검사다

Solidity에서 require 는 조건을 만족하지 않으면 즉시 실패합니다.

  • 잔액이 충분한가
  • 권한이 있는가
  • 입력이 유효한가
  • 상태가 기대한 값인가

web2에서라면 보통

  • validation 실패면 400
  • 권한 실패면 403
  • 비즈니스 조건 실패면 409

같은 식으로 분기할 텐데, 컨트랙트에서는 그냥 require 로 통일됩니다.

하지만 중요한 차이가 하나 있습니다.

  • require 실패는 롤백을 만든다
  • 그리고 실패해도 가스를 일부 소비한다

 

4) revert 는 롤백이다

컨트랙트 실행 도중 revert 되면

  • 지금까지의 상태 변경이 전부 무효
  • 이벤트 로그도 남지 않음
  • 다만 이미 소비한 실행 가스는 지불

web2 DB 트랜잭션으로 치면

  • begin
  • update update update
  • throw
  • rollback

과 거의 같습니다.


 

5) call 과 트랜잭션의 차이

여기서 혼동이 자주 생깁니다.

  • call eth_call: 상태를 바꾸지 않고 실행만 시뮬레이션
  • 트랜잭션: 상태를 실제로 바꾸는 커밋

web2 비유로는

  • call: read only 쿼리 또는 dry run
  • tx: 실제 commit 을 포함하는 write 트랜잭션

그래서 서비스는 보통

  • 먼저 call 로 실패 여부를 시뮬레이션하고
  • 그 다음 tx 를 보내는

패턴을 씁니다.


 

6) 동시성은 락이 아니라 전역 직렬화로 해결한다

web2에서는 여러 요청이 동시에 들어오면

  • 낙관적 락 버전 체크
  • 비관적 락 row lock

같은 걸 씁니다.

체인은 다르게 합니다.

  • 하나의 블록에서 트랜잭션은 정해진 순서로 실행
  • EOA 는 nonce 로 순서가 강제
  • 컨트랙트 상태는 실행 순서에 의해 결정

즉, 락 대신 순서를 고정해 직렬 실행을 만든다고 보면 됩니다.

하지만 이게 만능은 아닙니다.

  • 같은 블록 안에서 순서가 바뀌면 결과가 바뀐다
  • 그래서 순서 경쟁이 생기고, MEV 같은 현상이 등장한다

 

7) 이벤트 로그는 감사 로그이자 인덱싱 원재료다

컨트랙트는 이벤트 로그를 남깁니다.

  • Transfer
  • Approval
  • OrderFilled

이 로그는

  • 사용자에게는 트랜잭션 히스토리
  • 서비스에게는 인덱싱 원재료
  • 운영자에게는 감사 로그

역할을 합니다.

web2에서는 트랜잭션 로그 테이블과 CDC 같은 것을 붙이는 감각과 닮았습니다.


8) 체크리스트

1) 상태 변경 호출은 전부 트랜잭션이고 비용이 든다
2) require 는 validation 이 아니라 롤백 트리거다
3) revert 는 공짜가 아니고 가스를 소비한다
4) out of gas 는 실패 비용이 커서 최우선으로 줄인다
5) call 로 사전 시뮬레이션하되, 레이스 컨디션은 남는다
6) 이벤트는 진실 원천이 아니라, 상태 변경의 흔적이다
7) 인덱서는 reorg 와 중복을 전제로 설계한다
8) 컨트랙트 함수는 외부 호출과 재진입 위험을 가진다
9) 업그레이드 가능 컨트랙트는 운영 리스크가 더 크다
10) web2 결제처럼 상태 머신과 멱등성을 반드시 구현한다


결론

  • 스마트컨트랙트 호출은 DB 트랜잭션 함수 호출처럼 생각하면 이해가 빠르다
  • require 는 사전 조건, revert 는 롤백이다
  • call 은 시뮬레이션, 트랜잭션은 커밋이다
  • 락 대신 전역 직렬화로 동시성을 다룬다

 

Comments