[KO] 솔라나에서의 NFT 구현 2편: 캔디머신을 활용한 NFT 민팅 과정 이해하기

c0wjay
DSRV
Published in
21 min readJun 27, 2022
캔디머신을 이용하여 솔라나 NFT 민팅 과정 이해하기

DSRV Dev Guild에서는 더 많은 개발자들과 Web3 인프라를 만들어가기 위해, 다양한 메인넷과 스마트 컨트랙트에 대한 가이드를 연재합니다.

Disclaimer: 이 글은 정보 전달을 위한 목적으로 작성되었으며, 특정 프로젝트에 대한 투자 권고, 법률적 자문 등 목적으로 하지 않습니다. 모든 투자의 책임은 개인에게 있으며, 이로 발생된 결과에 대해 어떤 부분에서도 DSRV는 책임을 지지 않습니다. 본문이 포괄하는 내용들은 특정 자산에 대한 투자를 추천하는 것이 아니며, 언제나 본문의 내용만을 통한 의사결정은 지양하시길 바랍니다.

[솔라나 NFT 시리즈]

  1. 솔라나 기본 개념 이해하기 - 컨트랙트와 스토리지의 구조
  2. 캔디머신을 활용한 NFT 민팅 과정 이해하기
  3. 캔디머신 코드 분석해보기

시작하기 전에..

지난 번 [솔라나 NFT 시리즈] 1편: 솔라나 기본 개념 에서는 솔라나에서 ‘Program Account’와 ‘Data Account’가 각각 어떻게 구현되어 있는지 알아봤습니다. 또한, 트랜잭션의 구성과 프로그램이 어떻게 실행되는지에 대해서도 알아보았습니다. 그 과정에서 가장 중요한 개념인 PDA와 CPI에 대해 다루었고, 마지막으로는 솔라나에서 NFT가 어떻게 구현되어 있는지를 다루어 보았습니다.

이번 편에서는 메타플렉스(Metaplex)의 캔디머신(Candy Machine)을 이용하여 솔라나에서 NFT를 민팅하는 과정을 상위 수준의 코드로 다룹니다. 캔디머신은 메타플렉스가 만든 프로그램으로, NFT 프로젝트 팀이 NFT 컬렉션을 민팅하는 과정을 쉽게 만들 수 있도록 도와주는 일종의 SaaS(혹은 Program as a Service) 툴입니다.

본 글은 pencilflip.sol 의 트위터 타래를 참고하여, 현재 캔디머신 v4.0.0 업데이트를 반영하여 작성하였습니다. 원본 트위터 타래는 캔디머신 v1을 기점으로 작성되어 있습니다. 원 트윗 작성자의 아래 GitHub 저장소를 참고하면 이해에 도움이 되실 것입니다.

1. 실습해보기

1편의 마지막에서, 솔라나 NFT 발행 프로세스에 대해 가볍게 언급했는데요. 캔디머신의 동작 원리에 대해 살펴보기 전에, 먼저 캔디머신을 활용하여 NFT를 민팅하는 실습을 진행하겠습니다.

1.1. 개발 환경 설정

메타플렉스를 활용하기 위해서는 Solana CLI가 설치되어 있어야 합니다. 우리는 데브넷(Devnet, 이하 데브넷) 클러스터를 이용하여 실습을 진행할 것이므로, 월렛에 테스트용 SOL을 어느 정도 보유해야 합니다. 또한, Metaplex 저장소를 clone하여 개발 환경을 설정해야 합니다.

먼저, 다음 메타플렉스 공식 문서를 참고하여 각자 컴퓨터에 맞게 개발 환경 설정을 마쳐주세요. 개발환경이 성공적으로 설치되었다면, 터미널 창에 다음 명령어를 입력하여 도움말을 얻을 수 있습니다.

ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts --help

또한 아래 명령어를 입력하여 candy-machine 이라는 shell text의 별칭(alias)을 설정합니다. candy-machine --help 명령어를 입력하면 위 명령어를 입력했을 때와 같은 도움말을 받을 수 있습니다.

alias candy-machine="ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts"

