์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŒ ENS(์ด๋”๋ฆฌ์›€ ๋„ค์ž„์„œ๋น„์Šค)์˜ ๊ธฐ์ˆ  ๊ตฌ์กฐ: ๋ธ”๋ก์ฒด์ธ ์ฃผ์†Œ๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ด๋ฆ„์œผ๋กœ!

๐ŸŒ ENS(์ด๋”๋ฆฌ์›€ ๋„ค์ž„์„œ๋น„์Šค)์˜ ๊ธฐ์ˆ  ๊ตฌ์กฐ: ๋ธ”๋ก์ฒด์ธ ์ฃผ์†Œ๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ด๋ฆ„์œผ๋กœ!

๋ณต์žกํ•œ ์ด๋”๋ฆฌ์›€ ์ฃผ์†Œ๋ฅผ ๊ฐ„๋‹จํ•œ ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ENS์˜ ๊ธฐ์ˆ ์  ๋น„๋ฐ€์„ ํŒŒํ—ค์ณ๋ณด์ž ๐Ÿ”

์•ˆ๋…•! ๐Ÿ‘‹ ํ˜น์‹œ ์ด๋”๋ฆฌ์›€ ์ง€๊ฐ‘ ์ฃผ์†Œ ๋ณธ ์  ์žˆ์–ด? 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb ์ด๋Ÿฐ ์‹์œผ๋กœ ์ƒ๊ธด 42์ž๋ฆฌ 16์ง„์ˆ˜ ๋ฌธ์ž์—ด ๋ง์ด์•ผ. ์†”์งํžˆ ์ด๊ฑธ ์™ธ์šฐ๊ฑฐ๋‚˜ ์ •ํ™•ํ•˜๊ฒŒ ์ž…๋ ฅํ•˜๋Š” ๊ฑด ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์•„? ๐Ÿ˜…

๋ฐ”๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํƒ„์ƒํ•œ ๊ฒŒ ENS(Ethereum Name Service)์•ผ! ๋งˆ์น˜ ์ธํ„ฐ๋„ท์—์„œ 192.168.0.1 ๊ฐ™์€ IP ์ฃผ์†Œ ๋Œ€์‹  google.com์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ๋ณต์žกํ•œ ๋ธ”๋ก์ฒด์ธ ์ฃผ์†Œ๋ฅผ alice.eth ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ฑฐ์ง€. ๐ŸŽฏ

์˜ค๋Š˜์€ ์ด ENS๊ฐ€ ์–ด๋–ค ๊ธฐ์ˆ  ๊ตฌ์กฐ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€, ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€์ ์—์„œ ๊นŠ์ด ์žˆ๊ฒŒ ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ๋ธ”๋ก์ฒด์ธ ๊ฐœ๋ฐœ์— ๊ด€์‹ฌ ์žˆ๋‹ค๋ฉด, ๋˜๋Š” Web3 ์„ธ๊ณ„์— ๋ฐœ์„ ๋“ค์ด๊ณ  ์‹ถ๋‹ค๋ฉด ๊ผญ ์•Œ์•„์•ผ ํ•  ๋‚ด์šฉ์ด๋‹ˆ๊นŒ ์ง‘์ค‘ํ•ด์ค˜! ๐Ÿ’ช

๋ณต์žกํ•œ ์ฃผ์†Œ 0x742d35Cc6634C... ๊ฐ„๋‹จํ•œ ์ด๋ฆ„ alice.eth ENS Registry ๋„๋ฉ”์ธ ์†Œ์œ ๊ถŒ Resolver ์ฃผ์†Œ ๋งคํ•‘ Registrar ๋„๋ฉ”์ธ ๋“ฑ๋ก ENS ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์กฐ

๐Ÿ—๏ธ 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์˜ ๋ฏธ๋ž˜ ๋กœ๋“œ๋งต Layer 2 ํ†ตํ•ฉ ํฌ๋กœ์Šค ์ฒด์ธ AI ์ž๋™ํ™” DAO ๊ฑฐ๋ฒ„๋„Œ์Šค NFT ์œตํ•ฉ ์ง€์†์ ์ธ ํ˜์‹ ์œผ๋กœ Web3์˜ ํ•ต์‹ฌ ์ธํ”„๋ผ๋กœ ์„ฑ์žฅ ์ค‘!

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌ: 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
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.