솔리디티 스마트 컨트랙트에서 가스 요금을 최적화하는 방법
이 가이드는 솔리디티에서 스마트 컨트랙트를 작성할 때 가스 비용을 최적화하는 방법에 대한 실용적인 단계별 안내를 제공합니다.
가스 최적화가 중요한 이유는 무엇인가요?
가스 최적화는 스마트 컨트랙트 개발의 중요한 부분입니다. 이는 네트워크 혼잡도가 높은 상황에서도 스마트 컨트랙트가 효율적이고 비용 효율적으로 유지되도록 도와줍니다. 개발자는 컨트랙트 실행의 연산 오버헤드를 줄임으로써 트랜잭션 수수료를 낮추고, 확인 시간을 단축하며, 디앱의 전반적인 확장성을 개선할 수 있습니다.
개발자에게 있어 가스 최적화는 불필요한 계산을 최소화하는 깔끔하고 안전하며 예측 가능한 코드를 작성하는 것입니다. 사용자 입장에서는 과도한 수수료를 지불하지 않고도 계약과 상호작용할 수 있도록 보장하는 것이 중요합니다.
Kaia에서 특히 중요한 이유
83개 이상의 미니 디앱이 출시되었고 계속 증가하고 있는 Kaia 블록체인은 이러한 온체인 애플리케이션의 폭발적인 성장에 힘입어 거래량에서 선도적인 EVM 호환 체인으로 부상했습니다.
각 미니 디앱은 온체인 작업을 수행하기 위해 스마트 컨트랙트에 의존합니다. 아이템 채굴, 베팅, 게임 내 자산 관리 등 모든 컨트랙트 상호작용은 가스를 소모합니다. 최적화가 이루어지지 않으면 이러한 디앱은 사용자가 상호작용하기에는 너무 비싸질 수 있으며, 특히 대규모로 사용할 경우 더욱 그렇습니다.
그렇기 때문에 가스 효율은 단순히 있으면 좋은 것이 아닙니다. 필수입니다. Kaia를 기반으로 구축하는 개발자는 기능과 보안을 유지하면서 비용을 최소화할 수 있도록 각 함수 호출을 최적화해야 합니다.
가스 최적화 기술
보관 포장
블록체인에 데이터를 저장하고 검색하는 작업은 특히 데이터가 트랜잭션과 블록에 걸쳐 지속되어야 할 때 가장 가스 비용이 많이 드는 작업 중 하나입니다. 솔리디티에서 이 데이터는 계약 스토리지에 저장되며, 이는 영구적이고 가스 비용이 발생합니다. 이러한 비용을 줄이려면 개발자는 특히 상태 변수를 선언할 때 스토리지 사용 방식을 신중하게 최적화해야 합니다.
Kaia 가상 머신(KVM)은 스토리지 슬롯이라는 단위로 계약 데이터를 저장합니다. 각 스토리지 슬롯에는 정확히 256비트(32바이트)의 데이터를 저장할 수 있습니다. 솔리디티 데이터 유형은 다양한 크기로 제공되며, 예를 들어 부울은 1바이트, 주소는 20바이트입니다.
스토리지 패킹이라는 기술을 통해 작은 변수를 하나의 32바이트 스토리지 슬롯에 맞게 촘촘하게 배열할 수 있습니다. 이렇게 하면 하나의 스토리지 슬롯에서 읽거나 쓰는 것이 여러 개의 스토리지 슬롯에 액세스하는 것보다 훨씬 저렴하기 때문에 가스 사용량을 줄이는 데 도움이 됩니다.
다음 예시를 살펴보겠습니다:
분석:
최적화되지 않은 버전(SlotUnOptimized)에서 솔리디티는 구조체를 다음과 같이 저장합니다:
- 주소 -> 20바이트 소요 -> 슬롯 0에 저장
- uint256 숫자확인 -> 32바이트 필요 -> 슬롯 1에 저장됩니다.
- uint80 값(10바이트) 및 bool 실행(1바이트) -> 슬롯 2에 저장됩니다.
값**과 실행 변수는 크기가 작지만, 명시적으로 순서를 바꾸지 않는 한 정렬 패딩으로 인해 솔리디티는 자체 스토리지 슬롯에 배치합니다. 결과적으로 이 구조는 3개의 스토리지 슬롯을 사용하므로 스토리지 운영에 필요한 가스 비용이 3배가 됩니다. 그러나 '주소(20바이트) + uint80(10바이트) + bool(1바이트)`의 총 크기는 31바이트이며 단일 슬롯의 32바이트 한도 내에 해당합니다. 작은 변수가 함께 그룹화되도록 선언의 순서를 바꾸기만 하면 솔리디티는 변수를 같은 슬롯에 넣을 수 있습니다. 이것이 바로 스토리지 포장의 본질입니다.
위와 같이 최적화된 버전(SlotOptimized)에서는 모든 작은 변수가 서로 인접하여 배치되므로 컴파일러가 더 적은 슬롯에 저장할 수 있어 배포 및 런타임 가스 비용이 절감됩니다.
캐시 스토리지
스토리지 슬롯에 변수가 배치되는 방식 외에도 스토리지 액세스 및 수정과 관련된 가스 비용을 이해하는 것도 중요합니다.
Kaia 가상 머신의 각 스토리지 슬롯에는 비용이 듭니다: 초기화(첫 번째 쓰기) 20,000 가스 업데이트(후속 쓰기) 5,000 가스 이 때문에 특히 자주 호출되는 함수에서 직접 스토리지 읽기 및 쓰기 횟수를 최소화하는 것이 중요합니다. 한 가지 효과적인 패턴은 함수 내에서 여러 번 액세스해야 할 때 저장소 변수를 메모리에 캐시하는 것입니다.
다음 예시를 살펴보겠습니다:
변수를 기본값으로 초기화하지 않기
솔리디티의 모든 데이터 유형에는 미리 정의된 기본값이 있습니다. 예를 들어 address의 기본값은 address(0), bool의 기본값은 false, uint의 기본값은 0입니다. 개발자가 변수 선언 중에 bool isActive = false
또는 uint total = 0
을 작성하는 등 이러한 기본값을 명시적으로 지정할 때 일반적인 비효율성이 발생합니다.
기능적으로는 올바르지만, 솔리디티가 기본적으로 이러한 값을 설정하기 때문에 배포 중에 불필요한 가스 비용이 발생합니다. 상태 변수에 값을 할당하지 않고 선언하면 컨트랙트의 바이트코드 크기를 줄이고 추가 저장소 작업을 피할 수 있습니다. 이 작은 조정은 특히 여러 변수를 다룰 때 스마트 컨트랙트를 더 효율적이고 쉽게 유지 관리할 수 있도록 도와줍니다.
다음 예시를 살펴보겠습니다:
온체인 데이터 최소화
거래에서 발생하는 가스 비용의 대부분이 컨트랙트 스토리지에 저장된 데이터에서 발생한다는 사실을 잘 알고 있습니다. 어떤 데이터를 실제로 체인에 저장해야 하는지, 아니면 오프체인에 저장해야 하는지 항상 질문하고 두 옵션의 장단점을 고려하는 것이 가장 좋습니다. 이는 완전 온체인 NFT의 경우와 오프체인 메타데이터를 사용하는 기존 NFT에 비해 얼마나 비싼지를 통해 확인할 수 있습니다. 즉, 스토리지에 더 적은 변수를 할당했기 때문에 정보를 오프체인에 저장함으로써 스마트 컨트랙트의 가스 소비를 크게 줄일 수 있습니다.