마지막으로, solana address를 입력하여 솔라나 데브넷 지갑의 주소를 얻습니다. 우리는 이번 실습 동안에 아래와 같은 FiUvmTBZj5ZZwJn2sJdxRpHjUyS8JeWXVDcimwSRqGoY 를 지갑 주소로 사용하겠습니다.

1.2. 캔디머신 업로드 파일 준비

먼저, 홈 디렉토리에 nft-example 이라는 디렉토리를 만들겠습니다. 해당 디렉토리에는 아래와 같이 NFT 관련 파일을 준비할 것입니다.

nft-example
├── assets
│ ├── 0.json
│ └── 0.png
└── config.json

다음 코드는 config.json 파일입니다. 캔디머신 PDA 계정의 설정과 관련된 파일입니다. 캔디머신 PDA 계정에서는 아래에서 자세히 다룰테니, 지금은 간단하게 “민팅(Minting) 관련 설정 값" 정도라고 이해하고 넘어가면 됩니다.

💡 우리가 이번 시간에 실습해볼 때는, whitelist mint 설정이나 hidden settings 등 자세한 설정을 제외한, NFT 발행에 필요한 기본값을 설정할 것입니다.

"solTreasuryAccount": "<지갑 주소>" 에는 본인의 데브넷 지갑 주소를 입력해주세요. 저의 경우, FiUvmTBZj5ZZwJn2sJdxRpHjUyS8JeWXVDcimwSRqGoY를 입력했습니다.

💡 Configuration의 각 설정 값이 궁금하시다면 아래 3. 부록을 참고해주세요!

저는 그림 2–1과 같은 DSRV의 로고 이미지를 사용하여 NFT를 발행하고자 합니다. 독자 여러분의 경우, 각자 사용하고자 하는 NFT 이미지를 0.png 로 이름을 바꿔, ~/nft-example/assets에 저장해주세요.

[그림 2–1] DSRV 로고 이미지, 출처: DSRV

다음 코드는 0.json 파일인데요. 이 파일은 우리가 발행할 NFT의 메타데이터에 해당됩니다. 0.png 파일과 같은 위치에 저장해주세요.

1.3. 캔디머신에 업로드

먼저, solana balance 명령어를 통해, 지갑에 충분한 SOL을 보유하고 있는지 확인합니다. 만약 부족하다면, solana airdrop 2를 입력하여 Faucet으로부터 SOL을 지급받습니다. 그리고 cd ~/nft-example 명령어를 입력하여 프로젝트 디렉토리로 이동합니다.

다음 명령어를 입력하여, 업로드할 이미지와 메타데이터를 확인합니다.

candy-machine verify_assets ./assets

정상적으로 이미지 파일과 메타데이터 파일 정보가 출력된 것을 알 수 있습니다.

다음 명령어를 입력하여, 캔디머신 PDA 계정을 초기화하고 NFT를 Arweave에 업로드합니다.

candy-machine upload -e devnet -c example -k ~/.config/solana/devnet.json -cp config.json ./assets

다음 사진은 upload 명령어를 입력하여 생성된 캔디머신 PDA 계정입니다.

💡 DSRV’s Tip: Arweave(알위브)란 무엇인가요? 
Arweave(알위브)는 Web3의 모든 데이터를 담아내려는 목표를 가진 탈중앙화 분산 스토리지 서비스입니다. 자체적으로 Blockweave와 Endowment Pool라는 방식을 사용하여 스토리지 서비스의 확장성을 높였습니다. NFT의 메타데이터를 담아내는 탈중앙화된 방식으로 IPFS와 Arweave 두 가지를 꼽아볼 수 있습니다. 솔라나 NFT 생태계에서는 주로 Arweave를 사용하여 메타데이터를 보관합니다. [3]

1.4. NFT 민팅

candy-machine verify_upload -e devnet -c example -k ~/.config/solana/devnet.json

먼저 NFT를 민팅하기 전에, 위 명령어를 통해 NFT의 메타데이터가 업로드가 잘 되었는지 확인합니다. 아래는 실행 결과입니다.

verify_upload 명령어 수행 결과 화면
candy-machine mint_one_token -e devnet -c example -k ~/.config/solana/devnet.json

마지막으로, 위 명령어를 입력하여 NFT를 민팅해봅시다.

