From ripples to waves

스마트 컨트랙트와 RPC: 블록체인 네트워크와 상호작용하는 방법 본문

Blockchain, Web3

스마트 컨트랙트와 RPC: 블록체인 네트워크와 상호작용하는 방법

juyeong_ 2025. 7. 2. 13:51

스마트 컨트랙트와 RPC는 블록체인 네트워크 상에서 데이터를 읽고 쓰거나, 트랜잭션을 발생시키기 위해 프로그래머가 반드시 이해해야 할 필수 개념이다.
 
스마트 컨트랙트는 네트워크에 특정 함수를 배포하는 거라고 보면 된다. 트랜잭션을 발생시키고, 해당 트랜잭션 정보에 코드를 컴파일한 코드 데이터를 첨부한다. 그럼 해당 네트워크 상에 영구히 수정할 수 없는 함수 집합이 배포되는 것이다. 퍼블릭 네트워크에서는 누구든지 이 컨트랙트 주소만 알고 있다면 해당 컨트랙트의 함수들을 조회하고 실행할 수 있다.
 
RPC는 새로운 함수를 만들어서 네트워크에 배포하는 것이 아니라, 이미 존재하는 함수(예: 스마트컨트랙트의 view 함수 또는 블록체인 시스템 함수)를 호출할 때 사용된다. RPC(Remote Procedure Call)라는 개념은 원래 분산 시스템 전반에서 사용되던 통신 방식으로, 블록체인에 한정된 기술은 아니다. 일반적인 현대 웹 개발에서는 REST API를 많이 사용하지만, 블록체인에서는 함수 단위의 직접 호출이 필요한 경우가 많기 때문에, JSON-RPC 형식으로 메서드 호출을 하는 방식이 더 자연스럽고 적합하다.
 
RPC Call 중에서도 read 용도가 있고, write 용도가 있을 것이다. read는 단순히 네트워크 상태(예: 잔액, 토큰 보유량)를 조회하는 것으로, 트랜잭션이 아니며, 수수료나 서명이 필요 없다. 반면 write는 상태를 변경하는 호출로, 실제 트랜잭션이 발생하며 서명 및 가스 수수료가 요구된다. 이 경우 호출 내역은 블록에 기록되고, 누구나 검증할 수 있다.
 


 

RPC (Remote Procedure Call)

스마트컨트랙트나 블록체인 노드에 접근할 때, 일반적으로 HTTP + JSON-RPC 프로토콜을 사용한다.
 

POST / HTTP/1.1
Host: mainnet.infura.io
Content-Type: application/json

{
  "jsonrpc":"2.0",
  "method":"eth_getBalance",
  "params":["0xabc123...", "latest"],
  "id":1
}
  • 이건 이더리움 메인넷 노드에 잔액을 조회하는 요청이다
  • 이렇게 RPC를 통해 블록체인 노드에 직접 메서드 호출을 하는 구조
  • 흔히 쓰는 클라이언트로는 ethers.js, web3.js 등이 있다. 얘네는 내부적으로 이런 RPC 요청을 날린다.

 

ABI (Application Binary Interface)

스마트컨트랙트를 호출하거나 배포할 때 필요한 인터페이스 정의서
쉽게 말해, 컨트랙트의 함수와 변수에 어떻게 접근할 수 있는지 설명한 JSON 파일이다.
 

[
  {
    "constant":true,
    "inputs":[],
    "name":"totalSupply",
    "outputs":[{"name":"","type":"uint256"}],
    "type":"function"
  }
]

 

  • 이 ABI 정보를 기반으로 프론트엔드가 스마트컨트랙트의 특정 함수(e.g., totalSupply())를 호출할 수 있다.
  • ABI는 컴파일된 스마트컨트랙트에서 자동 생성되며, 프론트에서 ethers.Contract 등에 넣어서 사용한다

 

왜 블록체인 제품 개발에선 RPC를 주로 쓸까?

