Move 문해력 키우기(2): Move Design Pattern

verse2
verse2
Published in
11 min readMar 6, 2024

Move Design Pattern: Key takeaways

  1. Move는 Web3 자산을 안전하게 관리할 수 있는 다양한 디자인 패턴을 갖고 있다.
  2. Aptos Move는 리소스에 대한 엄격한 통제를 기본으로 하되, 리소스 모델의 한계를 극복하기 위해 오브젝트 모델을 함께 적용하고 있다.
  3. Move는 언어적 레벨에서 다양한 제약을 통해 개발자의 실수를 예방, 보다 안전한 컨트랙트 개발을 지향하는 언어이다.

도입

수 많은 Web3 해킹의 경우는 단 몇줄의 코드 작성 실수 또는 버그로 인해 발생했으며 이는 수십, 수백억 원의 피해를 초래했다. Move는 설계 단계부터 스마트 컨트랙트의 주요 목적인 자산 생성, 전송, 활용 세 가지에 집중하여 언어적 레벨에서 개발자의 인적 오류를 방지하고자 하였다. Move를 이용하여 컨트랙트를 개발해보면 자연스럽게 ‘이 자산의 소유자는 누구인가?’, ‘자산에 대한 권한을 갖고 있는가?’와 같이 현실에서 자산을 교환할 때의 논리와 동일한 흐름으로 개발을 하게 된다. 본 아티클에서는 Move의 어떤 디자인 패턴들이 이 같은 의식의 흐름을 만드는 지 살펴보도록 하겠다.

Pattern #1. Scarcity & Access control

Move는 희소성(scarcity)과 접근 제어(access control)라는 컨셉을 통해 Web3에 특화된 스마트 컨트랙트로써 안정성을 확보하고자 한다. 우선 Move 컨트랙트에서 자산을 어떤 방식으로 표현하는지를 우선 이해해야하는데, Move로 표현되는 모든 자산(asset)들은 기본적으로 구조체(struct)로 표현되는 리소스(resource)의 형태로 저장 된다. Move의 희소성은 이 리소스가 복제되는 것을 기본적으로 불가능하게 강제한다. 복제를 하기 위해서는 리소스에 명시적으로 복제(copy) 가능 여부를 표기해야만 가능하다.

이외에도 drop, key, store와 같은 어빌리티(ability)를 리소스에 명시 하여야만 원하는 기능들을 수행하는 것이 가능하다. Move에 존재하는 4가지 어빌리티에 대한 역할을 소개하면 다음과 같다.

  • copy: 리소스를 복제할 수 있도록 함
  • drop: 리소스를 제거할 수 있도록 함
  • store: 글로벌 스토리지(global storage)에 값을 저장할 수 있도록 함
  • key: 글로벌 스토리지 오퍼레이션을 수행할 수 있도록 함

어빌리티에 대한 구체적인 내용은 Aptos Move: Struct and its Abilities Explained — has Drop, Copy, Key, Store에서 알기 쉽게 설명하고 있으니 자세하게 알고 싶은 독자는 꼭 읽어볼 것을 권장한다.

희소성 이외에도 Move는 접근 제어를 통해 계정과 모듈에 대한 접근을 강력하게 통제하는 방식을 제공하고 있다. Move에서 모듈은 자산을 생성, 저장, 전송하는 프로그램을 의미하며, 모듈은 public 함수로만 외부에서 접근 가능하게 하여 모듈에 저장되어 있는 값의 변경을 엄격하게 제한하고 있다. 1차적으로 외부 호출에 대한 제약을 명시적으로 표현함으로써 재진입공격(re-entrancy attack)과 같은 고질적인 EVM 기반 스마트컨트랙트의 취약점을 방지하고 있는 것이다. 이외에도 Rust로 구현된 Aptos core 코드를 살펴보면 AccessPath라는 타입을 통해 리소스에 대한 접근 권한을 체크하게 되는데, address를 인자를 통해 해당 리소스에 대한 권한을 갖고 있는 계정인지를 체크하고, path 인자를 통해 모듈의 ID 값과 Struct Tag를 비교하여 적절한 경로인지를 확인하게 된다.

types/src/access_path.rs

