Ver3.0 ๐ ENS(์ด๋๋ฆฌ์ ๋ค์์๋น์ค)์ ๊ธฐ์ ๊ตฌ์กฐ: ๋ธ๋ก์ฒด์ธ ์ฃผ์๋ฅผ ์ฌ๋์ด ์ฝ์ ์ ์๋ ์ด๋ฆ์ผ๋ก!

๐ ENS(์ด๋๋ฆฌ์ ๋ค์์๋น์ค)์ ๊ธฐ์ ๊ตฌ์กฐ: ๋ธ๋ก์ฒด์ธ ์ฃผ์๋ฅผ ์ฌ๋์ด ์ฝ์ ์ ์๋ ์ด๋ฆ์ผ๋ก!
๋ณต์กํ ์ด๋๋ฆฌ์ ์ฃผ์๋ฅผ ๊ฐ๋จํ ์ด๋ฆ์ผ๋ก ๋ฐ๊ฟ์ฃผ๋ ENS์ ๊ธฐ์ ์ ๋น๋ฐ์ ํํค์ณ๋ณด์ ๐
์๋
! ๐ ํน์ ์ด๋๋ฆฌ์ ์ง๊ฐ ์ฃผ์ ๋ณธ ์ ์์ด? 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb ์ด๋ฐ ์์ผ๋ก ์๊ธด 42์๋ฆฌ 16์ง์ ๋ฌธ์์ด ๋ง์ด์ผ. ์์งํ ์ด๊ฑธ ์ธ์ฐ๊ฑฐ๋ ์ ํํ๊ฒ ์
๋ ฅํ๋ ๊ฑด ๊ฑฐ์ ๋ถ๊ฐ๋ฅํ์ง ์์? ๐
๋ฐ๋ก ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ํ์ํ ๊ฒ ENS(Ethereum Name Service)์ผ! ๋ง์น ์ธํฐ๋ท์์ 192.168.0.1 ๊ฐ์ IP ์ฃผ์ ๋์ google.com์ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ, ๋ณต์กํ ๋ธ๋ก์ฒด์ธ ์ฃผ์๋ฅผ alice.eth ๊ฐ์ ๊ฐ๋จํ ์ด๋ฆ์ผ๋ก ๋ฐ๊ฟ์ฃผ๋ ๊ฑฐ์ง. ๐ฏ
์ค๋์ ์ด ENS๊ฐ ์ด๋ค ๊ธฐ์ ๊ตฌ์กฐ๋ก ์๋ํ๋์ง, ํ๋ก๊ทธ๋๋ฐ ๊ด์ ์์ ๊น์ด ์๊ฒ ํํค์ณ๋ณผ ๊ฑฐ์ผ. ๋ธ๋ก์ฒด์ธ ๊ฐ๋ฐ์ ๊ด์ฌ ์๋ค๋ฉด, ๋๋ Web3 ์ธ๊ณ์ ๋ฐ์ ๋ค์ด๊ณ ์ถ๋ค๋ฉด ๊ผญ ์์์ผ ํ ๋ด์ฉ์ด๋๊น ์ง์คํด์ค! ๐ช
๐๏ธ ENS์ ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ ํ์ ๋ฐฐ๊ฒฝ
ENS๋ 2017๋
5์์ ์ด๋๋ฆฌ์ ์ฌ๋จ์์ ๊ณต์์ ์ผ๋ก ์ถ์ํ ๋ถ์ฐํ ๋ค์ด๋ฐ ์์คํ
์ด์ผ. ๊ธฐ์กด ์ธํฐ๋ท์ DNS(Domain Name System)์์ ์๊ฐ์ ๋ฐ์์ง๋ง, ์ค์ํ๋ ๊ด๋ฆฌ ๊ธฐ๊ด ์์ด ์์ ํ ๋ธ๋ก์ฒด์ธ ์์์ ์๋ํ๋ค๋ ์ ์ด ํ์ ์ ์ด์ง! ๐
์ ENS๊ฐ ํ์ํ์๊น? ๋ธ๋ก์ฒด์ธ ์ธ๊ณ์์๋ ๋ชจ๋ ๊ณ์ ์ด ๊ณต๊ฐํค ์ํธํ ๋ฐฉ์์ผ๋ก ์์ฑ๋ ๊ธด ์ฃผ์๋ฅผ ์ฌ์ฉํด. ์ด๋๋ฆฌ์์ ๊ฒฝ์ฐ 20๋ฐ์ดํธ(160๋นํธ) ์ฃผ์๋ฅผ 16์ง์๋ก ํํํ๋ฉด 42์๋ฆฌ๊ฐ ๋๋๋ฐ, ์ด๊ฑด ์ฌ๋์ด ๊ธฐ์ตํ๊ฑฐ๋ ์ ํํ ์
๋ ฅํ๊ธฐ์ ๋๋ฌด ๋ณต์กํด. ํ ๊ธ์๋ง ํ๋ ค๋ ์์ฐ์ ์์ ์์ ์ ์์ผ๋๊น ๋ง์ด์ผ. ๐ฑ
๐ ENS๊ฐ ํด๊ฒฐํ๋ ํต์ฌ ๋ฌธ์ ๋ค
1. ๊ฐ๋
์ฑ (Readability)
0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb โ alice.eth
์ฌ๋์ด ์ฝ๊ณ ๊ธฐ์ตํ๊ธฐ ์ฌ์ด ํํ๋ก ๋ณํ! ๐ง
2. ์์ ์ฑ (Safety)
๋ณต์กํ ์ฃผ์๋ฅผ ์ง์ ์
๋ ฅํ ๋ ๋ฐ์ํ๋ ์คํ ์ํ์ ํฌ๊ฒ ์ค์ฌ์ค. ํนํ ํผ์ฑ ๊ณต๊ฒฉ์๋ ํจ๊ณผ์ ์ด์ผ. ๐ก๏ธ
3. ๋ค๋ชฉ์ ์ฑ (Versatility)
์ด๋๋ฆฌ์ ์ฃผ์๋ฟ๋ง ์๋๋ผ ๋นํธ์ฝ์ธ ์ฃผ์, IPFS ํด์, ์ด๋ฉ์ผ, URL ๋ฑ ๋ค์ํ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ์ด๋ฆ์ ์ฐ๊ฒฐํ ์ ์์ด. ๐
4. ๋ถ์ฐ์ฑ (Decentralization)
์ค์ ๊ธฐ๊ด ์์ด ์ค๋งํธ ์ปจํธ๋ํธ๋ก ์์ ํ ์๋ํ๋์ด ์ด์๋ผ. ๋๊ตฌ๋ ์์๋ก ํต์ ํ ์ ์์ง! โก
๐ง ENS์ ํต์ฌ ๊ธฐ์ ๊ตฌ์กฐ
ENS๋ ํฌ๊ฒ ์ธ ๊ฐ์ง ํต์ฌ ์ปดํฌ๋ํธ๋ก ๊ตฌ์ฑ๋์ด ์์ด. ๊ฐ๊ฐ์ ์ญํ ์ ๋ช ํํ ์ดํดํ๋ ๊ฒ ENS ์ ์ฒด ์์คํ ์ ํ์ ํ๋ ์ด์ ์ผ! ๐
1๏ธโฃ Registry (๋ ์ง์คํธ๋ฆฌ) - ๋๋ฉ์ธ ์์ ๊ถ์ ์ค์ ์ฅ๋ถ
Registry๋ ENS์ ๊ฐ์ฅ ํต์ฌ์ด ๋๋ ์ค๋งํธ ์ปจํธ๋ํธ์ผ. ๋ชจ๋ ๋๋ฉ์ธ๊ณผ ์๋ธ๋๋ฉ์ธ์ ์์ ๊ถ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ์ผ์ข
์ "์ค์ ์ฅ๋ถ" ์ญํ ์ ํ์ง. ํ์ง๋ง ์ฌ๊ธฐ์ "์ค์"์ด๋ผ๋ ๋ง์ ์คํดํ์ง ๋ง! ๋ธ๋ก์ฒด์ธ ์์์ ์๋ํ๋ ๋ถ์ฐํ ์์คํ
์ด์ผ. ๐
Registry๋ ๊ฐ ๋๋ฉ์ธ์ ๋ํด ๋ค์ ์ธ ๊ฐ์ง ํต์ฌ ์ ๋ณด๋ฅผ ์ ์ฅํด:
| ์ ๋ณด ์ ํ | ์ค๋ช |
|---|---|
| Owner (์์ ์) | ํด๋น ๋๋ฉ์ธ์ ์์ ํ ์ด๋๋ฆฌ์ ์ฃผ์. ์ด ์ฃผ์๋ง์ด ๋๋ฉ์ธ ์ค์ ์ ๋ณ๊ฒฝํ ์ ์์ด. |
| Resolver (๋ฆฌ์กธ๋ฒ) | ๋๋ฉ์ธ ์ด๋ฆ์ ์ค์ ์ฃผ์๋ก ๋ณํํด์ฃผ๋ ์ปจํธ๋ํธ์ ์ฃผ์. |
| TTL (Time To Live) | ์บ์ฑ ์๊ฐ. DNS์ ์ ์ฌํ๊ฒ ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ์ผ๋ง๋ ์ค๋ ์บ์ํ ์ง ๊ฒฐ์ ํด. |
Registry ์ปจํธ๋ํธ์ ํต์ฌ ํจ์๋ค์ ์ดํด๋ณผ๊น? ์ค์ Solidity ์ฝ๋ ๊ตฌ์กฐ๋ ์ด๋ ๊ฒ ์๊ฒผ์ด:
// ENS Registry ํต์ฌ ์ธํฐํ์ด์ค
interface ENS {
// ๋๋ฉ์ธ์ ์์ ์ ์กฐํ
function owner(bytes32 node) external view returns (address);
// ๋๋ฉ์ธ์ ๋ฆฌ์กธ๋ฒ ์กฐํ
function resolver(bytes32 node) external view returns (address);
// ๋๋ฉ์ธ์ TTL ์กฐํ
function ttl(bytes32 node) external view returns (uint64);
// ๋๋ฉ์ธ ์์ ์ ๋ณ๊ฒฝ (์์ ์๋ง ๊ฐ๋ฅ)
function setOwner(bytes32 node, address owner) external;
// ์๋ธ๋๋ฉ์ธ ์์ฑ ๋ฐ ์์ ์ ์ค์
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
// ๋ฆฌ์กธ๋ฒ ์ค์
function setResolver(bytes32 node, address resolver) external;
// TTL ์ค์
function setTTL(bytes32 node, uint64 ttl) external;
}
๐ก Node์ Label์ ๊ฐ๋
ENS์์๋ ๋๋ฉ์ธ์ node๋ผ๋ 32๋ฐ์ดํธ ํด์๊ฐ์ผ๋ก ํํํด. ์๋ฅผ ๋ค์ด alice.eth๋ ๋ค์๊ณผ ๊ฐ์ด ๊ณ์ฐ๋ผ:
node = keccak256(keccak256(0x00...) + keccak256("eth")) + keccak256("alice")
์ด๋ ๊ฒ ํด์๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ๊ฐ์ค ๋น์ฉ์ ์ ์ฝํ๊ณ , ๋๋ฉ์ธ ๊ธธ์ด์ ๊ด๊ณ์์ด ๊ณ ์ ๋ ํฌ๊ธฐ๋ก ์ ์ฅํ๊ธฐ ์ํด์์ผ. ๋๋ํ์ง? ๐ง
2๏ธโฃ Resolver (๋ฆฌ์กธ๋ฒ) - ์ค์ ์ฃผ์ ๋ณํ ์์ง
Resolver๋ ๋๋ฉ์ธ ์ด๋ฆ์ ์ค์ ์ฃผ์๋ ๋ฐ์ดํฐ๋ก ๋ณํํด์ฃผ๋ ์ปดํฌ๋ํธ์ผ. Registry๊ฐ "๋๊ฐ ์ด ๋๋ฉ์ธ์ ์์ ํ๊ณ ์๋๊ฐ?"๋ฅผ ๊ด๋ฆฌํ๋ค๋ฉด, Resolver๋ "์ด ๋๋ฉ์ธ์ด ์ด๋ค ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํค๋๊ฐ?"๋ฅผ ๊ด๋ฆฌํ๋ ๊ฑฐ์ง. ๐ฏ
์ฌ๋ฏธ์๋ ๊ฑด, Resolver๋ ํ๋์ ๊ณ ์ ๋ ์ปจํธ๋ํธ๊ฐ ์๋๋ผ๋ ์ ์ด์ผ! ๊ฐ ๋๋ฉ์ธ ์์ ์๊ฐ ์์ ๋ง์ Resolver๋ฅผ ์ ํํ๊ฑฐ๋ ์ง์ ๋ง๋ค ์๋ ์์ด. ๋ฌผ๋ก ๋๋ถ๋ถ์ ENS์์ ์ ๊ณตํ๋ ํ์ค Public Resolver๋ฅผ ์ฌ์ฉํ์ง๋ง ๋ง์ด์ผ. ๐ง
Resolver๊ฐ ์ง์ํ๋ ์ฃผ์ ๋ ์ฝ๋ ํ์ ๋ค์ ์ดํด๋ณด์:
| ๋ ์ฝ๋ ํ์ | ์ค๋ช | ์ฌ์ฉ ์์ |
|---|---|---|
| addr (Address) | ์ด๋๋ฆฌ์ ์ฃผ์ ๋งคํ | alice.eth โ 0x742d35... |
| contenthash | IPFS/Swarm ํด์ | ๋ถ์ฐ ์น์ฌ์ดํธ ํธ์คํ |
| text | ์์์ ํ ์คํธ ๋ฐ์ดํฐ | ์ด๋ฉ์ผ, ํธ์ํฐ, ์ค๋ช ๋ฑ |
| multicoin | ๋ค๋ฅธ ๋ธ๋ก์ฒด์ธ ์ฃผ์ | ๋นํธ์ฝ์ธ, ๋ผ์ดํธ์ฝ์ธ ๋ฑ |
| ABI | ์ปจํธ๋ํธ ์ธํฐํ์ด์ค | ์ค๋งํธ ์ปจํธ๋ํธ ์ํธ์์ฉ |
Public Resolver์ ํต์ฌ ํจ์๋ค์ ์ฝ๋๋ก ์ดํด๋ณด์:
// Public Resolver ํต์ฌ ์ธํฐํ์ด์ค
interface IPublicResolver {
// ์ด๋๋ฆฌ์ ์ฃผ์ ์ค์
function setAddr(bytes32 node, address addr) external;
// ์ด๋๋ฆฌ์ ์ฃผ์ ์กฐํ
function addr(bytes32 node) external view returns (address);
// ๋ค๋ฅธ ๋ธ๋ก์ฒด์ธ ์ฃผ์ ์ค์ (coinType: 60=ETH, 0=BTC ๋ฑ)
function setAddr(bytes32 node, uint coinType, bytes memory a) external;
// ํ
์คํธ ๋ ์ฝ๋ ์ค์ (key: "email", "url", "avatar" ๋ฑ)
function setText(bytes32 node, string calldata key, string calldata value) external;
// ํ
์คํธ ๋ ์ฝ๋ ์กฐํ
function text(bytes32 node, string calldata key) external view returns (string memory);
// IPFS ์ปจํ
์ธ ํด์ ์ค์
function setContenthash(bytes32 node, bytes calldata hash) external;
// IPFS ์ปจํ
์ธ ํด์ ์กฐํ
function contenthash(bytes32 node) external view returns (bytes memory);
}
๐ Resolver์ ์ค์ ํ์ฉ ์์
์๋ฅผ ๋ค์ด, vitalik.eth ๋๋ฉ์ธ์ ๋ค์๊ณผ ๊ฐ์ ๋ค์ํ ์ ๋ณด๋ฅผ ์ ์ฅํ ์ ์์ด:
โข ETH ์ฃผ์: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
โข BTC ์ฃผ์: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
โข ์ด๋ฉ์ผ: vitalik@ethereum.org
โข ํธ์ํฐ: @VitalikButerin
โข ์น์ฌ์ดํธ: ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco
โข ์๋ฐํ: ipfs://QmSP4nq9fnN9dAiCj42ug9Wa79rqmQerZXZch82VqpiH7U
์ด ๋ชจ๋ ์ ๋ณด๊ฐ ํ๋์ ์ด๋ฆ vitalik.eth์ ์ฐ๊ฒฐ๋๋ ๊ฑฐ์ผ! ์ ๋ง ํธ๋ฆฌํ์ง? ๐
3๏ธโฃ Registrar (๋ ์ง์คํธ๋ผ) - ๋๋ฉ์ธ ๋ฑ๋ก ๊ด๋ฆฌ์
Registrar๋ ์ค์ ๋ก ๋๋ฉ์ธ์ ๋ฑ๋กํ๊ณ ๊ด๋ฆฌํ๋ ์ปจํธ๋ํธ์ผ. Registry๊ฐ ์์ ๊ถ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ "์ฅ๋ถ"๋ผ๋ฉด, Registrar๋ ๊ทธ ์ฅ๋ถ์ ์๋ก์ด ํญ๋ชฉ์ ์ถ๊ฐํ๊ณ ๊ด๋ฆฌํ๋ "์ฌ๋ฌด์ค" ๊ฐ์ ์ญํ ์ด์ง. ๐ข
ENS์์๋ ์ต์์ ๋๋ฉ์ธ(.eth)์ ๋ํด ํน๋ณํ Registrar๋ฅผ ์ด์ํ๊ณ ์์ด. ํ์ฌ ์ฌ์ฉ๋๋ ๊ฑด ETH Registrar Controller์ธ๋ฐ, ์ด๊ฑด ๊ฒฝ๋งค ๋ฐฉ์์ด ์๋ ์ ์ฐฉ์ ๋ฑ๋ก ๋ฐฉ์์ ์ฌ์ฉํด. ํจ์ฌ ๊ฐ๋จํ๊ณ ์ฌ์ฉ์ ์นํ์ ์ด์ง! ๐
.eth ๋๋ฉ์ธ ๋ฑ๋ก ํ๋ก์ธ์ค๋ ๋ค์๊ณผ ๊ฐ์:
๐ .eth ๋๋ฉ์ธ ๋ฑ๋ก ๋จ๊ณ
Step 1: ๊ฐ์ฉ์ฑ ํ์ธ (Availability Check)
์ํ๋ ๋๋ฉ์ธ ์ด๋ฆ์ด ์ด๋ฏธ ๋ฑ๋ก๋์ด ์๋์ง ํ์ธํด. ์ต์ 3๊ธ์ ์ด์์ด์ด์ผ ํ๊ณ , ํน์๋ฌธ์๋ ์ ํ์ ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅํด.
Step 2: ์ปค๋ฐ (Commit)
๋๋ฉ์ธ ์ด๋ฆ์ ํด์๊ฐ์ ๋จผ์ ์ ์ถํด. ์ด๊ฑด "ํ๋ก ํธ๋ฌ๋(front-running)" ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํ ๊ฑฐ์ผ. ๋ค๋ฅธ ์ฌ๋์ด ๋ด๊ฐ ๋ฑ๋กํ๋ ค๋ ๋๋ฉ์ธ์ ๊ฐ๋ก์ฑ์ง ๋ชปํ๋๋ก ๋ง์ด์ง! ๐ก๏ธ
Step 3: ๋๊ธฐ (Wait)
์ปค๋ฐ ํ ์ต์ 1๋ถ์ ๊ธฐ๋ค๋ ค์ผ ํด. ์ด ์๊ฐ ๋์ ๋ธ๋ก์ฒด์ธ์ ์ปค๋ฐ์ด ํ์ ๋ผ.
Step 4: ๋ฑ๋ก (Register)
์ค์ ๋ก ๋๋ฉ์ธ์ ๋ฑ๋กํ๊ณ ๋ฑ๋ก ๋น์ฉ์ ์ง๋ถํด. ๋น์ฉ์ ๋๋ฉ์ธ ๊ธธ์ด์ ๋ฐ๋ผ ๋ฌ๋ผ์ ธ:
โข 5๊ธ์ ์ด์: ์ฐ๊ฐ $5
โข 4๊ธ์: ์ฐ๊ฐ $160
โข 3๊ธ์: ์ฐ๊ฐ $640
Step 5: ์ค์ (Configure)
๋ฑ๋ก์ด ์๋ฃ๋๋ฉด Resolver๋ฅผ ์ค์ ํ๊ณ ์ํ๋ ์ฃผ์๋ฅผ ๋งคํํด! ๐
// ETH Registrar Controller ํต์ฌ ํจ์๋ค
interface IETHRegistrarController {
// ๋๋ฉ์ธ ๊ฐ์ฉ์ฑ ํ์ธ
function available(string memory name) external view returns (bool);
// ๋ฑ๋ก ๋น์ฉ ๊ณ์ฐ (์ด ๋จ์ ๊ธฐ๊ฐ)
function rentPrice(string memory name, uint duration) external view returns (uint);
// ์ปค๋ฐ ์ ์ถ (ํ๋ก ํธ๋ฌ๋ ๋ฐฉ์ง)
function commit(bytes32 commitment) external;
// ๋๋ฉ์ธ ๋ฑ๋ก
function register(
string calldata name,
address owner,
uint duration,
bytes32 secret
) external payable;
// ๋ฑ๋ก ๊ธฐ๊ฐ ์ฐ์ฅ
function renew(string calldata name, uint duration) external payable;
}
๐ ENS์ ๋ณด์ ๋ฉ์ปค๋์ฆ
๋ธ๋ก์ฒด์ธ ์์คํ ์์ ๋ณด์์ ์๋ช ๊ณผ๋ ๊ฐ์. ENS๋ ์ฌ๋ฌ ์ธต์ ๋ณด์ ๋ฉ์ปค๋์ฆ์ ๊ฐ์ถ๊ณ ์์ด์ ์ฌ์ฉ์์ ๋๋ฉ์ธ๊ณผ ์์ฐ์ ์์ ํ๊ฒ ๋ณดํธํด. ์ด๋ค ๋ณด์ ์ฅ์น๋ค์ด ์๋์ง ์ดํด๋ณด์! ๐
๐ก๏ธ 1. ์์ ๊ถ ๊ณ์ธต ๊ตฌ์กฐ
ENS๋ ๊ณ์ธต์ ์์ ๊ถ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํด. ์๋ฅผ ๋ค์ด alice.eth์ ์์ ์๋ pay.alice.eth, blog.alice.eth ๊ฐ์ ์๋ธ๋๋ฉ์ธ์ ์์ ๋กญ๊ฒ ๋ง๋ค ์ ์์ด.
์ด ๊ตฌ์กฐ์ ์ฅ์ ์:
โข ์์ ๋๋ฉ์ธ ์์ ์๋ง์ด ํ์ ๋๋ฉ์ธ์ ์์ฑ/๊ด๋ฆฌํ ์ ์์ด
โข ๊ฐ ๋ ๋ฒจ์์ ๋
๋ฆฝ์ ์ธ ๊ถํ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํด
โข ์๋ธ๋๋ฉ์ธ์ ๋ค๋ฅธ ์ฌ๋์๊ฒ ์์ํ ์๋ ์์ด
ํ์ง๋ง ์ฃผ์ํ ์ ๋ ์์ด! ์์ ๋๋ฉ์ธ ์์ ์๋ ์ธ์ ๋ ํ์ ๋๋ฉ์ธ์ ์์ ๊ถ์ ํ์ํ ์ ์๋ค๋ ๊ฑฐ์ผ. ๊ทธ๋์ ์๋ธ๋๋ฉ์ธ์ ์ฌ์ฉํ ๋๋ ์์ ๋๋ฉ์ธ ์์ ์๋ฅผ ์ ๋ขฐํ ์ ์๋์ง ํ์ธํด์ผ ํด. โ ๏ธ
๐ 2. ์ปค๋ฐ-๋ฆฌ๋น ์คํด (Commit-Reveal Scheme)
์์ ์ธ๊ธํ๋ ๋๋ฉ์ธ ๋ฑ๋ก ๊ณผ์ ์ ์ปค๋ฐ ๋จ๊ณ๋ฅผ ๊ธฐ์ตํด? ์ด๊ฑด ๋จ์ํ ์ ์ฐจ๊ฐ ์๋๋ผ ์ ๊ตํ ๋ณด์ ๋ฉ์ปค๋์ฆ์ด์ผ!
๋ฌธ์ ์ํฉ:
๋ธ๋ก์ฒด์ธ์ ํฌ๋ช
ํ๊ธฐ ๋๋ฌธ์, ๋๊ตฐ๊ฐ "alice.eth๋ฅผ ๋ฑ๋กํ๊ฒ ๋ค"๋ ํธ๋์ญ์
์ ๋ณด๋ด๋ฉด ๋ค๋ฅธ ์ฌ๋์ด ๊ทธ๊ฑธ ๋ณด๊ณ ๋ ๋์ ๊ฐ์ค๋น๋ฅผ ์ง๋ถํด์ ๋จผ์ ๋ฑ๋กํ ์ ์์ด. ์ด๊ฑธ ํ๋ก ํธ๋ฌ๋(front-running) ๊ณต๊ฒฉ์ด๋ผ๊ณ ํด. ๐
ํด๊ฒฐ ๋ฐฉ๋ฒ:
1. ์ปค๋ฐ ๋จ๊ณ: ๋๋ฉ์ธ ์ด๋ฆ์ ์ง์ ๋ณด๋ด์ง ์๊ณ , keccak256(name + secret) ๊ฐ์ ํด์๊ฐ๋ง ๋ณด๋ด
2. ๋๊ธฐ ๋จ๊ณ: ์ต์ 1๋ถ ๋๊ธฐ (๋ธ๋ก ํ์ )
3. ๋ฆฌ๋น ๋จ๊ณ: ์ค์ ๋๋ฉ์ธ ์ด๋ฆ๊ณผ ์ํฌ๋ฆฟ์ ๊ณต๊ฐํ๋ฉฐ ๋ฑ๋ก
์ด๋ ๊ฒ ํ๋ฉด ์ปค๋ฐ ์์ ์๋ ์๋ฌด๋ ์ด๋ค ๋๋ฉ์ธ์ ๋ฑ๋กํ๋ ค๋์ง ์ ์ ์์ด! ๐ญ
// ์ปค๋ฐ ์์ฑ ์์
function makeCommitment(
string memory name,
address owner,
bytes32 secret
) public pure returns (bytes32) {
return keccak256(
abi.encodePacked(
name,
owner,
secret
)
);
}
// ์ฌ์ฉ ์์
bytes32 secret = keccak256(abi.encodePacked("my-random-secret-12345"));
bytes32 commitment = makeCommitment("alice", msg.sender, secret);
// 1. ์ปค๋ฐ ์ ์ถ
controller.commit(commitment);
// 2. 1๋ถ ๋๊ธฐ...
// 3. ์ค์ ๋ฑ๋ก
controller.register("alice", msg.sender, duration, secret);
โฐ 3. ๋ง๋ฃ ๋ฐ ์ ์ ๊ธฐ๊ฐ
๋๋ฉ์ธ ๋ง๋ฃ ๋ฉ์ปค๋์ฆ
ENS ๋๋ฉ์ธ์ ์๊ตฌ์ ์ด์ง ์์. ๋ฑ๋ก ๊ธฐ๊ฐ์ด ๋๋๋ฉด ๋ค์๊ณผ ๊ฐ์ ํ๋ก์ธ์ค๋ฅผ ๊ฑฐ์ณ:
1. ๋ง๋ฃ (Expiration):
๋ฑ๋ก ๊ธฐ๊ฐ์ด ๋๋๋ฉด ๋๋ฉ์ธ์ด ๋ง๋ฃ๋ผ. ํ์ง๋ง ๋ฐ๋ก ๋ค๋ฅธ ์ฌ๋์ด ๊ฐ์ ธ๊ฐ ์๋ ์์ด!
2. ์ ์ ๊ธฐ๊ฐ (Grace Period) - 90์ผ:
๋ง๋ฃ ํ 90์ผ ๋์์ ์๋ ์์ ์๋ง ๊ฐฑ์ ํ ์ ์์ด. ์ด ๊ธฐ๊ฐ ๋์ ๋๋ฉ์ธ์ ์ฌ์ ํ ์๋ํ์ง๋ง, ๋ค๋ฅธ ์ฌ๋์ ๋ฑ๋กํ ์ ์์ด. ๐
3. ํ๋ฆฌ๋ฏธ์ ๊ฒฝ๋งค (Premium Auction) - 21์ผ:
์ ์ ๊ธฐ๊ฐ์ด ๋๋๋ฉด 21์ผ ๋์ "ํ๋ฆฌ๋ฏธ์ ๊ฒฝ๋งค" ๊ธฐ๊ฐ์ด ์์๋ผ. ์ด ๊ธฐ๊ฐ ๋์ ๋๋ฉ์ธ์ ๋ฑ๋กํ๋ ค๋ฉด ์ถ๊ฐ ๋น์ฉ์ ๋ด์ผ ํ๋๋ฐ, ์๊ฐ์ด ์ง๋ ์๋ก ๋น์ฉ์ด ๊ธฐํ๊ธ์์ ์ผ๋ก ๊ฐ์ํด. ์ฒ์์ $100,000๋ถํฐ ์์ํด์ 21์ผ ํ์ $0๊น์ง ๋จ์ด์ ธ!
4. ์ผ๋ฐ ๋ฑ๋ก ๊ฐ๋ฅ:
ํ๋ฆฌ๋ฏธ์ ๊ธฐ๊ฐ์ด ๋๋๋ฉด ๋๊ตฌ๋ ์ผ๋ฐ ๊ฐ๊ฒฉ์ผ๋ก ๋ฑ๋กํ ์ ์์ด. ๐ฐ
์ด๋ฐ ๋ฉ์ปค๋์ฆ์ ๋๋ฉ์ธ ์ค์ฟผํ
(squatting)์ ๋ฐฉ์งํ๊ณ , ์๋ ์์ ์์๊ฒ ์ถฉ๋ถํ ๊ฐฑ์ ๊ธฐํ๋ฅผ ์ฃผ๊ธฐ ์ํ ๊ฑฐ์ผ!
๐ป ENS ํตํฉํ๊ธฐ: ๊ฐ๋ฐ์ ๊ฐ์ด๋
์ด์ ์ค์ ๋ก ENS๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ํตํฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์! ์น3 ๊ฐ๋ฐ์๋ผ๋ฉด ๊ผญ ์์์ผ ํ ๋ด์ฉ์ด์ผ. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ๋ธ๋ก์ฒด์ธ ๊ด๋ จ ์ฌ๋ฅ์ ๊ณต์ ํ ๋ ์ด๋ฐ ์ง์์ด ์ ๋ง ์ ์ฉํ๊ฑฐ๋ ! ๐
๐ 1. ENS ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๊ณต์ ENS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฑฐ์ผ. JavaScript/TypeScript ํ๊ฒฝ์ด๋ผ๋ฉด:
// npm ๋๋ yarn์ผ๋ก ์ค์น
npm install @ensdomains/ensjs ethers
// ๋๋
yarn add @ensdomains/ensjs ethers
๐ 2. ๋๋ฉ์ธ โ ์ฃผ์ ์กฐํ (Forward Resolution)
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ์ธ ๋๋ฉ์ธ ์ด๋ฆ์ ์ฃผ์๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ด์ผ:
import { ethers } from 'ethers';
// ์ด๋๋ฆฌ์ ํ๋ก๋ฐ์ด๋ ์ค์
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR-PROJECT-ID');
// ENS ์ด๋ฆ์ ์ฃผ์๋ก ๋ณํ
async function resolveENS(name) {
try {
const address = await provider.resolveName(name);
console.log(`${name} โ ${address}`);
return address;
} catch (error) {
console.error('ENS ์กฐํ ์คํจ:', error);
return null;
}
}
// ์ฌ์ฉ ์์
resolveENS('vitalik.eth');
// ์ถ๋ ฅ: vitalik.eth โ 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
๐ 3. ์ฃผ์ โ ๋๋ฉ์ธ ์กฐํ (Reverse Resolution)
๋ฐ๋๋ก ์ฃผ์๋ฅผ ๋๋ฉ์ธ ์ด๋ฆ์ผ๋ก ๋ณํํ๋ ๊ฒ๋ ๊ฐ๋ฅํด! ์ด๊ฑธ "์ญ๋ฐฉํฅ ์กฐํ(Reverse Resolution)"๋ผ๊ณ ํด. ์ง๊ฐ ์ฑ์์ ์ฃผ์ ๋์ ENS ์ด๋ฆ์ ํ์ํ ๋ ์ ์ฉํ์ง. ๐ค
// ์ฃผ์๋ฅผ ENS ์ด๋ฆ์ผ๋ก ๋ณํ
async function lookupAddress(address) {
try {
const name = await provider.lookupAddress(address);
console.log(`${address} โ ${name}`);
return name;
} catch (error) {
console.error('์ญ๋ฐฉํฅ ์กฐํ ์คํจ:', error);
return null;
}
}
// ์ฌ์ฉ ์์
lookupAddress('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
// ์ถ๋ ฅ: 0xd8dA...6045 โ vitalik.eth
โ ๏ธ ์ค์ํ ์ฃผ์์ฌํญ!
์ญ๋ฐฉํฅ ์กฐํ๊ฐ ์๋ํ๋ ค๋ฉด ๋๋ฉ์ธ ์์ ์๊ฐ ๋ช
์์ ์ผ๋ก "Reverse Record"๋ฅผ ์ค์ ํด์ผ ํด. ๋ชจ๋ ์ฃผ์๊ฐ ENS ์ด๋ฆ์ ๊ฐ์ง ๊ฑด ์๋๋๊น, ํญ์ null ์ฒดํฌ๋ฅผ ํด์ค์ผ ํด!
๋ํ ๋ณด์์ ์ด์ ๋ก, ์ญ๋ฐฉํฅ ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ์ ๋ฐฉํฅ ์กฐํํด์ ์ผ์นํ๋์ง ํ์ธํ๋ ๊ฒ ์ข์. ๋๊ตฐ๊ฐ ์
์์ ์ผ๋ก ๋ค๋ฅธ ์ฌ๋์ ์ฃผ์๋ฅผ ์์ ์ ENS ์ญ๋ฐฉํฅ ๋ ์ฝ๋๋ก ์ค์ ํ ์๋ ์๊ฑฐ๋ ! ๐
๐ 4. ๋ค์ํ ๋ ์ฝ๋ ์กฐํํ๊ธฐ
ENS๋ ์ฃผ์๋ฟ๋ง ์๋๋ผ ๋ค์ํ ์ ๋ณด๋ฅผ ์ ์ฅํ ์ ์๋ค๊ณ ํ์ง? ์ด๋ฐ ์ ๋ณด๋ค์ ์กฐํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์:
import { ENS } from '@ensdomains/ensjs';
// ENS ์ธ์คํด์ค ์์ฑ
const ens = new ENS({ provider, ensAddress: getEnsAddress('1') });
// ํ
์คํธ ๋ ์ฝ๋ ์กฐํ
async function getTextRecord(name, key) {
try {
const record = await ens.getText(name, key);
console.log(`${name}์ ${key}: ${record}`);
return record;
} catch (error) {
console.error('ํ
์คํธ ๋ ์ฝ๋ ์กฐํ ์คํจ:', error);
return null;
}
}
// ์ฌ์ฉ ์์๋ค
await getTextRecord('vitalik.eth', 'email'); // ์ด๋ฉ์ผ ์กฐํ
await getTextRecord('vitalik.eth', 'url'); // ์น์ฌ์ดํธ URL ์กฐํ
await getTextRecord('vitalik.eth', 'avatar'); // ์๋ฐํ ์ด๋ฏธ์ง ์กฐํ
await getTextRecord('vitalik.eth', 'com.twitter'); // ํธ์ํฐ ํธ๋ค ์กฐํ
// ์ปจํ
์ธ ํด์ ์กฐํ (IPFS ์น์ฌ์ดํธ ๋ฑ)
async function getContentHash(name) {
try {
const hash = await ens.getContentHash(name);
console.log(`${name}์ ์ปจํ
์ธ ํด์: ${hash}`);
return hash;
} catch (error) {
console.error('์ปจํ
์ธ ํด์ ์กฐํ ์คํจ:', error);
return null;
}
}
// ๋ค๋ฅธ ๋ธ๋ก์ฒด์ธ ์ฃผ์ ์กฐํ
async function getMulticoinAddress(name, coinType) {
try {
// coinType: 0=BTC, 2=LTC, 3=DOGE, 60=ETH ๋ฑ
const address = await ens.getAddress(name, coinType);
console.log(`${name}์ ์ฝ์ธ ์ฃผ์ (${coinType}): ${address}`);
return address;
} catch (error) {
console.error('๋ฉํฐ์ฝ์ธ ์ฃผ์ ์กฐํ ์คํจ:', error);
return null;
}
}
await getMulticoinAddress('vitalik.eth', 0); // ๋นํธ์ฝ์ธ ์ฃผ์ ์กฐํ
โ๏ธ 5. ENS ๋ ์ฝ๋ ์ค์ ํ๊ธฐ
๋๋ฉ์ธ์ ์์ ํ๊ณ ์๋ค๋ฉด ๋ค์ํ ๋ ์ฝ๋๋ฅผ ์ค์ ํ ์ ์์ด. ๋ฌผ๋ก ํธ๋์ญ์ ์ ๋ณด๋ด์ผ ํ๋๊น ๊ฐ์ค๋น๊ฐ ๋ค์ด๊ฐ! โฝ
// Signer๊ฐ ํ์ํด (๊ฐ์ธํค๋ก ์๋ช
)
const signer = provider.getSigner();
// ์ฃผ์ ์ค์
async function setAddress(name, address) {
try {
const resolver = await provider.getResolver(name);
const tx = await resolver.setAddr(address);
await tx.wait();
console.log(`${name}์ ์ฃผ์๋ฅผ ${address}๋ก ์ค์ ์๋ฃ!`);
} catch (error) {
console.error('์ฃผ์ ์ค์ ์คํจ:', error);
}
}
// ํ
์คํธ ๋ ์ฝ๋ ์ค์
async function setTextRecord(name, key, value) {
try {
const resolver = await provider.getResolver(name);
const tx = await resolver.setText(key, value);
await tx.wait();
console.log(`${name}์ ${key}๋ฅผ ${value}๋ก ์ค์ ์๋ฃ!`);
} catch (error) {
console.error('ํ
์คํธ ๋ ์ฝ๋ ์ค์ ์คํจ:', error);
}
}
// ์ฌ์ฉ ์์
await setAddress('myname.eth', '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
await setTextRecord('myname.eth', 'email', 'hello@example.com');
await setTextRecord('myname.eth', 'com.twitter', '@myhandle');
๐จ 6. ํ๋ก ํธ์๋ ํตํฉ ์์
์ค์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ENS๋ฅผ ์ฌ์ฉํ๋ ์์ ํ ์์๋ฅผ ๋ณด์ฌ์ค๊ฒ. React๋ฅผ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์ ํ์:
import React, { useState, useEffect } from 'react';
import { ethers } from 'ethers';
function ENSLookup() {
const [input, setInput] = useState('');
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const provider = new ethers.providers.Web3Provider(window.ethereum);
const handleLookup = async () => {
setLoading(true);
setError(null);
setResult(null);
try {
// ENS ์ด๋ฆ์ธ์ง ์ฃผ์์ธ์ง ํ๋จ
if (input.endsWith('.eth')) {
// ENS โ ์ฃผ์
const address = await provider.resolveName(input);
if (address) {
setResult({ type: 'address', value: address });
} else {
setError('ํด๋น ENS ์ด๋ฆ์ ์ฐพ์ ์ ์์ต๋๋ค.');
}
} else if (ethers.utils.isAddress(input)) {
// ์ฃผ์ โ ENS
const name = await provider.lookupAddress(input);
if (name) {
setResult({ type: 'name', value: name });
} else {
setError('ํด๋น ์ฃผ์์ ENS ์ด๋ฆ์ด ์ค์ ๋์ง ์์์ต๋๋ค.');
}
} else {
setError('์ฌ๋ฐ๋ฅธ ENS ์ด๋ฆ ๋๋ ์ด๋๋ฆฌ์ ์ฃผ์๋ฅผ ์
๋ ฅํ์ธ์.');
}
} catch (err) {
setError('์กฐํ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: ' + err.message);
} finally {
setLoading(false);
}
};
return (
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
<h2>๐ ENS ์กฐํ ๋๊ตฌ</h2>
<div style={{ marginBottom: '20px' }}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="ENS ์ด๋ฆ ๋๋ ์ด๋๋ฆฌ์ ์ฃผ์ ์
๋ ฅ"
style={{
width: '100%',
padding: '10px',
fontSize: '16px',
borderRadius: '8px',
border: '2px solid #667eea'
}}
/>
</div>
<button
onClick={handleLookup}
disabled={loading || !input}
style={{
padding: '10px 20px',
fontSize: '16px',
backgroundColor: '#667eea',
color: 'white',
border: 'none',
borderRadius: '8px',
cursor: 'pointer'
}}
>
{loading ? '์กฐํ ์ค...' : '์กฐํํ๊ธฐ'}
</button>
{result && (
<div style={{
marginTop: '20px',
padding: '15px',
backgroundColor: '#f0fdf4',
borderRadius: '8px',
border: '2px solid #10b981'
}}>
<strong>๊ฒฐ๊ณผ:</strong><br />
{result.type === 'address'
? `์ฃผ์: ${result.value}`
: `ENS ์ด๋ฆ: ${result.value}`
}
</div>
)}
{error && (
<div style={{
marginTop: '20px',
padding: '15px',
backgroundColor: '#fef2f2',
borderRadius: '8px',
border: '2px solid #ef4444',
color: '#991b1b'
}}>
{error}
</div>
)}
</div>
);
}
export default ENSLookup;
๐ ENS์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค
๊ธฐ๋ณธ์ ์ธ ์ฃผ์ ๋งคํ์ ๋์ด์, ENS๋ ์ ๋ง ๋ค์ํ๊ณ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด. ์ด๋ฐ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์๋ฉด ENS๋ฅผ ํจ์ฌ ๋ ์ฐฝ์์ ์ผ๋ก ํ์ฉํ ์ ์์ด! ๐จ
๐ 1. ๋ถ์ฐ ์น์ฌ์ดํธ ํธ์คํ
ENS์ ๊ฐ์ฅ ํ์ ์ ์ธ ๊ธฐ๋ฅ ์ค ํ๋๋ contenthash ๋ ์ฝ๋๋ฅผ ํตํ ๋ถ์ฐ ์น์ฌ์ดํธ ํธ์คํ
์ด์ผ! IPFS๋ Swarm ๊ฐ์ ๋ถ์ฐ ์ ์ฅ์์ ์น์ฌ์ดํธ๋ฅผ ์ฌ๋ฆฌ๊ณ , ENS ๋๋ฉ์ธ์ผ๋ก ์ ๊ทผํ ์ ์์ด. ๐
์๋ฅผ ๋ค์ด, mysite.eth๋ผ๋ ๋๋ฉ์ธ์ contenthash๋ฅผ IPFS ํด์๋ก ์ค์ ํ๋ฉด, ๋ธ๋ ์ด๋ธ(Brave) ๋ธ๋ผ์ฐ์ ๋ MetaMask ๊ฐ์ Web3 ์ง์ ๋ธ๋ผ์ฐ์ ์์ ๋ฐ๋ก ์ ์ํ ์ ์์ด!
// IPFS์ ์น์ฌ์ดํธ ์
๋ก๋ ํ contenthash ์ค์
const ipfsHash = 'QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco';
// IPFS ํด์๋ฅผ contenthash ํฌ๋งท์ผ๋ก ๋ณํ
const contentHash = require('content-hash');
const encoded = contentHash.fromIpfs(ipfsHash);
// ENS์ ์ค์
const resolver = await provider.getResolver('mysite.eth');
const tx = await resolver.setContenthash(encoded);
await tx.wait();
console.log('๋ถ์ฐ ์น์ฌ์ดํธ ์ค์ ์๋ฃ! mysite.eth๋ก ์ ์ ๊ฐ๋ฅํฉ๋๋ค.');
// ์ด์ ๋ธ๋ผ์ฐ์ ์์ mysite.eth ์
๋ ฅํ๋ฉด IPFS ์น์ฌ์ดํธ๊ฐ ๋ก๋๋ผ!
๐ก ๋ถ์ฐ ์น์ฌ์ดํธ์ ์ฅ์
โข ๊ฒ์ด ์ ํญ์ฑ: ์ค์ ์๋ฒ๊ฐ ์์ด์ ๋๊ตฌ๋ ์น์ฌ์ดํธ๋ฅผ ์ฐจ๋จํ ์ ์์ด
โข ์๊ตฌ์ฑ: IPFS์ ์ฝํ
์ธ ์ฃผ์ ์ง์ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์๊ตฌ ๋ณด์กด๋ผ
โข ์ ๋ ดํ ๋น์ฉ: ์๋ฒ ํธ์คํ
๋น์ฉ์ด ๋ค์ง ์์
โข ๋น ๋ฅธ ์๋: P2P ๋คํธ์ํฌ๋ก ๊ฐ๊น์ด ๋
ธ๋์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ธ๋ก์ฒด์ธ ๊ฐ๋ฐ ์ฌ๋ฅ์ ๊ณต์ ํ ๋, ์ด๋ฐ ๋ถ์ฐ ์น ํธ์คํ
์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒ๋ ์ข์ ์์ด๋์ด์ผ! ๐
๐ฅ 2. ์๋ธ๋๋ฉ์ธ ๊ด๋ฆฌ
ENS๋ ๋ฌด์ ํ ์๋ธ๋๋ฉ์ธ์ ์ง์ํด. ์๋ฅผ ๋ค์ด company.eth๋ฅผ ์์ ํ๊ณ ์๋ค๋ฉด, alice.company.eth, bob.company.eth ๊ฐ์ ์๋ธ๋๋ฉ์ธ์ ์์ ๋กญ๊ฒ ๋ง๋ค ์ ์์ด!
// ์๋ธ๋๋ฉ์ธ ์์ฑ ์์
const { ethers } = require('ethers');
async function createSubdomain(parentName, label, newOwner) {
// ENS Registry ์ปจํธ๋ํธ
const registryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
const registryABI = [
'function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external',
'function owner(bytes32 node) external view returns (address)'
];
const signer = provider.getSigner();
const registry = new ethers.Contract(registryAddress, registryABI, signer);
// ๋ถ๋ชจ ๋๋ฉ์ธ์ node ๊ณ์ฐ
const parentNode = ethers.utils.namehash(parentName);
// ์๋ธ๋๋ฉ์ธ label ํด์
const labelHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(label));
// ์๋ธ๋๋ฉ์ธ ์์ฑ
const tx = await registry.setSubnodeOwner(parentNode, labelHash, newOwner);
await tx.wait();
console.log(`${label}.${parentName} ์๋ธ๋๋ฉ์ธ ์์ฑ ์๋ฃ!`);
}
// ์ฌ์ฉ ์์
await createSubdomain(
'company.eth',
'alice',
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
);
// alice.company.eth๊ฐ ์์ฑ๋๊ณ ์ง์ ๋ ์ฃผ์๊ฐ ์์ ์๊ฐ ๋จ!
๐ฏ ์๋ธ๋๋ฉ์ธ ํ์ฉ ์ฌ๋ก
1. ์กฐ์ง ๊ตฌ์ฑ์ ๊ด๋ฆฌ
ํ์ฌ๋ DAO์์ ๊ฐ ๊ตฌ์ฑ์์๊ฒ ์๋ธ๋๋ฉ์ธ์ ํ ๋นํ ์ ์์ด. alice.company.eth, bob.company.eth ์ด๋ฐ ์์ผ๋ก!
2. ์๋น์ค๋ณ ์ฃผ์ ๋ถ๋ฆฌ
pay.myname.eth (๊ฒฐ์ ์ฉ), nft.myname.eth (NFT ์์ง์ฉ), defi.myname.eth (๋ํ์ด์ฉ) ๋ฑ์ผ๋ก ์ฉ๋๋ณ ๋ถ๋ฆฌ!
3. ์ด๋ฒคํธ๋ ํ๋ก์ ํธ
hackathon2024.company.eth ๊ฐ์ ์์ ๋๋ฉ์ธ์ ๋ง๋ค์ด ํน์ ์ด๋ฒคํธ์ ์ฌ์ฉ!
4. ์ง์ญ๋ณ ์๋น์ค
korea.service.eth, japan.service.eth ๋ฑ ์ง์ญ๋ณ ์๋ธ๋๋ฉ์ธ ์ด์!
๐ 3. ๋ฉํฐ์ฒด์ธ ์ฃผ์ ์ง์
ENS๋ ์ด๋๋ฆฌ์๋ฟ๋ง ์๋๋ผ ๋ค์ํ ๋ธ๋ก์ฒด์ธ์ ์ฃผ์๋ฅผ ์ ์ฅํ ์ ์์ด! ํ๋์ ENS ์ด๋ฆ์ผ๋ก ์ฌ๋ฌ ์ฒด์ธ์ ์ฃผ์๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค๋ ๊ฑฐ์ง. ์ ๋ง ํธ๋ฆฌํ์ง? ๐
| ๋ธ๋ก์ฒด์ธ | Coin Type | ์์ ์ฃผ์ |
|---|---|---|
| Bitcoin | 0 | 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa |
| Litecoin | 2 | LhK2kQwiaAvhjWY799cZvMyYwnQAcxkarr |
| Dogecoin | 3 | DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L |
| Ethereum | 60 | 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb |
| Binance Smart Chain | 60 (ETH์ ๋์ผ) | 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb |
| Solana | 501 | 7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV |
// ๋ฉํฐ์ฒด์ธ ์ฃผ์ ์ค์ ์์
const resolver = await provider.getResolver('myname.eth');
// ๋นํธ์ฝ์ธ ์ฃผ์ ์ค์
await resolver.setAddr(
0, // Bitcoin coin type
'1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
);
// ์๋ผ๋ ์ฃผ์ ์ค์
await resolver.setAddr(
501, // Solana coin type
'7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV'
);
// ์กฐํํ ๋
const btcAddress = await resolver.getAddress(0);
const solAddress = await resolver.getAddress(501);
console.log('BTC ์ฃผ์:', btcAddress);
console.log('SOL ์ฃผ์:', solAddress);
๐ฑ 4. ์์ ํ๋กํ ํตํฉ
ENS์ ํ ์คํธ ๋ ์ฝ๋๋ฅผ ํ์ฉํ๋ฉด ๋ค์ํ ์์ ๋ฏธ๋์ด ํ๋กํ์ ํ๋์ ์ด๋ฆ์ ์ฐ๊ฒฐํ ์ ์์ด. ์ผ์ข ์ "Web3 ๋ช ํจ"์ด ๋๋ ๊ฑฐ์ง! ๐ผ
๐ญ ํ์ค ํ ์คํธ ๋ ์ฝ๋ ํค
ENS ์ปค๋ฎค๋ํฐ์์ ํฉ์ํ ํ์ค ํค๋ค์ด ์์ด:
โข email - ์ด๋ฉ์ผ ์ฃผ์
โข url - ๊ฐ์ธ ์น์ฌ์ดํธ
โข avatar - ํ๋กํ ์ด๋ฏธ์ง (IPFS ํด์ ๋๋ URL)
โข description - ์๊ธฐ์๊ฐ
โข notice - ๊ณต์ง์ฌํญ
โข keywords - ๊ฒ์ ํค์๋
โข com.twitter - ํธ์ํฐ ํธ๋ค
โข com.github - GitHub ์ฌ์ฉ์๋ช
โข com.discord - Discord ์ฌ์ฉ์๋ช
โข org.telegram - ํ
๋ ๊ทธ๋จ ์ฌ์ฉ์๋ช
โข com.reddit - Reddit ์ฌ์ฉ์๋ช
์ด๋ฐ ํ์ค์ ๋ฐ๋ฅด๋ฉด ๋ค์ํ dApp๋ค์ด ์๋์ผ๋ก ํ๋กํ ์ ๋ณด๋ฅผ ์ฝ์ด์ฌ ์ ์์ด!
// ์์ ํ ์์
ํ๋กํ ์ค์ ์์
const resolver = await provider.getResolver('myname.eth');
// ๊ธฐ๋ณธ ์ ๋ณด
await resolver.setText('email', 'hello@example.com');
await resolver.setText('url', 'https://mywebsite.com');
await resolver.setText('avatar', 'ipfs://QmSP4nq9fnN9dAiCj42ug9Wa79rqmQerZXZch82VqpiH7U');
await resolver.setText('description', 'Web3 ๊ฐ๋ฐ์์ด์ ๋ธ๋ก์ฒด์ธ ์ด์ ๊ฐ์
๋๋ค! ๐');
// ์์
๋ฏธ๋์ด
await resolver.setText('com.twitter', '@myhandle');
await resolver.setText('com.github', 'myusername');
await resolver.setText('com.discord', 'myname#1234');
await resolver.setText('org.telegram', '@mytelegram');
// ์ด์ myname.eth ํ๋๋ก ๋ชจ๋ ์์
ํ๋กํ์ ์ ๊ทผ ๊ฐ๋ฅ!
โก ENS์ ์ฑ๋ฅ ์ต์ ํ์ ๋ฒ ์คํธ ํ๋ํฐ์ค
ENS๋ฅผ ์ค์ ํ๋ก๋์ ํ๊ฒฝ์์ ์ฌ์ฉํ ๋๋ ์ฑ๋ฅ๊ณผ ๋น์ฉ์ ๊ณ ๋ คํด์ผ ํด. ๋ช ๊ฐ์ง ์ค์ํ ์ต์ ํ ๊ธฐ๋ฒ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ์์๋ณด์! ๐ฏ
๐ฐ 1. ๊ฐ์ค ๋น์ฉ ์ต์ ํ
ENS ์์
์ ๊ฐ์ค ๋น์ฉ (๋๋ต์ ์ธ ์์น)
โข ๋๋ฉ์ธ ๋ฑ๋ก: ~150,000 gas
โข ์ฃผ์ ์ค์ : ~45,000 gas
โข ํ
์คํธ ๋ ์ฝ๋ ์ค์ : ~50,000 gas
โข ์๋ธ๋๋ฉ์ธ ์์ฑ: ~80,000 gas
โข Resolver ๋ณ๊ฒฝ: ~50,000 gas
๊ฐ์ค ๊ฐ๊ฒฉ์ด 50 gwei์ผ ๋ ์ฃผ์ ์ค์ ํ๋์ ์ฝ $3-5 ์ ๋ ๋ค ์ ์์ด. ์ฌ๋ฌ ๋ ์ฝ๋๋ฅผ ์ค์ ํ ๋๋ ๋น์ฉ์ด ๋น ๋ฅด๊ฒ ๋์ด๋๋ ์ฃผ์ํด์ผ ํด! ๐ธ
๐ง ๊ฐ์ค ์ ์ฝ ํ
1. ๋ฐฐ์น ์ฒ๋ฆฌ (Multicall)
์ฌ๋ฌ ๋ ์ฝ๋๋ฅผ ํ ๋ฒ์ ์ค์ ํ๋ฉด ๊ฐ์ค๋ฅผ ์ ์ฝํ ์ ์์ด. Public Resolver๋ multicall ๊ธฐ๋ฅ์ ์ง์ํด!
2. ๊ฐ์ค ๊ฐ๊ฒฉ์ด ๋ฎ์ ๋ ์คํ
๊ธํ์ง ์์ ์์
์ ๊ฐ์ค ๊ฐ๊ฒฉ์ด ๋ฎ์ ์๊ฐ๋(์ฃผ๋ง, ์๋ฒฝ)์ ์คํํด. Etherscan Gas Tracker๋ฅผ ์ฐธ๊ณ ํ๋ฉด ์ข์!
3. Layer 2 ํ์ฉ
ENS๋ Optimism, Arbitrum ๊ฐ์ L2์์๋ ์ฌ์ฉ ๊ฐ๋ฅํด. ๊ฐ์ค๋น๊ฐ ํจ์ฌ ์ ๋ ดํ์ง!
4. ํ์ํ ๋ ์ฝ๋๋ง ์ค์
๋ชจ๋ ์์
๋ฏธ๋์ด๋ฅผ ๋ค ์ค์ ํ ํ์๋ ์์ด. ์ ๋ง ํ์ํ ๊ฒ๋ง ์ ํ์ ์ผ๋ก!
// Multicall์ ์ฌ์ฉํ ๋ฐฐ์น ์ฒ๋ฆฌ ์์
const resolver = await provider.getResolver('myname.eth');
// ์ฌ๋ฌ ์์
์ ๋ฐฐ์ด๋ก ์ค๋น
const calls = [
resolver.interface.encodeFunctionData('setAddr', [
ethers.utils.namehash('myname.eth'),
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
]),
resolver.interface.encodeFunctionData('setText', [
ethers.utils.namehash('myname.eth'),
'email',
'hello@example.com'
]),
resolver.interface.encodeFunctionData('setText', [
ethers.utils.namehash('myname.eth'),
'com.twitter',
'@myhandle'
])
];
// ํ ๋ฒ์ ํธ๋์ญ์
์ผ๋ก ๋ชจ๋ ์คํ
const tx = await resolver.multicall(calls);
await tx.wait();
console.log('3๊ฐ ๋ ์ฝ๋๋ฅผ ํ ๋ฒ์ ์ค์ ์๋ฃ! ๊ฐ์ค ์ ์ฝ! โก');
๐ 2. ์กฐํ ์ฑ๋ฅ ์ต์ ํ
ENS ์กฐํ๋ ๋ธ๋ก์ฒด์ธ RPC ํธ์ถ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ ๋๋ฆด ์ ์์ด. ํนํ ๋ง์ ์ฌ์ฉ์๊ฐ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฑ๋ฅ์ด ์ค์ํ์ง! ์ฌ๊ธฐ ๋ช ๊ฐ์ง ์ต์ ํ ์ ๋ต์ด ์์ด:
๐ ์บ์ฑ ์ ๋ต
1. ๋ก์ปฌ ์บ์
์กฐํ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจ๋ฆฌ๋ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์บ์ํด. ENS ๋ ์ฝ๋๋ ์์ฃผ ๋ณ๊ฒฝ๋์ง ์์ผ๋๊น ํจ๊ณผ์ ์ด์ผ!
2. TTL ์กด์ค
ENS์ TTL(Time To Live) ๊ฐ์ ํ์ธํ๊ณ ๊ทธ ์๊ฐ๋งํผ ์บ์ํด. ๊ธฐ๋ณธ๊ฐ์ ๋ณดํต 1์๊ฐ์ด์ผ.
3. ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์
์บ์๊ฐ ๋ง๋ฃ๋๊ธฐ ์ ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฏธ๋ฆฌ ๊ฐฑ์ ํด๋๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ์ข์์ ธ!
4. CDN ํ์ฉ
The Graph ๊ฐ์ ์ธ๋ฑ์ฑ ์๋น์ค๋ ENS ์ ์ฉ API๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ๋น ๋ฅธ ์กฐํ๊ฐ ๊ฐ๋ฅํด!
// ๊ฐ๋จํ ์บ์ฑ ๊ตฌํ ์์
class ENSCache {
constructor(provider, ttl = 3600000) { // ๊ธฐ๋ณธ 1์๊ฐ
this.provider = provider;
this.ttl = ttl;
this.cache = new Map();
}
async resolve(name) {
const now = Date.now();
const cached = this.cache.get(name);
// ์บ์๊ฐ ์๊ณ ์ ํจํ๋ฉด ๋ฐํ
if (cached && now - cached.timestamp < this.ttl) {
console.log(`์บ์ ํํธ: ${name}`);
return cached.address;
}
// ์บ์ ๋ฏธ์ค - ๋ธ๋ก์ฒด์ธ์์ ์กฐํ
console.log(`์บ์ ๋ฏธ์ค: ${name} - ๋ธ๋ก์ฒด์ธ ์กฐํ ์ค...`);
const address = await this.provider.resolveName(name);
// ์บ์์ ์ ์ฅ
this.cache.set(name, {
address,
timestamp: now
});
return address;
}
async lookupAddress(address) {
const now = Date.now();
const cached = this.cache.get(address);
if (cached && now - cached.timestamp < this.ttl) {
console.log(`์บ์ ํํธ: ${address}`);
return cached.name;
}
console.log(`์บ์ ๋ฏธ์ค: ${address} - ๋ธ๋ก์ฒด์ธ ์กฐํ ์ค...`);
const name = await this.provider.lookupAddress(address);
this.cache.set(address, {
name,
timestamp: now
});
return name;
}
// ์บ์ ๋ฌดํจํ
invalidate(key) {
this.cache.delete(key);
}
// ์ ์ฒด ์บ์ ํด๋ฆฌ์ด
clear() {
this.cache.clear();
}
}
// ์ฌ์ฉ ์์
const ensCache = new ENSCache(provider);
// ์ฒซ ์กฐํ - ๋ธ๋ก์ฒด์ธ์์ ๊ฐ์ ธ์ด
const addr1 = await ensCache.resolve('vitalik.eth');
// ๋ ๋ฒ์งธ ์กฐํ - ์บ์์์ ์ฆ์ ๋ฐํ!
const addr2 = await ensCache.resolve('vitalik.eth');
๐ก๏ธ 3. ๋ณด์ ๋ฒ ์คํธ ํ๋ํฐ์ค
โ ๏ธ ENS ์ฌ์ฉ ์ ์ฃผ์์ฌํญ
1. ์ ๋ฐฉํฅ-์ญ๋ฐฉํฅ ๊ฒ์ฆ
์ญ๋ฐฉํฅ ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ํญ์ ์ ๋ฐฉํฅ์ผ๋ก ๋ค์ ํ์ธํด! ๋๊ตฐ๊ฐ ์
์์ ์ผ๋ก ๋ค๋ฅธ ์ฌ๋์ ์ฃผ์๋ฅผ ์์ ์ ์ญ๋ฐฉํฅ ๋ ์ฝ๋๋ก ์ค์ ํ ์ ์์ด.
2. ์ ๋์ฝ๋ ํผ๋ ๊ณต๊ฒฉ ์ฃผ์
๋น์ทํ๊ฒ ์๊ธด ์ ๋์ฝ๋ ๋ฌธ์๋ก ํผ์ฑ ๋๋ฉ์ธ์ ๋ง๋ค ์ ์์ด. ์: vitalik.eth vs vัtalik.eth (๋ ๋ฒ์งธ๋ ํค๋ฆด ๋ฌธ์ 'ั')
3. ๋๋ฉ์ธ ๋ง๋ฃ ์๋ฆผ
๋๋ฉ์ธ ๋ง๋ฃ์ผ์ ์ถ์ ํ๊ณ ๋ฏธ๋ฆฌ ๊ฐฑ์ ํด! ๋ง๋ฃ๋๋ฉด ๋ค๋ฅธ ์ฌ๋์ด ๊ฐ์ ธ๊ฐ ์ ์์ด.
4. ํ๋์จ์ด ์ง๊ฐ ์ฌ์ฉ
์ค์ํ ENS ๋๋ฉ์ธ์ ํ๋์จ์ด ์ง๊ฐ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ ์์ ํด!
5. ์๋ธ๋๋ฉ์ธ ๊ถํ ๊ด๋ฆฌ
์๋ธ๋๋ฉ์ธ์ ๋ค๋ฅธ ์ฌ๋์๊ฒ ์์ํ ๋๋ ์ ์คํ๊ฒ! ์์ ๋๋ฉ์ธ ์์ ์๋ ์ธ์ ๋ ํ์ํ ์ ์๋ค๋ ๊ฑธ ๋ช
ํํ ํด์ผ ํด.
// ์ ๋ฐฉํฅ-์ญ๋ฐฉํฅ ๊ฒ์ฆ ์์
async function verifyENSOwnership(name, address) {
try {
// 1. ์ ๋ฐฉํฅ: ์ด๋ฆ โ ์ฃผ์
const resolvedAddress = await provider.resolveName(name);
// 2. ์ญ๋ฐฉํฅ: ์ฃผ์ โ ์ด๋ฆ
const reverseName = await provider.lookupAddress(address);
// 3. ๊ฒ์ฆ
const isValid =
resolvedAddress?.toLowerCase() === address.toLowerCase() &&
reverseName?.toLowerCase() === name.toLowerCase();
if (isValid) {
console.log(`โ
${name}์(๋) ${address}์ ์ ํจํ ENS์
๋๋ค.`);
return true;
} else {
console.log(`โ ENS ๊ฒ์ฆ ์คํจ!`);
console.log(` ์ ๋ฐฉํฅ: ${name} โ ${resolvedAddress}`);
console.log(` ์ญ๋ฐฉํฅ: ${address} โ ${reverseName}`);
return false;
}
} catch (error) {
console.error('๊ฒ์ฆ ์ค ์ค๋ฅ:', error);
return false;
}
}
// ์ฌ์ฉ ์์
await verifyENSOwnership(
'vitalik.eth',
'0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
);
๐ฑ 4. ์ฌ์ฉ์ ๊ฒฝํ ์ต์ ํ
ENS๋ฅผ ์ฌ์ฉ์ ์นํ์ ์ผ๋ก ๋ง๋๋ ํ๋ค:
1. ์
๋ ฅ ์ ๊ทํ
์ฌ์ฉ์๊ฐ ๋๋ฌธ์๋ก ์
๋ ฅํด๋ ์๋์ผ๋ก ์๋ฌธ์๋ก ๋ณํํด์ค. ENS๋ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์!
2. ์ค์๊ฐ ๊ฒ์ฆ
์ฌ์ฉ์๊ฐ ENS ์ด๋ฆ์ ์
๋ ฅํ๋ ๋์ ์ค์๊ฐ์ผ๋ก ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ ์ฃผ์๋ฅผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ก ๋ณด์ฌ์ค.
3. ์๋ฐํ ํ์
ENS์ avatar ๋ ์ฝ๋๋ฅผ ์ฝ์ด์ ํ๋กํ ์ด๋ฏธ์ง๋ฅผ ํ์ํ๋ฉด ๋ ์น๊ทผํด์ ธ!
4. ํด๋ฐฑ ์ฒ๋ฆฌ
ENS ์กฐํ๊ฐ ์คํจํ๋ฉด ์ผ๋ฐ ์ฃผ์ ์
๋ ฅ๋ ๋ฐ์ ์ ์๋๋ก ํด์ค.
5. ๋ก๋ฉ ์ํ ํ์
๋ธ๋ก์ฒด์ธ ์กฐํ๋ ์๊ฐ์ด ๊ฑธ๋ฆฌ๋๊น ๋ก๋ฉ ์ธ๋์ผ์ดํฐ๋ฅผ ๊ผญ ๋ณด์ฌ์ค!
// ์ฌ์ฉ์ ์นํ์ ์ธ ENS ์
๋ ฅ ์ปดํฌ๋ํธ ์์
function ENSInput({ onAddressResolved }) {
const [input, setInput] = useState('');
const [resolving, setResolving] = useState(false);
const [resolved, setResolved] = useState(null);
const [error, setError] = useState(null);
// ๋๋ฐ์ด์ค๋ฅผ ์ฌ์ฉํ ์ค์๊ฐ ์กฐํ
useEffect(() => {
const timer = setTimeout(async () => {
if (!input) {
setResolved(null);
setError(null);
return;
}
// ์ ๊ทํ (์๋ฌธ์, ๊ณต๋ฐฑ ์ ๊ฑฐ)
const normalized = input.toLowerCase().trim();
// ENS ์ด๋ฆ์ธ์ง ํ์ธ
if (normalized.endsWith('.eth')) {
setResolving(true);
setError(null);
try {
const address = await provider.resolveName(normalized);
if (address) {
setResolved({
name: normalized,
address: address,
avatar: await getAvatar(normalized)
});
onAddressResolved(address);
} else {
setError('ENS ์ด๋ฆ์ ์ฐพ์ ์ ์์ต๋๋ค.');
setResolved(null);
}
} catch (err) {
setError('์กฐํ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.');
setResolved(null);
} finally {
setResolving(false);
}
} else if (ethers.utils.isAddress(normalized)) {
// ์ง์ ์ฃผ์ ์
๋ ฅ
setResolved({ address: normalized });
onAddressResolved(normalized);
} else {
setError('์ฌ๋ฐ๋ฅธ ENS ์ด๋ฆ ๋๋ ์ฃผ์๋ฅผ ์
๋ ฅํ์ธ์.');
setResolved(null);
}
}, 500); // 500ms ๋๋ฐ์ด์ค
return () => clearTimeout(timer);
}, [input]);
return (
<div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="ENS ์ด๋ฆ ๋๋ ์ฃผ์ ์
๋ ฅ (์: vitalik.eth)"
/>
{resolving && <div>๐ ์กฐํ ์ค...</div>}
{resolved && (
<div style={{ display: 'flex', alignItems: 'center', marginTop: '10px' }}>
{resolved.avatar && (
<img src={resolved.avatar} alt="avatar" style={{ width: '40px', height: '40px', borderRadius: '50%', marginRight: '10px' }} />
)}
<div>
{resolved.name && <div><strong>{resolved.name}</strong></div>}
<div style={{ fontSize: '0.9em', color: '#666' }}>
{resolved.address.slice(0, 6)}...{resolved.address.slice(-4)}
</div>
</div>
<div style={{ marginLeft: 'auto', color: 'green' }}>โ
</div>
</div>
)}
{error && <div style={{ color: 'red', marginTop: '5px' }}>{error}</div>}
</div>
);
}
// ์๋ฐํ ์กฐํ ํฌํผ ํจ์
async function getAvatar(name) {
try {
const resolver = await provider.getResolver(name);
const avatar = await resolver.getText('avatar');
// IPFS ํด์๋ฅผ HTTP URL๋ก ๋ณํ
if (avatar.startsWith('ipfs://')) {
return `https://ipfs.io/ipfs/${avatar.slice(7)}`;
}
return avatar;
} catch {
return null;
}
}
๐ ENS์ ๋ฏธ๋์ ๋ฐ์ ๋ฐฉํฅ
ENS๋ ๊ณ์ํด์ ์งํํ๊ณ ์์ด. ์์ผ๋ก ์ด๋ค ๋ฐ์ ์ด ์์๋๋์ง, ๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ์๋ก์ ์ด๋ป๊ฒ ์ค๋นํ ์ ์๋์ง ์์๋ณด์! ๐
๐ฎ 1. Layer 2 ํตํฉ
ENS๋ ์ด๋ฏธ ์ฌ๋ฌ Layer 2 ์๋ฃจ์
๊ณผ ํตํฉ์ ์งํํ๊ณ ์์ด. Optimism, Arbitrum, zkSync ๋ฑ์์ ENS๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ฉด์ ๊ฐ์ค๋น ๋ถ๋ด์ด ํฌ๊ฒ ์ค์ด๋ค ๊ฑฐ์ผ!
ํนํ CCIP-Read (EIP-3668) ํ์ค์ ํตํด L2์ ์ ์ฅ๋ ENS ๋ฐ์ดํฐ๋ฅผ L1์์ ์กฐํํ ์ ์๊ฒ ๋์์ด. ์ด๊ฑด ์ ๋ง ํ์ ์ ์ธ ๋ฐ์ ์ด์ผ! ๐ฏ
๐ 2. ํฌ๋ก์ค์ฒด์ธ ํ์ฅ
ENS๋ ์ด๋๋ฆฌ์์ ๋์ด ๋ค๋ฅธ ๋ธ๋ก์ฒด์ธ ์ํ๊ณ๋ก๋ ํ์ฅ๋๊ณ ์์ด:
โข Polygon: ์ด๋ฏธ ENS๋ฅผ ์ง์ํ๋ฉฐ ์ ๋ ดํ ๋น์ฉ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
โข BNB Chain: SpaceID ๊ฐ์ ENS ํธํ ์๋น์ค ๋ฑ์ฅ
โข Avalanche: AVVY Domains ๋ฑ ENS ์ ์ฌ ์๋น์ค
โข Cosmos: Starname ๋ฑ IBC ๊ธฐ๋ฐ ๋ค์ด๋ฐ ์๋น์ค
๋ฏธ๋์๋ ํ๋์ ENS ์ด๋ฆ์ผ๋ก ๋ชจ๋ ์ฒด์ธ์ ์ฃผ์๋ฅผ ๊ด๋ฆฌํ ์ ์์ ๊ฑฐ์ผ! ๐
๐ค 3. AI์ ์๋ํ
ENS์ AI์ ๊ฒฐํฉ๋ ํฅ๋ฏธ๋ก์ด ๋ถ์ผ์ผ:
โข ์ค๋งํธ ์ถ์ฒ: ์ฌ์ฉ์ ํ๋ ํจํด์ ๋ถ์ํด ์ต์ ์ ENS ์ด๋ฆ ์ถ์ฒ
โข ์๋ ๊ฐฑ์ : AI๊ฐ ๋๋ฉ์ธ ๋ง๋ฃ๋ฅผ ์์ธกํ๊ณ ์๋์ผ๋ก ๊ฐฑ์
โข ํผ์ฑ ํ์ง: ๋จธ์ ๋ฌ๋์ผ๋ก ์ ์ฌ ๋๋ฉ์ธ ํผ์ฑ ๊ณต๊ฒฉ ํ์ง
โข ๊ฐ๊ฒฉ ์์ธก: ENS ๋๋ฉ์ธ์ ๋ฏธ๋ ๊ฐ์น ์์ธก ๋ชจ๋ธ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ด๋ฐ AI ๊ธฐ๋ฐ ENS ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒ๋ ์ข์ ๋น์ฆ๋์ค ์์ด๋์ด๊ฐ ๋ ์ ์์ด! ๐ก
๐๏ธ 4. DAO ๊ฑฐ๋ฒ๋์ค ๊ฐํ
ENS๋ ์์ ํ DAO(ํ์ค์ํ ์์จ ์กฐ์ง)๋ก ์ด์๋๊ณ ์์ด. ENS ํ ํฐ ํ๋๋ค์ด ํ๋กํ ์ฝ์ ๋ฏธ๋๋ฅผ ๊ฒฐ์ ํ์ง!
์์ผ๋ก ์์๋๋ ๊ฑฐ๋ฒ๋์ค ์ฃผ์ ๋ค:
โข ์๋ก์ด TLD(.eth ์ธ) ์ถ๊ฐ ์ฌ๋ถ
โข ๊ฐ๊ฒฉ ์ ์ฑ
์กฐ์
โข Layer 2 ํตํฉ ์ฐ์ ์์
โข ํ๋กํ ์ฝ ์
๊ทธ๋ ์ด๋
โข ์ฌ๋ฌด ๊ด๋ฆฌ ๋ฐ ํฌ์
๊ฐ๋ฐ์๋ก์ ENS DAO์ ์ฐธ์ฌํ๋ฉด ํ๋กํ ์ฝ์ ๋ฏธ๋๋ฅผ ํจ๊ป ๋ง๋ค์ด๊ฐ ์ ์์ด! ๐ณ๏ธ
๐จ 5. NFT์์ ์ตํฉ
ENS ๋๋ฉ์ธ ์์ฒด๊ฐ ERC-721 NFT์ผ! ์ด๋ฅผ ํ์ฉํ ๋ค์ํ ํ์ ์ด ์ผ์ด๋๊ณ ์์ด:
โข ๋๋ฉ์ธ ๊ฑฐ๋: OpenSea ๊ฐ์ NFT ๋ง์ผํ๋ ์ด์ค์์ ENS ๋๋ฉ์ธ ๊ฑฐ๋
โข ๋ด๋ณด ๋์ถ: ENS ๋๋ฉ์ธ์ ๋ด๋ณด๋ก ์ํธํํ ๋์ถ
โข ํ๋กํ NFT: ENS์ ์ฐ๊ฒฐ๋ PFP(Profile Picture) NFT
โข ๋ฉค๋ฒ์ญ: ํน์ ENS ๋๋ฉ์ธ ์์ ์๋ง ์ ๊ทผ ๊ฐ๋ฅํ ์ปค๋ฎค๋ํฐ
์ด๋ฐ ์ตํฉ์ ENS์ ๊ฐ์น๋ฅผ ๋์ฑ ๋์ด๊ณ ์์ด! ๐ญ
๐ ๋ง๋ฌด๋ฆฌ: ENS ๋ง์คํฐ๊ฐ ๋๋ ๊ธธ
์! ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐ ENS์ ๊ธฐ์ ๊ตฌ์กฐ๋ถํฐ ์ค์ ๊ตฌํ, ์ต์ ํ, ๋ฏธ๋ ์ ๋ง๊น์ง ์ ๋ง ๋ง์ ๋ด์ฉ์ ๋ค๋ค์ด. ์ด์ ENS์ ๋ํด ๊ฝค ๊น์ด ์๋ ์ดํด๋ฅผ ๊ฐ๊ฒ ๋์์ ๊ฑฐ์ผ!
ENS๋ ๋จ์ํ "๋๋ฉ์ธ ์ด๋ฆ ์๋น์ค"๋ฅผ ๋์ด์, Web3 ์ํ๊ณ์ ํต์ฌ ์ธํ๋ผ๋ก ์๋ฆฌ์ก๊ณ ์์ด. ๋ธ๋ก์ฒด์ธ ๊ฐ๋ฐ์๋ผ๋ฉด ๋ฐ๋์ ์์์ผ ํ ๊ธฐ์ ์ด์ง. ๐
๐ ๋ ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด?
๊ณต์ ๋ฌธ์์ ๋ฆฌ์์ค:
โข ENS ๊ณต์ ๋ฌธ์: docs.ens.domains
โข ENS GitHub: github.com/ensdomains
โข ENS ํฌ๋ผ: discuss.ens.domains
โข ENS DAO: ensdao.org
์ค์ต ํ๋ก์ ํธ ์์ด๋์ด:
1. ENS ์กฐํ ์น ์ฑ ๋ง๋ค๊ธฐ
2. ENS ๊ธฐ๋ฐ ์์
ํ๋กํ ์๋น์ค
3. ENS ๋๋ฉ์ธ ๋ง๋ฃ ์๋ฆผ ๋ด
4. ENS ๊ฐ๊ฒฉ ์ถ์ ๋์๋ณด๋
5. ์๋ธ๋๋ฉ์ธ ์๋ ๋ฐ๊ธ ์์คํ
์ฌ๋ฅ๋ท์์ ์ด๋ฐ ํ๋ก์ ํธ๋ฅผ ํฌํธํด๋ฆฌ์ค๋ก ๊ณต์ ํ๊ฑฐ๋, ENS ๊ด๋ จ ๊ฐ๋ฐ ์ฌ๋ฅ์ ์ ๊ณตํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐ผ
๐ก ํต์ฌ ์์ฝ
1. ENS์ 3๋ ์ปดํฌ๋ํธ:
โข Registry: ๋๋ฉ์ธ ์์ ๊ถ ๊ด๋ฆฌ
โข Resolver: ์ด๋ฆ-์ฃผ์ ๋งคํ
โข Registrar: ๋๋ฉ์ธ ๋ฑ๋ก ๊ด๋ฆฌ
2. ์ฃผ์ ๊ธฐ๋ฅ:
โข ์ฃผ์ ๋งคํ (ETH ๋ฐ ๋ค๋ฅธ ์ฒด์ธ)
โข ํ
์คํธ ๋ ์ฝ๋ (์์
ํ๋กํ)
โข ์ปจํ
์ธ ํด์ (๋ถ์ฐ ์น์ฌ์ดํธ)
โข ์๋ธ๋๋ฉ์ธ ์์ฑ
3. ๊ฐ๋ฐ ์ ์ฃผ์์ฌํญ:
โข ๊ฐ์ค ๋น์ฉ ์ต์ ํ
โข ์บ์ฑ์ผ๋ก ์ฑ๋ฅ ํฅ์
โข ๋ณด์ ๊ฒ์ฆ ์ฒ ์ ํ
โข ์ฌ์ฉ์ ๊ฒฝํ ๊ณ ๋ ค
4. ๋ฏธ๋ ์ ๋ง:
โข Layer 2 ํตํฉ
โข ํฌ๋ก์ค์ฒด์ธ ํ์ฅ
โข AI ์๋ํ
โข NFT ์ตํฉ
ENS๋ Web3์ ๋ฏธ๋๋ฅผ ๋ง๋ค์ด๊ฐ๋ ํต์ฌ ๊ธฐ์ ์ด์ผ. ๋ณต์กํ ๋ธ๋ก์ฒด์ธ ์ฃผ์๋ฅผ ์ฌ๋์ด ์ฝ์ ์ ์๋ ์ด๋ฆ์ผ๋ก ๋ฐ๊ฟ์ฃผ๋ ๊ฒ์ ๋จ์ํด ๋ณด์ด์ง๋ง, ๊ทธ ์์๋ ์ ๊ตํ ๊ธฐ์ ๊ตฌ์กฐ์ ๋ณด์ ๋ฉ์ปค๋์ฆ์ด ์จ์ด์์ด. ๐
์ด ๊ธ์ ํตํด ENS์ ๊ธฐ์ ์ ๊น์ด๋ฅผ ์ดํดํ๊ณ , ์ค์ ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ํตํฉํ ์ ์๋ ๋ฅ๋ ฅ์ ๊ฐ์ถ๊ฒ ๋์๊ธฐ๋ฅผ ๋ฐ๋ผ! ๋ธ๋ก์ฒด์ธ ๊ฐ๋ฐ์ ์ธ๊ณ๋ ์ ๋ง ๋๊ณ ๊น์ด. ๊ณ์ ๊ณต๋ถํ๊ณ ์คํํ๋ฉด์ ์ฑ์ฅํด๋๊ฐ์! ๐
ํน์ ENS ๊ด๋ จํด์ ๋ ๊ถ๊ธํ ์ ์ด ์๊ฑฐ๋, ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๋์์ด ํ์ํ๋ค๋ฉด ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ ๋ฌธ๊ฐ๋ค์ ๋์์ ๋ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ํจ๊ป ๋ฐฐ์ฐ๊ณ ์ฑ์ฅํ๋ Web3 ์ปค๋ฎค๋ํฐ์ ์ผ์์ด ๋์ด๋ณด์! ๐ค
ํ์ด์ ๋น์ด! ๊ทธ๋ฆฌ๊ณ ์ธ์ ๊ฐ ๋น์ ์ ๋ฉ์ง ENS ๋๋ฉ์ธ์ผ๋ก ๋ง๋๊ธธ ๋ฐ๋๊ฒ! ๐โจ
๐ ์ถํํฉ๋๋ค!
ENS์ ๊ธฐ์ ๊ตฌ์กฐ๋ฅผ ์๋ฒฝํ๊ฒ ๋ง์คํฐํ์ด์!
์ด์ ๋น์ ๋ Web3 ๋ค์ด๋ฐ ์ ๋ฌธ๊ฐ์
๋๋ค! ๐
๊ณ์ํด์ ๋ฐฐ์ฐ๊ณ , ์คํํ๊ณ , ๊ณต์ ํ์ธ์.
๋ธ๋ก์ฒด์ธ์ ๋ฏธ๋๋ ์ฐ๋ฆฌ๊ฐ ํจ๊ป ๋ง๋ค์ด๊ฐ๋๋ค! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.