mint_one_token 명령어 수행 결과 화면

블록 익스플로러를 통해 확인해보면, 위와 같은 메타데이터를 갖고 있는 NFT가 발행된 것을 확인할 수 있습니다.

드디어 솔라나에서 캔디머신으로 NFT를 발행하였습니다. 생각보다 간단한 과정이었는데요, 그렇다면 이 실습과정 동안, 실제로 어떤 일이 일어나는지 한번 알아봅시다.

2. 전체 과정

캔디머신 사용 과정
[그림 2–2] 캔디머신 사용 과정, 출처: Twitter, @pencilflip

먼저, 캔디머신의 역할을 간단히 소개해 보겠습니다. 이 글에서는 NFT 프로젝트를 진행하고자 하는 팀을 “NFT 컬렉션 소유자 (NFT Collection Owner)” 라고 부르겠습니다.

과정 1. (그림에서 1 & 2) NFT 컬렉션 소유자는 캔디머신 프로그램에 새로운 캔디머신 계정을 생성해 달라는 요청(InitializeCandyMachine Instruction)을 보냅니다. 해당 요청을 보낼 때 NFT를 발행하는데 필요한 여러 환경 설정(Configuration) 값을 담게 되는데요, 캔디머신 프로그램이 새롭게 만들게 되는 캔디머신 계정에는 NFT 발행과 관련된 여러 설정 값들이 저장됩니다.

과정 2. (그림에서 3 & 4) NFT 구매자인 민터(Minter)가 캔디머신 프로그램에게 NFT 민팅을 요청(MintNFT instruction)하면, 캔디머신 프로그램은 캔디머신 PDA 계정을 참고하여, 민터로부터 구매 비용을 받고 NFT를 발행하게 됩니다.

그렇다면, 캔디머신 코드에서는 위 과정이 어떻게 표현될 수 있을까요?

과정 1. InitializeCandyMachine Instruction이 수행됩니다.

  • 해당 Instruction은 NFT 컬렉션 소유자에게 설정 값을 받아 캔디머신 PDA 계정을 초기화(initialize)하는 책임을 수행합니다.

과정 2. MintNFT Instruction이 수행됩니다.

  • 해당 Instruction은 Token Metadata Program을 호출하여, NFT 토큰에 대하여 Metadata PDA 계정을 생성합니다. 참고로, MintNFT Instruction은 NFT의 Mint AccountAssociated Token Account(이하 ATA) 를 초기화하지 않습니다.
  • 대신, 이미 초기화된 Mint Account를 넘겨받아, Metadata PDA 계정을 생성합니다. 이 Metadata PDA 계정에는 인자값으로 넘겨받은 Mint Account를 포함한 여러 정보를 담고 있습니다.

1. 실습해보기에 사용된 NFT의 민팅 과정을 블록 익스플로러에서 찾아보면, 위 과정을 직접 확인하실 수 있습니다.

먼저 아래 트랜잭션InitializeCandyMachine 과정입니다.

1번 및 2번 Instruction 수행 결과 화면

1번 Instruction은 System Program을 호출하여 빈 캔디머신 PDA 계정을 생성하고, 2번 Instruction은 생성된 PDA 계정을 받아 initialize합니다.

아래 내용은 MintNFT 과정입니다.

1번, 2번 및 3번 Instruction 수행 결과 화면

1번부터 3번 Instruction은 Token ProgramAssociated Token Program을 호출하여, NFT에 대한 Mint AccountATA를 생성합니다.

4번 Instruction 수행 결과 화면

4번 Instruction은 해당 ATA에 NFT 1개를 발행합니다.

5번 Instruction 수행 결과 화면

5번 Instruction은 캔디머신 프로그램을 호출하고, 이는 다시 Token Metadata ProgramCross-Program Invocation(이하 CPI)하여, 위 NFT Mint AccountMetadata PDA 계정 정보를 기입합니다.

아직 무슨 말인지 잘 와닿지 않으실 수 있습니다. 각각의 Instruction이 실행되는 과정을 조금 더 자세하게 살펴보겠습니다.

2.1. InitializeCandyMachine Instruction

