본문 바로가기

CHALLENGER : BNB 체인 해커톤

4회차 강의 정리 | NFT 메타데이터 추가 | NFT 민팅하기 | Pinata 사용하기 | IPFS 란

# NFT 메타데이터란 ?

 

NFT 에 필요한 모든 정보를 블록체인 상에 저장하게 되면, 저장해야 하는 데이터가 많아져 가스비가 많이 들게 된다. 따라서 NFT에 핵심적인 정보는 블록체인 상에 저장해두고, 그외의 데이터는 Token URI (메타데이터)로 관리한다. 

 

NFT 핵심 데이터는 Token ID, Owner, Token URI 이다. 유명한 NFT 프로젝트인 Bayc 를 보며 알아보자. 

 

Token ID는 위 이미지의 #3216 으로 해당 프로젝트의 몇번째 NFT인지를 나타낸다.

Owner는 camy.eth로, NFT의 주인 Address이다. 

Token URI 는 이미지, 타이틀, 설명과 같은 메타데이터를 담고있는 경로이다.

 

우리는 NFT를 이미지라고 생각하지만, 사실 상 이미지는 다른 서버에 있고 NFT에는 해당 이미지의 경로가 담긴 메타데이터의 주소가 있는 것이다.

 

메타데이터는 JSON 형식으로 작성되어야 한다. 알맞게 정보를 넣어주면 NFT를 예쁘게 나타내 줄 수 있다.

아래와 같은 형식인데, oepnsea 에서 더 자세히 확인할 수 있다. 

 

{
  "description": "Friendly OpenSea Creature that enjoys long swims in the ocean.", 
  "external_url": "https://openseacreatures.io/3", 
  "image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png", 
  "name": "Dave Starbelly",
  "attributes": [ ... ]
}

 

https://docs.opensea.io/docs/metadata-standards

 

Metadata Standards

How to add rich metadata to your ERC721 or ERC1155 NFTs

docs.opensea.io

 

 

 

# IPFS 란?

IPFS는 InterPlanetary File System 의 약어로, 파일이나 웹사이트, 어플, 데이터 등을 분산 저장하는 시스템이다. 

(출처 : https://docs.ipfs.tech/concepts/what-is-ipfs/)

 

NFT를 블록체인 상에 발행해두고, 정작 이미지는 기존의 클라우드 서버에 저장해 둔다면 반쪽짜리 탈 중앙화 서비스가 된다. NFT의 정보는 해킹되지 않지만, NFT 메타데이터를 저장해둔 서버가 해킹되면, 내가 가지고 있던 NFT 이미지가 변경될 수도 있다. 따라서 NFT의 메타데이터도 분산형 파일 시스템을 활용해서 관리하면, 보다 탈 중앙화에 맞고 안정적으로 데이터를 관리할 수 있다. 

 

 

# Pinata

 

IPFS를 지원하는 서비스이며, bayc를 만든 유가랩스도 이 서비스를 사용한다고 한다. 

 

NFT 메타데이터를 작성하기 위해서는 아래와 같은 순서로 진행하면 된다.

1. 먼저 이미지를 Pinata 서비스에 업로드한다.
2. 업로드한 이미지 주소를 포함하여 메타데이터 JSON 파일 작성
3. JSON 파일을 Pinata 서비스에 업로드 

그러면 아래와 같이 메타데이터 주소를 받을 수 있다. 

 

 

# 메타데이터 추가하는 코드 작성하기

 

ERC721에 tokenURI(uint _tokenId) returns(string) 함수가 있다. 해당 함수를 오버라이드하여, 저장해둔 tokenUri를 return해주도록 작성해주면 된다. 지난 시간에 작성했던 contract에 이어서 작성한다.

 

먼저 metadata URI를 저장할 변수를 하나 선언해준다. 프로젝트 내에서 여러 NFT를 발행할 예정이기 때문에 tokenId를 받으면 uri를 리턴하는 mapping 변수를 선언한다. public 으로 선언하면 get metadataURIs가 자동으로 public 으로 생성된다.

 

    mapping(uint => string) public metadataURIs;

 

그리고 이 변수를 set 해줄 함수를 작성해준다. 

 

function setTokenURI(uint _tokenId, string memory _metadataURI) public{
        metadataURIs[_tokenId] = _metadataURI;
}

 

마지막으로 tokenURI 를 override 해주면 메타데이터 추가하는 코드가 완료된다.

 

function tokenURI(uint _tokenId) public override view returns(string memory){
        return metadataURIs[_tokenId];
}

 

최종적으로 작성한 코드는 아래와 같다.

 

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

contract MintNFT is ERC721Enumerable{
    constructor() ERC721("projectName", "projectSymbol"){}

    function mintNFT() public {
        uint tokenId = totalSupply() + 1;
        _mint(msg.sender, tokenId);
    }

    mapping(uint => string) public metadataURIs;
    
    function setTokenURI(uint _tokenId, string memory _metadataURI) public{
        metadataURIs[_tokenId] = _metadataURI;
    }

    function tokenURI(uint _tokenId) public override view returns(string memory){
        return metadataURIs[_tokenId];
    }
}

 

remix ide에서 deploy한 뒤, minting 하고 setTokenURI를 하면 아래와 같이 openSea에서 잘 등록이 되는 것을 확인할 수 있다.

 

 

 

#  솔리디티 문법 추가 

 

접근 제한 키워드

* public : 컨트랙트 외부 + 내부 에서 사용 가능

* private: 컨트랙트 내부에서만 사용 가능

* internal : 컨트랙트 내부 + 상속받은 컨트랙트에서 사용 가능

* external : 상속받은 컨트랙트에서만 사용 가능 => 인터페이스에서 사용됨

 

메모리 관련 키워드

* storage : 컨트랙트 내에서 사용 가능 => 영구히 컨트랙트에 저장되는 변수

* memory : 함수  내에서 생성되었다 함수가 종료되면서 같이 소멸되는 변수 => 함수 내에서 호출하면 default  

 

반응형