> THE-ARSENAL
> _

우리가 발을 딛고 있는 이 디지털 세계는 종종 무한한 풍요의 공간으로 오해받는다. 데이터는 복제되고, 저장은 거의 무료이며, 대역폭은 넘쳐난다. 하지만 블록체인, 특히 이더리움의 세계는 정반대의 철학 위에 세워져 있다. 이곳은 풍요가 아닌 극도의 '희소성'이 지배하는 영역이다. 이곳의 모든 연산, 모든 저장 공간, 심지어 트랜잭션에 실어 보내는 데이터 한 바이트조차도 '가스(Gas)'라는 이름의 냉정한 비용으로 환산된다.

 

이유는 간단하다. 당신이 실행하는 transfer 함수 하나는 당신의 컴퓨터에서만 실행되는 것이 아니다. 그것은 전 세계에 흩어진 수천, 수만 대의 노드 컴퓨터가 동시에 검증하고 실행해야 하는 '공공의 명령'이다. 이 무거운 책임을 아무런 대가 없이 허용한다면, 시스템은 악의적인 while(true) 루프 하나만으로도 즉시 마비될 것이다. 따라서 가스는 이 분산 네트워크를 무의미한 작업으로부터 보호하는 핵심적인 방어 기제다.

 

이 '모든 바이트는 돈이다'라는 대전제 아래에서, 시스템 설계자들은 극단적인 효율성을 추구해야만 했다. 단 한 바이트라도 아낄 수 있다면, 그것은 수백만 건의 트랜잭션에 걸쳐 엄청난 사회적 비용을 절감하는 것과 같았다. 이 치열한 최적화의 전쟁터 한가운데에, 오늘 우리가 이야기할 '함수 선택자(Function Selector)'라는 기가 막힌 타협안이 존재한다.

 

 

비효율이라는 이름의 원죄

컨트랙트는 수많은 기능을 가진 하나의 거대한 기계다. transfer, approve, mint, burn 등 수십 개의 함수가 각자의 역할을 기다리고 있다. 우리가 이 기계에 작업을 지시하려면, "정확히 어떤 기능을 실행하라"고 알려줘야 한다. 이 '지시'는 calldata라는 데이터 묶음에 담겨 네트워크에 전송된다.

 

여기서 첫 번째 질문이 생긴다. 이 지시를 어떻게 표현해야 가장 효율적일까?

 

가장 인간친화적인 방법은 우리가 아는 그대로, 함수 이름 전체를 문자열로 보내는 것이다. transfer(address,uint256)라고 텍스트 그대로 보내는 방식이다. 하지만 이는 상상할 수 있는 최악의 비효율이다. transfer(address,uint256)는 무려 26바이트다. approve(address,uint256)는 27바이트다. 함수 이름이 길어지면 50바이트, 100바이트가 될 수도 있다.

 

이 방식은 두 가지 재앙을 초래한다. 첫째, 가스비가 폭증한다. calldata의 모든 바이트는 비용이다. 둘째, EVM(이더리움 가상 머신)이 이 문자열을 해석하기 위해 복잡한 '문자열 비교' 로직을 수행해야 한다. 이는 연산 측면에서도 끔찍한 낭비다. 이 방식은 애초에 고려 대상이 될 수 없었다.

 

 

완벽함이라는 이름의 낭비

그렇다면 다음 대안은 무엇인가? 텍스트가 안 된다면, 고유한 식별자를 쓰면 된다. 가장 확실한 고유 식별자는 암호학적 해시다. 함수 시그니처(transfer(address,uint256))를 Keccak-256으로 해시하면, 0xa9059cbb...로 시작하는 32바이트(256비트) 길이의 완벽하게 고유한 '지문'이 생성된다.

 

이 방식은 완벽해 보인다. 충돌 가능성은 사실상 0에 가깝다. 모든 함수가 자신만의 32바이트 ID를 갖는다. 하지만 이 '완벽함'은 이더리움의 세계에서는 '낭비'의 다른 이름이다.

 

생각해 보라. 우리가 필요한 것은 단지 '함수를 구별하는 것'뿐이다. 이를 위해 32바이트 길이의 거대한 식별자를 매번 트랜잭션에 실어 보내는 것은 엄청난 가스 낭비다. 수신자인 컨트랙트 역시, 이 32바이트짜리 ID를 자신의 함수 목록과 일일이 비교해야 한다. 이 역시 비싼 연산이다. 이더리움은 128비트 UUID가 수십억 개 테이블의 행을 식별하는 풍요로운 데이터베이스 서버가 아니다. 이곳은 한정된 자원을 놓고 경쟁하는 가장 비싼 땅이다. 32바이트는 명백한 사치였다.

 

 