캔디머신 Initialization 과정에서의 Account 도표
[그림 2–3] 캔디머신 Initialization 과정에서의 Account 도표, 출처: @c0wjay

먼저, InitializeCandyMachine Instruction에 대해 살펴봅시다.

이 과정은 NFT 발행이 시작되기 전에, NFT 컬렉션 소유자가 NFT 발행 과정에 대해 미리 설정하고, 캔디머신 PDA 계정을 만드는 과정입니다. 쉽게 설명하자면, NFT 프로젝트 팀이 NFT의 퍼블릭 세일을 진행하기 전에 사전에 판매 방식과 판매 시작 날짜 등을 설정하는 과정입니다.

handle_initialize_candy_machine 메소드는 이전의 Instruction으로 이미 생성된 CandyMachine의 PDA 계정을 초기화하는 책임을 수행합니다. 이 메소드는 InitializeCandyMachine Instruction을 통해 발행 설정과 관련된 정보 및 CandyMachine 계정을 받는데, 이 과정에서 remaining_account라는 포맷으로 인자 값을 받게 됩니다.

💡 DSRV’s Tip: 캔디머신 PDA 계정은 어떻게 초기화 과정이 이루어지나요?
handle_initialize_candy_machine 은 캔디머신 프로그램 내부에서 캔디머신 PDA 계정을 초기화하는 과정입니다. 미리 생성만 된 PDA 계정을 캔디머신 프로그램이 넘겨 받은 후, 해당 PDA 계정을 초기화하는 작업만 캔디머신이 작업하는 것이라고 이해하시면 되겠습니다.
일반적으로 10KiB 용량보다 작은 Data Account의 경우에는 #[account(init)] attribute를 사용하여 프로그램이 생성과 동시에 초기화하는 것이 일반적입니다. 이 때, CPI를 활용하여 Data Account를 생성합니다.다만 10KiB 용량보다 큰 Account의 경우엔, 위 방식의 CPI를 통한 계정 생성 및 초기화가 불가능합니다. 이러한 용량 제한이 왜 존재할까요? 솔라나의 런타임이 계정을 생성할 때 미리 공간을 어느 정도 할당합니다. 이 때 CPI를 통해 계정의 크기를 사전 할당할 수 있는 “realloc” 리밋이 존재하는데, 이것이 10KiB입니다. 밸리데이터들의 RAM 용량을 너무 많이 차지하지 않도록 하기 위한 조치로 보입니다. [4]만약 10KiB 용량보다 큰 Account를 초기화하고 싶다면, CPI를 활용하지 않고 미리 System Program을 호출하여 PDA 계정만 생성시키고 빈 계정을 캔디머신에 넘겨, 캔디머신은 초기화만 합니다. [5]자세한 사항은 Anchor Language 문서에서 #[account(init, payer = <target_account>, space = <num_bytes>)]#[account(zero)] 애트리뷰트 설명을 확인하시기 바랍니다.PDA 계정의 초기화 과정은 3편 캔디머신 코드 분석에서 상세하게 설명하도록 하겠습니다.

설명이 조금은 어렵게 느껴지실 수도 있는데요, 간단하게 “NFT 컬렉션 소유자가 메타플렉스에서 정한 JSON 포맷에 맞춰 캔디머신 프로그램에 정보를 보내면, 캔디머신 내부적으로 초기화 과정을 수행한다.”라고 이해하시면 됩니다.

🎛️ Config 설정 값의 예로는 go_live_date, gatekeeper, whitelist_mint_settings 등이 있습니다. 이에 대한 자세한 내용은 3. 부록 을 참고해 주세요.

위 도표에 등장하는 각 계정들에 대해 상세하게 알아볼까요?

1. Candy Machine PDA Account: NFT의 판매를 전반적으로 관리하는 계정

  • CandyMachine PDA 계정은 다음와 같은 구조로 되어 있습니다. 아래에서 설명할 wallet 계정이나, authority, token_mint 등에 대한 정보를 담고 있습니다.
  • CandyMachine PDA 계정의 data 필드에 저장되는 CandyMachineData struct는 다음 코드와 같습니다. 이 곳에는 클라이언트에서 보내준 환경 설정 값인 config.json을 비롯하여, NFT 판매와 관련된 설정 값들이 저장됩니다.
  • 이 값들이 저장되는 정확한 시기는 CandyMachine PDA 계정의 초기화 과정이며, 캔디머신 프로그램에서는 ctx.remaining_accounts라는 포맷으로 값을 받게 됩니다.