특히, 트랜잭션을 통해 모듈의 함수를 실행할 때에는 트랜잭션에 서명한 서명자(signer)를 인자로 갖게 되는데, 이 서명자를 활용하여 모듈의 리소스에 대한 접근 권한을 관리 할 수 있다. 예를 들어 아래와 같이 transfer라는 함수를 실행하기 위해서는 &signer 타입을 가진 sender가 갖고 있는 자산을 FungibleStore라는 리소스에서 withdraw 함수를 통해 값을 가져오게 되는데, 리소스에 대한 접근은 transfer 함수를 호출하는 트랜잭션에 서명한 계정의 값을 확인하여 권한을 확인한다. 즉, 트랜잭션을 호출하는 계정에 대한 정보를 함수의 인자로 자동적으로 포함시킴으로 코드 레벨에서 특정 리소스에 대한 접근 권한을 체크하게 된다. Move를 이용한 스마트 컨트랙트 개발 시에는 이처럼 접근 권한을 지속적으로 체크하며 개발하는 것이 필수적이다.

aptos-move/framework/aptos-framework/sources/coin.move

Tip. Move는 컨트랙트 상의 값인 리소스에 대한 변경과 접근을 엄격하게 통제한다.

Pattern #2. Global Storage

리소스에 대해 간략하게 요약하면 Move에서 표현되는 특정 데이터 값들을 의미한다. 리소스가 어떠한 형태로 어디에 저장되는 지를 알기 위해서는 글로벌 스토리지에 대한 이해가 필요하다. 글로벌 스토리지란 리소스와 모듈로 구성된 트리 형태(tree-shaped)의 구조체로 구현된 영속(persistent) 저장소이다. Move 컨트랙트의 역할은 바로 이 글로벌 저장소에 값을 저장하거나 읽는 행위를 비즈니스 로직에 맞게 구현하는 것이다.

위에 작성된 GlobalStorage의 수도 코드에서 알 수 있듯이, 리소스와 모듈은 address와 맵핑되어 값을 저장하고 있는데 이를 통해 특정 주소를 통해 접근을 제어하는 것을 확인할 수 있다. 글로벌 스토리지에 리소스를 생성하거나 제거, 업데이트하기 위해 아래와 같은 글로벌 스토리지 오퍼레이션(global storage operation)을 사용해야만 한다. 여기서 재밌는 점은 borrow와 같은 키워드에서 알 수 있듯이 Move가 Rust의 ownership 모델을 참고하여 구현 되었다는 것을 알 수 있다.

각 오퍼레이션에는 <T>와 같이 파라미터화 되어있는 제너릭 타입이 있는데, 이 T 타입은 반드시 현재 Move 모듈 내에서 존재하는 리소스 타입이어야만 한다. 이 역시 리소스가 해당 리소스를 정의한 모듈 내에서만 조작되는 것을 허용하기 위해서 정해진 제약으로 값의 변경에 대한 권한을 확인하는데 컴파일 단계에서 확인하는 것을 가능하게 한다. 또한 move_to와 같은 오퍼레이션은 &signer를 인자로 받으며, 이는 특정 리소스를 특정 계정에 배포하기 위해서는 해당 계정의 권한을 갖고 있어야만 가능하게 하는 것을 의미한다. 단순히 address 인자만을 요하는 오퍼레이션들은 해당 계정의 권한과 상관없이 호출하는 것이 가능하다.

Tip. &signer 타입은 트랜잭션에 서명한 계정이며 해당 계정이 갖고 있는 리소스에 대한 최상위 권한을 가진다.

Pattern #3. Object Model

Aptos에서 2023년 8월 AIP-10, 11을 통해 디지털 자산(Digital Asset)과 대체 가능 자산(Fungible Asset)에 대한 새로운 표준을 발표했는데, 이는 오브젝트 모델(Object Model)이라는 새로운 디자인 패턴에 대해 소개였으며 Aptos Move에 많은 변화를 가져다 주었다. 리소스 모델은 소유권 기반하에 안전한 컨트랙트 개발에 도움을 주지만 여러가지 제약 사항을 갖고 있다. 리소스 모델의 이슈들은 다음과 같다.

  1. 낮은 데이터 가용성: 리소스 모델은 데이터에 대한 접근을 보장하지 않는다. 유저가 자신의 계정 내 커스텀 저장소에 있는 값을 외부에서 조회하는데에는 많은 제약이 있다. 그렇기 때문에 데이터를 직접 참조하는데 어려움이 있으며, 데이터를 직접 참조하기 위해서는 복잡한 과정을 수행해야만 한다. 예를 들어, 리소스 모델 기반의 토큰 발행시에는 유저가 코인을 보유하고 있다는 정보를 토큰 컨트랙트에 등록하는 과정을 필요로 하는 것이 그 예시이다.
  2. 데이터 타입 구분의 어려움 : 리소스 모델에서는 any 타입을 이용하여 하나의 데이터에 여러 타입을 저장할 수가 있는데, 이 any 타입은 별도의 역직렬화(deserialization) 과정을 요구하고 이는 컨트랙트 개발자가 any 타입을 처리할 때 많은 번거로움을 요한다.
  3. 데이터 구조의 비효율: Aptos Move에는 연관된 리소스를 묶어 놓은 리소스 그룹(resource group)이라는 타입이 존재하여 복잡한 데이터 구조를 다룰 때 효율성을 제공하는데, 리소스 모델에서는 이를 활용하는데 어려움이 있다. 리소스 그룹은 데이터의 지역성(locality)을 높히고 저장 비용을 줄이는데, 글로벌 저장소를 통해 값에 대한 접근이 가능한 리소스 모델만을 사용할 경우 이러한 이점을 얻는 것이 불가능하다.