4바이트라는 이름의 위대한 타협

그래서 EVM 설계자들은 지극히 실용적이고도 대담한 '타협'을 선택한다. "32바이트는 낭비다. 그렇다면 식별에 필요한 최소한의 바이트는 몇 개일까?"

 

그들의 답은 4바이트였다.

 

함수 시그니처의 Keccak-256 해시값에서, 전체 32바이트를 쓰지 않고, 오직 맨 앞의 4바이트만 떼어내어 그것을 '함수 선택자'로 삼기로 한 것이다. transfer(address,uint256)의 선택자는 0xa9059cbb가 되었다.

 

4바이트는 32비트다. 이는 약 43억() 개의 고유한 조합을 만들 수 있다는 의미다. 누군가는 반문할 수 있다. 43억 개? 전 세계의 모든 컨트랙트가 쓰는 함수가 43억 개를 넘으면 어떡하나? 충돌이 발생하지 않을까?

 

 

이것은 확률에 대한 오해다. 이 위험은 전 세계의 함수가 충돌할 위험이 아니다. 진짜 위험은, 단 하나의 컨트랙트 내에서 두 개의 서로 다른 함수가 우연히 동일한 4바이트 선택자를 가질 확률이다. 한 컨트랙트에 100개의 함수가 있다고 해도, 그중 두 개가 43억 분의 1의 확률로 겹치는 것은 거의 불가능에 가깝다.

 

EVM 설계자들은 이 '거의 불가능에 가까운' 충돌 위험을 감수하는 대신, 모든 함수 호출에서 28바이트(32바이트 - 4바이트)의 calldata를 아끼는 실리를 택했다. 이것은 '이만하면 됐다(good enough)'는 공학적 실용주의의 정수다.

 

 

교환기, 그 자체의 효율성

이 4바이트 최적화는 컨트랙트 내부의 작동 방식까지 극적으로 단순화했다. 트랜잭션이 컨트랙트에 도달하면, calldata의 맨 앞 4바이트가 컨트랙트의 '교환기(Dispatcher)'로 전달된다.

 

이 교환기는 컴파일러가 생성한 매우 효율적인 코드 덩어리다. 그 역할은 calldata의 4바이트 선택자 값을 자신이 아는 함수 목록과 비교하여, 일치하는 코드 블록으로 실행 흐름을 '점프'시키는 것이다.

 

만약 선택자가 32바이트였다면, 이 교환기는 매번 32바이트짜리 값들을 비교해야 했을 것이다. EVM에서 32바이트 비교(EQ) 연산은 4바이트 비교보다 훨씬 더 많은 가스를 소모한다. 4바이트 선택자는 이 교환기 로직마저 가볍게 만들었다. 시스템은 더 적은 calldata를 받고, 더 적은 비교 연산을 수행한다. 모든 단계에서 가스가 절약된다.

 

 

희소성이 낳은 최적화

함수 선택자는 단순히 함수를 식별하는 기술적인 세부 사항이 아니다. 그것은 이더리움이라는 플랫폼의 근본적인 철학, 즉 '희소성'이 낳은 가장 실용적인 산물이다.

 

만약 이더리움이 무한한 자원을 가진 시스템이었다면, 우리는 지금도 32바이트짜리 완벽한 해시값을 식별자로 쓰고 있을 것이다. 하지만 현실은 다르다. 모든 바이트가 비용인 이 가혹한 환경에서, 4바이트 선택자는 '완벽한 고유성'이라는 이상 대신 '충분한 고유성'과 '극단적인 비용 절감'이라는 현실을 선택한 위대한 타협이다.

 

우리가 매일같이 지불하는 가스비는, 사실 이 4바이트라는 작은 압축 기술 덕분에 그나마 저렴하게 유지되고 있는 것이다. 4바이트 선택자는 가스가 제한된 환경을 위해 탄생한, 지극히 실용적이고 경제적인 최적화의 상징이다.

 

 

3줄 요약
[1] 이더리움의 모든 데이터는 가스 비용이 발생하므로, 함수 식별자는 극도로 압축되어야 했다.
[2] 함수 시그니처의 해시값 32바이트 전체 대신, 4바이트(약 43억 개)만 사용하여 충돌 위험을 감수하고 가스비를 획기적으로 절약했다.
[3] 이 4바이트 선택자는 트랜잭션 데이터 크기를 줄일 뿐만 아니라, 컨트랙트 내부의 라우팅(비교) 비용까지 절감하는 핵심 최적화다.