2. Wallet: NFT의 판매 과정에서 NFT 구매자가 결제한 금액을 보관하는 곳입니다.

  • 보관된 금액은 추후 NFT 컬렉션 소유자의 지갑으로 인출할 수 있으며, 혹은 처음부터 NFT 컬렉션 소유자의 지갑으로 설정할 수 있습니다.

3. Collection Owner: 캔디머신 PDA 계정에 대한 권한이 있는 사용자 계정만이 캔디머신의 설정 값을 수정할 수 있습니다.

4. CM Token Mint : 이 계정은 개발자가 별도로 생성을 해도 되고, 하지 않아도 되는 선택 사항입니다. 해당 프로퍼티를 설정하면 NFT 구매자가 지불할 비용을 SPL Token (솔라나의 ERC20에 대응되는 Fungible Token을 의미)로 지불할 수 있습니다.

  • NFT 컬렉션 소유자가 환경 설정 값(config)으로 token_mint를 설정하면, NFT 구매자는 Custom SPL Token으로 NFT에 대한 비용을 지불할 수 있습니다. 만약 token_mint 의 프로퍼티 값이 설정되어 있다면 해당 SPL token에 대한 Token Mint 계정이 연결됩니다. 반대로, token_mint 의 프로퍼티 값이 설정되어 있지 않다면, NFT 구매자는 SOL을 지불하여 NFT를 발행해야 합니다.

5. CandyMachineCreator : candy_machine 이라는 string과 CandyMachine PDA 계정의 주소를 seed로 사용하여 만들어지는 PDA입니다.

  • 1편에서 다뤘듯이, 이 PDA 주소는 따로 만들 필요 없이, seed 값만 정해지면 “찾아낼” 수 있는 정보입니다. PDA이기 때문에 내부에 어떠한 정보도 담지 않습니다.
  • 다만, Metadata 계정의 업데이트 권한을 갖고 있기 때문에, CandyMachine PDA 계정이 CandyMachineCreator를 이용하여 메타데이터를 수정할 수 있습니다.
💡 자세한 코드 이야기는 3편에서 다룰 예정이며, 구체적으로 각 인자들이 어떤 역할을 하는지에 대해서는 3. 부록을 참고해주세요!

2.2. MintNFT instruction

전체 캔디머신 Account 도표
[그림 2–4] 전체 캔디머신 Account 도표, 출처: @c0wjay
🎛️ DSRV’s Tip! MintNFT Instruction을 실행하기 전에 꼭 확인하세요.MintNFT Instruction이 실행되기 전에, NFT 자체는 이미 발행되어 있어야 합니다. 이 말은 위 도표에서 NFT Token Mint 계정과, Token Account는 미리 Token Program을 호출하여 만들어진다는 것입니다.이는 위에서 우리가 살펴봤던 예시 NFT의 블록 익스플로러를 확인해봐도 알 수 있습니다. 앞서 말했듯이, 1번부터 4번 instruction이 MintNFT instruction 실행 전에 이루어져, NFT에 대한 Mint account와 ATA를 생성하고, 해당 ATA에 토큰 1개를 민팅해줍니다.즉, 아래의 사항이 반드시 만족되어야 MintNFT Instruction을 실행할 수 있습니다.1. NFT의 Token Mint 계정이 생성되어 있어야 합니다. 
2. 해당 Token Mint 계정에 대한 ATA (Associated Token Account) 가 생성되어 있어야 합니다. 참고로, ATA는 NFT를 보관하기 위해 사용됩니다.
3. 1개의 NFT가 해당 ATA에 존재합니다.

캔디머신 프로그램을 NFT 발행 도구로 활용하여 NFT 컬렉션 소유자는, 각자만의 CandyMachine PDA 계정을 만듭니다. 해당 계정에는 미리 저장한 환경설정 값(configuration)이 있습니다.

