Ver3.0
์ ์ฒด ์ด๋
Ver3.0 ๐ฎ ๊ฒ์ ์์ดํ ์ NFT๋ก ๋ง๋๋ ๋ง๋ฒ ๊ฐ์ ์ด์ผ๊ธฐ

๐ฎ ๊ฒ์ ์์ดํ ์ NFT๋ก ๋ง๋๋ ๋ง๋ฒ ๊ฐ์ ์ด์ผ๊ธฐ
๋ธ๋ก์ฒด์ธ ๊ธฐ์ ๋ก ๊ฒ์ ์์ดํ
์ ์ง์ง ๊ฐ์น๋ฅผ ๋ถ์ฌํ๋ ๋ฐฉ๋ฒ
์๋
! ์ค๋์ ๊ฒ์์ ์ข์ํ๋ ์น๊ตฌ๋ค์ด๋ผ๋ฉด ํ ๋ฒ์ฏค ๊ฟ๊ฟ๋ดค์ ์ด์ผ๊ธฐ๋ฅผ ํด๋ณผ๊น ํด. ๐ฏ ๋ฐ๋ก ๊ฒ์ ์์์ ์ด์ฌํ ๋ชจ์ ์์ดํ
์ ์ง์ง ๋ด ๊ฒ์ผ๋ก ๋ง๋ค๊ณ , ์ฌ์ง์ด ๋ค๋ฅธ ์ฌ๋์๊ฒ ํ ์๋ ์๋ ์ธ์ ๋ง์ด์ผ. ์์๋ง ํด๋ ์ ๋์ง ์์?
์์ ์๋ ๊ฒ์ ํ์ฌ๊ฐ ์๋ฒ๋ฅผ ๋ซ์ผ๋ฉด ๋ด๊ฐ ์๋ฐฑ ์๊ฐ ๋์ ๋ชจ์ ์ ์ค์ ๊ฒ๋, ํฌ๊ทํ ์คํจ๋ ๋ชจ๋ ์ฌ๋ผ์ก์ด. ๐ข ํ์ง๋ง NFT ๊ธฐ์ ์ด ๋ฑ์ฅํ๋ฉด์ ์ด ๋ชจ๋ ๊ฒ ๋ฐ๋๊ณ ์์ด. ๊ฒ์ ์์ดํ ์ NFT๋ก ๋ง๋ค๋ฉด ์ง์ง ๋ด ์์ ๊ฐ ๋๊ณ , ๊ฒ์ ๋ฐ์์๋ ๊ฑฐ๋ํ ์ ์๊ฒ ๋๋ ๊ฑฐ์ง.
์ด ๊ธ์์๋ ๊ฒ์ ์์ดํ ์ NFT๋ก ๋ง๋๋ ์ ์ฒด ๊ตฌ์กฐ๋ฅผ ์ฒ์๋ถํฐ ๋๊น์ง ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ฏ์ด ์ฝ๊ฒ ํ์ด๋ณผ๊ฒ. ๊ธฐ์ ์ ์ธ ๋ถ๋ถ๋ ์์ง๋ง ๊ฑฑ์ ๋ง! ์ต๋ํ ์ฌ๋ฏธ์๊ฒ ์ค๋ช ํด์ค ํ ๋๊น. ๐
์์ ์๋ ๊ฒ์ ํ์ฌ๊ฐ ์๋ฒ๋ฅผ ๋ซ์ผ๋ฉด ๋ด๊ฐ ์๋ฐฑ ์๊ฐ ๋์ ๋ชจ์ ์ ์ค์ ๊ฒ๋, ํฌ๊ทํ ์คํจ๋ ๋ชจ๋ ์ฌ๋ผ์ก์ด. ๐ข ํ์ง๋ง NFT ๊ธฐ์ ์ด ๋ฑ์ฅํ๋ฉด์ ์ด ๋ชจ๋ ๊ฒ ๋ฐ๋๊ณ ์์ด. ๊ฒ์ ์์ดํ ์ NFT๋ก ๋ง๋ค๋ฉด ์ง์ง ๋ด ์์ ๊ฐ ๋๊ณ , ๊ฒ์ ๋ฐ์์๋ ๊ฑฐ๋ํ ์ ์๊ฒ ๋๋ ๊ฑฐ์ง.
์ด ๊ธ์์๋ ๊ฒ์ ์์ดํ ์ NFT๋ก ๋ง๋๋ ์ ์ฒด ๊ตฌ์กฐ๋ฅผ ์ฒ์๋ถํฐ ๋๊น์ง ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ฏ์ด ์ฝ๊ฒ ํ์ด๋ณผ๊ฒ. ๊ธฐ์ ์ ์ธ ๋ถ๋ถ๋ ์์ง๋ง ๊ฑฑ์ ๋ง! ์ต๋ํ ์ฌ๋ฏธ์๊ฒ ์ค๋ช ํด์ค ํ ๋๊น. ๐
๐ฏ NFT๊ฐ ๋ญ๋ฐ? ๊ฒ์ ์์ดํ ๊ณผ ๋ฌด์จ ๊ด๊ณ์ผ?
๋จผ์ NFT๊ฐ ๋ญ์ง๋ถํฐ ์์์ผ๊ฒ ์ง? NFT๋ Non-Fungible Token์ ์ฝ์์ผ. ๐ ์ฝ๊ฒ ๋งํ๋ฉด '๋์ฒด ๋ถ๊ฐ๋ฅํ ํ ํฐ'์ด๋ผ๋ ๋ป์ธ๋ฐ, ์ด๊ฒ ๋ฌด์จ ๋ง์ด๋๋ฉด...
์ผ๋ฐ์ ์ธ ๋์ '๋์ฒด ๊ฐ๋ฅ'ํด. ๋ด๊ฐ ๊ฐ์ง 1๋ง์๊ณผ ๋ค๊ฐ ๊ฐ์ง 1๋ง์์ ๋๊ฐ์ ๊ฐ์น๋ฅผ ๊ฐ์ง๊ณ ์์ด์ ์๋ก ๋ฐ๊ฟ๋ ์๋ฌด ๋ฌธ์ ๊ฐ ์์ง. ํ์ง๋ง NFT๋ ๋ฌ๋ผ. ๊ฐ๊ฐ์ NFT๋ ๊ณ ์ ํ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ด์ ์๋ก ๋ฐ๊ฟ ์ ์์ด. ๋ง์น ์ธ์์ ํ๋๋ฟ์ธ ๊ทธ๋ฆผ์ฒ๋ผ ๋ง์ด์ผ. ๐ผ๏ธ
๊ฒ์ ์์ดํ ๋ ๋ง์ฐฌ๊ฐ์ง์ผ. ๋ด๊ฐ ๊ฒ์์์ ์ป์ '๋ถ์ ๊ฒ +15 ๊ฐํ ์ต์ '์ ๋ค๋ฅธ ์ฌ๋์ '๋ถ์ ๊ฒ +10'๊ณผ๋ ์์ ํ ๋ค๋ฅธ ์์ดํ ์ด์์? ์ด๋ฐ ๊ณ ์ ์ฑ์ ๋ธ๋ก์ฒด์ธ ๊ธฐ์ ๋ก ์ฆ๋ช ํ๊ณ ๋ณด์ฅํ๋ ๊ฒ ๋ฐ๋ก NFT์ผ.
์ผ๋ฐ์ ์ธ ๋์ '๋์ฒด ๊ฐ๋ฅ'ํด. ๋ด๊ฐ ๊ฐ์ง 1๋ง์๊ณผ ๋ค๊ฐ ๊ฐ์ง 1๋ง์์ ๋๊ฐ์ ๊ฐ์น๋ฅผ ๊ฐ์ง๊ณ ์์ด์ ์๋ก ๋ฐ๊ฟ๋ ์๋ฌด ๋ฌธ์ ๊ฐ ์์ง. ํ์ง๋ง NFT๋ ๋ฌ๋ผ. ๊ฐ๊ฐ์ NFT๋ ๊ณ ์ ํ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ด์ ์๋ก ๋ฐ๊ฟ ์ ์์ด. ๋ง์น ์ธ์์ ํ๋๋ฟ์ธ ๊ทธ๋ฆผ์ฒ๋ผ ๋ง์ด์ผ. ๐ผ๏ธ
๊ฒ์ ์์ดํ ๋ ๋ง์ฐฌ๊ฐ์ง์ผ. ๋ด๊ฐ ๊ฒ์์์ ์ป์ '๋ถ์ ๊ฒ +15 ๊ฐํ ์ต์ '์ ๋ค๋ฅธ ์ฌ๋์ '๋ถ์ ๊ฒ +10'๊ณผ๋ ์์ ํ ๋ค๋ฅธ ์์ดํ ์ด์์? ์ด๋ฐ ๊ณ ์ ์ฑ์ ๋ธ๋ก์ฒด์ธ ๊ธฐ์ ๋ก ์ฆ๋ช ํ๊ณ ๋ณด์ฅํ๋ ๊ฒ ๋ฐ๋ก NFT์ผ.
๐ก ํต์ฌ ํฌ์ธํธ:
NFT๋ ๋์งํธ ์์ฐ์ ์์ ๊ถ์ ๋ธ๋ก์ฒด์ธ์ ๊ธฐ๋กํด์ ๋๊ตฌ๋ ํ์ธํ ์ ์๊ฒ ๋ง๋๋ ๊ธฐ์ ์ด์ผ. ๊ฒ์ ์์ดํ
์ NFT๋ก ๋ง๋ค๋ฉด ๊ทธ ์์ดํ
์ด ์ง์ง ๋ด ๊ฒ์ด๋ผ๋ ๊ฑธ ์ฆ๋ช
ํ ์ ์๊ณ , ๊ฒ์ ํ์ฌ๊ฐ ๋งํด๋ ๋ด ์์ดํ
์ ์ฌ๋ผ์ง์ง ์์! ๐ก๏ธ
๐๏ธ ๊ฒ์ ์์ดํ NFTํ์ ์ ์ฒด ๊ตฌ์กฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๊ฒ์ ์์ดํ
์ NFT๋ก ๋ง๋๋ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณผ๊น? ์ ์ฒด ๊ณผ์ ์ ํฌ๊ฒ 5๋จ๊ณ๋ก ๋๋ ์ ์์ด. ๊ฐ ๋จ๊ณ๋ฅผ ํ๋์ฉ ์์ธํ ์์๋ณด์! ๐
๐ 1๋จ๊ณ: ๊ฒ์ ์์ดํ ๋ฐ์ดํฐ ์ค๊ณ
์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ๊ฒ์ ์์ดํ
์ ์์ฑ์ ์ ์ํ๋ ๊ฑฐ์ผ. NFT๋ก ๋ง๋ค ์์ดํ
์ด ์ด๋ค ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๋ช
ํํ๊ฒ ์ค๊ณํด์ผ ํด.
์๋ฅผ ๋ค์ด RPG ๊ฒ์์ ๊ฒ์ NFT๋ก ๋ง๋ ๋ค๊ณ ์๊ฐํด๋ณด์. ์ด ๊ฒ์ ๋ค์๊ณผ ๊ฐ์ ์ ๋ณด๋ฅผ ๊ฐ์ง ์ ์์ด:
์๋ฅผ ๋ค์ด RPG ๊ฒ์์ ๊ฒ์ NFT๋ก ๋ง๋ ๋ค๊ณ ์๊ฐํด๋ณด์. ์ด ๊ฒ์ ๋ค์๊ณผ ๊ฐ์ ์ ๋ณด๋ฅผ ๊ฐ์ง ์ ์์ด:
๐ก๏ธ ๊ฒ์ ์์ดํ
์์ฑ ์์:
โข ์์ดํ
์ด๋ฆ: ๋๋๊ณค ์ฌ๋ ์ด์ด
โข ๋ฑ๊ธ: ์ ์ค (Legendary)
โข ๊ณต๊ฒฉ๋ ฅ: 250
โข ํน์ ๋ฅ๋ ฅ: ๋๋๊ณค์๊ฒ 50% ์ถ๊ฐ ๋ฐ๋ฏธ์ง
โข ๊ฐํ ์์น: +15
โข ๋ด๊ตฌ๋: 100/100
โข ํ๋ ๋ ์ง: 2024-01-15
โข ์ด์ ์์ ์ ๊ธฐ๋ก: ๊ฑฐ๋ ์ด๋ ฅ
โข ์๊ฐ์ ์ธํ: 3D ๋ชจ๋ธ ํ์ผ ๋งํฌ
โข ๋ฑ๊ธ: ์ ์ค (Legendary)
โข ๊ณต๊ฒฉ๋ ฅ: 250
โข ํน์ ๋ฅ๋ ฅ: ๋๋๊ณค์๊ฒ 50% ์ถ๊ฐ ๋ฐ๋ฏธ์ง
โข ๊ฐํ ์์น: +15
โข ๋ด๊ตฌ๋: 100/100
โข ํ๋ ๋ ์ง: 2024-01-15
โข ์ด์ ์์ ์ ๊ธฐ๋ก: ๊ฑฐ๋ ์ด๋ ฅ
โข ์๊ฐ์ ์ธํ: 3D ๋ชจ๋ธ ํ์ผ ๋งํฌ
์ด๋ฐ ์์ฑ๋ค์ JSON ํ์์ผ๋ก ๊ตฌ์กฐํํด์ผ ํด. JSON์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ ์กํ๋ ํ์ค ํ์์ด์ผ. ์ค์ ๋ก๋ ์ด๋ ๊ฒ ์๊ฒผ์ด:
{
"name": "๋๋๊ณค ์ฌ๋ ์ด์ด",
"description": "๊ณ ๋ ๋๋๊ณค์ ๋ฌผ๋ฆฌ์น ์ ์ค์ ๊ฒ",
"image": "ipfs://QmX7x8x9x.../dragon-slayer.png",
"attributes": [
{
"trait_type": "๋ฑ๊ธ",
"value": "์ ์ค"
},
{
"trait_type": "๊ณต๊ฒฉ๋ ฅ",
"value": 250
},
{
"trait_type": "๊ฐํ",
"value": 15
},
{
"trait_type": "ํน์๋ฅ๋ ฅ",
"value": "๋๋๊ณค ํฌ๋ฌ"
}
],
"game_stats": {
"durability": 100,
"acquired_date": "2024-01-15",
"level_requirement": 50
}
}
โ ๏ธ ์ฃผ์์ฌํญ:
๊ฒ์ ์์ดํ
์ ์ด๋ฏธ์ง๋ 3D ๋ชจ๋ธ ๊ฐ์ ํฐ ํ์ผ์ ๋ธ๋ก์ฒด์ธ์ ์ง์ ์ ์ฅํ๋ฉด ๋น์ฉ์ด ์์ฒญ๋๊ฒ ๋ง์ด ๋ค์ด. ๊ทธ๋์ ๋ณดํต IPFS(InterPlanetary File System)๋ผ๋ ๋ถ์ฐ ์ ์ฅ ์์คํ
์ ํ์ผ์ ์ฌ๋ฆฌ๊ณ , ๋ธ๋ก์ฒด์ธ์๋ ๊ทธ ํ์ผ์ ์ฃผ์๋ง ์ ์ฅํด. ๐พ
โ๏ธ 2๋จ๊ณ: ๋ธ๋ก์ฒด์ธ ๋คํธ์ํฌ ์ ํ
๊ฒ์ ์์ดํ
์ NFT๋ก ๋ง๋ค๋ ค๋ฉด ์ด๋ค ๋ธ๋ก์ฒด์ธ์ ์ฌ์ฉํ ์ง ๊ฒฐ์ ํด์ผ ํด. ๊ฐ ๋ธ๋ก์ฒด์ธ๋ง๋ค ์ฅ๋จ์ ์ด ์๊ฑฐ๋ . ์ฃผ์ ์ ํ์ง๋ฅผ ๋น๊ตํด๋ณผ๊น? ๐ค
| ๋ธ๋ก์ฒด์ธ | ์ฅ์ | ๋จ์ | ๊ฒ์ ์ ํฉ๋ |
|---|---|---|---|
| ์ด๋๋ฆฌ์ | ๊ฐ์ฅ ํฐ ์ํ๊ณ, ๋์ ๋ณด์์ฑ, ๋ง์ ๊ฐ๋ฐ์ | ๋์ ๊ฐ์ค๋น, ๋๋ฆฐ ์๋ | โญโญโญ |
| ํด๋ฆฌ๊ณค | ๋ฎ์ ์์๋ฃ, ๋น ๋ฅธ ์๋, ์ด๋๋ฆฌ์ ํธํ | ์๋์ ์ผ๋ก ๋ฎ์ ํ์ค์ํ | โญโญโญโญโญ |
| ํด๋ ์ดํผ | ํ๊ตญ ๊ธฐ๋ฐ, ๋น ๋ฅธ ์๋, ๊ฒ์ ์นํ์ | ์์ ๊ธ๋ก๋ฒ ์ํ๊ณ | โญโญโญโญ |
| ์ด๋ฎคํฐ๋ธX | ๊ฒ์ ํนํ, ๊ฐ์ค๋น ๋ฌด๋ฃ, NFT ๊ฑฐ๋ ์ต์ ํ | ์ ํ์ ์ธ ์ค๋งํธ ์ปจํธ๋ํธ | โญโญโญโญโญ |
| ์๋ผ๋ | ๋งค์ฐ ๋น ๋ฅธ ์๋, ๋ฎ์ ์์๋ฃ | ๋คํธ์ํฌ ์์ ์ฑ ์ด์ | โญโญโญโญ |
๊ฒ์์ฉ์ผ๋ก๋ ํด๋ฆฌ๊ณค์ด๋ ์ด๋ฎคํฐ๋ธX๊ฐ ๊ฐ์ฅ ์ธ๊ธฐ๊ฐ ๋ง์. ์๋ํ๋ฉด ๊ฒ์์ ์์ดํ
๊ฑฐ๋๊ฐ ์์ฃผ ์ผ์ด๋๋๋ฐ, ๊ฑฐ๋ํ ๋๋ง๋ค ๋์ ์์๋ฃ๋ฅผ ๋ด๋ฉด ์ฌ์ฉ์๋ค์ด ์ซ์ดํ๊ฑฐ๋ . ๐
์๋ฅผ ๋ค์ด ์ด๋๋ฆฌ์ ๋ฉ์ธ๋ท์์๋ NFT ํ๋๋ฅผ ๋ฐํ(๋ฏผํ )ํ๋ ๋ฐ ์์๋ฃ๊ฐ ๋ช๋ง ์์ฉ ๋ค ์ ์์ด. ํ์ง๋ง ํด๋ฆฌ๊ณค์์๋ ๋ช ์์์ ๋ช๋ฐฑ ์ ์ ๋๋ก ์์ฒญ๋๊ฒ ์ ๋ ดํด. ๊ฒ์์์ ์์ดํ ์ ์ป์ ๋๋ง๋ค ์๋ง ์์ฉ ๋ด์ผ ํ๋ค๋ฉด... ๋๊ฐ ํ๊ฒ ์ด? ๐ โโ๏ธ
์๋ฅผ ๋ค์ด ์ด๋๋ฆฌ์ ๋ฉ์ธ๋ท์์๋ NFT ํ๋๋ฅผ ๋ฐํ(๋ฏผํ )ํ๋ ๋ฐ ์์๋ฃ๊ฐ ๋ช๋ง ์์ฉ ๋ค ์ ์์ด. ํ์ง๋ง ํด๋ฆฌ๊ณค์์๋ ๋ช ์์์ ๋ช๋ฐฑ ์ ์ ๋๋ก ์์ฒญ๋๊ฒ ์ ๋ ดํด. ๊ฒ์์์ ์์ดํ ์ ์ป์ ๋๋ง๋ค ์๋ง ์์ฉ ๋ด์ผ ํ๋ค๋ฉด... ๋๊ฐ ํ๊ฒ ์ด? ๐ โโ๏ธ
๐ 3๋จ๊ณ: ์ค๋งํธ ์ปจํธ๋ํธ ๊ฐ๋ฐ
์ด์ ๊ฐ์ฅ ํต์ฌ์ ์ธ ๋ถ๋ถ์ด์ผ! ์ค๋งํธ ์ปจํธ๋ํธ๋ ๋ธ๋ก์ฒด์ธ ์์์ ์๋์ผ๋ก ์คํ๋๋ ํ๋ก๊ทธ๋จ์ด์ผ. ๊ฒ์ ์์ดํ
NFT๋ฅผ ๋ง๋ค๊ณ , ์ ์กํ๊ณ , ๊ด๋ฆฌํ๋ ๋ชจ๋ ๊ท์น์ด ์ฌ๊ธฐ์ ์ฝ๋๋ก ์์ฑ๋ผ. ๐ป
์ค๋งํธ ์ปจํธ๋ํธ๋ ๋ณดํต Solidity๋ผ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก ์์ฑํด. ์ด๋๋ฆฌ์๊ณผ ํด๋ฆฌ๊ณค ๊ฐ์ EVM(Ethereum Virtual Machine) ํธํ ๋ธ๋ก์ฒด์ธ์์ ์ฌ์ฉํ๋ ์ธ์ด์ผ.
์ค๋งํธ ์ปจํธ๋ํธ๋ ๋ณดํต Solidity๋ผ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก ์์ฑํด. ์ด๋๋ฆฌ์๊ณผ ํด๋ฆฌ๊ณค ๊ฐ์ EVM(Ethereum Virtual Machine) ํธํ ๋ธ๋ก์ฒด์ธ์์ ์ฌ์ฉํ๋ ์ธ์ด์ผ.
๐ง ์ค๋งํธ ์ปจํธ๋ํธ์ ์ฃผ์ ๊ธฐ๋ฅ:
1. ๋ฏผํ (Minting): ์๋ก์ด NFT๋ฅผ ์์ฑํ๋ ๊ธฐ๋ฅ
2. ์ ์ก(Transfer): NFT๋ฅผ ๋ค๋ฅธ ์ฌ๋์๊ฒ ๋ณด๋ด๋ ๊ธฐ๋ฅ
3. ์๊ฐ(Burn): NFT๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ญ์ ํ๋ ๊ธฐ๋ฅ
4. ๋ฉํ๋ฐ์ดํฐ ๊ด๋ฆฌ: NFT์ ์์ฑ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ ์กฐํํ๋ ๊ธฐ๋ฅ
5. ๊ถํ ๊ด๋ฆฌ: ๋๊ฐ NFT๋ฅผ ๋ฐํํ ์ ์๋์ง ์ ์ดํ๋ ๊ธฐ๋ฅ
1. ๋ฏผํ (Minting): ์๋ก์ด NFT๋ฅผ ์์ฑํ๋ ๊ธฐ๋ฅ
2. ์ ์ก(Transfer): NFT๋ฅผ ๋ค๋ฅธ ์ฌ๋์๊ฒ ๋ณด๋ด๋ ๊ธฐ๋ฅ
3. ์๊ฐ(Burn): NFT๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ญ์ ํ๋ ๊ธฐ๋ฅ
4. ๋ฉํ๋ฐ์ดํฐ ๊ด๋ฆฌ: NFT์ ์์ฑ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ ์กฐํํ๋ ๊ธฐ๋ฅ
5. ๊ถํ ๊ด๋ฆฌ: ๋๊ฐ NFT๋ฅผ ๋ฐํํ ์ ์๋์ง ์ ์ดํ๋ ๊ธฐ๋ฅ
์ค์ ์ค๋งํธ ์ปจํธ๋ํธ ์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ์ดํด๋ณผ๊น? ERC-721 ํ์ค์ ์ฌ์ฉํ ๊ธฐ๋ณธ์ ์ธ ๊ฒ์ ์์ดํ
NFT ์ปจํธ๋ํธ์ผ:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract GameItemNFT is ERC721, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
// ์์ดํ
์์ฑ ๊ตฌ์กฐ์ฒด
struct ItemStats {
string itemName;
uint256 attackPower;
uint256 defense;
uint256 rarity; // 1: ์ผ๋ฐ, 2: ํฌ๊ท, 3: ์์
, 4: ์ ์ค
uint256 level;
bool isUpgradeable;
}
// ํ ํฐ ID๋ณ ์์ดํ
์์ฑ ๋งคํ
mapping(uint256 => ItemStats) public itemStats;
// ๊ฒ์ ์๋ฒ ์ฃผ์ (์์ดํ
๋ฐํ ๊ถํ)
address public gameServer;
constructor() ERC721("GameItem", "GITEM") {
gameServer = msg.sender;
}
// ๊ฒ์ ์๋ฒ๋ง ์์ดํ
๋ฐํ ๊ฐ๋ฅ
modifier onlyGameServer() {
require(msg.sender == gameServer, "Only game server can mint");
_;
}
// ์๋ก์ด ๊ฒ์ ์์ดํ
NFT ๋ฐํ
function mintItem(
address player,
string memory tokenURI,
string memory itemName,
uint256 attackPower,
uint256 defense,
uint256 rarity,
uint256 level
) public onlyGameServer returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
// ์์ดํ
์์ฑ ์ ์ฅ
itemStats[newItemId] = ItemStats({
itemName: itemName,
attackPower: attackPower,
defense: defense,
rarity: rarity,
level: level,
isUpgradeable: true
});
return newItemId;
}
// ์์ดํ
๊ฐํ ๊ธฐ๋ฅ
function upgradeItem(uint256 tokenId, uint256 newAttackPower)
public onlyGameServer {
require(_exists(tokenId), "Item does not exist");
require(itemStats[tokenId].isUpgradeable, "Item cannot be upgraded");
itemStats[tokenId].attackPower = newAttackPower;
itemStats[tokenId].level += 1;
}
// ์์ดํ
์ ๋ณด ์กฐํ
function getItemStats(uint256 tokenId)
public view returns (ItemStats memory) {
require(_exists(tokenId), "Item does not exist");
return itemStats[tokenId];
}
// ๊ฒ์ ์๋ฒ ์ฃผ์ ๋ณ๊ฒฝ
function setGameServer(address newGameServer) public onlyOwner {
gameServer = newGameServer;
}
// ํ์ ์ค๋ฒ๋ผ์ด๋ ํจ์๋ค
function _burn(uint256 tokenId)
internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public view override(ERC721, ERC721URIStorage)
returns (string memory) {
return super.tokenURI(tokenId);
}
}
์ฝ๋๊ฐ ์ข ๋ณต์กํด ๋ณด์ด์ง? ๐
ํ๋์ฉ ์ค๋ช
ํด์ค๊ฒ:
1. ERC-721 ํ์ค ์ฌ์ฉ: ERC-721์ NFT์ ํ์ค ๊ท๊ฒฉ์ด์ผ. ์ด ํ์ค์ ๋ฐ๋ฅด๋ฉด ๋ค๋ฅธ ์ง๊ฐ์ด๋ ๋ง์ผํ๋ ์ด์ค์์๋ ๋ด NFT๋ฅผ ์ธ์ํ ์ ์์ด.
2. OpenZeppelin ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ๋ณด์์ด ๊ฒ์ฆ๋ ์ฝ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ์์ ํ๊ฒ ๊ฐ๋ฐํ ์ ์์ด. ์ฒ์๋ถํฐ ๋ค ๋ง๋ค ํ์ ์์ด ๊ฒ์ฆ๋ ์ฝ๋๋ฅผ ๊ฐ์ ธ๋ค ์ฐ๋ ๊ฑฐ์ง.
3. ItemStats ๊ตฌ์กฐ์ฒด: ๊ฒ์ ์์ดํ ์ ์์ฑ์ ์ ์ฅํ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ผ. ๊ณต๊ฒฉ๋ ฅ, ๋ฐฉ์ด๋ ฅ, ํฌ๊ท๋ ๊ฐ์ ๊ฒ์ ๋ด ์ ๋ณด๋ฅผ ๋ธ๋ก์ฒด์ธ์ ๊ธฐ๋กํด.
4. mintItem ํจ์: ์๋ก์ด ์์ดํ NFT๋ฅผ ๋ฐํํ๋ ํต์ฌ ํจ์์ผ. ๊ฒ์ ์๋ฒ๋ง ํธ์ถํ ์ ์๋๋ก ๊ถํ์ ์ ํํ์ด.
5. upgradeItem ํจ์: ๊ฒ์ ๋ด์์ ์์ดํ ์ ๊ฐํํ๋ฉด ๋ธ๋ก์ฒด์ธ์ ๋ฐ์ดํฐ๋ ์ ๋ฐ์ดํธ๋๋ ๊ธฐ๋ฅ์ด์ผ.
1. ERC-721 ํ์ค ์ฌ์ฉ: ERC-721์ NFT์ ํ์ค ๊ท๊ฒฉ์ด์ผ. ์ด ํ์ค์ ๋ฐ๋ฅด๋ฉด ๋ค๋ฅธ ์ง๊ฐ์ด๋ ๋ง์ผํ๋ ์ด์ค์์๋ ๋ด NFT๋ฅผ ์ธ์ํ ์ ์์ด.
2. OpenZeppelin ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ๋ณด์์ด ๊ฒ์ฆ๋ ์ฝ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ์์ ํ๊ฒ ๊ฐ๋ฐํ ์ ์์ด. ์ฒ์๋ถํฐ ๋ค ๋ง๋ค ํ์ ์์ด ๊ฒ์ฆ๋ ์ฝ๋๋ฅผ ๊ฐ์ ธ๋ค ์ฐ๋ ๊ฑฐ์ง.
3. ItemStats ๊ตฌ์กฐ์ฒด: ๊ฒ์ ์์ดํ ์ ์์ฑ์ ์ ์ฅํ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ผ. ๊ณต๊ฒฉ๋ ฅ, ๋ฐฉ์ด๋ ฅ, ํฌ๊ท๋ ๊ฐ์ ๊ฒ์ ๋ด ์ ๋ณด๋ฅผ ๋ธ๋ก์ฒด์ธ์ ๊ธฐ๋กํด.
4. mintItem ํจ์: ์๋ก์ด ์์ดํ NFT๋ฅผ ๋ฐํํ๋ ํต์ฌ ํจ์์ผ. ๊ฒ์ ์๋ฒ๋ง ํธ์ถํ ์ ์๋๋ก ๊ถํ์ ์ ํํ์ด.
5. upgradeItem ํจ์: ๊ฒ์ ๋ด์์ ์์ดํ ์ ๊ฐํํ๋ฉด ๋ธ๋ก์ฒด์ธ์ ๋ฐ์ดํฐ๋ ์ ๋ฐ์ดํธ๋๋ ๊ธฐ๋ฅ์ด์ผ.
โ
์ค๋ฌด ํ:
์ค์ ๊ฒ์ ๊ฐ๋ฐ์์๋ ๋ชจ๋ ์์ดํ
์์ฑ์ ๋ธ๋ก์ฒด์ธ์ ์ ์ฅํ์ง ์์. ๊ฐ์ค๋น๊ฐ ๋๋ฌด ๋ง์ด ๋ค๊ฑฐ๋ . ๋์ ํต์ฌ ์์ฑ๋ง ๋ธ๋ก์ฒด์ธ์ ์ ์ฅํ๊ณ , ์ธ๋ถ ์ ๋ณด๋ IPFS๋ ๊ฒ์ ์๋ฒ์ ์ ์ฅํ ํ ๋งํฌ๋ง ๋ธ๋ก์ฒด์ธ์ ๊ธฐ๋กํ๋ ๋ฐฉ์์ ๋ง์ด ์จ. ๐ฏ
๐ 4๋จ๊ณ: ๊ฒ์ ์๋ฒ์ ๋ธ๋ก์ฒด์ธ ์ฐ๋
์ค๋งํธ ์ปจํธ๋ํธ๋ฅผ ๋ง๋ค์๋ค๊ณ ๋์ด ์๋์ผ. ์ด์ ์ค์ ๊ฒ์ ์๋ฒ์ ๋ธ๋ก์ฒด์ธ์ ์ฐ๊ฒฐํด์ผ ํด. ํ๋ ์ด์ด๊ฐ ๊ฒ์์์ ์์ดํ
์ ํ๋ํ๋ฉด ์๋์ผ๋ก NFT๊ฐ ๋ฐํ๋๋๋ก ๋ง๋๋ ๊ฑฐ์ง. ๐ฎ
์ด ๊ณผ์ ์์ Web3 ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด. Web3๋ ์ผ๋ฐ ํ๋ก๊ทธ๋จ์ด ๋ธ๋ก์ฒด์ธ๊ณผ ์ํตํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ์ผ.
์ด ๊ณผ์ ์์ Web3 ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด. Web3๋ ์ผ๋ฐ ํ๋ก๊ทธ๋จ์ด ๋ธ๋ก์ฒด์ธ๊ณผ ์ํตํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ์ผ.
๐ ๊ฒ์-๋ธ๋ก์ฒด์ธ ์ฐ๋ ํ๋ก์ธ์ค:
Step 1: ํ๋ ์ด์ด๊ฐ ๊ฒ์์์ ๋ณด์ค๋ฅผ ์ก๊ณ ์ ์ค ์์ดํ ํ๋
Step 2: ๊ฒ์ ์๋ฒ๊ฐ ์์ดํ ํ๋ ์ด๋ฒคํธ ๊ฐ์ง
Step 3: ๊ฒ์ ์๋ฒ๊ฐ Web3๋ฅผ ํตํด ์ค๋งํธ ์ปจํธ๋ํธ์ mintItem ํจ์ ํธ์ถ
Step 4: ๋ธ๋ก์ฒด์ธ์ NFT ๋ฐํ ํธ๋์ญ์ ์ ์ก
Step 5: ํธ๋์ญ์ ํ์ธ ํ ํ๋ ์ด์ด ์ง๊ฐ์ NFT ์ ์ก
Step 6: ๊ฒ์ UI์ NFT ํ๋ ์๋ฆผ ํ์
Step 1: ํ๋ ์ด์ด๊ฐ ๊ฒ์์์ ๋ณด์ค๋ฅผ ์ก๊ณ ์ ์ค ์์ดํ ํ๋
Step 2: ๊ฒ์ ์๋ฒ๊ฐ ์์ดํ ํ๋ ์ด๋ฒคํธ ๊ฐ์ง
Step 3: ๊ฒ์ ์๋ฒ๊ฐ Web3๋ฅผ ํตํด ์ค๋งํธ ์ปจํธ๋ํธ์ mintItem ํจ์ ํธ์ถ
Step 4: ๋ธ๋ก์ฒด์ธ์ NFT ๋ฐํ ํธ๋์ญ์ ์ ์ก
Step 5: ํธ๋์ญ์ ํ์ธ ํ ํ๋ ์ด์ด ์ง๊ฐ์ NFT ์ ์ก
Step 6: ๊ฒ์ UI์ NFT ํ๋ ์๋ฆผ ํ์
Node.js ํ๊ฒฝ์์ Web3.js๋ฅผ ์ฌ์ฉํ ์ฐ๋ ์ฝ๋ ์์๋ฅผ ๋ณผ๊น?
const Web3 = require('web3');
const contractABI = require('./GameItemNFT.json');
// ๋ธ๋ก์ฒด์ธ ์ฐ๊ฒฐ ์ค์
const web3 = new Web3('https://polygon-rpc.com'); // ํด๋ฆฌ๊ณค RPC
const contractAddress = '0x1234567890abcdef...'; // ๋ฐฐํฌ๋ ์ปจํธ๋ํธ ์ฃผ์
// ๊ฒ์ ์๋ฒ ์ง๊ฐ ์ค์
const gameServerPrivateKey = process.env.GAME_SERVER_PRIVATE_KEY;
const gameServerAccount = web3.eth.accounts.privateKeyToAccount(gameServerPrivateKey);
web3.eth.accounts.wallet.add(gameServerAccount);
// ์ค๋งํธ ์ปจํธ๋ํธ ์ธ์คํด์ค ์์ฑ
const gameItemContract = new web3.eth.Contract(contractABI, contractAddress);
// ์์ดํ
NFT ๋ฐํ ํจ์
async function mintItemNFT(playerAddress, itemData) {
try {
console.log('NFT ๋ฐํ ์์...');
// IPFS์ ๋ฉํ๋ฐ์ดํฐ ์
๋ก๋ (์ค์ ๋ก๋ IPFS ํด๋ผ์ด์ธํธ ์ฌ์ฉ)
const metadataURI = await uploadToIPFS(itemData);
// ๊ฐ์ค ๊ฐ๊ฒฉ ์ถ์
const gasPrice = await web3.eth.getGasPrice();
// ํธ๋์ญ์
์์ฑ ๋ฐ ์ ์ก
const tx = gameItemContract.methods.mintItem(
playerAddress,
metadataURI,
itemData.name,
itemData.attackPower,
itemData.defense,
itemData.rarity,
itemData.level
);
const gas = await tx.estimateGas({ from: gameServerAccount.address });
const receipt = await tx.send({
from: gameServerAccount.address,
gas: gas,
gasPrice: gasPrice
});
console.log('NFT ๋ฐํ ์๋ฃ!');
console.log('ํธ๋์ญ์
ํด์:', receipt.transactionHash);
console.log('ํ ํฐ ID:', receipt.events.Transfer.returnValues.tokenId);
return {
success: true,
tokenId: receipt.events.Transfer.returnValues.tokenId,
txHash: receipt.transactionHash
};
} catch (error) {
console.error('NFT ๋ฐํ ์คํจ:', error);
return {
success: false,
error: error.message
};
}
}
// IPFS ์
๋ก๋ ํจ์ (๊ฐ๋จํ ์์)
async function uploadToIPFS(itemData) {
// ์ค์ ๋ก๋ Pinata, NFT.Storage ๊ฐ์ ์๋น์ค ์ฌ์ฉ
const metadata = {
name: itemData.name,
description: itemData.description,
image: itemData.imageUrl,
attributes: [
{ trait_type: "๊ณต๊ฒฉ๋ ฅ", value: itemData.attackPower },
{ trait_type: "๋ฐฉ์ด๋ ฅ", value: itemData.defense },
{ trait_type: "ํฌ๊ท๋", value: itemData.rarity },
{ trait_type: "๋ ๋ฒจ", value: itemData.level }
]
};
// IPFS ์
๋ก๋ ๋ก์ง
// ...
return 'ipfs://QmX7x8x9x.../metadata.json';
}
// ๊ฒ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์์
function onPlayerObtainedItem(playerId, itemData) {
// ํ๋ ์ด์ด์ ์ง๊ฐ ์ฃผ์ ์กฐํ
const playerWalletAddress = getPlayerWalletAddress(playerId);
// NFT ๋ฐํ
mintItemNFT(playerWalletAddress, itemData)
.then(result => {
if (result.success) {
// ๊ฒ์ ๋ด ์๋ฆผ ์ ์ก
notifyPlayer(playerId, {
message: `${itemData.name} NFT๋ฅผ ํ๋ํ์ต๋๋ค!`,
tokenId: result.tokenId,
txHash: result.txHash
});
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ธฐ๋ก
saveNFTRecord(playerId, result.tokenId, itemData);
}
})
.catch(error => {
console.error('NFT ๋ฐํ ์ค ์ค๋ฅ:', error);
});
}
module.exports = {
mintItemNFT,
onPlayerObtainedItem
};
์ด ์ฝ๋๋ ๊ฒ์ ์๋ฒ์์ ์คํ๋๋ ๋ฐฑ์๋ ๋ก์ง์ด์ผ. ํ๋ ์ด์ด๊ฐ ์์ดํ
์ ํ๋ํ๋ฉด ์๋์ผ๋ก NFT๋ฅผ ๋ฐํํด์ฃผ๋ ์์คํ
์ด์ง. ๐ค
์ค์ํ ๊ฑด ๋น๋๊ธฐ ์ฒ๋ฆฌ์ผ. ๋ธ๋ก์ฒด์ธ ํธ๋์ญ์ ์ ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ฑฐ๋ . ํด๋ฆฌ๊ณค์์๋ ๋ณดํต 2-5์ด ์ ๋ ๊ฑธ๋ ค. ๊ทธ๋์ ํ๋ ์ด์ด๊ฐ ๊ฒ์์ ํ๋ ๋์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ NFT ๋ฐํ์ด ์งํ๋๊ณ , ์๋ฃ๋๋ฉด ์๋ฆผ์ ๋ณด๋ด๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํด์ผ ํด.
์ค์ํ ๊ฑด ๋น๋๊ธฐ ์ฒ๋ฆฌ์ผ. ๋ธ๋ก์ฒด์ธ ํธ๋์ญ์ ์ ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ฑฐ๋ . ํด๋ฆฌ๊ณค์์๋ ๋ณดํต 2-5์ด ์ ๋ ๊ฑธ๋ ค. ๊ทธ๋์ ํ๋ ์ด์ด๊ฐ ๊ฒ์์ ํ๋ ๋์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ NFT ๋ฐํ์ด ์งํ๋๊ณ , ์๋ฃ๋๋ฉด ์๋ฆผ์ ๋ณด๋ด๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํด์ผ ํด.
โ ๏ธ ๋ณด์ ์ฃผ์์ฌํญ:
๊ฒ์ ์๋ฒ์ ํ๋ผ์ด๋น ํค๋ ์ ๋๋ก ์ฝ๋์ ์ง์ ๋ฃ์ผ๋ฉด ์ ๋ผ! ํ๊ฒฝ ๋ณ์๋ AWS Secrets Manager ๊ฐ์ ์์ ํ ๊ณณ์ ์ ์ฅํด์ผ ํด. ํ๋ผ์ด๋น ํค๊ฐ ์ ์ถ๋๋ฉด ๋๊ตฌ๋ NFT๋ฅผ ๋ง์๋๋ก ๋ฐํํ ์ ์๊ฑฐ๋ . ๐
๐ 5๋จ๊ณ: ์ง๊ฐ ์ฐ๋ ๋ฐ ์ฌ์ฉ์ ๊ฒฝํ
๋ง์ง๋ง ๋จ๊ณ๋ ํ๋ ์ด์ด๊ฐ ์์ ์ NFT๋ฅผ ๊ด๋ฆฌํ ์ ์๋๋ก ์ง๊ฐ์ ์ฐ๋ํ๋ ๊ฑฐ์ผ. ๋๋ถ๋ถ์ ๊ฒ์์ MetaMask ๊ฐ์ Web3 ์ง๊ฐ์ ์ฌ์ฉํด. ๐ฑ
ํ์ง๋ง ์ผ๋ฐ ๊ฒ์ด๋จธ๋ค์ ๋ธ๋ก์ฒด์ธ์ด๋ ์ง๊ฐ์ ์ต์ํ์ง ์์. ๊ทธ๋์ ์ต๊ทผ์๋ '์ปค์คํฐ๋ ์ง๊ฐ(Custodial Wallet)' ๋ฐฉ์์ ๋ง์ด ์ฌ์ฉํด. ๊ฒ์ ํ์ฌ๊ฐ ํ๋ ์ด์ด ๋์ ์ง๊ฐ์ ๊ด๋ฆฌํด์ฃผ๋ ๊ฑฐ์ง.
ํ์ง๋ง ์ผ๋ฐ ๊ฒ์ด๋จธ๋ค์ ๋ธ๋ก์ฒด์ธ์ด๋ ์ง๊ฐ์ ์ต์ํ์ง ์์. ๊ทธ๋์ ์ต๊ทผ์๋ '์ปค์คํฐ๋ ์ง๊ฐ(Custodial Wallet)' ๋ฐฉ์์ ๋ง์ด ์ฌ์ฉํด. ๊ฒ์ ํ์ฌ๊ฐ ํ๋ ์ด์ด ๋์ ์ง๊ฐ์ ๊ด๋ฆฌํด์ฃผ๋ ๊ฑฐ์ง.
๐ ์ง๊ฐ ์ฐ๋ ๋ฐฉ์ ๋น๊ต:
1. ๋
ผ-์ปค์คํฐ๋ ์ง๊ฐ (Non-Custodial):
โข ํ๋ ์ด์ด๊ฐ ์ง์ MetaMask ๊ฐ์ ์ง๊ฐ ์ค์น
โข ํ๋ ์ด์ด๊ฐ ํ๋ผ์ด๋น ํค ์ง์ ๊ด๋ฆฌ
โข ์ฅ์ : ์์ ํ ์์ ๊ถ, ํ์ค์ํ
โข ๋จ์ : ์ง์ ์ฅ๋ฒฝ ๋์, ํค ๋ถ์ค ์ํ
2. ์ปค์คํฐ๋ ์ง๊ฐ (Custodial):
โข ๊ฒ์ ํ์ฌ๊ฐ ํ๋ ์ด์ด ์ง๊ฐ ์์ฑ ๋ฐ ๊ด๋ฆฌ
โข ์ผ๋ฐ ๊ฒ์ ๋ก๊ทธ์ธ์ฒ๋ผ ๊ฐํธํ๊ฒ ์ฌ์ฉ
โข ์ฅ์ : ์ฌ์ด ์ฌ์ฉ์ฑ, ๋ฎ์ ์ง์ ์ฅ๋ฒฝ
โข ๋จ์ : ๊ฒ์ ํ์ฌ ์์กด, ์ค์ํ
3. ํ์ด๋ธ๋ฆฌ๋ ๋ฐฉ์:
โข ์ฒ์์ ์ปค์คํฐ๋๋ก ์์, ๋์ค์ ๋ ผ-์ปค์คํฐ๋๋ก ์ ํ ๊ฐ๋ฅ
โข ์ต๊ทผ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๋ฐฉ์
โข ์: ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ๋ฐฉ์์ ๊ณ ๋ คํ ์ ์์ด
โข ํ๋ ์ด์ด๊ฐ ์ง์ MetaMask ๊ฐ์ ์ง๊ฐ ์ค์น
โข ํ๋ ์ด์ด๊ฐ ํ๋ผ์ด๋น ํค ์ง์ ๊ด๋ฆฌ
โข ์ฅ์ : ์์ ํ ์์ ๊ถ, ํ์ค์ํ
โข ๋จ์ : ์ง์ ์ฅ๋ฒฝ ๋์, ํค ๋ถ์ค ์ํ
2. ์ปค์คํฐ๋ ์ง๊ฐ (Custodial):
โข ๊ฒ์ ํ์ฌ๊ฐ ํ๋ ์ด์ด ์ง๊ฐ ์์ฑ ๋ฐ ๊ด๋ฆฌ
โข ์ผ๋ฐ ๊ฒ์ ๋ก๊ทธ์ธ์ฒ๋ผ ๊ฐํธํ๊ฒ ์ฌ์ฉ
โข ์ฅ์ : ์ฌ์ด ์ฌ์ฉ์ฑ, ๋ฎ์ ์ง์ ์ฅ๋ฒฝ
โข ๋จ์ : ๊ฒ์ ํ์ฌ ์์กด, ์ค์ํ
3. ํ์ด๋ธ๋ฆฌ๋ ๋ฐฉ์:
โข ์ฒ์์ ์ปค์คํฐ๋๋ก ์์, ๋์ค์ ๋ ผ-์ปค์คํฐ๋๋ก ์ ํ ๊ฐ๋ฅ
โข ์ต๊ทผ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๋ฐฉ์
โข ์: ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ๋ฐฉ์์ ๊ณ ๋ คํ ์ ์์ด
ํ๋ก ํธ์๋์์ MetaMask๋ฅผ ์ฐ๋ํ๋ ์ฝ๋๋ฅผ ๋ณผ๊น?
// React ํ๊ฒฝ์์ MetaMask ์ฐ๋ ์์
import { useState, useEffect } from 'react';
import Web3 from 'web3';
function GameWalletConnect() {
const [account, setAccount] = useState(null);
const [web3, setWeb3] = useState(null);
const [nftBalance, setNftBalance] = useState(0);
// MetaMask ์ฐ๊ฒฐ
const connectWallet = async () => {
try {
// MetaMask ์ค์น ํ์ธ
if (typeof window.ethereum === 'undefined') {
alert('MetaMask๋ฅผ ์ค์นํด์ฃผ์ธ์!');
return;
}
// ๊ณ์ ์ฐ๊ฒฐ ์์ฒญ
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const web3Instance = new Web3(window.ethereum);
setWeb3(web3Instance);
setAccount(accounts[0]);
// ๋คํธ์ํฌ ํ์ธ (ํด๋ฆฌ๊ณค์ธ์ง ์ฒดํฌ)
const chainId = await web3Instance.eth.getChainId();
if (chainId !== 137) { // ํด๋ฆฌ๊ณค ๋ฉ์ธ๋ท
alert('ํด๋ฆฌ๊ณค ๋คํธ์ํฌ๋ก ์ ํํด์ฃผ์ธ์!');
await switchToPolygon();
}
// NFT ์์ก ์กฐํ
await loadNFTBalance(accounts[0], web3Instance);
} catch (error) {
console.error('์ง๊ฐ ์ฐ๊ฒฐ ์คํจ:', error);
}
};
// ํด๋ฆฌ๊ณค ๋คํธ์ํฌ๋ก ์ ํ
const switchToPolygon = async () => {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x89' }], // 137์ 16์ง์๋ก
});
} catch (error) {
// ๋คํธ์ํฌ๊ฐ ์ถ๊ฐ๋์ง ์์ ๊ฒฝ์ฐ
if (error.code === 4902) {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0x89',
chainName: 'Polygon Mainnet',
nativeCurrency: {
name: 'MATIC',
symbol: 'MATIC',
decimals: 18
},
rpcUrls: ['https://polygon-rpc.com/'],
blockExplorerUrls: ['https://polygonscan.com/']
}]
});
}
}
};
// NFT ์์ก ์กฐํ
const loadNFTBalance = async (address, web3Instance) => {
const contractAddress = '0x1234567890abcdef...';
const contractABI = [...]; // ์ปจํธ๋ํธ ABI
const contract = new web3Instance.eth.Contract(
contractABI,
contractAddress
);
const balance = await contract.methods.balanceOf(address).call();
setNftBalance(balance);
// ์์ ํ NFT ๋ชฉ๋ก ์กฐํ
const tokenIds = [];
for (let i = 0; i < balance; i++) {
const tokenId = await contract.methods
.tokenOfOwnerByIndex(address, i)
.call();
tokenIds.push(tokenId);
}
// ๊ฐ NFT์ ๋ฉํ๋ฐ์ดํฐ ๋ก๋
const nfts = await Promise.all(
tokenIds.map(async (tokenId) => {
const tokenURI = await contract.methods
.tokenURI(tokenId)
.call();
const metadata = await fetch(tokenURI).then(r => r.json());
return { tokenId, ...metadata };
})
);
console.log('๋ด NFT ๋ชฉ๋ก:', nfts);
};
// ๊ณ์ ๋ณ๊ฒฝ ๊ฐ์ง
useEffect(() => {
if (window.ethereum) {
window.ethereum.on('accountsChanged', (accounts) => {
setAccount(accounts[0]);
if (accounts[0] && web3) {
loadNFTBalance(accounts[0], web3);
}
});
window.ethereum.on('chainChanged', () => {
window.location.reload();
});
}
}, [web3]);
return (
<div className="wallet-connect">
{!account ? (
<button onClick={connectWallet}>
๐ฆ ์ง๊ฐ ์ฐ๊ฒฐํ๊ธฐ
</button>
) : (
<div>
<p>์ฐ๊ฒฐ๋ ์ง๊ฐ: {account.slice(0, 6)}...{account.slice(-4)}</p>
<p>๋ณด์ NFT: {nftBalance}๊ฐ</p>
</div>
)}
</div>
);
}
export default GameWalletConnect;
์ด ์ฝ๋๋ ๊ฒ์ ์น์ฌ์ดํธ๋ ์ฑ์์ ํ๋ ์ด์ด๊ฐ ์ง๊ฐ์ ์ฐ๊ฒฐํ๊ณ ์์ ์ NFT๋ฅผ ํ์ธํ ์ ์๊ฒ ํด์ค. ๐ฎ
์ค์ ๊ฒ์์์๋ ์ด๋ณด๋ค ํจ์ฌ ๋ณต์กํ UI/UX๋ฅผ ์ ๊ณตํ์ง๋ง, ๊ธฐ๋ณธ ์๋ฆฌ๋ ๊ฐ์. ํ๋ ์ด์ด๊ฐ ๋ฒํผ ํ๋๋ง ๋๋ฅด๋ฉด ์ง๊ฐ์ด ์ฐ๊ฒฐ๋๊ณ , ์์ ์ด ์์ ํ ๊ฒ์ ์์ดํ NFT๋ฅผ ๋ณผ ์ ์๋ ๊ฑฐ์ง.
์ค์ ๊ฒ์์์๋ ์ด๋ณด๋ค ํจ์ฌ ๋ณต์กํ UI/UX๋ฅผ ์ ๊ณตํ์ง๋ง, ๊ธฐ๋ณธ ์๋ฆฌ๋ ๊ฐ์. ํ๋ ์ด์ด๊ฐ ๋ฒํผ ํ๋๋ง ๋๋ฅด๋ฉด ์ง๊ฐ์ด ์ฐ๊ฒฐ๋๊ณ , ์์ ์ด ์์ ํ ๊ฒ์ ์์ดํ NFT๋ฅผ ๋ณผ ์ ์๋ ๊ฑฐ์ง.
๐จ NFT ๋ฉํ๋ฐ์ดํฐ์ IPFS ์ ์ฅ
NFT์ ํต์ฌ์ ๋ฉํ๋ฐ์ดํฐ์ผ. ๋ฉํ๋ฐ์ดํฐ๋ NFT๊ฐ ๋ฌด์์ธ์ง ์ค๋ช
ํ๋ ์ ๋ณด๋ค์ด์ง. ๊ฒ์ ์์ดํ
์ ์ด๋ฏธ์ง, ์ด๋ฆ, ์์ฑ ๊ฐ์ ๊ฑฐ ๋ง์ด์ผ. ๐
๋ฌธ์ ๋ ์ด๋ฐ ๋ฐ์ดํฐ๋ฅผ ์ด๋์ ์ ์ฅํ๋๋์ผ. ๋ธ๋ก์ฒด์ธ์ ์ง์ ์ ์ฅํ๋ฉด ๋น์ฉ์ด ์์ฒญ๋๊ฒ ๋น์ธ. ๊ทธ๋์ ๋๋ถ๋ถ IPFS(InterPlanetary File System)๋ฅผ ์ฌ์ฉํด.
๋ฌธ์ ๋ ์ด๋ฐ ๋ฐ์ดํฐ๋ฅผ ์ด๋์ ์ ์ฅํ๋๋์ผ. ๋ธ๋ก์ฒด์ธ์ ์ง์ ์ ์ฅํ๋ฉด ๋น์ฉ์ด ์์ฒญ๋๊ฒ ๋น์ธ. ๊ทธ๋์ ๋๋ถ๋ถ IPFS(InterPlanetary File System)๋ฅผ ์ฌ์ฉํด.
๐ฆ IPFS๋ ๋ฌด์์ธ๊ฐ?
IPFS๋ ๋ถ์ฐ ํ์ผ ์ ์ฅ ์์คํ
์ด์ผ. ์ผ๋ฐ์ ์ธ ์น ์๋ฒ์ฒ๋ผ ์ค์ ์๋ฒ์ ํ์ผ์ ์ ์ฅํ๋ ๊ฒ ์๋๋ผ, ์ ์ธ๊ณ ์ฌ๋ฌ ์ปดํจํฐ์ ๋ถ์ฐํด์ ์ ์ฅํด. ๐
IPFS์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ์๊ตฌ์ฑ์ด์ผ. ํ์ผ์ ์ ๋ก๋ํ๋ฉด ๊ณ ์ ํ ํด์๊ฐ(CID)์ด ์์ฑ๋๋๋ฐ, ์ด ํด์๊ฐ์ผ๋ก ์ธ์ ๋ ์ง ํ์ผ์ ์ฐพ์ ์ ์์ด. ๊ฒ์ ํ์ฌ๊ฐ ๋งํด๋ NFT ์ด๋ฏธ์ง๋ ์ฌ๋ผ์ง์ง ์๋ ๊ฑฐ์ง! ๐ช
IPFS์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ์๊ตฌ์ฑ์ด์ผ. ํ์ผ์ ์ ๋ก๋ํ๋ฉด ๊ณ ์ ํ ํด์๊ฐ(CID)์ด ์์ฑ๋๋๋ฐ, ์ด ํด์๊ฐ์ผ๋ก ์ธ์ ๋ ์ง ํ์ผ์ ์ฐพ์ ์ ์์ด. ๊ฒ์ ํ์ฌ๊ฐ ๋งํด๋ NFT ์ด๋ฏธ์ง๋ ์ฌ๋ผ์ง์ง ์๋ ๊ฑฐ์ง! ๐ช
๐ IPFS ์ฃผ์ ์์:
ipfs://QmX7x8x9xAbCdEfGhIjKlMnOpQrStUvWxYz123456789
์ด๋ฐ ์ฃผ์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณํ๋ผ:
https://ipfs.io/ipfs/QmX7x8x9xAbCdEfGhIjKlMnOpQrStUvWxYz123456789
๋๋ Pinata, NFT.Storage ๊ฐ์ ๊ฒ์ดํธ์จ์ด๋ฅผ ํตํด:
https://gateway.pinata.cloud/ipfs/QmX7x8x9x...
์ด๋ฐ ์ฃผ์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณํ๋ผ:
https://ipfs.io/ipfs/QmX7x8x9xAbCdEfGhIjKlMnOpQrStUvWxYz123456789
๋๋ Pinata, NFT.Storage ๊ฐ์ ๊ฒ์ดํธ์จ์ด๋ฅผ ํตํด:
https://gateway.pinata.cloud/ipfs/QmX7x8x9x...
๐ NFT ๋ฉํ๋ฐ์ดํฐ ํ์ค ๊ตฌ์กฐ
NFT ๋ฉํ๋ฐ์ดํฐ๋ ๋ณดํต JSON ํ์์ผ๋ก ์์ฑํด. OpenSea ๊ฐ์ NFT ๋ง์ผํ๋ ์ด์ค์์ ์ธ์ํ ์ ์๋๋ก ํ์ค ํ์์ ๋ฐ๋ผ์ผ ํด:
{
"name": "๋๋๊ณค ์ฌ๋ ์ด์ด +15",
"description": "๊ณ ๋ ๋๋๊ณค์ ๋ฌผ๋ฆฌ์น ์ ์ค์ ๊ฒ. ๊ฐํ ์์น +15๋ก ์
๊ทธ๋ ์ด๋๋์ด ๋ง๊ฐํ ํ์ ์๋ํฉ๋๋ค.",
"image": "ipfs://QmX7x8x9xAbCdEfGhIjKlMnOpQrStUvWxYz/dragon-slayer.png",
"external_url": "https://mygame.com/items/12345",
"animation_url": "ipfs://QmY8y9y0yBcDeFgHiJkLmNoPqRsTuVwXyZ/dragon-slayer-3d.glb",
"attributes": [
{
"trait_type": "๋ฌด๊ธฐ ์ข
๋ฅ",
"value": "์์๊ฒ"
},
{
"trait_type": "๋ฑ๊ธ",
"value": "์ ์ค"
},
{
"trait_type": "๊ณต๊ฒฉ๋ ฅ",
"value": 250,
"max_value": 300
},
{
"trait_type": "๊ฐํ ์์น",
"value": 15,
"max_value": 20
},
{
"trait_type": "ํน์ ๋ฅ๋ ฅ",
"value": "๋๋๊ณค ํฌ๋ฌ"
},
{
"trait_type": "๋ด๊ตฌ๋",
"value": 100,
"max_value": 100,
"display_type": "boost_percentage"
},
{
"trait_type": "ํ๋ ๋ ์ง",
"value": 1705276800,
"display_type": "date"
}
],
"background_color": "8B0000",
"properties": {
"game_id": "dragon-quest-online",
"item_id": "sword_001",
"rarity_score": 95,
"is_tradeable": true,
"is_upgradeable": true,
"level_requirement": 50,
"class_requirement": ["์ ์ฌ", "๊ธฐ์ฌ"]
}
}
๊ฐ ํ๋๋ฅผ ์ค๋ช
ํด๋ณผ๊ฒ:
โข name: NFT์ ์ด๋ฆ
โข description: ์์ธ ์ค๋ช
โข image: ๋ํ ์ด๋ฏธ์ง (PNG, JPG ๋ฑ)
โข animation_url: 3D ๋ชจ๋ธ์ด๋ ์ ๋๋ฉ์ด์ ํ์ผ (GLB, MP4 ๋ฑ)
โข attributes: ์์ดํ ์ ์์ฑ๋ค (๋ง์ผํ๋ ์ด์ค์์ ํํฐ๋ง ๊ฐ๋ฅ)
โข properties: ๊ฒ์ ์ ์ฉ ์ถ๊ฐ ์ ๋ณด
โข name: NFT์ ์ด๋ฆ
โข description: ์์ธ ์ค๋ช
โข image: ๋ํ ์ด๋ฏธ์ง (PNG, JPG ๋ฑ)
โข animation_url: 3D ๋ชจ๋ธ์ด๋ ์ ๋๋ฉ์ด์ ํ์ผ (GLB, MP4 ๋ฑ)
โข attributes: ์์ดํ ์ ์์ฑ๋ค (๋ง์ผํ๋ ์ด์ค์์ ํํฐ๋ง ๊ฐ๋ฅ)
โข properties: ๊ฒ์ ์ ์ฉ ์ถ๊ฐ ์ ๋ณด
โ๏ธ IPFS ์ ๋ก๋ ์ค์ ์ฝ๋
์ค์ ๋ก ๊ฒ์ ์์ดํ
์ด๋ฏธ์ง์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ IPFS์ ์
๋ก๋ํ๋ ์ฝ๋๋ฅผ ๋ณผ๊น? Pinata API๋ฅผ ์ฌ์ฉํ ์์์ผ:
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const pinataApiKey = process.env.PINATA_API_KEY;
const pinataSecretApiKey = process.env.PINATA_SECRET_API_KEY;
// ์ด๋ฏธ์ง ํ์ผ์ IPFS์ ์
๋ก๋
async function uploadImageToIPFS(imagePath) {
const url = 'https://api.pinata.cloud/pinning/pinFileToIPFS';
let data = new FormData();
data.append('file', fs.createReadStream(imagePath));
const metadata = JSON.stringify({
name: 'Game Item Image',
keyvalues: {
type: 'game-item',
game: 'dragon-quest-online'
}
});
data.append('pinataMetadata', metadata);
const options = JSON.stringify({
cidVersion: 1,
});
data.append('pinataOptions', options);
try {
const response = await axios.post(url, data, {
maxBodyLength: 'Infinity',
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
'pinata_api_key': pinataApiKey,
'pinata_secret_api_key': pinataSecretApiKey
}
});
console.log('์ด๋ฏธ์ง ์
๋ก๋ ์๋ฃ:', response.data.IpfsHash);
return `ipfs://${response.data.IpfsHash}`;
} catch (error) {
console.error('์ด๋ฏธ์ง ์
๋ก๋ ์คํจ:', error);
throw error;
}
}
// ๋ฉํ๋ฐ์ดํฐ๋ฅผ IPFS์ ์
๋ก๋
async function uploadMetadataToIPFS(metadata) {
const url = 'https://api.pinata.cloud/pinning/pinJSONToIPFS';
try {
const response = await axios.post(url, metadata, {
headers: {
'Content-Type': 'application/json',
'pinata_api_key': pinataApiKey,
'pinata_secret_api_key': pinataSecretApiKey
}
});
console.log('๋ฉํ๋ฐ์ดํฐ ์
๋ก๋ ์๋ฃ:', response.data.IpfsHash);
return `ipfs://${response.data.IpfsHash}`;
} catch (error) {
console.error('๋ฉํ๋ฐ์ดํฐ ์
๋ก๋ ์คํจ:', error);
throw error;
}
}
// ์ ์ฒด NFT ์์ฑ ํ๋ก์ธ์ค
async function createGameItemNFT(itemData) {
try {
// 1. ์ด๋ฏธ์ง ์
๋ก๋
console.log('1๋จ๊ณ: ์ด๋ฏธ์ง ์
๋ก๋ ์ค...');
const imageURI = await uploadImageToIPFS(itemData.imagePath);
// 2. ๋ฉํ๋ฐ์ดํฐ ์์ฑ
console.log('2๋จ๊ณ: ๋ฉํ๋ฐ์ดํฐ ์์ฑ ์ค...');
const metadata = {
name: itemData.name,
description: itemData.description,
image: imageURI,
attributes: [
{ trait_type: "๊ณต๊ฒฉ๋ ฅ", value: itemData.attackPower },
{ trait_type: "๋ฐฉ์ด๋ ฅ", value: itemData.defense },
{ trait_type: "ํฌ๊ท๋", value: itemData.rarity },
{ trait_type: "๊ฐํ", value: itemData.enhancement }
],
properties: {
game_id: "dragon-quest-online",
item_id: itemData.itemId,
is_tradeable: true
}
};
// 3. ๋ฉํ๋ฐ์ดํฐ ์
๋ก๋
console.log('3๋จ๊ณ: ๋ฉํ๋ฐ์ดํฐ ์
๋ก๋ ์ค...');
const metadataURI = await uploadMetadataToIPFS(metadata);
console.log('โ
NFT ์ค๋น ์๋ฃ!');
console.log('๋ฉํ๋ฐ์ดํฐ URI:', metadataURI);
return metadataURI;
} catch (error) {
console.error('NFT ์์ฑ ์คํจ:', error);
throw error;
}
}
// ์ฌ์ฉ ์์
const itemData = {
name: "๋๋๊ณค ์ฌ๋ ์ด์ด +15",
description: "๊ณ ๋ ๋๋๊ณค์ ๋ฌผ๋ฆฌ์น ์ ์ค์ ๊ฒ",
imagePath: "./assets/dragon-slayer.png",
attackPower: 250,
defense: 50,
rarity: "์ ์ค",
enhancement: 15,
itemId: "sword_001"
};
createGameItemNFT(itemData)
.then(uri => {
console.log('์ด์ ์ด URI๋ก NFT๋ฅผ ๋ฏผํ
ํ๋ฉด ๋ฉ๋๋ค:', uri);
})
.catch(error => {
console.error('์ค๋ฅ ๋ฐ์:', error);
});
module.exports = {
uploadImageToIPFS,
uploadMetadataToIPFS,
createGameItemNFT
};
โ
IPFS ์๋น์ค ์ถ์ฒ:
โข Pinata: ๊ฐ์ฅ ์ธ๊ธฐ ์๋ IPFS ์๋น์ค, ๋ฌด๋ฃ ํ๋ ์ ๊ณต
โข NFT.Storage: Protocol Labs์์ ์ด์, NFT ์ ์ฉ, ์์ ๋ฌด๋ฃ
โข Infura IPFS: ์ด๋๋ฆฌ์ ๋ ธ๋ ์ ๊ณต์ ์ฒด, ์์ ์
โข Web3.Storage: Filecoin ๊ธฐ๋ฐ, ๋์ฉ๋ ํ์ผ์ ์ ํฉ
โข NFT.Storage: Protocol Labs์์ ์ด์, NFT ์ ์ฉ, ์์ ๋ฌด๋ฃ
โข Infura IPFS: ์ด๋๋ฆฌ์ ๋ ธ๋ ์ ๊ณต์ ์ฒด, ์์ ์
โข Web3.Storage: Filecoin ๊ธฐ๋ฐ, ๋์ฉ๋ ํ์ผ์ ์ ํฉ
๐ฐ NFT ๊ฑฐ๋ ์์คํ ๊ตฌ์ถ
๊ฒ์ ์์ดํ
์ NFT๋ก ๋ง๋ค์์ผ๋ฉด ์ด์ ๊ฑฐ๋ํ ์ ์์ด์ผ๊ฒ ์ง? ํ๋ ์ด์ด๋ค์ด ์์ ๋กญ๊ฒ ์์ดํ
์ ์ฌ๊ณ ํ ์ ์๋ ๋ง์ผํ๋ ์ด์ค๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์์๋ณด์! ๐ช
๐ ๋ง์ผํ๋ ์ด์ค ๊ตฌ์กฐ
NFT ๋ง์ผํ๋ ์ด์ค๋ ํฌ๊ฒ ๋ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ๊ตฌํํ ์ ์์ด:
1. ์ค์ํ ๋ง์ผํ๋ ์ด์ค:
โข ๊ฒ์ ํ์ฌ๊ฐ ์ง์ ์ด์ํ๋ ๊ฑฐ๋์
โข ๊ฑฐ๋ ์์๋ฃ๋ฅผ ๊ฒ์ ํ์ฌ๊ฐ ๋ฐ์
โข ๊ฒ์ ๋ด ํํ๋ก๋ ๊ฑฐ๋ ๊ฐ๋ฅ
โข ์: ๊ฒ์ ๋ด์ฅ ์์
2. ํ์ค์ํ ๋ง์ผํ๋ ์ด์ค:
โข ์ค๋งํธ ์ปจํธ๋ํธ๋ก ์๋ ์ด์
โข ๋๊ตฌ๋ ์์ ๋กญ๊ฒ ๊ฑฐ๋ ๊ฐ๋ฅ
โข ๋ธ๋ก์ฒด์ธ ์์๋ฃ๋ง ๋ฐ์
โข ์: OpenSea, Rarible ์ฐ๋
โข ๊ฒ์ ํ์ฌ๊ฐ ์ง์ ์ด์ํ๋ ๊ฑฐ๋์
โข ๊ฑฐ๋ ์์๋ฃ๋ฅผ ๊ฒ์ ํ์ฌ๊ฐ ๋ฐ์
โข ๊ฒ์ ๋ด ํํ๋ก๋ ๊ฑฐ๋ ๊ฐ๋ฅ
โข ์: ๊ฒ์ ๋ด์ฅ ์์
2. ํ์ค์ํ ๋ง์ผํ๋ ์ด์ค:
โข ์ค๋งํธ ์ปจํธ๋ํธ๋ก ์๋ ์ด์
โข ๋๊ตฌ๋ ์์ ๋กญ๊ฒ ๊ฑฐ๋ ๊ฐ๋ฅ
โข ๋ธ๋ก์ฒด์ธ ์์๋ฃ๋ง ๋ฐ์
โข ์: OpenSea, Rarible ์ฐ๋
๋๋ถ๋ถ์ ๊ฒ์์ ๋ ๊ฐ์ง๋ฅผ ํผํฉํด์ ์ฌ์ฉํด. ๊ฒ์ ๋ด์์๋ ์ค์ํ ๋ง์ผํ๋ ์ด์ค๋ฅผ ์ ๊ณตํ๊ณ , ๋์์ OpenSea ๊ฐ์ ์ธ๋ถ ๋ง์ผํ๋ ์ด์ค์์๋ ๊ฑฐ๋ํ ์ ์๊ฒ ํ๋ ๊ฑฐ์ง. ๐
๐ ๋ง์ผํ๋ ์ด์ค ์ค๋งํธ ์ปจํธ๋ํธ
๊ฐ๋จํ NFT ๋ง์ผํ๋ ์ด์ค ์ปจํธ๋ํธ๋ฅผ ๋ง๋ค์ด๋ณผ๊น? ํ๋งค ๋ฑ๋ก, ๊ตฌ๋งค, ์ทจ์ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด์:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract GameItemMarketplace is ReentrancyGuard, Ownable {
// ํ๋งค ์ ๋ณด ๊ตฌ์กฐ์ฒด
struct Listing {
address seller;
address nftContract;
uint256 tokenId;
uint256 price;
bool isActive;
}
// ํ๋งค ID๋ณ ํ๋งค ์ ๋ณด
mapping(uint256 => Listing) public listings;
uint256 public listingCounter;
// ๋ง์ผํ๋ ์ด์ค ์์๋ฃ (2.5%)
uint256 public feePercentage = 250; // 250 = 2.5%
uint256 public constant FEE_DENOMINATOR = 10000;
// ๋์ ์์๋ฃ
uint256 public accumulatedFees;
// ์ด๋ฒคํธ
event ItemListed(
uint256 indexed listingId,
address indexed seller,
address indexed nftContract,
uint256 tokenId,
uint256 price
);
event ItemSold(
uint256 indexed listingId,
address indexed buyer,
address indexed seller,
uint256 price
);
event ListingCancelled(uint256 indexed listingId);
event PriceUpdated(uint256 indexed listingId, uint256 newPrice);
// ์์ดํ
ํ๋งค ๋ฑ๋ก
function listItem(
address nftContract,
uint256 tokenId,
uint256 price
) external nonReentrant returns (uint256) {
require(price > 0, "Price must be greater than 0");
IERC721 nft = IERC721(nftContract);
require(nft.ownerOf(tokenId) == msg.sender, "Not the owner");
require(
nft.isApprovedForAll(msg.sender, address(this)) ||
nft.getApproved(tokenId) == address(this),
"Marketplace not approved"
);
listingCounter++;
uint256 listingId = listingCounter;
listings[listingId] = Listing({
seller: msg.sender,
nftContract: nftContract,
tokenId: tokenId,
price: price,
isActive: true
});
emit ItemListed(listingId, msg.sender, nftContract, tokenId, price);
return listingId;
}
// ์์ดํ
๊ตฌ๋งค
function buyItem(uint256 listingId)
external
payable
nonReentrant
{
Listing storage listing = listings[listingId];
require(listing.isActive, "Listing is not active");
require(msg.value >= listing.price, "Insufficient payment");
require(msg.sender != listing.seller, "Cannot buy your own item");
IERC721 nft = IERC721(listing.nftContract);
require(
nft.ownerOf(listing.tokenId) == listing.seller,
"Seller no longer owns the item"
);
// ์์๋ฃ ๊ณ์ฐ
uint256 fee = (listing.price * feePercentage) / FEE_DENOMINATOR;
uint256 sellerAmount = listing.price - fee;
// ํ๋งค ์๋ฃ ์ฒ๋ฆฌ
listing.isActive = false;
accumulatedFees += fee;
// NFT ์ ์ก
nft.safeTransferFrom(listing.seller, msg.sender, listing.tokenId);
// ํ๋งค์์๊ฒ ๋๊ธ ์ ์ก
(bool success, ) = payable(listing.seller).call{value: sellerAmount}("");
require(success, "Transfer to seller failed");
// ์ด๊ณผ ์ง๋ถ์ก ๋ฐํ
if (msg.value > listing.price) {
(bool refundSuccess, ) = payable(msg.sender).call{
value: msg.value - listing.price
}("");
require(refundSuccess, "Refund failed");
}
emit ItemSold(listingId, msg.sender, listing.seller, listing.price);
}
// ํ๋งค ์ทจ์
function cancelListing(uint256 listingId) external nonReentrant {
Listing storage listing = listings[listingId];
require(listing.isActive, "Listing is not active");
require(listing.seller == msg.sender, "Not the seller");
listing.isActive = false;
emit ListingCancelled(listingId);
}
// ๊ฐ๊ฒฉ ๋ณ๊ฒฝ
function updatePrice(uint256 listingId, uint256 newPrice)
external
nonReentrant
{
Listing storage listing = listings[listingId];
require(listing.isActive, "Listing is not active");
require(listing.seller == msg.sender, "Not the seller");
require(newPrice > 0, "Price must be greater than 0");
listing.price = newPrice;
emit PriceUpdated(listingId, newPrice);
}
// ํ์ฑ ํ๋งค ๋ชฉ๋ก ์กฐํ
function getActiveListing(uint256 listingId)
external
view
returns (Listing memory)
{
require(listings[listingId].isActive, "Listing is not active");
return listings[listingId];
}
// ์์๋ฃ์จ ๋ณ๊ฒฝ (๊ด๋ฆฌ์๋ง)
function setFeePercentage(uint256 newFee) external onlyOwner {
require(newFee <= 1000, "Fee too high"); // ์ต๋ 10%
feePercentage = newFee;
}
// ์์๋ฃ ์ธ์ถ (๊ด๋ฆฌ์๋ง)
function withdrawFees() external onlyOwner {
uint256 amount = accumulatedFees;
accumulatedFees = 0;
(bool success, ) = payable(owner()).call{value: amount}("");
require(success, "Withdrawal failed");
}
// ๊ธด๊ธ ์ํฉ ๋๋น ํจ์
function emergencyWithdraw() external onlyOwner {
(bool success, ) = payable(owner()).call{value: address(this).balance}("");
require(success, "Emergency withdrawal failed");
}
}
์ด ๋ง์ผํ๋ ์ด์ค ์ปจํธ๋ํธ๋ ๋ค์ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด:
โข listItem: ํ๋งค์๊ฐ NFT๋ฅผ ํ๋งค ๋ฑ๋ก
โข buyItem: ๊ตฌ๋งค์๊ฐ NFT ๊ตฌ๋งค
โข cancelListing: ํ๋งค์๊ฐ ํ๋งค ์ทจ์
โข updatePrice: ํ๋งค์๊ฐ ๊ฐ๊ฒฉ ๋ณ๊ฒฝ
โข ์์๋ฃ ์์คํ : ๊ฑฐ๋๋ง๋ค 2.5% ์์๋ฃ ์ง์
โข listItem: ํ๋งค์๊ฐ NFT๋ฅผ ํ๋งค ๋ฑ๋ก
โข buyItem: ๊ตฌ๋งค์๊ฐ NFT ๊ตฌ๋งค
โข cancelListing: ํ๋งค์๊ฐ ํ๋งค ์ทจ์
โข updatePrice: ํ๋งค์๊ฐ ๊ฐ๊ฒฉ ๋ณ๊ฒฝ
โข ์์๋ฃ ์์คํ : ๊ฑฐ๋๋ง๋ค 2.5% ์์๋ฃ ์ง์
โ ๏ธ ๋ณด์ ๊ณ ๋ ค์ฌํญ:
โข ReentrancyGuard: ์ฌ์ง์
๊ณต๊ฒฉ ๋ฐฉ์ง
โข ์์ ๊ถ ํ์ธ: NFT ์์ ์๋ง ํ๋งค ๊ฐ๋ฅ
โข ์น์ธ ํ์ธ: ๋ง์ผํ๋ ์ด์ค๊ฐ NFT๋ฅผ ์ ์กํ ๊ถํ์ด ์๋์ง ํ์ธ
โข ์ด๊ณผ ์ง๋ถ ๋ฐํ: ๊ตฌ๋งค์๊ฐ ๋ ๋ง์ด ๋ณด๋ธ ๊ธ์ก์ ๋ฐํ
โข ์์ ๊ถ ํ์ธ: NFT ์์ ์๋ง ํ๋งค ๊ฐ๋ฅ
โข ์น์ธ ํ์ธ: ๋ง์ผํ๋ ์ด์ค๊ฐ NFT๋ฅผ ์ ์กํ ๊ถํ์ด ์๋์ง ํ์ธ
โข ์ด๊ณผ ์ง๋ถ ๋ฐํ: ๊ตฌ๋งค์๊ฐ ๋ ๋ง์ด ๋ณด๋ธ ๊ธ์ก์ ๋ฐํ
๐ฅ๏ธ ๋ง์ผํ๋ ์ด์ค ํ๋ก ํธ์๋
์ด์ ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ์ฌ์ฉํ ๋ง์ผํ๋ ์ด์ค UI๋ฅผ ๋ง๋ค์ด๋ณด์. React๋ก ๊ฐ๋จํ ์์๋ฅผ ์์ฑํด๋ณผ๊ฒ:
import React, { useState, useEffect } from 'react';
import Web3 from 'web3';
function NFTMarketplace() {
const [web3, setWeb3] = useState(null);
const [account, setAccount] = useState(null);
const [listings, setListings] = useState([]);
const [myNFTs, setMyNFTs] = useState([]);
const marketplaceAddress = '0xMarketplace...';
const nftContractAddress = '0xNFTContract...';
// ์ด๊ธฐํ
useEffect(() => {
initWeb3();
}, []);
const initWeb3 = async () => {
if (window.ethereum) {
const web3Instance = new Web3(window.ethereum);
setWeb3(web3Instance);
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
setAccount(accounts[0]);
await loadMarketplace(web3Instance, accounts[0]);
}
};
// ๋ง์ผํ๋ ์ด์ค ๋ฐ์ดํฐ ๋ก๋
const loadMarketplace = async (web3Instance, userAccount) => {
const marketplaceContract = new web3Instance.eth.Contract(
marketplaceABI,
marketplaceAddress
);
const nftContract = new web3Instance.eth.Contract(
nftABI,
nftContractAddress
);
// ํ์ฑ ํ๋งค ๋ชฉ๋ก ๋ก๋
const listingCounter = await marketplaceContract.methods
.listingCounter()
.call();
const activeListings = [];
for (let i = 1; i <= listingCounter; i++) {
try {
const listing = await marketplaceContract.methods
.getActiveListing(i)
.call();
// NFT ๋ฉํ๋ฐ์ดํฐ ๋ก๋
const tokenURI = await nftContract.methods
.tokenURI(listing.tokenId)
.call();
const metadata = await fetch(
tokenURI.replace('ipfs://', 'https://ipfs.io/ipfs/')
).then(r => r.json());
activeListings.push({
listingId: i,
...listing,
metadata
});
} catch (error) {
// ๋นํ์ฑ ํ๋งค๋ ๊ฑด๋๋ฐ๊ธฐ
continue;
}
}
setListings(activeListings);
// ๋ด NFT ๋ก๋
const balance = await nftContract.methods
.balanceOf(userAccount)
.call();
const myNFTList = [];
for (let i = 0; i < balance; i++) {
const tokenId = await nftContract.methods
.tokenOfOwnerByIndex(userAccount, i)
.call();
const tokenURI = await nftContract.methods
.tokenURI(tokenId)
.call();
const metadata = await fetch(
tokenURI.replace('ipfs://', 'https://ipfs.io/ipfs/')
).then(r => r.json());
myNFTList.push({ tokenId, metadata });
}
setMyNFTs(myNFTList);
};
// NFT ๊ตฌ๋งค
const buyNFT = async (listingId, price) => {
try {
const marketplaceContract = new web3.eth.Contract(
marketplaceABI,
marketplaceAddress
);
await marketplaceContract.methods
.buyItem(listingId)
.send({
from: account,
value: price
});
alert('๊ตฌ๋งค ์๋ฃ! ๐');
await loadMarketplace(web3, account);
} catch (error) {
console.error('๊ตฌ๋งค ์คํจ:', error);
alert('๊ตฌ๋งค์ ์คํจํ์ต๋๋ค.');
}
};
// NFT ํ๋งค ๋ฑ๋ก
const listNFT = async (tokenId, price) => {
try {
const nftContract = new web3.eth.Contract(
nftABI,
nftContractAddress
);
const marketplaceContract = new web3.eth.Contract(
marketplaceABI,
marketplaceAddress
);
// 1. ๋ง์ผํ๋ ์ด์ค์ ์น์ธ
await nftContract.methods
.approve(marketplaceAddress, tokenId)
.send({ from: account });
// 2. ํ๋งค ๋ฑ๋ก
const priceInWei = web3.utils.toWei(price.toString(), 'ether');
await marketplaceContract.methods
.listItem(nftContractAddress, tokenId, priceInWei)
.send({ from: account });
alert('ํ๋งค ๋ฑ๋ก ์๋ฃ! ๐ช');
await loadMarketplace(web3, account);
} catch (error) {
console.error('ํ๋งค ๋ฑ๋ก ์คํจ:', error);
alert('ํ๋งค ๋ฑ๋ก์ ์คํจํ์ต๋๋ค.');
}
};
return (
<div className="marketplace">
<h1>๐ฎ ๊ฒ์ ์์ดํ
๋ง์ผํ๋ ์ด์ค</h1>
{/* ํ๋งค ์ค์ธ ์์ดํ
*/}
<section className="listings-section">
<h2>๐ ํ๋งค ์ค์ธ ์์ดํ
</h2>
<div className="nft-grid">
{listings.map(listing => (
<div key={listing.listingId} className="nft-card">
<img
src={listing.metadata.image.replace(
'ipfs://',
'https://ipfs.io/ipfs/'
)}
alt={listing.metadata.name}
/>
<h3>{listing.metadata.name}</h3>
<p className="price">
{web3.utils.fromWei(listing.price, 'ether')} MATIC
</p>
<div className="attributes">
{listing.metadata.attributes.map((attr, i) => (
<span key={i} className="attribute">
{attr.trait_type}: {attr.value}
</span>
))}
</div>
{listing.seller.toLowerCase() !== account.toLowerCase() && (
<button
onClick={() => buyNFT(
listing.listingId,
listing.price
)}
>
๊ตฌ๋งคํ๊ธฐ
</button>
)}
</div>
))}
</div>
</section>
{/* ๋ด ์์ดํ
*/}
<section className="my-nfts-section">
<h2>๐ ๋ด ์์ดํ
</h2>
<div className="nft-grid">
{myNFTs.map(nft => (
<div key={nft.tokenId} className="nft-card">
<img
src={nft.metadata.image.replace(
'ipfs://',
'https://ipfs.io/ipfs/'
)}
alt={nft.metadata.name}
/>
<h3>{nft.metadata.name}</h3>
<div className="sell-form">
<input
type="number"
placeholder="๊ฐ๊ฒฉ (MATIC)"
id={`price-${nft.tokenId}`}
/>
<button onClick={() => {
const price = document.getElementById(
`price-${nft.tokenId}`
).value;
listNFT(nft.tokenId, price);
}}>
ํ๋งค ๋ฑ๋ก
</button>
</div>
</div>
))}
</div>
</section>
</div>
);
}
export default NFTMarketplace;
์ด ์ฝ๋๋ ์ค์ ๋ก ์๋ํ๋ NFT ๋ง์ผํ๋ ์ด์ค์ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ผ. ๋ฌผ๋ก ์ค์ ์๋น์ค์์๋ ๋ ๋ง์ ๊ธฐ๋ฅ์ด ํ์ํ์ง๋ง, ํต์ฌ ์๋ฆฌ๋ ์ด๊ฑฐ์ผ! ๐ฏ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ๊ธฐ์ ์ ํ์ฉํด์ ๋์งํธ ์ฌ๋ฅ์ NFT๋ก ๊ฑฐ๋ํ๋ ์์คํ ์ ๋ง๋ค ์ ์์ ๊ฑฐ์ผ. ์๋ฅผ ๋ค์ด ๋์์ด๋๊ฐ ๋ง๋ ์บ๋ฆญํฐ ๋์์ธ์ NFT๋ก ๋ฐํํด์ ํ๋งคํ๋ ์์ผ๋ก ๋ง์ด์ผ! ๐จ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ๊ธฐ์ ์ ํ์ฉํด์ ๋์งํธ ์ฌ๋ฅ์ NFT๋ก ๊ฑฐ๋ํ๋ ์์คํ ์ ๋ง๋ค ์ ์์ ๊ฑฐ์ผ. ์๋ฅผ ๋ค์ด ๋์์ด๋๊ฐ ๋ง๋ ์บ๋ฆญํฐ ๋์์ธ์ NFT๋ก ๋ฐํํด์ ํ๋งคํ๋ ์์ผ๋ก ๋ง์ด์ผ! ๐จ
๐ ๋ณด์๊ณผ ์ค๋งํธ ์ปจํธ๋ํธ ๊ฐ์ฌ
๊ฒ์ ์์ดํ
NFT ์์คํ
์ ๋ง๋ค ๋ ๊ฐ์ฅ ์ค์ํ ๊ฒ ๋ฐ๋ก ๋ณด์์ด์ผ. ๋ธ๋ก์ฒด์ธ์ ํ ๋ฒ ๋ฐฐํฌํ๋ฉด ์์ ์ด ๊ฑฐ์ ๋ถ๊ฐ๋ฅํ๊ฑฐ๋ . ๊ทธ๋์ ๋ฐฐํฌ ์ ์ ์ฒ ์ ํ ๊ฒ์ฆ์ด ํ์ํด! ๐ก๏ธ
โ ๏ธ ์ฃผ์ ๋ณด์ ์ํ
| ์ํ ์ ํ | ์ค๋ช | ๋์ ๋ฐฉ๋ฒ |
|---|---|---|
| ์ฌ์ง์ ๊ณต๊ฒฉ | ํจ์ ์คํ ์ค ๋ค์ ๊ฐ์ ํจ์ ํธ์ถ | ReentrancyGuard ์ฌ์ฉ |
| ์ ์ ์ค๋ฒํ๋ก์ฐ | ์ซ์ ๊ณ์ฐ ์ ๋ฒ์ ์ด๊ณผ | Solidity 0.8+ ์ฌ์ฉ (์๋ ์ฒดํฌ) |
| ๊ถํ ํ์ทจ | ๊ด๋ฆฌ์ ๊ถํ ๋ฌด๋จ ์ฌ์ฉ | Ownable, AccessControl ์ฌ์ฉ |
| ํ๋ก ํธ๋ฌ๋ | ๊ฑฐ๋ ์ ๋ณด ๋ฏธ๋ฆฌ ๋ณด๊ณ ์ ์ | ์ปค๋ฐ-๋ฆฌ๋น ์คํด, ์ต์ ๊ฐ๊ฒฉ ์ค์ |
| ์ค๋งํธ ์ปจํธ๋ํธ ๋ฒ๊ทธ | ์ฝ๋ ๋ ผ๋ฆฌ ์ค๋ฅ | ์ ๋ฌธ ๊ฐ์ฌ ๊ธฐ๊ด ๊ฒ์ฆ |
โ ๋ณด์ ์ฒดํฌ๋ฆฌ์คํธ
๐ ๋ฐฐํฌ ์ ํ์ ํ์ธ์ฌํญ:
1. ์ฝ๋ ๊ฒ์ฆ:
โข OpenZeppelin ๊ฐ์ ๊ฒ์ฆ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
โข ๋ชจ๋ ํจ์์ ์ ์ ํ ์ ๊ทผ ์ ์ด ์ ์ฉ
โข ์ธ๋ถ ํธ์ถ ์ต์ํ
2. ํ ์คํธ:
โข ๋จ์ ํ ์คํธ ์์ฑ (Hardhat, Truffle)
โข ํตํฉ ํ ์คํธ ์คํ
โข ํ ์คํธ๋ท์์ ์ถฉ๋ถํ ๊ฒ์ฆ
3. ๊ฐ์ฌ:
โข ์ ๋ฌธ ๊ฐ์ฌ ๊ธฐ๊ด์ ์๋ขฐ (CertiK, OpenZeppelin ๋ฑ)
โข ์ปค๋ฎค๋ํฐ ์ฝ๋ ๋ฆฌ๋ทฐ
โข ๋ฒ๊ทธ ๋ฐ์ดํฐ ํ๋ก๊ทธ๋จ ์ด์
4. ์ ๊ทธ๋ ์ด๋ ์ ๋ต:
โข Proxy ํจํด ์ฌ์ฉ ๊ณ ๋ ค
โข ๊ธด๊ธ ์ ์ง ๊ธฐ๋ฅ ๊ตฌํ
โข ํ์๋ฝ ์ ์ฉ
โข OpenZeppelin ๊ฐ์ ๊ฒ์ฆ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
โข ๋ชจ๋ ํจ์์ ์ ์ ํ ์ ๊ทผ ์ ์ด ์ ์ฉ
โข ์ธ๋ถ ํธ์ถ ์ต์ํ
2. ํ ์คํธ:
โข ๋จ์ ํ ์คํธ ์์ฑ (Hardhat, Truffle)
โข ํตํฉ ํ ์คํธ ์คํ
โข ํ ์คํธ๋ท์์ ์ถฉ๋ถํ ๊ฒ์ฆ
3. ๊ฐ์ฌ:
โข ์ ๋ฌธ ๊ฐ์ฌ ๊ธฐ๊ด์ ์๋ขฐ (CertiK, OpenZeppelin ๋ฑ)
โข ์ปค๋ฎค๋ํฐ ์ฝ๋ ๋ฆฌ๋ทฐ
โข ๋ฒ๊ทธ ๋ฐ์ดํฐ ํ๋ก๊ทธ๋จ ์ด์
4. ์ ๊ทธ๋ ์ด๋ ์ ๋ต:
โข Proxy ํจํด ์ฌ์ฉ ๊ณ ๋ ค
โข ๊ธด๊ธ ์ ์ง ๊ธฐ๋ฅ ๊ตฌํ
โข ํ์๋ฝ ์ ์ฉ
๐งช ํ ์คํธ ์ฝ๋ ์์
Hardhat์ ์ฌ์ฉํ ์ค๋งํธ ์ปจํธ๋ํธ ํ
์คํธ ์ฝ๋๋ฅผ ๋ณผ๊น?
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("GameItemNFT", function () {
let gameItemNFT;
let marketplace;
let owner;
let player1;
let player2;
beforeEach(async function () {
// ๊ณ์ ์ค์
[owner, player1, player2] = await ethers.getSigners();
// NFT ์ปจํธ๋ํธ ๋ฐฐํฌ
const GameItemNFT = await ethers.getContractFactory("GameItemNFT");
gameItemNFT = await GameItemNFT.deploy();
await gameItemNFT.deployed();
// ๋ง์ผํ๋ ์ด์ค ์ปจํธ๋ํธ ๋ฐฐํฌ
const Marketplace = await ethers.getContractFactory("GameItemMarketplace");
marketplace = await Marketplace.deploy();
await marketplace.deployed();
});
describe("NFT ๋ฐํ", function () {
it("๊ฒ์ ์๋ฒ๊ฐ NFT๋ฅผ ๋ฐํํ ์ ์์ด์ผ ํจ", async function () {
const tokenURI = "ipfs://QmTest123";
await gameItemNFT.mintItem(
player1.address,
tokenURI,
"๋๋๊ณค ์ฌ๋ ์ด์ด",
250, // attackPower
50, // defense
4, // rarity (์ ์ค)
1 // level
);
expect(await gameItemNFT.ownerOf(1)).to.equal(player1.address);
expect(await gameItemNFT.tokenURI(1)).to.equal(tokenURI);
});
it("์ผ๋ฐ ์ฌ์ฉ์๋ NFT๋ฅผ ๋ฐํํ ์ ์์ด์ผ ํจ", async function () {
await expect(
gameItemNFT.connect(player1).mintItem(
player1.address,
"ipfs://QmTest123",
"๋๋๊ณค ์ฌ๋ ์ด์ด",
250, 50, 4, 1
)
).to.be.revertedWith("Only game server can mint");
});
});
describe("์์ดํ
๊ฐํ", function () {
beforeEach(async function () {
await gameItemNFT.mintItem(
player1.address,
"ipfs://QmTest123",
"๋๋๊ณค ์ฌ๋ ์ด์ด",
250, 50, 4, 1
);
});
it("์์ดํ
์ ๊ฐํํ ์ ์์ด์ผ ํจ", async function () {
await gameItemNFT.upgradeItem(1, 300);
const stats = await gameItemNFT.getItemStats(1);
expect(stats.attackPower).to.equal(300);
expect(stats.level).to.equal(2);
});
});
describe("๋ง์ผํ๋ ์ด์ค", function () {
beforeEach(async function () {
// NFT ๋ฐํ
await gameItemNFT.mintItem(
player1.address,
"ipfs://QmTest123",
"๋๋๊ณค ์ฌ๋ ์ด์ด",
250, 50, 4, 1
);
// ๋ง์ผํ๋ ์ด์ค์ ์น์ธ
await gameItemNFT.connect(player1)
.approve(marketplace.address, 1);
});
it("NFT๋ฅผ ํ๋งค ๋ฑ๋กํ ์ ์์ด์ผ ํจ", async function () {
const price = ethers.utils.parseEther("10");
await marketplace.connect(player1).listItem(
gameItemNFT.address,
1,
price
);
const listing = await marketplace.getActiveListing(1);
expect(listing.seller).to.equal(player1.address);
expect(listing.price).to.equal(price);
});
it("NFT๋ฅผ ๊ตฌ๋งคํ ์ ์์ด์ผ ํจ", async function () {
const price = ethers.utils.parseEther("10");
// ํ๋งค ๋ฑ๋ก
await marketplace.connect(player1).listItem(
gameItemNFT.address,
1,
price
);
// ๊ตฌ๋งค
await marketplace.connect(player2).buyItem(1, {
value: price
});
// ์์ ๊ถ ํ์ธ
expect(await gameItemNFT.ownerOf(1)).to.equal(player2.address);
});
it("์์๋ฃ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๊ณ์ฐ๋์ด์ผ ํจ", async function () {
const price = ethers.utils.parseEther("100");
const expectedFee = price.mul(250).div(10000); // 2.5%
await marketplace.connect(player1).listItem(
gameItemNFT.address,
1,
price
);
const player1BalanceBefore = await ethers.provider.getBalance(
player1.address
);
await marketplace.connect(player2).buyItem(1, {
value: price
});
const accumulatedFees = await marketplace.accumulatedFees();
expect(accumulatedFees).to.equal(expectedFee);
});
it("์์ ์ NFT๋ ๊ตฌ๋งคํ ์ ์์ด์ผ ํจ", async function () {
const price = ethers.utils.parseEther("10");
await marketplace.connect(player1).listItem(
gameItemNFT.address,
1,
price
);
await expect(
marketplace.connect(player1).buyItem(1, {
value: price
})
).to.be.revertedWith("Cannot buy your own item");
});
});
describe("๊ฐ์ค ์ต์ ํ", function () {
it("๋ฐฐ์น ๋ฏผํ
์ ๊ฐ์ค ๋น์ฉ ์ธก์ ", async function () {
const tx = await gameItemNFT.mintItem(
player1.address,
"ipfs://QmTest123",
"๋๋๊ณค ์ฌ๋ ์ด์ด",
250, 50, 4, 1
);
const receipt = await tx.wait();
console.log("๋ฏผํ
๊ฐ์ค ์ฌ์ฉ๋:", receipt.gasUsed.toString());
// ๊ฐ์ค ์ฌ์ฉ๋์ด ํฉ๋ฆฌ์ ์ธ ๋ฒ์ ๋ด์ธ์ง ํ์ธ
expect(receipt.gasUsed).to.be.below(200000);
});
});
});
โ
ํ
์คํธ ์คํ ๋ฐฉ๋ฒ:
npx hardhat test
npx hardhat coverage (์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ํ์ธ)
npx hardhat test --network polygon-mumbai (ํ ์คํธ๋ท ํ ์คํธ)
npx hardhat coverage (์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ํ์ธ)
npx hardhat test --network polygon-mumbai (ํ ์คํธ๋ท ํ ์คํธ)
๐ฏ ์ค์ ๊ฒ์ ์ ์ฉ ์ฌ๋ก
์ด๋ก ๋ง ๋ฐฐ์ฐ๋ฉด ์ฌ๋ฏธ์์์? ์ค์ ๋ก ๊ฒ์ ์์ดํ
NFT๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ ์ฉํ ์ฌ๋ก๋ค์ ์ดํด๋ณด์! ๐ฎ
๐ ์ฑ๊ณต ์ฌ๋ก
1. Axie Infinity (์ก์ ์ธํผ๋ํฐ):
โข ํฌ์ผ๋ชฌ ์คํ์ผ์ NFT ๊ฒ์
โข ์บ๋ฆญํฐ(์ก์)๊ฐ ๋ชจ๋ NFT
โข 2021๋ ๊ฑฐ๋๋ 40์ต ๋ฌ๋ฌ ๋ํ
โข Play-to-Earn ๋ชจ๋ธ์ ์ ๊ตฌ์
โข ์ด๋๋ฆฌ์ โ ์์ฒด ์ฌ์ด๋์ฒด์ธ(Ronin) ์ ํ์ผ๋ก ์์๋ฃ ํด๊ฒฐ
2. The Sandbox (๋ ์๋๋ฐ์ค):
โข ๋ฉํ๋ฒ์ค ๊ฒ์ ํ๋ซํผ
โข ๊ฐ์ ํ ์ง, ์์ดํ , ์บ๋ฆญํฐ ๋ชจ๋ NFT
โข ์ ๋ช ๋ธ๋๋๋ค๊ณผ ํ์ (Gucci, Adidas ๋ฑ)
โข ํฌ๋ฆฌ์์ดํฐ๊ฐ ์ง์ NFT ์์ดํ ์ ์ ๊ฐ๋ฅ
3. Gods Unchained (๊ฐ์ฆ ์ธ์ฒด์ธ๋):
โข ๋ธ๋ก์ฒด์ธ ๊ธฐ๋ฐ ์นด๋ ๊ฒ์
โข ๋ชจ๋ ์นด๋๊ฐ NFT
โข ์ด๋ฎคํฐ๋ธX ์ฌ์ฉ์ผ๋ก ๊ฐ์ค๋น ์ ๋ก
โข ์ง์ ํ ์นด๋ ์์ ๊ถ ์ ๊ณต
4. Illuvium (์ผ๋ฃจ๋น์):
โข AAA๊ธ RPG ๊ฒ์
โข ๊ณ ํ์ง ๊ทธ๋ํฝ๊ณผ NFT ๊ฒฐํฉ
โข ์ด๋ฎคํฐ๋ธX ๊ธฐ๋ฐ
โข ํฌ๋ฆฌ์ฒ, ๋ฌด๊ธฐ, ํ ์ง ๋ชจ๋ NFT
โข ํฌ์ผ๋ชฌ ์คํ์ผ์ NFT ๊ฒ์
โข ์บ๋ฆญํฐ(์ก์)๊ฐ ๋ชจ๋ NFT
โข 2021๋ ๊ฑฐ๋๋ 40์ต ๋ฌ๋ฌ ๋ํ
โข Play-to-Earn ๋ชจ๋ธ์ ์ ๊ตฌ์
โข ์ด๋๋ฆฌ์ โ ์์ฒด ์ฌ์ด๋์ฒด์ธ(Ronin) ์ ํ์ผ๋ก ์์๋ฃ ํด๊ฒฐ
2. The Sandbox (๋ ์๋๋ฐ์ค):
โข ๋ฉํ๋ฒ์ค ๊ฒ์ ํ๋ซํผ
โข ๊ฐ์ ํ ์ง, ์์ดํ , ์บ๋ฆญํฐ ๋ชจ๋ NFT
โข ์ ๋ช ๋ธ๋๋๋ค๊ณผ ํ์ (Gucci, Adidas ๋ฑ)
โข ํฌ๋ฆฌ์์ดํฐ๊ฐ ์ง์ NFT ์์ดํ ์ ์ ๊ฐ๋ฅ
3. Gods Unchained (๊ฐ์ฆ ์ธ์ฒด์ธ๋):
โข ๋ธ๋ก์ฒด์ธ ๊ธฐ๋ฐ ์นด๋ ๊ฒ์
โข ๋ชจ๋ ์นด๋๊ฐ NFT
โข ์ด๋ฎคํฐ๋ธX ์ฌ์ฉ์ผ๋ก ๊ฐ์ค๋น ์ ๋ก
โข ์ง์ ํ ์นด๋ ์์ ๊ถ ์ ๊ณต
4. Illuvium (์ผ๋ฃจ๋น์):
โข AAA๊ธ RPG ๊ฒ์
โข ๊ณ ํ์ง ๊ทธ๋ํฝ๊ณผ NFT ๊ฒฐํฉ
โข ์ด๋ฎคํฐ๋ธX ๊ธฐ๋ฐ
โข ํฌ๋ฆฌ์ฒ, ๋ฌด๊ธฐ, ํ ์ง ๋ชจ๋ NFT
๐ ๋น์ฆ๋์ค ๋ชจ๋ธ
๊ฒ์ ์์ดํ
NFT๋ก ์์ต์ ์ฐฝ์ถํ๋ ๋ค์ํ ๋ฐฉ๋ฒ์ด ์์ด:
๐ฐ ์์ต ๋ชจ๋ธ:
1. 1์ฐจ ํ๋งค (Primary Sales):
โข ๊ฒ์ ํ์ฌ๊ฐ ์ง์ NFT ํ๋งค
โข ์ ๊ท ์์ดํ ์ถ์ ์ ์์ต
โข ์: ํ์ ํ ์ ์ค ์์ดํ ํ๋งค
2. ๋ก์ดํฐ (Royalty):
โข 2์ฐจ ๊ฑฐ๋ ์ ์ผ์ ๋น์จ ์์๋ฃ
โข ๋ณดํต 2.5% ~ 10%
โข ์๊ตฌ์ ์ธ ์์ต์
3. ๋ง์ผํ๋ ์ด์ค ์์๋ฃ:
โข ์์ฒด ๋ง์ผํ๋ ์ด์ค ์ด์
โข ๊ฑฐ๋๋ง๋ค ์์๋ฃ ์ง์
โข ์: 2.5% ๊ฑฐ๋ ์์๋ฃ
4. ๊ฒ์ ๋ด ๊ฒฝ์ ์์คํ :
โข ์์ดํ ๊ฐํ ๋น์ฉ
โข ์์ดํ ํฉ์ฑ ๋น์ฉ
โข ํน์ ๊ธฐ๋ฅ ์ด์ฉ๋ฃ
5. ๋ธ๋๋ ํ์ :
โข ์ ๋ช ๋ธ๋๋ ์์ดํ ์ถ์
โข ๋ผ์ด์ ์ค ์์ต
โข ์: ๋์ดํค ์ ๋ฐ ์คํจ
โข ๊ฒ์ ํ์ฌ๊ฐ ์ง์ NFT ํ๋งค
โข ์ ๊ท ์์ดํ ์ถ์ ์ ์์ต
โข ์: ํ์ ํ ์ ์ค ์์ดํ ํ๋งค
2. ๋ก์ดํฐ (Royalty):
โข 2์ฐจ ๊ฑฐ๋ ์ ์ผ์ ๋น์จ ์์๋ฃ
โข ๋ณดํต 2.5% ~ 10%
โข ์๊ตฌ์ ์ธ ์์ต์
3. ๋ง์ผํ๋ ์ด์ค ์์๋ฃ:
โข ์์ฒด ๋ง์ผํ๋ ์ด์ค ์ด์
โข ๊ฑฐ๋๋ง๋ค ์์๋ฃ ์ง์
โข ์: 2.5% ๊ฑฐ๋ ์์๋ฃ
4. ๊ฒ์ ๋ด ๊ฒฝ์ ์์คํ :
โข ์์ดํ ๊ฐํ ๋น์ฉ
โข ์์ดํ ํฉ์ฑ ๋น์ฉ
โข ํน์ ๊ธฐ๋ฅ ์ด์ฉ๋ฃ
5. ๋ธ๋๋ ํ์ :
โข ์ ๋ช ๋ธ๋๋ ์์ดํ ์ถ์
โข ๋ผ์ด์ ์ค ์์ต
โข ์: ๋์ดํค ์ ๋ฐ ์คํจ
๐ ์ฑ๊ณต ์์ธ ๋ถ์
์ฑ๊ณตํ NFT ๊ฒ์๋ค์ ๊ณตํต์ ์ ๋ถ์ํด๋ณด๋ฉด:
| ์์ | ์ค์๋ | ์ค๋ช |
|---|---|---|
| ์ฌ๋ฏธ์๋ ๊ฒ์์ฑ | โญโญโญโญโญ | NFT๋ณด๋ค ๊ฒ์ ์์ฒด๊ฐ ์ฌ๋ฏธ์์ด์ผ ํจ |
| ๋ฎ์ ์ง์ ์ฅ๋ฒฝ | โญโญโญโญโญ | ์ฌ์ด ์ง๊ฐ ์ฐ๋, ์ ๋ ดํ ์์๋ฃ |
| ๊ณต์ ํ ๊ฒฝ์ | โญโญโญโญ | Pay-to-Win ์ง์, ๊ท ํ์กํ ๋ณด์ |
| ์ปค๋ฎค๋ํฐ | โญโญโญโญ | ํ๋ฐํ ์ ์ ์ปค๋ฎค๋ํฐ, DAO ๊ฑฐ๋ฒ๋์ค |
| ๊ธฐ์ ์์ ์ฑ | โญโญโญโญโญ | ๋น ๋ฅธ ์๋, ๋ฎ์ ์์๋ฃ, ๋ณด์ |
โ ๏ธ ์คํจ ์ฌ๋ก์์ ๋ฐฐ์ฐ๊ธฐ:
๋ง์ NFT ๊ฒ์์ด ์คํจํ ์ด์ :
โข ๊ฒ์์ฑ ์์ด ๋๋ฒ์ด๋ง ๊ฐ์กฐ
โข ๋์ ์ด๊ธฐ ํฌ์ ๋น์ฉ
โข ์ง์ ๋ถ๊ฐ๋ฅํ ํ ํฐ ๊ฒฝ์
โข ํฐ์ง ์คํด ๊ตฌ์กฐ
โข ๋ณด์ ์ฌ๊ณ (ํดํน, ๋ฒ๊ทธ)
NFT๋ ๊ฒ์์ ๋ ์ฌ๋ฏธ์๊ฒ ๋ง๋๋ ๋๊ตฌ์ฌ์ผ ํด. ๊ทธ ์์ฒด๊ฐ ๋ชฉ์ ์ด ๋๋ฉด ์ ๋ผ! ๐ฏ
โข ๊ฒ์์ฑ ์์ด ๋๋ฒ์ด๋ง ๊ฐ์กฐ
โข ๋์ ์ด๊ธฐ ํฌ์ ๋น์ฉ
โข ์ง์ ๋ถ๊ฐ๋ฅํ ํ ํฐ ๊ฒฝ์
โข ํฐ์ง ์คํด ๊ตฌ์กฐ
โข ๋ณด์ ์ฌ๊ณ (ํดํน, ๋ฒ๊ทธ)
NFT๋ ๊ฒ์์ ๋ ์ฌ๋ฏธ์๊ฒ ๋ง๋๋ ๋๊ตฌ์ฌ์ผ ํด. ๊ทธ ์์ฒด๊ฐ ๋ชฉ์ ์ด ๋๋ฉด ์ ๋ผ! ๐ฏ
๐ฎ ๋ฏธ๋ ์ ๋ง๊ณผ ๋ฐ์ ๋ฐฉํฅ
๊ฒ์ ์์ดํ
NFT ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์์ผ๋ก ์ด๋ค ๋ณํ๊ฐ ์์์ง ์ดํด๋ณด์! ๐
๐ ๋ ์ค๋ฅด๋ ํธ๋ ๋
1. ํฌ๋ก์ค ๊ฒ์ ์์ดํ
:
โข ํ ๊ฒ์์ ์์ดํ ์ ๋ค๋ฅธ ๊ฒ์์์๋ ์ฌ์ฉ
โข ํ์คํ๋ ๋ฉํ๋ฐ์ดํฐ ํฌ๋งท
โข ๊ฒ์ ๊ฐ ์ํธ์ด์ฉ์ฑ
โข ์: A๊ฒ์์ ๊ฒ์ B๊ฒ์์์๋ ์ฌ์ฉ
2. ๋ค์ด๋๋ฏน NFT:
โข ์๊ฐ์ ๋ฐ๋ผ ๋ณํ๋ NFT
โข ๊ฒ์ ํ๋ ์ด์ ๋ฐ๋ผ ์ธํ ๋ณํ
โข ๋ ๋ฒจ์ , ์งํ ์์คํ
โข Chainlink Oracle ํ์ฉ
3. AI ์์ฑ ์์ดํ :
โข AI๊ฐ ์๋์ผ๋ก ๊ณ ์ ํ ์์ดํ ์์ฑ
โข ๋ฌดํํ ์กฐํฉ ๊ฐ๋ฅ
โข ํ๋ ์ด์ด ์คํ์ผ์ ๋ง์ถคํ ์์ดํ
4. ์์ NFT:
โข ์น๊ตฌ์ ํจ๊ป ์์ ํ๋ NFT
โข ๊ธธ๋ ๊ณต๋ ์์ฐ
โข ๋ถํ ์์ ๊ถ
5. ์ง์๊ฐ๋ฅํ NFT:
โข ์นํ๊ฒฝ ๋ธ๋ก์ฒด์ธ ์ฌ์ฉ
โข ํ์ ์ค๋ฆฝ NFT
โข Layer 2 ์๋ฃจ์ ํ๋
โข ํ ๊ฒ์์ ์์ดํ ์ ๋ค๋ฅธ ๊ฒ์์์๋ ์ฌ์ฉ
โข ํ์คํ๋ ๋ฉํ๋ฐ์ดํฐ ํฌ๋งท
โข ๊ฒ์ ๊ฐ ์ํธ์ด์ฉ์ฑ
โข ์: A๊ฒ์์ ๊ฒ์ B๊ฒ์์์๋ ์ฌ์ฉ
2. ๋ค์ด๋๋ฏน NFT:
โข ์๊ฐ์ ๋ฐ๋ผ ๋ณํ๋ NFT
โข ๊ฒ์ ํ๋ ์ด์ ๋ฐ๋ผ ์ธํ ๋ณํ
โข ๋ ๋ฒจ์ , ์งํ ์์คํ
โข Chainlink Oracle ํ์ฉ
3. AI ์์ฑ ์์ดํ :
โข AI๊ฐ ์๋์ผ๋ก ๊ณ ์ ํ ์์ดํ ์์ฑ
โข ๋ฌดํํ ์กฐํฉ ๊ฐ๋ฅ
โข ํ๋ ์ด์ด ์คํ์ผ์ ๋ง์ถคํ ์์ดํ
4. ์์ NFT:
โข ์น๊ตฌ์ ํจ๊ป ์์ ํ๋ NFT
โข ๊ธธ๋ ๊ณต๋ ์์ฐ
โข ๋ถํ ์์ ๊ถ
5. ์ง์๊ฐ๋ฅํ NFT:
โข ์นํ๊ฒฝ ๋ธ๋ก์ฒด์ธ ์ฌ์ฉ
โข ํ์ ์ค๋ฆฝ NFT
โข Layer 2 ์๋ฃจ์ ํ๋
๐ ๏ธ ๊ธฐ์ ๋ฐ์ ๋ฐฉํฅ
์์ผ๋ก ์ด๋ฐ ๊ธฐ์ ๋ค์ด ๋ ๋ฐ์ ํ ๊ฑฐ์ผ:
๐ง ํต์ฌ ๊ธฐ์ ๋ฐ์ :
1. Layer 2 ํ์ฅ:
โข Optimistic Rollup, ZK-Rollup
โข ๋ ๋น ๋ฅด๊ณ ์ ๋ ดํ ๊ฑฐ๋
โข ์ด๋๋ฆฌ์ ๋ฉ์ธ๋ท ๋ณด์ ์ ์ง
2. Account Abstraction:
โข ์ง๊ฐ ์์ด ๊ฒ์ ํ๋ ์ด
โข ์์ ๋ก๊ทธ์ธ์ผ๋ก NFT ๊ด๋ฆฌ
โข ๊ฐ์ค๋น ๋๋ฉ (Gasless Transaction)
3. ์์ง์ ์ฆ๋ช (ZK-Proof):
โข ํ๋ผ์ด๋ฒ์ ๋ณดํธ
โข ๊ฒ์ ๋ด ๋น๋ฐ ์ ๋ณด ๋ณดํธ
โข ํจ์จ์ ์ธ ๊ฒ์ฆ
4. ํฌ๋ก์ค์ฒด์ธ ๋ธ๋ฆฟ์ง:
โข ์ฌ๋ฌ ๋ธ๋ก์ฒด์ธ ๊ฐ NFT ์ด๋
โข ํตํฉ๋ ์ํ๊ณ
โข ์ ๋์ฑ ์ฆ๊ฐ
โข Optimistic Rollup, ZK-Rollup
โข ๋ ๋น ๋ฅด๊ณ ์ ๋ ดํ ๊ฑฐ๋
โข ์ด๋๋ฆฌ์ ๋ฉ์ธ๋ท ๋ณด์ ์ ์ง
2. Account Abstraction:
โข ์ง๊ฐ ์์ด ๊ฒ์ ํ๋ ์ด
โข ์์ ๋ก๊ทธ์ธ์ผ๋ก NFT ๊ด๋ฆฌ
โข ๊ฐ์ค๋น ๋๋ฉ (Gasless Transaction)
3. ์์ง์ ์ฆ๋ช (ZK-Proof):
โข ํ๋ผ์ด๋ฒ์ ๋ณดํธ
โข ๊ฒ์ ๋ด ๋น๋ฐ ์ ๋ณด ๋ณดํธ
โข ํจ์จ์ ์ธ ๊ฒ์ฆ
4. ํฌ๋ก์ค์ฒด์ธ ๋ธ๋ฆฟ์ง:
โข ์ฌ๋ฌ ๋ธ๋ก์ฒด์ธ ๊ฐ NFT ์ด๋
โข ํตํฉ๋ ์ํ๊ณ
โข ์ ๋์ฑ ์ฆ๊ฐ
๐ก ์ค์ฉ์ ์ธ ์กฐ์ธ
๊ฒ์ ๊ฐ๋ฐ์๋ ๊ธฐํ์๋ผ๋ฉด ์ด๋ฐ ์ ๋ค์ ๊ณ ๋ คํด๋ด:
๐ฎ ๊ฒ์ ๊ฐ๋ฐ์๋ฅผ ์ํ ํ:
1. ์๊ฒ ์์ํ๊ธฐ:
โข ์ฒ์๋ถํฐ ๋ชจ๋ ์์ดํ ์ NFT๋ก ๋ง๋ค ํ์ ์์ด
โข ํน๋ณํ ์์ดํ ๋ง NFT๋ก ์์
โข ์ ์ง์ ์ผ๋ก ํ๋
2. ์ ์ ๊ฒฝํ ์ฐ์ :
โข ๋ธ๋ก์ฒด์ธ ๊ธฐ์ ์ ์จ๊ฒจ๋ผ
โข ์ผ๋ฐ ๊ฒ์์ฒ๋ผ ์ฝ๊ฒ
โข ๋ณต์กํ ์ฉ์ด ์ฌ์ฉ ์์
3. ์ปค๋ฎค๋ํฐ์ ์ํต:
โข ํฌ๋ช ํ ๋ก๋๋งต ๊ณต์
โข ์ ์ ํผ๋๋ฐฑ ์ ๊ทน ๋ฐ์
โข DAO ๊ฑฐ๋ฒ๋์ค ๊ณ ๋ ค
4. ๋ฒ๋ฅ ๊ฒํ :
โข ๊ฐ๊ตญ์ NFT ๊ท์ ํ์ธ
โข ์ฆ๊ถ๋ฒ ์ ์ด ์ฌ๋ถ ๊ฒํ
โข ์ธ๊ธ ๋ฌธ์ ๊ณ ๋ ค
5. ์ง์๊ฐ๋ฅ์ฑ:
โข ์ฅ๊ธฐ์ ์ธ ํ ํฐ ๊ฒฝ์ ์ค๊ณ
โข ์ธํ๋ ์ด์ ๋ฐฉ์ง ๋ฉ์ปค๋์ฆ
โข ์ค์ ๊ฐ์น ์ ๊ณต
โข ์ฒ์๋ถํฐ ๋ชจ๋ ์์ดํ ์ NFT๋ก ๋ง๋ค ํ์ ์์ด
โข ํน๋ณํ ์์ดํ ๋ง NFT๋ก ์์
โข ์ ์ง์ ์ผ๋ก ํ๋
2. ์ ์ ๊ฒฝํ ์ฐ์ :
โข ๋ธ๋ก์ฒด์ธ ๊ธฐ์ ์ ์จ๊ฒจ๋ผ
โข ์ผ๋ฐ ๊ฒ์์ฒ๋ผ ์ฝ๊ฒ
โข ๋ณต์กํ ์ฉ์ด ์ฌ์ฉ ์์
3. ์ปค๋ฎค๋ํฐ์ ์ํต:
โข ํฌ๋ช ํ ๋ก๋๋งต ๊ณต์
โข ์ ์ ํผ๋๋ฐฑ ์ ๊ทน ๋ฐ์
โข DAO ๊ฑฐ๋ฒ๋์ค ๊ณ ๋ ค
4. ๋ฒ๋ฅ ๊ฒํ :
โข ๊ฐ๊ตญ์ NFT ๊ท์ ํ์ธ
โข ์ฆ๊ถ๋ฒ ์ ์ด ์ฌ๋ถ ๊ฒํ
โข ์ธ๊ธ ๋ฌธ์ ๊ณ ๋ ค
5. ์ง์๊ฐ๋ฅ์ฑ:
โข ์ฅ๊ธฐ์ ์ธ ํ ํฐ ๊ฒฝ์ ์ค๊ณ
โข ์ธํ๋ ์ด์ ๋ฐฉ์ง ๋ฉ์ปค๋์ฆ
โข ์ค์ ๊ฐ์น ์ ๊ณต
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ๊ธฐ์ ์ ํ์ฉํ ์ ์์ด. ์๋ฅผ ๋ค์ด ๋์งํธ ์ํธ, ์์
, ๊ฐ์ ์ฝํ
์ธ ๋ฑ์ NFT๋ก ๋ง๋ค์ด์ ๊ฑฐ๋ํ๋ ์์คํ
์ ๊ตฌ์ถํ ์ ์๊ฒ ์ง? ๐จ
์ค์ํ ๊ฑด ๊ธฐ์ ์์ฒด๊ฐ ์๋๋ผ ์ฌ์ฉ์์๊ฒ ์ด๋ค ๊ฐ์น๋ฅผ ์ ๊ณตํ๋๋์ผ. NFT๋ ๊ทธ์ ๋๊ตฌ์ผ ๋ฟ์ด๊ณ , ์ง์ง ์ค์ํ ๊ฑด ์ข์ ๊ฒ์, ์ข์ ์ฝํ ์ธ , ์ข์ ๊ฒฝํ์ ๋ง๋๋ ๊ฑฐ์ผ! ๐ช
์ค์ํ ๊ฑด ๊ธฐ์ ์์ฒด๊ฐ ์๋๋ผ ์ฌ์ฉ์์๊ฒ ์ด๋ค ๊ฐ์น๋ฅผ ์ ๊ณตํ๋๋์ผ. NFT๋ ๊ทธ์ ๋๊ตฌ์ผ ๋ฟ์ด๊ณ , ์ง์ง ์ค์ํ ๊ฑด ์ข์ ๊ฒ์, ์ข์ ์ฝํ ์ธ , ์ข์ ๊ฒฝํ์ ๋ง๋๋ ๊ฑฐ์ผ! ๐ช
๐ ๋ง์น๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐ ๊ฒ์ ์์ดํ
์ NFT๋ก ๋ง๋๋ ์ ์ฒด ๊ณผ์ ์ ํจ๊ป ์ดํด๋ดค์ด. ์ฒ์์ ๋ณต์กํด ๋ณด์์ง๋ง, ํ๋์ฉ ๋ฏ์ด๋ณด๋ ๊ทธ๋ ๊ฒ ์ด๋ ต์ง ์์ง?
ํต์ฌ์ ์ ๋ฆฌํ๋ฉด ์ด๋:
1. ์์ดํ ๋ฐ์ดํฐ ์ค๊ณ: ๊ฒ์ ์์ดํ ์ ์์ฑ์ JSON์ผ๋ก ๊ตฌ์กฐํ
2. ๋ธ๋ก์ฒด์ธ ์ ํ: ๊ฒ์์ ์ ํฉํ ๋ธ๋ก์ฒด์ธ ์ ํ (ํด๋ฆฌ๊ณค, ์ด๋ฎคํฐ๋ธX ๋ฑ)
3. ์ค๋งํธ ์ปจํธ๋ํธ: ERC-721 ํ์ค์ผ๋ก NFT ๋ฐํ ๋ฐ ๊ด๋ฆฌ
4. ๊ฒ์ ์ฐ๋: Web3๋ก ๊ฒ์ ์๋ฒ์ ๋ธ๋ก์ฒด์ธ ์ฐ๊ฒฐ
5. ์ง๊ฐ ์ฐ๋: ์ฌ์ฉ์ ์นํ์ ์ธ ์ง๊ฐ ๊ฒฝํ ์ ๊ณต
6. IPFS ์ ์ฅ: ๋ฉํ๋ฐ์ดํฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ฐ ์ ์ฅ
7. ๋ง์ผํ๋ ์ด์ค: ์์ ๋ก์ด ๊ฑฐ๋ ์์คํ ๊ตฌ์ถ
8. ๋ณด์: ์ฒ ์ ํ ํ ์คํธ์ ๊ฐ์ฌ
๊ฐ์ฅ ์ค์ํ ๊ฑด ๊ธฐ์ ์ ์๋จ์ผ ๋ฟ์ด๋ผ๋ ๊ฑฐ์ผ. NFT ์์ฒด๊ฐ ๋ชฉ์ ์ด ๋๋ฉด ์ ๋ผ. ํ๋ ์ด์ด์๊ฒ ์ง์ง ๊ฐ์น๋ฅผ ์ ๊ณตํ๊ณ , ์ฌ๋ฏธ์๋ ๊ฒ์์ ๋ง๋๋ ๊ฒ ์ฐ์ ์ด์ผ. NFT๋ ๊ทธ๊ฑธ ๋ ํ๋ถํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ๋๊ตฌ์ผ ๋ฟ์ด์ง. ๐ฏ
๋ธ๋ก์ฒด์ธ ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. Layer 2 ์๋ฃจ์ ์ผ๋ก ์์๋ฃ๊ฐ ๊ฑฐ์ ๋ฌด๋ฃ์ ๊ฐ๊น์์ง๊ณ , Account Abstraction์ผ๋ก ์ง๊ฐ ์์ด๋ ๊ฒ์์ ์ฆ๊ธธ ์ ์๊ฒ ๋ ๊ฑฐ์ผ. ์์ผ๋ก NFT๋ ์ฐ๋ฆฌ๊ฐ ์์ํ์ง ๋ชปํ๋ ์ฌ์ด์ ์์ฐ์ค๋ฝ๊ฒ ๊ฒ์์ ์ผ๋ถ๊ฐ ๋ ๊ฑฐ์ผ. ๐
ํน์ ๊ฒ์ ๊ฐ๋ฐ์ ๊ด์ฌ์ด ์๊ฑฐ๋, NFT ํ๋ก์ ํธ๋ฅผ ์์ํ๊ณ ์ถ๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ๊ด๋ จ ์ ๋ฌธ๊ฐ๋ค์ ์ฐพ์๋ณผ ์๋ ์์ด. ๋ธ๋ก์ฒด์ธ ๊ฐ๋ฐ์, ์ค๋งํธ ์ปจํธ๋ํธ ๊ฐ์ฌ์, NFT ์ํฐ์คํธ ๋ฑ ๋ค์ํ ์ฌ๋ฅ์ ๊ฐ์ง ์ฌ๋๋ค์ด ๋ชจ์ฌ ์๊ฑฐ๋ ! ๐ค
์ด ๊ธ์ด ๊ฒ์ ์์ดํ NFTํ์ ๋ํ ์ดํด๋ฅผ ๋์ด๋ ๋ฐ ๋์์ด ๋์ผ๋ฉด ์ข๊ฒ ์ด. ์ง๋ฌธ์ด ์๊ฑฐ๋ ๋ ์๊ณ ์ถ์ ๊ฒ ์๋ค๋ฉด ์ธ์ ๋ ์ฐพ์์! ํจ๊ป ๋ธ๋ก์ฒด์ธ ๊ฒ์์ ๋ฏธ๋๋ฅผ ๋ง๋ค์ด๊ฐ์! ๐ชโจ
ํต์ฌ์ ์ ๋ฆฌํ๋ฉด ์ด๋:
1. ์์ดํ ๋ฐ์ดํฐ ์ค๊ณ: ๊ฒ์ ์์ดํ ์ ์์ฑ์ JSON์ผ๋ก ๊ตฌ์กฐํ
2. ๋ธ๋ก์ฒด์ธ ์ ํ: ๊ฒ์์ ์ ํฉํ ๋ธ๋ก์ฒด์ธ ์ ํ (ํด๋ฆฌ๊ณค, ์ด๋ฎคํฐ๋ธX ๋ฑ)
3. ์ค๋งํธ ์ปจํธ๋ํธ: ERC-721 ํ์ค์ผ๋ก NFT ๋ฐํ ๋ฐ ๊ด๋ฆฌ
4. ๊ฒ์ ์ฐ๋: Web3๋ก ๊ฒ์ ์๋ฒ์ ๋ธ๋ก์ฒด์ธ ์ฐ๊ฒฐ
5. ์ง๊ฐ ์ฐ๋: ์ฌ์ฉ์ ์นํ์ ์ธ ์ง๊ฐ ๊ฒฝํ ์ ๊ณต
6. IPFS ์ ์ฅ: ๋ฉํ๋ฐ์ดํฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ฐ ์ ์ฅ
7. ๋ง์ผํ๋ ์ด์ค: ์์ ๋ก์ด ๊ฑฐ๋ ์์คํ ๊ตฌ์ถ
8. ๋ณด์: ์ฒ ์ ํ ํ ์คํธ์ ๊ฐ์ฌ
๊ฐ์ฅ ์ค์ํ ๊ฑด ๊ธฐ์ ์ ์๋จ์ผ ๋ฟ์ด๋ผ๋ ๊ฑฐ์ผ. NFT ์์ฒด๊ฐ ๋ชฉ์ ์ด ๋๋ฉด ์ ๋ผ. ํ๋ ์ด์ด์๊ฒ ์ง์ง ๊ฐ์น๋ฅผ ์ ๊ณตํ๊ณ , ์ฌ๋ฏธ์๋ ๊ฒ์์ ๋ง๋๋ ๊ฒ ์ฐ์ ์ด์ผ. NFT๋ ๊ทธ๊ฑธ ๋ ํ๋ถํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ๋๊ตฌ์ผ ๋ฟ์ด์ง. ๐ฏ
๋ธ๋ก์ฒด์ธ ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. Layer 2 ์๋ฃจ์ ์ผ๋ก ์์๋ฃ๊ฐ ๊ฑฐ์ ๋ฌด๋ฃ์ ๊ฐ๊น์์ง๊ณ , Account Abstraction์ผ๋ก ์ง๊ฐ ์์ด๋ ๊ฒ์์ ์ฆ๊ธธ ์ ์๊ฒ ๋ ๊ฑฐ์ผ. ์์ผ๋ก NFT๋ ์ฐ๋ฆฌ๊ฐ ์์ํ์ง ๋ชปํ๋ ์ฌ์ด์ ์์ฐ์ค๋ฝ๊ฒ ๊ฒ์์ ์ผ๋ถ๊ฐ ๋ ๊ฑฐ์ผ. ๐
ํน์ ๊ฒ์ ๊ฐ๋ฐ์ ๊ด์ฌ์ด ์๊ฑฐ๋, NFT ํ๋ก์ ํธ๋ฅผ ์์ํ๊ณ ์ถ๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ๊ด๋ จ ์ ๋ฌธ๊ฐ๋ค์ ์ฐพ์๋ณผ ์๋ ์์ด. ๋ธ๋ก์ฒด์ธ ๊ฐ๋ฐ์, ์ค๋งํธ ์ปจํธ๋ํธ ๊ฐ์ฌ์, NFT ์ํฐ์คํธ ๋ฑ ๋ค์ํ ์ฌ๋ฅ์ ๊ฐ์ง ์ฌ๋๋ค์ด ๋ชจ์ฌ ์๊ฑฐ๋ ! ๐ค
์ด ๊ธ์ด ๊ฒ์ ์์ดํ NFTํ์ ๋ํ ์ดํด๋ฅผ ๋์ด๋ ๋ฐ ๋์์ด ๋์ผ๋ฉด ์ข๊ฒ ์ด. ์ง๋ฌธ์ด ์๊ฑฐ๋ ๋ ์๊ณ ์ถ์ ๊ฒ ์๋ค๋ฉด ์ธ์ ๋ ์ฐพ์์! ํจ๊ป ๋ธ๋ก์ฒด์ธ ๊ฒ์์ ๋ฏธ๋๋ฅผ ๋ง๋ค์ด๊ฐ์! ๐ชโจ
โ
๋ค์ ๋จ๊ณ:
โข Hardhat์ผ๋ก ์ค๋งํธ ์ปจํธ๋ํธ ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถํ๊ธฐ
โข ํด๋ฆฌ๊ณค ํ ์คํธ๋ท์์ ์ฒซ NFT ๋ฐํํด๋ณด๊ธฐ
โข OpenSea ํ ์คํธ๋ท์์ NFT ํ์ธํ๊ธฐ
โข ๊ฐ๋จํ ๊ฒ์ ํ๋กํ ํ์ ๋ง๋ค์ด๋ณด๊ธฐ
โข ์ปค๋ฎค๋ํฐ์ ์ฐธ์ฌํด์ ๊ฒฝํ ๊ณต์ ํ๊ธฐ
โข ํด๋ฆฌ๊ณค ํ ์คํธ๋ท์์ ์ฒซ NFT ๋ฐํํด๋ณด๊ธฐ
โข OpenSea ํ ์คํธ๋ท์์ NFT ํ์ธํ๊ธฐ
โข ๊ฐ๋จํ ๊ฒ์ ํ๋กํ ํ์ ๋ง๋ค์ด๋ณด๊ธฐ
โข ์ปค๋ฎค๋ํฐ์ ์ฐธ์ฌํด์ ๊ฒฝํ ๊ณต์ ํ๊ธฐ
๐ฎ Happy Coding & Gaming! ๐ฎ
๋ธ๋ก์ฒด์ธ ๊ธฐ์ ๋ก ๋ ์ฌ๋ฏธ์๋ ๊ฒ์ ์ธ์์ ๋ง๋ค์ด๊ฐ์!
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
๋๊ธ์ ๋ถ๋ฌ์ค๋ ์ค์
๋๋ค.
๋๊ธ์ ๋ถ๋ฌ์ค์ง ๋ชปํ์ต๋๋ค. ์ฐ๊ฒฐ ์ํ๋ฅผ ํ์ธํ๊ณ ๋ค์ ์๋ํด ์ฃผ์ธ์.
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.