RPC 는 원격에서 프로시저(함수) 호출.
API, 특히 REST API 는 자원 중심적 행위 (특정 자원을 GET, POST, PATCH, DELETE ..)
 
어찌보면 RPC가 훨씬 Atomic 한 개념이라고 볼 수 있음. API 대비 보통은 low-level 하고 명령적인 단위.
 

RPC (Remote Procedure Call) vs API (REST API 등)

목적 함수 호출처럼 원격 명령 실행 자원(Resource)에 접근하거나 조작
구조 메서드명 + 파라미터로 요청 (eth_getBalance) HTTP URL 기반으로 요청 (GET /users/123)
주 사용처 블록체인 노드, 시스템 간 내부 통신 웹 서비스, 서버–클라이언트 구조
형식 JSON-RPC, gRPC 등 RESTful HTTP, GraphQL 등
철학 "저 함수 좀 실행시켜줘" "저 리소스 좀 보여줘/바꿔줘"

 

블록체인은 하나의 큰 상태 머신 (state machine) 임.

  • 상태를 읽거나,
  • 트랜잭션을 보내서 상태를 바꾸거나

이런 일들을 하는데 트랜잭션 = 함수 호출 단위로 처리된다. 

  • 상태 질의: eth_getBalance, eth_blockNumber
  • 상태 변경: eth_sendRawTransaction

그래서 REST보다는 RPC처럼 메서드를 직접 실행시키는 구조가 자연스럽다.
 
 

익스플로러와 같은 제품을 만든다면?

(익스플로러 제품이란?: 블록체인 네트워크는 기본적으로 투명하게 공개되어있으나, 일반적인 사람들이 이를 까보기는 사실상 어렵다. 개발자라고 할지라도 많은 노력이 필요하며 큰 구조를 한눈에 보기는 사실상 어렵다. 따라서 특정 메인넷 상에서 발생하는 Transaction, Block, 지갑 정보 등을 보기 편하도록 시각화해서 보여주는 서비스가 등장했는데, 그게 이더스캔, mempool 등의 익스플로러이다.)
 
이더스캔 등 익스플로러 제품을 만든다고 하면, 일반적으로는 다음과 같은 단계를 거칠 것이다.
굳이 스마트 컨트랙트 배포 없이, RPC 만으로도 기초적인 제품은 만들 수 있을 거라 생각한다.
 
 
1. [백엔드] RPC 호출해서 노드 정보를 가져오기
 

  • Alchemy, Infura 등에서 제공하는 JSON-RPC API를 사용해 새로운 블록 / 트랜잭션 정보를 읽어온다
  • getBlockByNumber, getTransactionByHash, getLogs 등의 RPC 메서드를 호출

 
 
2. [백엔드] 내부 DB 에 저장해서 자체 인덱스 만들기
 

  • 블록이 새로 생기면 RPC로 블록 데이터 + 트랜잭션 데이터를 파싱
  • 내부 DB에 저장 (예: blocks, transactions, wallets 테이블 등)
  • 즉, 블록체인을 스캔하며 자체 인덱스를 만드는 과정

 

blocks blockNumber, timestamp, miner, txCount...
transactions hash, from, to, value, gas, blockNumber...
wallets address, txCount, lastActiveTime...
tokens contractAddress, name, symbol, holders... (선택)

 
 
3. [백엔드] JSON으로 잘 말아서 REST API, GraphQL 등으로 프론트에 제공
 
4. [프론트] 마크업 개발
 
5. [프론트] API 연동 및 화면에 뿌려주기
 
 

스마트 컨트랙트

그럼 스마트 컨트랙트는 언제 필요한가?

바로 블록체인 상에서 "새로운 규칙"이나 "서비스 로직"을 만들고자 할 때. 예를 들어 다음과 같은 경우엔 스마트 컨트랙트 개발이 필요하다.
 
1. DeFi 프로토콜을 만들고 싶을 때

  • 예: 유동성 풀, 스왑, 렌딩 등
  • Uniswap, Aave 같은 서비스는 대부분 스마트컨트랙트로 로직을 구현