임의의 NFT 구매자가 NFT를 민팅하고자 하면, 이는 MintNFT instruction에 담겨져 캔디머신 프로그램의 handle_mint_nft 메소드를 호출하게 됩니다.

handle_mint_nft 메소드의 기능을 간략하게 소개하면 다음과 같습니다.

1. MintNFT instruction으로 받은 값들을 토대로, CandyMachineCandyMachineData struct에 저장된 설정 값들이 유효한지 확인합니다.

  • 설정 값들에 어떤 종류들이 있는지는 아래 3. 부록에 서술했습니다.

2. NFT 구매자로부터 지정된 토큰을 값으로 받으며, 해당 토큰은 캔디머신 PDA 계정에 저장된 지갑 주소로 전송됩니다.

3. metaplex_token_metadata::create_metadata_accounts_v2 Instruction을 만들고, Metaplex Token Metadata 프로그램을 호출하여 Metadata 계정을 만듭니다.

4. Master Edition NFT의 경우, metaplex_token_metadata::create_master_edition_v3 Instruction을 만들고, Metaplex Token Metadata 프로그램을 호출하여 Master Edition 계정을 만듭니다.

5. 만약 NFT Token Mint 계정이 메타플렉스의 표준에 맞지 않는다면, MintNFT Instruction은 취소됩니다.

  • 해당 NFT에 대한 Token Mint 계정 및 NFT 구매자 소유의 ATA는 MintNFT Instruction의 전송 이전에 이미 생성해 두었습니다.

2.3. 정리

캔디머신에서 이루어지는 과정을 정리하자면 아래와 같습니다.

캔디머신의 Instructions 목록

3. 부록

각각의 Config 설정들이 갖는 의미에 대해 알고자 하시는 분은 아래 표를 참고해주세요. [6]

Config 설정 목록

글을 마무리하며..

이번 시간에는 메타플렉스의 캔디머신을 이용하여 솔라나에서 NFT를 발행하는 과정을 살펴보았습니다.

Pencilflip의 말을 빌리자면, 캔디머신은 기본적으로 솔라나의 Token Program과 메타플렉스의 Token Metadata Program의 wrapper에 불과합니다. NFT 컬렉션을 발행하고, 메타데이터를 연결하기 위해 좋은 API를 제공하지만, 그렇다고 캔디머신을 꼭 사용해야만 솔라나에서 NFT를 만들 수 있는 것은 아닙니다.

공식 문서에 나와 있듯이, NFT는 spl-token CLI만으로도 만들 수 있으며, NFT에 메타데이터를 연결하는 것도 메타플렉스의 Token Metadata Program을 따로 호출하여 진행할 수 있습니다.

하지만, 캔디머신은 분명 유용한 도구입니다. 캔디머신은 솔라나에서 NFT 컬렉션을 만드는 프로젝트 팀들이 비즈니스 로직을 구현하는 데 온전히 집중할 수 있도록 도와줍니다. 이러한 장점 덕분에 캔디머신은 솔라나 생태계의 많은 NFT 프로젝트 팀들이 애용하는 도구가 되었습니다.

다음 시간에는 위 과정을 코드 수준에서 분석해보도록 하겠습니다. 단순히 캔디머신 자체를 도구로 사용하여 NFT를 발행하려는 독자보다는, 캔디머신의 작동 원리 및 솔라나에 배포되어 동작하는 실제 프로그램의 코드 구현이 궁금하신 독자, 그리고 본인만의 커스텀 프로그램을 개발하려는 독자에게 더욱 알맞는 아티클이 될 것입니다.

이 글이 캔디머신을 이용하여 NFT를 발행하고자 하는 개발자에게 도움이 되었길 바랍니다. 감사합니다.

Author
c0wjay | Jay Soh, Contributing Writer (Twitter @c0wjay)

Reviewed by
Sigrid Jin of DSRV, Technical Writer & Developer Evangelist (Twitter @sigridjin_eth)

Reference
[1] Candy Machine Github Repository
[2] Pencilflip Twitter
[3] Arweave
[4] Solana Docs — Input Parameter Serialization
[5] docs.rs — Instruction Attribute
[6] Metaplex Docs — Configuration

--

--

c0wjay
DSRV
Writer for

Rustacean interested in Programming Languages.