---
title: "NFT를 만드는 방법"
description: "Metamask, Solidity, Hardhat, Pinata, Alchemy를 사용하여 Ethereum 블록체인에 직접 NFT를 만들고 배포하는 단계별 가이드입니다."
---

# NFT를 만드는 방법

이 튜토리얼에서는 Ethereum과 Inter Planetary File System \(IPFS\)을 사용하여 Non Fungible \(ERC721\) Token 스마트 컨트랙트를 작성하고 배포하는 과정을 다룹니다.

_가이드 예상 소요 시간: 약 15분_

바위 밑에서 살고 있었다 해도, 모든 주요 뉴스 매체가 NFT의 부상을 다루고 있다는 것을 눈치챘을 것입니다.

NFT가 블록체인을 대중의 눈앞으로 끌어낸 지금, 직접 Ethereum 블록체인에 자신만의 NFT \(ERC-721 Token\)를 게시해봄으로써 이 열풍을 이해해볼 좋은 기회입니다!

이 튜토리얼에서는 [Metamask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), [Pinata](https://pinata.cloud/), [Alchemy](https://alchemy.com/?r=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf)를 사용하여 Sepolia 테스트 네트워크에 ERC-721 스마트 컨트랙트를 생성하고 배포하는 과정을 살펴봅니다 \(아직 이 용어들이 무엇을 의미하는지 몰라도 괜찮습니다. 차차 설명하겠습니다\). 이 튜토리얼의 Part II에서는 작성한 스마트 컨트랙트를 사용해 [NFT를 민팅](/blog/how-to-mint-an-nft-using-web3-js)하는 방법을 다루고, Part III에서는 [Metamask에서 NFT를 확인](/blog/how-to-view-your-nft-in-your-mobile-wallet)하는 방법을 다룹니다.

그리고 진행 중 궁금한 점이 있다면 언제든지 [Alchemy 지원 센터](https://www.alchemy.com/support)를 방문하세요!

## 1단계: Ethereum 네트워크에 연결하기

Ethereum 블록체인에 요청을 보내는 방법은 여러 가지가 있지만, 가장 쉬운 방법으로 [Alchemy](https://alchemy.com/?r=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf)의 무료 계정을 사용하겠습니다. Alchemy는 블록체인 개발자 플랫폼이자 API로, 직접 노드를 운영하지 않고도 Ethereum 체인과 통신할 수 있게 해줍니다.

이 튜토리얼에서는 스마트 컨트랙트 배포 과정에서 내부적으로 어떤 일이 일어나는지 파악하기 위해 Alchemy의 모니터링 및 분석 개발자 도구도 활용하겠습니다. 아직 Alchemy 계정이 없다면 [여기](https://alchemy.com/?r=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf)에서 무료로 가입할 수 있습니다.

## 2단계: 앱 생성하기 \(및 API 키 발급\)

Alchemy 계정을 생성했다면, 앱을 만들어 API 키를 발급받을 수 있습니다. 이 키를 통해 Sepolia 테스트 네트워크에 요청을 보낼 수 있습니다. 테스트 네트워크에 대해 더 알아보고 싶다면 [이 가이드](https://www.alchemy.com/docs/choosing-a-web3-network)를 참고하세요.

1. Alchemy Dashboard의 내비게이션 바에서 "Apps"에 마우스를 올려 "Create App"을 클릭하고 "Create App" 페이지로 이동합니다

<ImageBlock
  src="https://media.alchemy.com/1704631152-create-a-new-app.jpg"
  alt="Alchemy 대시보드: Apps 메뉴에서 새 앱 생성하기"
  width={2880}
  height={1640}
  caption="새 앱 생성"
/>

2. 앱 이름을 지정하고 \(여기서는 "My First NFT!"로 지정했습니다\), 간단한 설명을 입력한 후 Environment로 "Staging"을 선택합니다 \(앱 관리용\). Network는 "Sepolia"를 선택합니다.

<ImageBlock
  src="https://media.alchemy.com/1704802198-name-your-app.png"
  alt="테스트 네트워크 선택기가 있는 이전 버전 Alchemy 대시보드 앱 생성 화면"
  width={1518}
  height={912}
  caption="앱 이름과 설명 입력"
/>

3. "Create app"을 클릭하면 끝입니다! 아래 표에 앱이 나타날 것입니다.

## 3단계: Ethereum 계정 \(주소\) 생성하기

트랜잭션을 주고받으려면 Ethereum 계정이 필요합니다. 이 튜토리얼에서는 브라우저에서 Ethereum 계정 주소를 관리하는 가상 지갑인 Metamask를 사용합니다. Ethereum에서 트랜잭션이 어떻게 작동하는지 더 알고 싶다면 Ethereum 재단의 [이 페이지](https://ethereum.org/en/developers/docs/transactions/)를 참고하세요.

[여기](https://metamask.io/download.html)에서 Metamask를 무료로 다운로드하고 계정을 생성할 수 있습니다. 계정을 생성할 때 \(또는 이미 계정이 있다면\), 실제 자금을 다루지 않도록 Sepolia 테스트 네트워크로 전환해야 합니다.

<ImageBlock
  src="https://media.alchemy.com/1704802303-download-and-create-a-metamask-account.png"
  alt="테스트 네트워크 선택기가 있는 이전 버전 MetaMask 지갑 설정 화면"
  width={712}
  height={1194}
  caption="MetaMask 다운로드 및 계정 생성"
/>

## 4단계: 파우셋에서 ether 받기

스마트 컨트랙트를 테스트 네트워크에 배포하려면 가짜 Eth가 필요합니다. Eth를 받으려면 [Sepolia 파우셋](/faucets/ethereum-sepolia)으로 이동하여 Sepolia 계정 주소를 입력하고 Sepolia Eth를 요청하면 됩니다. 곧 Metamask 계정에 Eth가 표시될 것입니다!

## 5단계: 잔액 확인하기

잔액이 제대로 들어왔는지 확인하기 위해, [Alchemy의 composer 도구](https://www.alchemy.com/composer/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)를 사용해 `eth_getBalance` 요청을 보내봅시다. 이 요청은 우리 지갑에 있는 Eth 양을 반환합니다. Metamask 계정 주소를 입력하고 "Send Request"를 클릭하면 다음과 같은 응답을 볼 수 있습니다:

\{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"\}

**참고:** 이 결과는 eth가 아니라 wei 단위입니다. Wei는 ether의 가장 작은 단위로 사용됩니다. wei에서 eth로의 변환은 다음과 같습니다: 1 eth = 10¹⁸ wei. 따라서 0xde0b6b3a7640000을 10진수로 변환하면 1*10¹⁸이 되며, 이는 1 eth와 같습니다.

좋습니다! 가짜 돈이 모두 들어왔군요! 🤑

## 6단계: 프로젝트 초기화하기

먼저, 프로젝트를 위한 폴더를 생성해야 합니다. [명령줄](https://www.computerhope.com/jargon/c/commandi.htm)로 이동하여 다음을 입력합니다:

프로젝트 폴더 안에 들어왔다면, npm init을 사용하여 프로젝트를 초기화합니다. npm이 아직 설치되어 있지 않다면 [다음 안내](https://nodejs.org/en/download/)를 따르세요 \([Node.js](https://nodejs.org/en/download/)도 필요하니 함께 다운로드하세요!\).

설치 질문에 어떻게 답하든 크게 상관없지만, 참고용으로 저희가 답한 방식은 다음과 같습니다:

package.json을 승인하면 준비가 끝납니다!

7단계: [Hardhat](https://hardhat.org/getting-started/#overview) 다운로드하기

Hardhat은 Ethereum 소프트웨어를 컴파일, 배포, 테스트, 디버깅하기 위한 개발 환경입니다. 실제 체인에 배포하기 전 로컬에서 스마트 컨트랙트와 [앱](https://www.alchemy.com/dapps/top/defi-dapps)을 빌드할 때 개발자에게 도움을 줍니다.

터미널로 이동해서 my-nft 프로젝트 폴더 안에 있는지 확인한 다음, 다음을 실행합니다:

[설치 안내](https://hardhat.org/getting-started/#overview)에 대한 자세한 내용은 이 페이지를 참고하세요.

## 8단계: Hardhat 프로젝트 생성하기

프로젝트 폴더 안에서 다음을 실행합니다:

그러면 환영 메시지와 함께 무엇을 하고 싶은지 선택할 수 있는 옵션이 나타납니다. "create an empty hardhat.config.js"를 선택합니다:

이렇게 하면 **hardhat.config.js** 파일이 생성되며, 여기에 프로젝트에 필요한 모든 설정을 지정하게 됩니다 \(13단계에서\).

## 9단계: 프로젝트 폴더 추가하기

프로젝트를 정리하기 위해 새로운 폴더 두 개를 만들겠습니다. 명령줄에서 프로젝트의 루트 디렉토리로 이동하여 다음을 입력합니다:

**contracts/**는 NFT 스마트 컨트랙트 코드를 보관할 곳입니다

scripts/는 스마트 컨트랙트를 배포하고 상호작용하는 스크립트를 보관할 곳입니다

## 10단계: 컨트랙트 작성하기

환경 설정이 끝났으니, 이제 더 흥미로운 작업, 즉 스마트 컨트랙트 코드 작성으로 넘어갑니다!

선호하는 에디터로 my-nft 프로젝트를 엽니다 \([VSCode](https://code.visualstudio.com)를 추천합니다\). 스마트 컨트랙트는 [Solidity](https://www.alchemy.com/overviews/solidity)라는 언어로 작성되며, 이를 사용해 MyNFT.sol 스마트 컨트랙트를 작성하겠습니다.‌

1. "contracts" 폴더로 이동하여 MyNFT.sol이라는 새 파일을 생성합니다

2. 아래는 [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721) 라이브러리의 ERC721 구현을 기반으로 한 NFT 스마트 컨트랙트 코드입니다. 아래 내용을 복사하여 MyNFT.sol 파일에 붙여넣으세요.

<CalloutBlock>

참고: 스마트 컨트랙트를 통해 NFT에 가격을 붙이고 싶다면 [이 튜토리얼](https://www.alchemy.com/docs/reference/nft-api-endpoints)을 참고하세요.

</CalloutBlock>

1. OpenZepplin 컨트랙트 라이브러리에서 클래스를 상속받고 있으므로, 명령줄에서 다음을 실행해 라이브러리를 폴더에 설치합니다:

**textnpm install @openzeppelin/contracts@3.1.0-solc-0.7**

그렇다면 이 코드는 정확히 무엇을 하는 걸까요? 한 줄씩 살펴보겠습니다.

5-7번째 줄에서, 우리 코드는 세 개의 [OpenZepplin](https://openzeppelin.com) 스마트 컨트랙트 클래스를 상속받습니다:

- @[openzeppelin](https://www.alchemy.com/dapps/openzeppelin)/contracts/token/ERC721/ERC721.sol에는 ERC721 표준 구현이 들어 있으며, 우리 NFT 스마트 컨트랙트가 이를 상속받습니다 \(유효한 NFT가 되려면 스마트 컨트랙트가 ERC721 표준의 모든 메서드를 구현해야 합니다\). 상속받은 ERC721 함수에 대해 더 알고 싶다면 [여기](https://eips.ethereum.org/EIPS/eip-721)에서 인터페이스 정의를 확인하세요.

- @openzeppelin/contracts/utils/Counters.sol은 1씩만 증가하거나 감소할 수 있는 카운터를 제공합니다. 우리 스마트 컨트랙트는 이 카운터를 사용해 민팅된 NFT의 총 개수를 추적하고, 새 NFT에 고유 ID를 부여합니다. 스마트 컨트랙트로 민팅되는 각 NFT에는 고유 ID가 부여되어야 하는데, 여기서는 고유 ID가 단순히 현재까지 존재하는 NFT의 총 개수로 결정됩니다. 예를 들어, 스마트 컨트랙트로 민팅하는 첫 번째 NFT는 ID가 "1"이고, 두 번째 NFT는 ID가 "2"가 되는 식입니다.

- @openzeppelin/contracts/access/Ownable.sol은 스마트 컨트랙트에 [접근 제어](https://docs.openzeppelin.com/contracts/3.x/access-control)를 설정하여, 스마트 컨트랙트의 소유자\(즉, 여러분\)만 NFT를 민팅할 수 있도록 합니다. 참고로 접근 제어를 포함할지는 전적으로 선택사항입니다. 누구나 여러분의 스마트 컨트랙트로 NFT를 민팅할 수 있게 하려면, 10번째 줄의 Ownable과 17번째 줄의 onlyOwner를 제거하세요.

10-28번째 줄에는 우리의 커스텀 NFT 스마트 컨트랙트가 있는데, 놀랍도록 짧습니다 — 카운터, 생성자, 함수 하나만 포함되어 있습니다! 이는 상속받은 OpenZepplin 컨트랙트가 NFT를 생성하는 데 필요한 대부분의 메서드, 예를 들어 ownerOf \(NFT의 소유자를 반환\)와 transferFrom \(NFT의 소유권을 이전\) 등을 이미 구현해두었기 때문입니다.

14번째 줄을 보면 "MyNFT"와 "NFT"라는 두 개의 문자열을 ERC721 생성자에 전달하는 것을 볼 수 있습니다. 첫 번째 변수는 스마트 컨트랙트의 이름이고, 두 번째는 심볼입니다. 이 변수들의 이름은 원하는 대로 지정할 수 있습니다!

마지막으로, 16번째 줄부터 시작하는 mintNFT\(\) 함수를 통해 NFT를 민팅할 수 있습니다! 이 함수는 두 개의 변수를 받습니다:

- address recipient는 새로 민팅된 NFT를 받을 주소를 지정합니다

- string memory tokenURI는 NFT의 메타데이터를 설명하는 JSON 문서로 연결되어야 하는 문자열입니다. NFT의 메타데이터는 NFT에 생명을 불어넣는 핵심 요소로, 이름, 설명, 이미지 등 추가적인 속성을 부여할 수 있게 해줍니다. 이 튜토리얼의 2부에서 이 메타데이터를 구성하는 방법을 설명하겠습니다.

**mintNFT**는 상속받은 ERC721 라이브러리의 몇몇 메서드를 호출하며, 최종적으로 새로 민팅된 NFT의 ID를 나타내는 숫자를 반환합니다.

## 11단계: MetaMask와 Alchemy를 프로젝트에 연결하기

Metamask 지갑과 Alchemy 계정을 생성하고 스마트 컨트랙트도 작성했으니, 이제 이 세 가지를 연결할 차례입니다.

가상 지갑에서 전송되는 모든 트랜잭션에는 고유한 개인 키를 이용한 서명이 필요합니다. 프로그램에 이 권한을 부여하기 위해, 개인 키\(및 Alchemy API 키\)를 환경 파일에 안전하게 저장할 수 있습니다.

트랜잭션 전송에 대해 더 알고 싶다면, web3를 사용한 트랜잭션 전송에 관한 [이 튜토리얼](https://docs.alchemy.com/docs/how-to-send-transactions-on-ethereum)을 참고하세요.

먼저, 프로젝트 디렉토리에 dotenv 패키지를 설치합니다:

그런 다음 프로젝트 루트 디렉토리에 **.env** 파일을 생성하고, Metamask 개인 키와 HTTP Alchemy API URL을 추가합니다.

참고: .env 파일의 이름은 반드시 .env여야 합니다! xx.env 등으로 이름을 바꾸지 마세요

- Metamask에서 개인 키를 내보내는 방법은 [다음 안내](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)를 따르세요

- HTTP Alchemy API URL을 얻어 클립보드에 복사하려면 아래를 참고하세요

<ImageBlock
  src="https://media.alchemy.com/1704631290-copy-your-alchemy-api-key.gif"
  alt="Alchemy 대시보드: .env 파일에 사용할 앱의 HTTP API URL 복사하기"
  width={843}
  height={480}
  caption="Alchemy API 키 복사"
/>

**.env** 파일은 다음과 같은 형태여야 합니다:

## 12단계: Ethers.js 설치하기

Ethers.js는 [표준 JSON-RPC 메서드](https://www.alchemy.com/docs)를 더 사용하기 쉬운 메서드로 감싸서 Ethereum과의 상호작용 및 요청을 더 쉽게 만들어주는 라이브러리입니다.

Hardhat은 추가적인 도구와 확장 기능을 위한 [Plugins](https://hardhat.org/plugins/) 통합을 매우 쉽게 해줍니다. 여기서는 컨트랙트 배포를 위해 [Ethers plugin](https://hardhat.org/plugins/nomiclabs-hardhat-ethers.html)을 활용하겠습니다 \([Ethers.js](https://github.com/ethers-io/ethers.js/)에는 매우 깔끔한 컨트랙트 배포 메서드들이 있습니다\).

프로젝트 디렉토리에서 다음을 입력합니다:

다음 단계에서 **hardhat.config.js**에도 ethers를 require해야 합니다.

## 13단계: hardhat.config.js 업데이트하기

지금까지 여러 의존성과 플러그인을 추가했으니, 이제 **hardhat.config.js**가 이들을 모두 인식하도록 업데이트해야 합니다.

**hardhat.config.js**를 다음과 같이 업데이트합니다:

## 14단계: 컨트랙트 컴파일하기

지금까지 잘 진행되고 있는지 확인하기 위해, 컨트랙트를 컴파일해봅시다. compile 태스크는 Hardhat에 내장된 태스크 중 하나입니다.

명령줄에서 다음을 실행합니다:

소스 파일에 SPDX 라이선스 식별자가 제공되지 않았다는 경고가 나타날 수 있지만, 이는 걱정할 필요 없습니다 — 그 외에는 모두 문제없을 것입니다! 만약 그렇지 않다면 Alchemy Dashboard에서 지원 티켓을 열 수 있습니다.

## 15단계: 배포 스크립트 작성하기

컨트랙트 작성과 설정 파일 준비가 끝났으니, 이제 컨트랙트 배포 스크립트를 작성할 차례입니다.

**scripts/** 폴더로 이동하여 **deploy.js**라는 새 파일을 생성하고, 다음 내용을 추가합니다:

Hardhat은 [Contracts 튜토리얼](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)에서 이 각 코드 줄이 하는 일을 훌륭하게 설명하고 있으며, 여기서는 그 설명을 그대로 가져왔습니다.

[ethers.js](https://www.alchemy.com/dapps/ethers-js)의 **ContractFactory**는 새로운 스마트 컨트랙트를 배포하는 데 사용되는 추상화이므로, 여기서 MyNFT는 우리 NFT 컨트랙트의 인스턴스를 만드는 팩토리입니다. **hardhat-ethers** 플러그인을 사용할 때 **ContractFactory**와 **Contract** 인스턴스는 기본적으로 첫 번째 signer에 연결됩니다.

**ContractFactory**에서 **deploy\(\)**를 호출하면 배포가 시작되고, **Contract**로 해소되는 **Promise**가 반환됩니다. 이 객체는 우리 스마트 컨트랙트의 각 함수에 대응하는 메서드를 가지고 있습니다.

## 16단계: 컨트랙트 배포하기

드디어 스마트 컨트랙트를 배포할 준비가 되었습니다! 프로젝트 디렉토리의 루트로 돌아가서, 명령줄에서 다음을 실행합니다:

그러면 다음과 같은 결과가 나타날 것입니다:

[Sepolia Etherscan](https://sepolia.etherscan.io)으로 이동하여 컨트랙트 주소를 검색하면 성공적으로 배포된 것을 확인할 수 있습니다. 트랜잭션은 다음과 같은 모습일 것입니다:

<ImageBlock
  src="https://media.alchemy.com/1704803201-ropsten-etherscan.png"
  alt="NFT 스마트 컨트랙트가 성공적으로 배포되었음을 확인하는 Etherscan 트랜잭션 화면"
  width={2880}
  height={1646}
  caption="주소가 성공적으로 배포됨"
/>

**From** 주소는 여러분의 Metamask 계정 주소와 일치해야 하며, To 주소에는 "Contract Creation"이라고 표시될 것입니다. 트랜잭션을 클릭해 들어가면, **To** 필드에서 컨트랙트 주소를 확인할 수 있습니다:

<ImageBlock
  src="https://media.alchemy.com/1704803309-nft-deployed-successfully.png"
  alt="야호! 방금 NFT 스마트 컨트랙트를 Ethereum 체인에 배포했습니다 🎉"
  width={2880}
  height={1538}
  caption="야호! 방금 NFT 스마트 컨트랙트를 Ethereum 체인에 배포했습니다 🎉"
/>

내부적으로 어떤 일이 일어나는지 확인하려면 [Alchemy dashboard](https://dashboard.alchemy.com/signup?referral=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf)의 Explorer 탭으로 이동해봅시다. Alchemy 앱이 여러 개 있다면 앱별로 필터링하여 "MyNFT"를 선택해야 합니다.

<ImageBlock
  src="https://media.alchemy.com/1704803382-navigate-to-the-explorer-tab-in-our-alchemy-dashboard.jpg"
  alt="컨트랙트 배포 중 발생한 JSON-RPC 호출을 나열하는 Alchemy 대시보드 Explorer 탭"
  width={2880}
  height={1636}
  caption="Alchemy 대시보드에서 Explorer 탭으로 이동"
/>

여기서 **.deploy\(\)** 함수를 호출했을 때 Hardhat/Ethers가 내부적으로 만든 여러 JSON-RPC 호출들을 볼 수 있습니다.

여기서 주목할 만한 두 가지는 실제로 Sepolia 체인에 스마트 컨트랙트를 기록하는 요청인 **`eth_sendRawTransaction`**과, 해당 해시를 기반으로 트랜잭션 정보를 조회하는 요청인 **`eth_getTransactionByHash`**입니다 \(트랜잭션 전송 시 일반적인 패턴입니다\).

트랜잭션 전송에 대해 더 알고 싶다면, [Web3를 사용한 트랜잭션 전송](https://docs.alchemy.com/docs/how-to-send-transactions-on-ethereum)에 관한 이 튜토리얼을 참고하세요.

이것으로 이 튜토리얼의 Part I을 마칩니다. Part II에서는 스마트 컨트랙트를 사용해 실제로 [NFT를 민팅](/blog/how-to-mint-an-nft-using-web3-js)해보고, Part III에서는 [Metamask에서 NFT를 확인](/blog/how-to-view-your-nft-in-your-mobile-wallet)하는 방법을 설명하겠습니다! 🤑