2. NFT 프로젝트를 런칭할 때

  • ERC-721 또는 ERC-1155 스마트컨트랙트를 직접 작성해서 배포
  • 민팅, 메타데이터 URI 관리, 로열티 로직 등

3. DAO (탈중앙 조직) 를 구성할 때

  • 의사결정, 투표, 거버넌스 로직 등을 스마트컨트랙트로 설계

4. 온체인 게임이나 인증 시스템을 만들 때

  • 게임 규칙, 자산 상태 관리, 승패 판정 등을 온체인에서 처리

5. 스테이블코인, 토큰 이코노미 설계

  • 예: 리베이스 토큰, 수익 공유 구조, 발행량 조절 로직 등

6. Web2 + Web3 연동 서비스

  • 예: 사용자가 Web2에서 가입하면 NFT를 발급해주는 등
  • 스마트컨트랙트를 통해 블록체인 상의 액션을 트리거

 
 

배포된 컨트랙트는 누구나 접근 가능

이더리움 메인넷은 퍼블릭 블록체인이라서 컨트랙트 주소와 ABI만 알면, 누구든 RPC를 통해 다음 작업이 가능하다.


view 함수 호출 eth_call 잔액 확인, 상태 조회 등 무료
트랜잭션 전송 eth_sendTransaction, eth_sendRawTransaction 실제 상태 변경 (예: 입금, 민팅 등)
이벤트 조회 eth_getLogs 이벤트 로그 필터링해서 보기 가능

 

예를 들어

const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider)
const balance = await contract.getBalance()
 
 

 

스마트 컨트랙트 간단한 예제 (Solidity)

사용자가 이더(ETH)를 보내면, 그에 따라 내부 카운터가 증가하는 식의 기초 입금 스마트 컨트랙트 예제를 살펴보자.
 

pragma solidity ^0.8.0;

contract SimpleDeposit {
    address public owner;
    uint public totalDeposits;

    constructor() {
        owner = msg.sender;
    }

    // 입금받기 (이더를 받을 수 있는 함수)
    receive() external payable {
        totalDeposits += msg.value;
    }

    // 잔액 조회
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }

    // 오너만 출금 가능
    function withdraw(uint amount) public {
        require(msg.sender == owner, "Not the owner");
        require(amount <= address(this).balance, "Insufficient balance");
        payable(owner).transfer(amount);
    }
}

 
주요 구성함수는 다음 3가지이다.
 

receive() msg.value를 통해 들어온 이더를 받고, 합계 저장
getBalance() 현재 컨트랙트가 보유 중인 이더 잔액 반환
withdraw() 소유자(owner)만 인출 가능

 
 
 

스마트 컨트랙트 배포 == 트랜잭션 발생시키기 (but 코드를 담은)

 
컨트랙트를 배포하는 것도 하나의 트랜잭션이다. 단지 일반적인 송금 트랜잭션과는 달리, 이 트랜잭션에는 코드를 담은 바이트코드가 실려 있다. 
 
 
배포 트랜잭션의 구조는 다음과 같다.

필드 내용
to 없음 (컨트랙트 생성 트랜잭션이라 비워둠)
data Solidity 코드가 EVM 바이트코드로 컴파일된 결과
from 배포자 주소
value (선택) 컨트랙트에 초기 이더 전송 가능
gas 컴파일된 바이트코드를 실행하고 저장하는 데 필요한 연산 비용

 
 
배포한 컨트랙트는 바꿀 수 없기 때문에 아래 사항을 잘 신경 써야 한다.

접근 제한 (onlyOwner) 누구나 호출할 수 있으므로 함수마다 접근 제어 필요
재진입 방지 (nonReentrant) 입출금 관련 함수는 재진입 공격 위험 있음
가스 최적화 메인넷 수수료 부담 큼
업그레이드 가능성 일반 컨트랙트는 불변이므로, 필요시 UUPS 패턴 등 도입