이외에도 재귀적 타입을 표현하기 어렵다는 문제와 컨트랙트에서 이벤트를 발생시키는데 어려움이 있다는 문제가 있다. 이를 극복하고자 제안된 오브젝트 모델은 아래와 같은 그림으로 표현 가능하다.

Token v1의 경우 유저가 소유한 자산을 등록(register)하는 과정을 거쳐야만 데이터에 대한 추적이 가능하지만 Token v2와 같은 오브젝트 모델에서는 Token이 발행됨과 동시에 새로운 오브젝트 계정 주소가 생성이 되어 해당 오브젝트 계정에 값을 저장하게 된다. 이는 아래와 같은 ObjectCore라는 리소스로 정의되어 생성될 때 부여 받는 guid값을 통해 구별되며, 해당 오브젝트를 생성한 소유자(owner)의 정보를 갖게 된다. 이렇게 생성되는 오브젝트는 글로벌 주소를 통해 접근하는 것이 가능하게 되어 보다 직관적이고 간단하게 데이터를 처리하는 것이 가능하다.

오브젝트 모델이 가져다 준 효과를 정리하면 아래와 같다.

1. 데이터 접근성: 항상 접근이 가능하지 않음 → 참조자(reference)를 이용한 접근성 보장

2. 데이터 타입: any 타입을 이용한 제너릭 타입핑 → 명시적인 Object 저장소 주소 활용

3. 데이터 확장성: 모듈별 제한적 API 활용 → 모듈에 상관없이 전역 접근 가능

4. 데이터 효율성: 리소스 로딩 시 오버헤드 발생 → 개별 리소스 로딩 불필요

오브젝트 모델에 대해 Move가 리소스 모델 기반으로 컨트랙트의 안정성을 보장하려는 철학을 위배한다는 의견도 존재하지만, 개발적인 측면에서의 이점이 분명하기 때문에 Move가 프로그래밍 언어로써 성장하기 위해서는 꼭 필요한 디자인 패턴이라 생각된다.

마무리

Move 언어와 Aptos 플랫폼을 통해 제시된 디자인 패턴들은 Web3 환경에서 자산 관리의 안정성과 개발 효율성을 크게 향상시킨다는 것을 본 아티클을 통해 살펴보았다. 희소성, 접근 제어, 글로벌 스토리지, 오브젝트 모델과 같은 패턴들은 개발자가 보다 안전하게 스마트 컨트랙트를 설계하고 구현할 수 있는 환경을 제공한다. 이러한 패턴들은 기술적인 측면을 넘어서, 개발자가 자산에 대해 더 깊이 이해하고, 실수를 줄이며, 더욱 강력하고 안전한 애플리케이션을 구축할 수 있게 하는 기반을 마련하는 데 큰 역할을 하고 있다. 특히 주목할 부분은 오브젝트 모델의 도입과 같이 데이터의 접근성, 타입 안정성, 확장성, 효율성을 향상시키려는 다양한 시도가 있다는 점이다. 이는 Move를 사용하는 개발자들이 더 넓은 범위의 애플리케이션과 사용 사례를 탐색할 수 있는 기회를 제공한다고 생각된다.

Reference

  1. Why the New Aptos Token Object Is a Game Changer
  2. Aptos Move: Struct and its Abilities Explained — has Drop, Copy, Key, Store

작성자 : Harvey
검수자 : Ryan

verse2는 DeFi 서비스 개발에 전문화된 팀이자, 높은 잠재력을 가진 Crypto 프로젝트의 인큐베이터입니다. 팀은 다양한 프로토콜을 개발 및 운영하여 DeFi 분야에 대한 심층적인 지식과 경험을 보유하고 있습니다.

verse2 [Homepage | Twitter | Medium]

--

--

verse2
verse2
Editor for

Build, incubate, invest — Making all possible in the crypto. / verse2.io