์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ”ง ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์—…๊ทธ๋ ˆ์ด๋“œ ๋ฐฉ๋ฒ•: Proxy ํŒจํ„ด์œผ๋กœ ๋ธ”๋ก์ฒด์ธ์˜ ๋ถˆ๋ณ€์„ฑ ๊ทน๋ณตํ•˜๊ธฐ

๐Ÿ”ง ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์—…๊ทธ๋ ˆ์ด๋“œ ๋ฐฉ๋ฒ•: Proxy ํŒจํ„ด์œผ๋กœ ๋ธ”๋ก์ฒด์ธ์˜ ๋ถˆ๋ณ€์„ฑ ๊ทน๋ณตํ•˜๊ธฐ

๋ธ”๋ก์ฒด์ธ ์œ„์—์„œ ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค๊ณ ? ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค! ๐Ÿš€

Proxy Contract delegatecall ๐Ÿ”„ Logic V1 Old Version Logic V2 New Version Storage Data Preserved ๐Ÿ’พ Upgrade Path

๐ŸŽฏ ์™œ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด์•ผ ํ• ๊นŒ?

๋ธ”๋ก์ฒด์ธ์„ ์ฒ˜์Œ ์ ‘ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด ๊ฐ€์žฅ ๋†€๋ผ์›Œํ•˜๋Š” ํŠน์ง• ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ๋ถˆ๋ณ€์„ฑ(Immutability)์ด์—์š”. ํ•œ๋ฒˆ ๋ฐฐํฌ๋œ ์ฝ”๋“œ๋Š” ์˜์›ํžˆ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฑฐ์ฃ . ์ด๊ฒŒ ๋ธ”๋ก์ฒด์ธ์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์ด๋ฉด์„œ ๋™์‹œ์— ๊ฐ€์žฅ ํฐ ๋‹จ์ ์ด๊ธฐ๋„ ํ•ด์š”. ๐Ÿ˜…

์ƒ๊ฐํ•ด๋ณด์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์ด ์—ด์‹ฌํžˆ ๊ฐœ๋ฐœํ•œ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ด๋”๋ฆฌ์›€ ๋ฉ”์ธ๋„ท์— ๋ฐฐํฌํ–ˆ๋Š”๋ฐ, ๊ฐ‘์ž๊ธฐ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๋‹ค๋ฉด? ์•„๋‹ˆ๋ฉด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ์ผ๋ฐ˜์ ์ธ ์›น ์„œ๋น„์Šค๋ผ๋ฉด ๊ทธ๋ƒฅ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์žฌ๋ฐฐํฌํ•˜๋ฉด ๋˜์ง€๋งŒ, ๋ธ”๋ก์ฒด์ธ์—์„œ๋Š” ๊ทธ๊ฒŒ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ 2016๋…„ The DAO ํ•ดํ‚น ์‚ฌ๊ฑด์—์„œ๋Š” ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ์˜ ์ทจ์•ฝ์ ์œผ๋กœ ์•ฝ 5์ฒœ๋งŒ ๋‹ฌ๋Ÿฌ ์ƒ๋‹น์˜ ์ด๋”๋ฆฌ์›€์ด ํƒˆ์ทจ๋‹นํ–ˆ์–ด์š”. ๋งŒ์•ฝ ๊ทธ๋•Œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค๋ฉด ์–ด๋• ์„๊นŒ์š”? ๐Ÿค”

๐Ÿ’ก ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ๋“ค: โ€ข ๋ณด์•ˆ ์ทจ์•ฝ์  ๋ฐœ๊ฒฌ ๋ฐ ํŒจ์น˜ ํ•„์š”
โ€ข ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ณ€๊ฒฝ
โ€ข ๊ฐ€์Šค ์ตœ์ ํ™”๋ฅผ ํ†ตํ•œ ๋น„์šฉ ์ ˆ๊ฐ
โ€ข ์™ธ๋ถ€ ํ”„๋กœํ† ์ฝœ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ํ˜ธํ™˜์„ฑ ์—…๋ฐ์ดํŠธ
โ€ข ๊ทœ์ œ ์š”๊ตฌ์‚ฌํ•ญ ๋ณ€๊ฒฝ ๋Œ€์‘

๐Ÿ” Proxy ํŒจํ„ด์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

Proxy ํŒจํ„ด์€ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ์˜ ๋ถˆ๋ณ€์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•์ด์—์š”. ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด, ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋‘ ๊ฐœ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฑฐ์ฃ .

ํ•˜๋‚˜๋Š” Proxy Contract(ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ)๋กœ, ์‚ฌ์šฉ์ž์™€ ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์—ญํ• ์„ ํ•ด์š”. ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” Implementation Contract(๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ๋˜๋Š” Logic Contract)๋กœ, ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๊ณ  ์žˆ์ฃ .

ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋Š” ํ•œ๋ฒˆ ๋ฐฐํฌ๋˜๋ฉด ์ฃผ์†Œ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์•„์š”. ์‚ฌ์šฉ์ž๋“ค์€ ๊ณ„์† ๊ฐ™์€ ์ฃผ์†Œ๋กœ ์ ‘๊ทผํ•˜์ง€๋งŒ, ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ์˜ˆ์š”. ๋งˆ์น˜ ๊ฑด๋ฌผ์˜ ์™ธ๊ด€์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ๋‚ด๋ถ€ ์ธํ…Œ๋ฆฌ์–ด๋งŒ ๋ฐ”๊พธ๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์ฃ ! ๐Ÿข

๐Ÿ“Š Proxy ํŒจํ„ด์˜ ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜

๐Ÿ”‘ delegatecall์˜ ๋งˆ๋ฒ• Proxy ํŒจํ„ด์˜ ํ•ต์‹ฌ์€ Solidity์˜ delegatecall ํ•จ์ˆ˜์—์š”. ์ด ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ์ปจํŠธ๋ž™ํŠธ์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋˜, ํ˜„์žฌ ์ปจํŠธ๋ž™ํŠธ์˜ ์ปจํ…์ŠคํŠธ(storage, msg.sender ๋“ฑ)๋ฅผ ์œ ์ง€ํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”.

์ผ๋ฐ˜์ ์ธ call์€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ง‘์— ๊ฐ€์„œ ๊ทธ ์‚ฌ๋žŒ์˜ ๋ฌผ๊ฑด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด, delegatecall์€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๋„๊ตฌ๋ฅผ ๋นŒ๋ ค์™€์„œ ๋‚ด ์ง‘์—์„œ ๋‚ด ๋ฌผ๊ฑด์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„์š”! ๐Ÿ› ๏ธ

์˜ˆ๋ฅผ ๋“ค์–ด ์„ค๋ช…ํ•ด๋ณผ๊ฒŒ์š”:

1. ์‚ฌ์šฉ์ž๊ฐ€ Proxy Contract์˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
2. Proxy Contract๋Š” ํ•ด๋‹น ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ Implementation Contract๋กœ delegatecallํ•ฉ๋‹ˆ๋‹ค.
3. Implementation Contract์˜ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜์ง€๋งŒ, ๋ชจ๋“  ์ƒํƒœ ๋ณ€๊ฒฝ์€ Proxy Contract์˜ storage์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
4. ๊ฒฐ๊ณผ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์—์„œ ์‚ฌ์šฉ์ž๋Š” Proxy Contract์˜ ์ฃผ์†Œ๋งŒ ์•Œ๋ฉด ๋˜๊ณ , Implementation Contract๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ์ „ํ˜€ ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†์–ด์š”. ๐Ÿ˜Š

๐ŸŽจ ์ฃผ์š” Proxy ํŒจํ„ด ์ข…๋ฅ˜

Proxy ํŒจํ„ด์—๋„ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ณ€ํ˜•์ด ์žˆ์–ด์š”. ๊ฐ๊ฐ์˜ ํŠน์ง•๊ณผ ์žฅ๋‹จ์ ์„ ์•Œ์•„๋ณด๋ฉด์„œ, ์–ด๋–ค ์ƒํ™ฉ์— ์–ด๋–ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณผ๊ฒŒ์š”! ๐ŸŽฏ

1๏ธโƒฃ Transparent Proxy Pattern (ํˆฌ๋ช… ํ”„๋ก์‹œ ํŒจํ„ด)

OpenZeppelin์—์„œ ์ œ์•ˆํ•œ ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์ด์—์š”. "ํˆฌ๋ช…ํ•˜๋‹ค"๋Š” ์˜๋ฏธ๋Š” ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ํ”„๋ก์‹œ์˜ ์กด์žฌ๋ฅผ ์˜์‹ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ๋œป์ด์ฃ .

ํ•ต์‹ฌ ํŠน์ง•:
โ€ข Admin(๊ด€๋ฆฌ์ž)๊ณผ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๋ฅผ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•ด์š”.
โ€ข Admin์ด ํ˜ธ์ถœํ•˜๋ฉด ํ”„๋ก์‹œ ์ž์ฒด์˜ ๊ด€๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ณ , ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๊ฐ€ ํ˜ธ์ถœํ•˜๋ฉด ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ๋กœ delegatecall ๋ฉ๋‹ˆ๋‹ค.
โ€ข ํ•จ์ˆ˜ ์„ ํƒ์ž ์ถฉ๋Œ(Function Selector Collision) ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์–ด์š”.

โš ๏ธ ํ•จ์ˆ˜ ์„ ํƒ์ž ์ถฉ๋Œ์ด๋ž€? Solidity์—์„œ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๋ช…๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์„ ํ•ด์‹ฑํ•œ 4๋ฐ”์ดํŠธ ๊ฐ’์œผ๋กœ ์‹๋ณ„๋ผ์š”. ๋งŒ์•ฝ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์˜ upgrade() ํ•จ์ˆ˜์™€ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜๊ฐ€ ์šฐ์—ฐํžˆ ๊ฐ™์€ ์„ ํƒ์ž๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋˜๋ฉด ํฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”. Transparent Proxy๋Š” ํ˜ธ์ถœ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ์ด ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค! ๐Ÿ›ก๏ธ
// Transparent Proxy ์˜ˆ์‹œ (๋‹จ์ˆœํ™”๋œ ๋ฒ„์ „)
contract TransparentProxy {
    address private _admin;
    address private _implementation;
    
    modifier ifAdmin() {
        if (msg.sender == _admin) {
            _;
        } else {
            _fallback();
        }
    }
    
    function upgradeTo(address newImplementation) external ifAdmin {
        _implementation = newImplementation;
    }
    
    function _fallback() internal {
        address impl = _implementation;
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
    
    fallback() external payable {
        _fallback();
    }
}

2๏ธโƒฃ UUPS (Universal Upgradeable Proxy Standard)

UUPS๋Š” ๋น„๊ต์  ์ตœ๊ทผ์— ๋“ฑ์žฅํ•œ ํŒจํ„ด์œผ๋กœ, EIP-1822์—์„œ ์ œ์•ˆ๋˜์—ˆ์–ด์š”. Transparent Proxy์˜ ๊ฐ€์Šค ๋น„ํšจ์œจ์„ฑ์„ ๊ฐœ์„ ํ•œ ๋ฒ„์ „์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์ฃ .

ํ•ต์‹ฌ ํŠน์ง•:
โ€ข ์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง์ด ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— ํฌํ•จ๋˜์–ด ์žˆ์–ด์š”.
โ€ข ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ€๋ฒผ์›Œ์š”.
โ€ข ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ๊ฐ€์Šค ๋น„์šฉ์ด ๋” ์ €๋ ดํ•ด์š”.
โ€ข ํ•˜์ง€๋งŒ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— ์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง์„ ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•ด์•ผ ํ•ด์š”.

๐Ÿšจ UUPS ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ UUPS์—์„œ๋Š” ์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง์ด ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋งŒ์•ฝ ์ƒˆ๋กœ์šด ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— ์—…๊ทธ๋ ˆ์ด๋“œ ํ•จ์ˆ˜๋ฅผ ๊นœ๋นกํ•˜๊ณ  ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉด ์˜์›ํžˆ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค! ๐Ÿ˜ฑ ๋ฐ˜๋“œ์‹œ ๋ชจ๋“  ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— ์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง์„ ์ƒ์†๋ฐ›์•„์•ผ ํ•ด์š”.
// UUPS ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ์˜ˆ์‹œ
contract MyContractV1 is UUPSUpgradeable {
    uint256 public value;
    
    function initialize(uint256 _value) public initializer {
        value = _value;
    }
    
    function setValue(uint256 _value) public {
        value = _value;
    }
    
    // UUPS์—์„œ๋Š” ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— ์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง์ด ์žˆ์–ด์š”
    function _authorizeUpgrade(address newImplementation) internal override {
        require(msg.sender == owner(), "Only owner can upgrade");
    }
}

3๏ธโƒฃ Beacon Proxy Pattern (๋น„์ฝ˜ ํ”„๋ก์‹œ ํŒจํ„ด)

์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋™์‹œ์— ๊ด€๋ฆฌํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•œ ํŒจํ„ด์ด์—์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ฐœ๋ณ„ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋ฐฐํฌํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์„ธ์š”.

ํ•ต์‹ฌ ํŠน์ง•:
โ€ข Beacon ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์˜ ์ฃผ์†Œ๋ฅผ ์ค‘์•™์—์„œ ๊ด€๋ฆฌํ•ด์š”.
โ€ข ์—ฌ๋Ÿฌ ํ”„๋ก์‹œ๊ฐ€ ํ•˜๋‚˜์˜ Beacon์„ ์ฐธ์กฐํ•ด์š”.
โ€ข Beacon์˜ ๊ตฌํ˜„ ์ฃผ์†Œ๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ชจ๋“  ํ”„๋ก์‹œ๊ฐ€ ๋™์‹œ์— ์—…๊ทธ๋ ˆ์ด๋“œ๋ผ์š”.
โ€ข ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ ํšจ์œจ์ ์ด์—์š”.

โœจ Beacon Proxy์˜ ์‹ค์ œ ํ™œ์šฉ ์‚ฌ๋ก€ ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๊ฐ ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฐœ์ธ ์ง€๊ฐ‘ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณผ๊ฒŒ์š”. ์ˆ˜์ฒœ ๋ช…์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ๋‹ค๋ฉด ์ˆ˜์ฒœ ๊ฐœ์˜ ํ”„๋ก์‹œ๊ฐ€ ํ•„์š”ํ•˜๊ฒ ์ฃ ? Beacon Proxy๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•œ ๋ฒˆ์˜ ์—…๊ทธ๋ ˆ์ด๋“œ๋กœ ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ์ง€๊ฐ‘์„ ๋™์‹œ์— ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์–ด์š”! ๐ŸŽ‰
ํŒจํ„ด ์žฅ์  ๋‹จ์  ์ ํ•ฉํ•œ ์ƒํ™ฉ
Transparent Proxy ์•ˆ์ „ํ•˜๊ณ  ๊ฒ€์ฆ๋จ
ํ•จ์ˆ˜ ์ถฉ๋Œ ๋ฐฉ์ง€
๊ฐ€์Šค ๋น„์šฉ์ด ๋†’์Œ
๋ณต์žกํ•œ ๊ตฌ์กฐ
๋‹จ์ผ ์ปจํŠธ๋ž™ํŠธ
๋ณด์•ˆ์ด ์ตœ์šฐ์„ 
UUPS ๊ฐ€์Šค ํšจ์œจ์ 
๊ฐ„๋‹จํ•œ ํ”„๋ก์‹œ
๊ตฌํ˜„ ์‹ค์ˆ˜ ์œ„ํ—˜
์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง ํ•„์ˆ˜
๊ฐ€์Šค ์ตœ์ ํ™” ์ค‘์š”
์ˆ™๋ จ๋œ ๊ฐœ๋ฐœ์ž
Beacon Proxy ๋‹ค์ค‘ ํ”„๋ก์‹œ ๊ด€๋ฆฌ
์ผ๊ด„ ์—…๊ทธ๋ ˆ์ด๋“œ
์ถ”๊ฐ€ ์ปจํŠธ๋ž™ํŠธ ํ•„์š”
๋ณต์žก๋„ ์ฆ๊ฐ€
๋‹ค์ˆ˜์˜ ํ”„๋ก์‹œ
๋™์ผ ๋กœ์ง ๊ณต์œ 

๐Ÿ› ๏ธ Proxy ํŒจํ„ด ๊ตฌํ˜„ํ•˜๊ธฐ: ์‹ค์ „ ๊ฐ€์ด๋“œ

์ด์ œ ์‹ค์ œ๋กœ Proxy ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์•Œ์•„๋ณผ๊ฒŒ์š”. OpenZeppelin์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ํ›จ์”ฌ ์‰ฝ๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ’ช

๐Ÿ”ง Step 1: ํ™˜๊ฒฝ ์„ค์ •

๋จผ์ € ํ•„์š”ํ•œ ๋„๊ตฌ๋“ค์„ ์„ค์น˜ํ•ด์•ผ ํ•ด์š”. Hardhat์ด๋‚˜ Truffle ๊ฐ™์€ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์™€ OpenZeppelin Contracts๋ฅผ ์‚ฌ์šฉํ•  ๊ฑฐ์˜ˆ์š”.

// package.json ์„ค์ •
npm install --save-dev hardhat
npm install @openzeppelin/contracts
npm install @openzeppelin/contracts-upgradeable
npm install @openzeppelin/hardhat-upgrades

๐Ÿ“ Step 2: ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ์ž‘์„ฑ

์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ๊ทœ์น™์„ ๋”ฐ๋ผ์•ผ ํ•ด์š”. ์ผ๋ฐ˜์ ์ธ ์ปจํŠธ๋ž™ํŠธ์™€๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅธ ์ ๋“ค์ด ์žˆ๊ฑฐ๋“ ์š”! ๐ŸŽ“

๐Ÿ“Œ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ์ปจํŠธ๋ž™ํŠธ ์ž‘์„ฑ ๊ทœ์น™: 1. constructor ๋Œ€์‹  initialize ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์š”.
2. Initializable ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ƒ์†๋ฐ›์•„์•ผ ํ•ด์š”.
3. ์ƒํƒœ ๋ณ€์ˆ˜์˜ ์ˆœ์„œ๋ฅผ ์ ˆ๋Œ€ ๋ฐ”๊พธ๋ฉด ์•ˆ ๋ผ์š”.
4. ๊ธฐ์กด ์ƒํƒœ ๋ณ€์ˆ˜๋ฅผ ์‚ญ์ œํ•˜๋ฉด ์•ˆ ๋ผ์š”.
5. ์ƒํƒœ ๋ณ€์ˆ˜์˜ ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์•ˆ ๋ผ์š”.
// MyContractV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MyContractV1 is Initializable, OwnableUpgradeable {
    uint256 public value;
    string public name;
    
    // constructor ๋Œ€์‹  initialize ์‚ฌ์šฉ
    function initialize(string memory _name) public initializer {
        __Ownable_init();
        name = _name;
        value = 0;
    }
    
    function setValue(uint256 _value) public {
        value = _value;
    }
    
    function getValue() public view returns (uint256) {
        return value;
    }
}

๐Ÿš€ Step 3: ํ”„๋ก์‹œ ๋ฐฐํฌํ•˜๊ธฐ

OpenZeppelin์˜ Hardhat Upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋ก์‹œ ๋ฐฐํฌ๊ฐ€ ์ •๋ง ๊ฐ„๋‹จํ•ด์ ธ์š”. ๋ณต์žกํ•œ ํ”„๋ก์‹œ ๋กœ์ง์„ ์ง์ ‘ ์ž‘์„ฑํ•  ํ•„์š” ์—†์ด ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋งŒ์œผ๋กœ ๊ฐ€๋Šฅํ•˜๋‹ต๋‹ˆ๋‹ค! ๐Ÿ˜Š

// scripts/deploy.js
const { ethers, upgrades } = require("hardhat");

async function main() {
    // ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
    const MyContract = await ethers.getContractFactory("MyContractV1");
    
    // ํ”„๋ก์‹œ์™€ ํ•จ๊ป˜ ๋ฐฐํฌ (Transparent Proxy ์‚ฌ์šฉ)
    const proxy = await upgrades.deployProxy(
        MyContract,
        ["My First Contract"], // initialize ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ
        { initializer: 'initialize' }
    );
    
    await proxy.deployed();
    
    console.log("Proxy deployed to:", proxy.address);
    console.log("Implementation deployed to:", await upgrades.erc1967.getImplementationAddress(proxy.address));
}

main();

๐Ÿ”„ Step 4: ์ปจํŠธ๋ž™ํŠธ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ธฐ

์ด์ œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด์—์š”! ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ V2 ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณผ๊ฒŒ์š”. ๐ŸŽฏ

// MyContractV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./MyContractV1.sol";

contract MyContractV2 is MyContractV1 {
    // ์ƒˆ๋กœ์šด ์ƒํƒœ ๋ณ€์ˆ˜๋Š” ๋งจ ๋’ค์— ์ถ”๊ฐ€!
    uint256 public multiplier;
    
    // ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ์ถ”๊ฐ€
    function setMultiplier(uint256 _multiplier) public {
        multiplier = _multiplier;
    }
    
    // ๊ธฐ์กด ํ•จ์ˆ˜ ์˜ค๋ฒ„๋ผ์ด๋“œ
    function getValue() public view override returns (uint256) {
        return value * multiplier;
    }
    
    // V2์—์„œ ์ถ”๊ฐ€๋œ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
    function initializeV2(uint256 _multiplier) public reinitializer(2) {
        multiplier = _multiplier;
    }
}
// scripts/upgrade.js
const { ethers, upgrades } = require("hardhat");

async function main() {
    const PROXY_ADDRESS = "0x..."; // ๊ธฐ์กด ํ”„๋ก์‹œ ์ฃผ์†Œ
    
    // ์ƒˆ๋กœ์šด ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
    const MyContractV2 = await ethers.getContractFactory("MyContractV2");
    
    // ์—…๊ทธ๋ ˆ์ด๋“œ ์‹คํ–‰
    const upgraded = await upgrades.upgradeProxy(PROXY_ADDRESS, MyContractV2);
    
    console.log("Contract upgraded successfully!");
    console.log("Proxy address:", upgraded.address);
    console.log("New implementation:", await upgrades.erc1967.getImplementationAddress(upgraded.address));
    
    // V2 ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ํ˜ธ์ถœ
    await upgraded.initializeV2(10);
}

main();
โœ… ์—…๊ทธ๋ ˆ์ด๋“œ ์„ฑ๊ณต ํ™•์ธํ•˜๊ธฐ ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ์„ ํ™•์ธํ•ด๋ณด์„ธ์š”:
โ€ข ํ”„๋ก์‹œ ์ฃผ์†Œ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๋‚˜์š”? โœ“
โ€ข ๊ธฐ์กด ๋ฐ์ดํ„ฐ(value, name)๊ฐ€ ๋ณด์กด๋˜์–ด ์žˆ๋‚˜์š”? โœ“
โ€ข ์ƒˆ๋กœ์šด ํ•จ์ˆ˜(setMultiplier)๊ฐ€ ์ž‘๋™ํ•˜๋‚˜์š”? โœ“
โ€ข ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋‚˜์š”? โœ“

โš ๏ธ Storage Layout: ๊ฐ€์žฅ ์กฐ์‹ฌํ•ด์•ผ ํ•  ๋ถ€๋ถ„

Proxy ํŒจํ„ด์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‹ค์ˆ˜ํ•˜๊ณ , ๊ฐ€์žฅ ์น˜๋ช…์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ๋ฐ”๋กœ Storage Layout์ด์—์š”. ์ด ๋ถ€๋ถ„์„ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฉด ์—…๊ทธ๋ ˆ์ด๋“œ ํ›„ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ผฌ์ด๊ฑฐ๋‚˜ ์™„์ „ํžˆ ๋ง๊ฐ€์งˆ ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ˜ฑ

๐Ÿงฉ Storage Slot์˜ ์ดํ•ด

EVM(Ethereum Virtual Machine)์—์„œ ์ปจํŠธ๋ž™ํŠธ์˜ ์ƒํƒœ ๋ณ€์ˆ˜๋Š” storage slot์ด๋ผ๋Š” 32๋ฐ”์ดํŠธ ๊ณต๊ฐ„์— ์ˆœ์ฐจ์ ์œผ๋กœ ์ €์žฅ๋ผ์š”. ์ฒซ ๋ฒˆ์งธ ๋ณ€์ˆ˜๋Š” slot 0, ๋‘ ๋ฒˆ์งธ๋Š” slot 1, ์ด๋Ÿฐ ์‹์œผ๋กœ์š”.

Proxy ํŒจํ„ด์—์„œ๋Š” ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์˜ storage๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ storage slot์˜ ์ˆœ์„œ์™€ ํƒ€์ž…์ด ์ผ์น˜ํ•ด์•ผ ํ•ด์š”!

๐Ÿšซ ์ ˆ๋Œ€ ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ๋“ค:

โŒ ์ž˜๋ชป๋œ ์˜ˆ์‹œ 1: ๋ณ€์ˆ˜ ์ˆœ์„œ ๋ณ€๊ฒฝ
// V1
contract V1 {
    uint256 public a;
    uint256 public b;
}

// V2 - ์ž˜๋ชป๋จ!
contract V2 {
    uint256 public b;  // ์ˆœ์„œ ๋ฐ”๋€œ
    uint256 public a;  // ์ˆœ์„œ ๋ฐ”๋€œ
}
โŒ ์ž˜๋ชป๋œ ์˜ˆ์‹œ 2: ๋ณ€์ˆ˜ ํƒ€์ž… ๋ณ€๊ฒฝ
// V1
contract V1 {
    uint256 public value;
}

// V2 - ์ž˜๋ชป๋จ!
contract V2 {
    address public value;  // ํƒ€์ž… ๋ณ€๊ฒฝ
}
โŒ ์ž˜๋ชป๋œ ์˜ˆ์‹œ 3: ์ค‘๊ฐ„์— ๋ณ€์ˆ˜ ์‚ฝ์ž…
// V1
contract V1 {
    uint256 public a;
    uint256 public c;
}

// V2 - ์ž˜๋ชป๋จ!
contract V2 {
    uint256 public a;
    uint256 public b;  // ์ค‘๊ฐ„์— ์‚ฝ์ž…
    uint256 public c;
}
โœ… ์˜ฌ๋ฐ”๋ฅธ ์—…๊ทธ๋ ˆ์ด๋“œ ๋ฐฉ๋ฒ•:

โœ“ ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์‹œ: ๋์— ๋ณ€์ˆ˜ ์ถ”๊ฐ€
// V1
contract V1 {
    uint256 public a;
    uint256 public b;
}

// V2 - ์˜ฌ๋ฐ”๋ฆ„!
contract V2 {
    uint256 public a;
    uint256 public b;
    uint256 public c;  // ๋์— ์ถ”๊ฐ€
    address public newAddress;  // ๋์— ์ถ”๊ฐ€
}

๐Ÿ” Storage Gap ํŒจํ„ด

๋ฏธ๋ž˜์˜ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ๋Œ€๋น„ํ•ด์„œ storage gap์„ ๋ฏธ๋ฆฌ ํ™•๋ณดํ•ด๋‘๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ด€ํ–‰์ด์—์š”. OpenZeppelin์—์„œ๋„ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ฃ ! ๐ŸŽฏ

contract MyContractV1 {
    uint256 public value;
    string public name;
    
    // ๋ฏธ๋ž˜์˜ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์œ„ํ•œ ์˜ˆ์•ฝ ๊ณต๊ฐ„
    // 50๊ฐœ์˜ slot์„ ์˜ˆ์•ฝ (50 * 32 bytes = 1600 bytes)
    uint256[48] private __gap;
}

contract MyContractV2 {
    uint256 public value;
    string public name;
    
    // ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜ 2๊ฐœ ์ถ”๊ฐ€
    uint256 public multiplier;
    address public admin;
    
    // gap์„ 48๊ฐœ๋กœ ์ค„์ž„ (50 - 2 = 48)
    uint256[46] private __gap;
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚˜์ค‘์— ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ gap์—์„œ ๋นผ์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋‹ˆ๊นŒ, storage layout์ด ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€๋ผ์š”! ๐Ÿ˜Š

๐Ÿ” ๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญ

Proxy ํŒจํ„ด์€ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, ๊ทธ๋งŒํผ ๋ณด์•ˆ์— ์‹ ๊ฒฝ ์จ์•ผ ํ•  ๋ถ€๋ถ„๋„ ๋งŽ์•„์š”. ์—…๊ทธ๋ ˆ์ด๋“œ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ์ด ์•…์˜์ ์œผ๋กœ ํ–‰๋™ํ•˜๋ฉด ํฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”. ๐Ÿ›ก๏ธ

๐ŸŽฏ 1. ์—…๊ทธ๋ ˆ์ด๋“œ ๊ถŒํ•œ ๊ด€๋ฆฌ

๊ถŒํ•œ ๊ด€๋ฆฌ ์ „๋žต: โ€ข Timelock ์‚ฌ์šฉ
์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์ฆ‰์‹œ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์ผ์ • ์‹œ๊ฐ„(์˜ˆ: 48์‹œ๊ฐ„) ํ›„์— ์‹คํ–‰๋˜๋„๋ก ํ•ด์š”. ์ด ๊ธฐ๊ฐ„ ๋™์•ˆ ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฒ€ํ† ํ•  ์ˆ˜ ์žˆ์–ด์š”.

โ€ข Multi-sig Wallet
ํ•œ ์‚ฌ๋žŒ์ด ์•„๋‹Œ ์—ฌ๋Ÿฌ ๋ช…์˜ ์Šน์ธ์ด ์žˆ์–ด์•ผ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด 5๋ช… ์ค‘ 3๋ช…์˜ ์„œ๋ช…์ด ํ•„์š”ํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์ฃ .

โ€ข DAO ๊ฑฐ๋ฒ„๋„Œ์Šค
ํ† ํฐ ํ™€๋”๋“ค์˜ ํˆฌํ‘œ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”. ๊ฐ€์žฅ ํƒˆ์ค‘์•™ํ™”๋œ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ๊ตฌํ˜„์ด ๋ณต์žกํ•ด์š”.
// Timelock์„ ์‚ฌ์šฉํ•œ ์—…๊ทธ๋ ˆ์ด๋“œ ์˜ˆ์‹œ
import "@openzeppelin/contracts/governance/TimelockController.sol";

contract UpgradeableWithTimelock {
    TimelockController public timelock;
    
    constructor(address[] memory proposers, address[] memory executors) {
        // 48์‹œ๊ฐ„ ์ง€์—ฐ
        timelock = new TimelockController(
            2 days,
            proposers,
            executors,
            address(0)
        );
    }
    
    function scheduleUpgrade(address newImplementation) public {
        // ์—…๊ทธ๋ ˆ์ด๋“œ ์˜ˆ์•ฝ
        bytes memory data = abi.encodeWithSignature(
            "upgradeTo(address)",
            newImplementation
        );
        
        timelock.schedule(
            address(this),
            0,
            data,
            bytes32(0),
            bytes32(0),
            2 days
        );
    }
}

๐Ÿ”’ 2. ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ๋ณดํ˜ธ

initialize ํ•จ์ˆ˜๋Š” ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•ด์š”. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ณต๊ฒฉ์ž๊ฐ€ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์žฌ์ดˆ๊ธฐํ™”ํ•ด์„œ ์†Œ์œ ๊ถŒ์„ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ˜จ

โš ๏ธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ๋ณด์•ˆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
  • โœ“ initializer modifier ์‚ฌ์šฉํ–ˆ๋‚˜์š”?
  • โœ“ ๋ชจ๋“  ์ƒ์†๋ฐ›์€ ์ปจํŠธ๋ž™ํŠธ์˜ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ–ˆ๋‚˜์š”?
  • โœ“ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๊ฐ€ ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•œ๊ฐ€์š”? (public/external)
  • โœ“ ๋ฐฐํฌ ์งํ›„ ์ฆ‰์‹œ ์ดˆ๊ธฐํ™”ํ–ˆ๋‚˜์š”?

๐ŸŽญ 3. Delegatecall์˜ ์œ„ํ—˜์„ฑ

delegatecall์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ์œ„ํ—˜ํ•œ ๊ธฐ๋Šฅ์ด์—์š”. ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”.

ํŠนํžˆ selfdestruct๋ฅผ ํฌํ•จํ•œ ์ปจํŠธ๋ž™ํŠธ๋กœ delegatecallํ•˜๋ฉด ํ”„๋ก์‹œ ์ž์ฒด๊ฐ€ ํŒŒ๊ดด๋  ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ˜ฑ

๐Ÿ’ก ์•ˆ์ „ํ•œ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ์ž‘์„ฑ ๊ฐ€์ด๋“œ: โ€ข selfdestruct ์‚ฌ์šฉ ๊ธˆ์ง€
โ€ข delegatecall์„ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ๋‚ด์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ
โ€ข ์ƒ์„ฑ์ž์—์„œ ์ƒํƒœ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ธฐ
โ€ข ๋ถˆ๋ณ€(immutable) ๋ณ€์ˆ˜ ์‚ฌ์šฉ ์ฃผ์˜
โ€ข OpenZeppelin์˜ Upgradeable ๋ฒ„์ „ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ

๐Ÿงช ํ…Œ์ŠคํŠธ ์ „๋žต

์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ์ปจํŠธ๋ž™ํŠธ๋Š” ์ผ๋ฐ˜ ์ปจํŠธ๋ž™ํŠธ๋ณด๋‹ค ํ›จ์”ฌ ๋” ์ฒ ์ €ํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ด์š”. ํ•œ๋ฒˆ ๋ฐฐํฌ๋œ ํ›„์—๋Š” ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ์š”! ๐ŸŽฏ

๐Ÿ“‹ ํ…Œ์ŠคํŠธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โœ… ํ•„์ˆ˜ ํ…Œ์ŠคํŠธ ํ•ญ๋ชฉ: 1. ์ดˆ๊ธฐ ๋ฐฐํฌ ํ…Œ์ŠคํŠธ
โ€ข ํ”„๋ก์‹œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐฐํฌ๋˜๋Š”๊ฐ€?
โ€ข ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•˜๋Š”๊ฐ€?
โ€ข ์ดˆ๊ธฐ ์ƒํƒœ ๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅธ๊ฐ€?

2. ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ
โ€ข ๋ชจ๋“  ํ•จ์ˆ˜๊ฐ€ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ •์ƒ ์ž‘๋™ํ•˜๋Š”๊ฐ€?
โ€ข ์ƒํƒœ ๋ณ€๊ฒฝ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ €์žฅ๋˜๋Š”๊ฐ€?
โ€ข ์ด๋ฒคํŠธ๊ฐ€ ์ œ๋Œ€๋กœ ๋ฐœ์ƒํ•˜๋Š”๊ฐ€?

3. ์—…๊ทธ๋ ˆ์ด๋“œ ํ…Œ์ŠคํŠธ
โ€ข ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š”๊ฐ€?
โ€ข ๊ธฐ์กด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณด์กด๋˜๋Š”๊ฐ€?
โ€ข ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๊ฐ€ ์ž‘๋™ํ•˜๋Š”๊ฐ€?
โ€ข ์ˆ˜์ •๋œ ํ•จ์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€?

4. ๊ถŒํ•œ ํ…Œ์ŠคํŠธ
โ€ข ๊ถŒํ•œ ์—†๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์—†๋Š”๊ฐ€?
โ€ข ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋Š”๊ฐ€?
โ€ข Admin ์ „์šฉ ํ•จ์ˆ˜๊ฐ€ ๋ณดํ˜ธ๋˜๋Š”๊ฐ€?

5. Storage Layout ํ…Œ์ŠคํŠธ
โ€ข ์—…๊ทธ๋ ˆ์ด๋“œ ํ›„ storage slot์ด ์˜ฌ๋ฐ”๋ฅธ๊ฐ€?
โ€ข ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์ผ์น˜ํ•˜๋Š”๊ฐ€?
โ€ข ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์œ„์น˜์— ์žˆ๋Š”๊ฐ€?
// Hardhat ํ…Œ์ŠคํŠธ ์˜ˆ์‹œ
const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");

describe("Upgradeable Contract Test", function() {
    let proxy, owner, user;
    
    beforeEach(async function() {
        [owner, user] = await ethers.getSigners();
        
        const V1 = await ethers.getContractFactory("MyContractV1");
        proxy = await upgrades.deployProxy(V1, ["Test"], {
            initializer: 'initialize'
        });
        await proxy.deployed();
    });
    
    it("Should initialize correctly", async function() {
        expect(await proxy.name()).to.equal("Test");
        expect(await proxy.value()).to.equal(0);
    });
    
    it("Should set value correctly", async function() {
        await proxy.setValue(100);
        expect(await proxy.getValue()).to.equal(100);
    });
    
    it("Should upgrade successfully", async function() {
        // V2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ
        const V2 = await ethers.getContractFactory("MyContractV2");
        const upgraded = await upgrades.upgradeProxy(proxy.address, V2);
        
        // ๊ธฐ์กด ๋ฐ์ดํ„ฐ ๋ณด์กด ํ™•์ธ
        expect(await upgraded.name()).to.equal("Test");
        
        // ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ
        await upgraded.initializeV2(10);
        await upgraded.setValue(5);
        expect(await upgraded.getValue()).to.equal(50); // 5 * 10
    });
    
    it("Should preserve data across upgrade", async function() {
        // V1์—์„œ ๋ฐ์ดํ„ฐ ์„ค์ •
        await proxy.setValue(999);
        
        // V2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ
        const V2 = await ethers.getContractFactory("MyContractV2");
        const upgraded = await upgrades.upgradeProxy(proxy.address, V2);
        
        // ๋ฐ์ดํ„ฐ ๋ณด์กด ํ™•์ธ
        await upgraded.initializeV2(1);
        expect(await upgraded.getValue()).to.equal(999);
    });
});

๐Ÿ”ฌ Storage Layout ๊ฒ€์ฆ

OpenZeppelin์˜ Upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ž๋™์œผ๋กœ storage layout์„ ๊ฒ€์ฆํ•ด์ค˜์š”. ํ•˜์ง€๋งŒ ์ˆ˜๋™์œผ๋กœ๋„ ํ™•์ธํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด๋Š” ๊ฒŒ ์ข‹์•„์š”! ๐Ÿ”

// Storage layout ๊ฒ€์ฆ ๋ช…๋ น์–ด
npx hardhat verify-storage-layout

// ํŠน์ • ์ปจํŠธ๋ž™ํŠธ์˜ storage layout ํ™•์ธ
npx hardhat storage-layout MyContractV1
npx hardhat storage-layout MyContractV2

๐ŸŒŸ ์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€

์ด๋ก ๋งŒ ์•Œ์•„์„œ๋Š” ๋ถ€์กฑํ•˜์ฃ ! ์‹ค์ œ๋กœ Proxy ํŒจํ„ด์ด ์–ด๋–ป๊ฒŒ ํ™œ์šฉ๋˜๋Š”์ง€ ๋ช‡ ๊ฐ€์ง€ ์‚ฌ๋ก€๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ์š”. ๐ŸŽช

๐Ÿ’ฐ 1. DeFi ํ”„๋กœํ† ์ฝœ

Aave, Compound, Uniswap ๊ฐ™์€ ๋Œ€ํ˜• DeFi ํ”„๋กœํ† ์ฝœ๋“ค์€ ๋ชจ๋‘ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด Aave V2์—์„œ V3๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ, ์‚ฌ์šฉ์ž๋“ค์€ ์•„๋ฌด๊ฒƒ๋„ ํ•  ํ•„์š” ์—†์ด ์ž๋™์œผ๋กœ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ๋งŒ์•ฝ Proxy ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ์ž๊ธˆ์„ ์ด๋™ํ•ด์•ผ ํ–ˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿ˜…

๐ŸŽฎ 2. NFT ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค

NFT ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค๋Š” ๋น ๋ฅด๊ฒŒ ๋ณ€ํ™”ํ•˜๋Š” ์‹œ์žฅ ์š”๊ตฌ์‚ฌํ•ญ์— ๋Œ€์‘ํ•ด์•ผ ํ•ด์š”. ์ƒˆ๋กœ์šด ๋กœ์—ดํ‹ฐ ์‹œ์Šคํ…œ, ๊ฒฝ๋งค ๋ฐฉ์‹, ๊ฒฐ์ œ ์˜ต์…˜ ๋“ฑ์„ ๊ณ„์† ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜์ฃ .

OpenSea์˜ Seaport ํ”„๋กœํ† ์ฝœ๋„ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋กœ ์„ค๊ณ„๋˜์–ด ์žˆ์–ด์š”. ์ด๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด NFT ํ‘œ์ค€์ด๋‚˜ ๊ธฐ๋Šฅ์„ ๋น ๋ฅด๊ฒŒ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค! ๐Ÿ–ผ๏ธ

๐Ÿฆ 3. ํ† ํฐ ์ปจํŠธ๋ž™ํŠธ

์ผ๋ถ€ ํ† ํฐ ํ”„๋กœ์ ํŠธ๋Š” ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ํ† ํฐ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์š”. ํ•˜์ง€๋งŒ ์ด๊ฑด ๋…ผ๋ž€์˜ ์—ฌ์ง€๊ฐ€ ์žˆ์–ด์š”.

์žฅ์ : ๋ฒ„๊ทธ ์ˆ˜์ •, ๊ธฐ๋Šฅ ์ถ”๊ฐ€, ๊ทœ์ œ ๋Œ€์‘์ด ๊ฐ€๋Šฅํ•ด์š”.
๋‹จ์ : ์ค‘์•™ํ™” ์šฐ๋ ค, ์‹ ๋ขฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”.

๊ทธ๋ž˜์„œ ๋งŽ์€ ํ”„๋กœ์ ํŠธ๋“ค์ด ์ดˆ๊ธฐ์—๋Š” ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , ์ถฉ๋ถ„ํžˆ ์•ˆ์ •ํ™”๋˜๋ฉด ์—…๊ทธ๋ ˆ์ด๋“œ ๊ถŒํ•œ์„ ํฌ๊ธฐ(renounce)ํ•˜๋Š” ์ „๋žต์„ ์‚ฌ์šฉํ•ด์š”. ๐ŸŽฏ

๐ŸŒ 4. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ

์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์—์„œ ๋ธ”๋ก์ฒด์ธ์„ ํ™œ์šฉํ•œ๋‹ค๋ฉด, Proxy ํŒจํ„ด์€ ํ•„์ˆ˜์ ์ด์—์š”!

์˜ˆ๋ฅผ ๋“ค์–ด:
โ€ข ์‚ฌ์šฉ์ž ํ‰ํŒ ์‹œ์Šคํ…œ ๊ฐœ์„ 
โ€ข ์ƒˆ๋กœ์šด ๊ฒฐ์ œ ๋ฐฉ์‹ ์ถ”๊ฐ€
โ€ข ์ˆ˜์ˆ˜๋ฃŒ ๊ตฌ์กฐ ๋ณ€๊ฒฝ
โ€ข ๋ถ„์Ÿ ํ•ด๊ฒฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์—…๋ฐ์ดํŠธ

์ด๋Ÿฐ ๋ณ€๊ฒฝ์‚ฌํ•ญ๋“ค์„ Proxy ํŒจํ„ด์„ ํ†ตํ•ด ์›ํ™œํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์‚ฌ์šฉ์ž๋“ค์€ ๊ธฐ์กด ๊ณ„์ •๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ฃ ! ๐Ÿš€

โšก ๊ฐ€์Šค ์ตœ์ ํ™” ํŒ

Proxy ํŒจํ„ด์€ ํŽธ๋ฆฌํ•˜์ง€๋งŒ ์ถ”๊ฐ€์ ์ธ ๊ฐ€์Šค ๋น„์šฉ์ด ๋ฐœ์ƒํ•ด์š”. ํŠนํžˆ delegatecall์€ ์ผ๋ฐ˜ call๋ณด๋‹ค ๋น„์šฉ์ด ๋†’๊ฑฐ๋“ ์š”. ๊ฐ€์Šค๋ฅผ ์ ˆ์•ฝํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ํŒ์„ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”! ๐Ÿ’ฐ

๐Ÿ”ฅ ๊ฐ€์Šค ์ตœ์ ํ™” ์ „๋žต: 1. UUPS ํŒจํ„ด ์„ ํƒ
Transparent Proxy๋ณด๋‹ค UUPS๊ฐ€ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ๊ฐ€์Šค๋ฅผ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์–ด์š”. Admin ์ฒดํฌ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

2. Minimal Proxy (EIP-1167)
๋™์ผํ•œ ๋กœ์ง์„ ๊ฐ€์ง„ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด Minimal Proxy(Clone)๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ๋ฐฐํฌ ๋น„์šฉ์ด 10๋ฐฐ ์ด์ƒ ์ €๋ ดํ•ด์š”!

3. Storage ์ตœ์ ํ™”
โ€ข ์ž‘์€ ํƒ€์ž…๋“ค์„ ํ•จ๊ป˜ ๋ฌถ์–ด์„œ ํ•˜๋‚˜์˜ slot์— ์ €์žฅ
โ€ข ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ์•ž์ชฝ slot์— ๋ฐฐ์น˜
โ€ข ๋ถˆํ•„์š”ํ•œ storage ์ฝ๊ธฐ/์“ฐ๊ธฐ ์ตœ์†Œํ™”

4. ํ•จ์ˆ˜ ์ตœ์ ํ™”
โ€ข View/Pure ํ•จ์ˆ˜๋Š” ๊ฐ€์Šค๋ฅผ ์†Œ๋น„ํ•˜์ง€ ์•Š์•„์š”
โ€ข ๋ฐ˜๋ณต์ ์ธ storage ์ ‘๊ทผ์„ memory ๋ณ€์ˆ˜๋กœ ์บ์‹ฑ
โ€ข ๋ถˆํ•„์š”ํ•œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์ค„์ด๊ธฐ
// ๊ฐ€์Šค ์ตœ์ ํ™” ์˜ˆ์‹œ
contract OptimizedContract {
    // โŒ ๋น„ํšจ์œจ์ : ๊ฐ๊ฐ ํ•˜๋‚˜์˜ slot ์‚ฌ์šฉ
    uint8 public a;
    uint8 public b;
    uint8 public c;
    
    // โœ… ํšจ์œจ์ : ํ•˜๋‚˜์˜ slot์— ํ•จ๊ป˜ ์ €์žฅ
    struct PackedData {
        uint8 a;
        uint8 b;
        uint8 c;
    }
    PackedData public data;
    
    // โŒ ๋น„ํšจ์œจ์ : storage๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์ฝ์Œ
    function sumBad() public view returns (uint256) {
        return data.a + data.b + data.c;
    }
    
    // โœ… ํšจ์œจ์ : memory์— ์บ์‹ฑ
    function sumGood() public view returns (uint256) {
        PackedData memory cached = data;
        return cached.a + cached.b + cached.c;
    }
}

๐Ÿšจ ํ”ํ•œ ์‹ค์ˆ˜์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

Proxy ํŒจํ„ด์„ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋น„์Šทํ•œ ์‹ค์ˆ˜๋ฅผ ํ•ด์š”. ์ œ๊ฐ€ ์ง์ ‘ ๊ฒช์—ˆ๊ฑฐ๋‚˜ ๋ณธ ์‹ค์ˆ˜๋“ค์„ ๊ณต์œ ํ• ๊ฒŒ์š”! ๐Ÿ˜…

โŒ ์‹ค์ˆ˜ 1: Constructor ์‚ฌ์šฉ

๋ฌธ์ œ:
contract MyContract {
    uint256 public value;
    
    constructor(uint256 _value) {
        value = _value;  // ํ”„๋ก์‹œ์—๋Š” ์ ์šฉ ์•ˆ ๋จ!
    }
}
ํ•ด๊ฒฐ:
contract MyContract is Initializable {
    uint256 public value;
    
    function initialize(uint256 _value) public initializer {
        value = _value;  // ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•!
    }
}

โŒ ์‹ค์ˆ˜ 2: ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ๋ฏธํ˜ธ์ถœ

๋ฌธ์ œ: ํ”„๋ก์‹œ๋ฅผ ๋ฐฐํฌํ•œ ํ›„ initialize ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด, ๊ณต๊ฒฉ์ž๊ฐ€ ๋จผ์ € ํ˜ธ์ถœํ•ด์„œ ์†Œ์œ ๊ถŒ์„ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ์–ด์š”!

ํ•ด๊ฒฐ:
๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ž๋™์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ฑฐ๋‚˜, deployProxy ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

โŒ ์‹ค์ˆ˜ 3: Storage Layout ๋ณ€๊ฒฝ

๋ฌธ์ œ:
// V1
contract V1 {
    uint256 public a;
    string public b;
}

// V2 - ์ž˜๋ชป๋จ!
contract V2 {
    string public b;  // ์ˆœ์„œ ๋ฐ”๋€œ
    uint256 public a;  // ๋ฐ์ดํ„ฐ ์†์ƒ!
}
๊ฒฐ๊ณผ:
์—…๊ทธ๋ ˆ์ด๋“œ ํ›„ ๋ฐ์ดํ„ฐ๊ฐ€ ์™„์ „ํžˆ ๊ผฌ์—ฌ๋ฒ„๋ ค์š”. uint256 ๊ฐ’์ด string์œผ๋กœ ํ•ด์„๋˜๊ณ , string์ด uint256์œผ๋กœ ํ•ด์„๋˜๋Š” ๋”์ฐํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ด์š”! ๐Ÿ˜ฑ

ํ•ด๊ฒฐ:
์ ˆ๋Œ€ ๋ณ€์ˆ˜ ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ์ง€ ๋ง๊ณ , ํ•ญ์ƒ ๋์— ์ถ”๊ฐ€ํ•˜์„ธ์š”!

โŒ ์‹ค์ˆ˜ 4: Selfdestruct ์‚ฌ์šฉ

๋ฌธ์ œ: ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ์— selfdestruct๋ฅผ ํฌํ•จํ•˜๋ฉด ํ”„๋ก์‹œ๊ฐ€ ํŒŒ๊ดด๋  ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๊ฒฐ:
์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅํ•œ ์ปจํŠธ๋ž™ํŠธ์—์„œ๋Š” ์ ˆ๋Œ€ selfdestruct๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”!

โŒ ์‹ค์ˆ˜ 5: ๋ถˆ์ถฉ๋ถ„ํ•œ ํ…Œ์ŠคํŠธ

๋ฌธ์ œ: ๋กœ์ปฌ ํ…Œ์ŠคํŠธ๋งŒ ํ•˜๊ณ  ๋ฉ”์ธ๋„ท์— ๋ฐฐํฌํ–ˆ๋‹ค๊ฐ€ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ ๋ฐœ์ƒ!

ํ•ด๊ฒฐ:
โ€ข ๋กœ์ปฌ ํ…Œ์ŠคํŠธ โ†’ ํ…Œ์ŠคํŠธ๋„ท ๋ฐฐํฌ โ†’ ๊ฐ์‚ฌ(Audit) โ†’ ๋ฉ”์ธ๋„ท ๋ฐฐํฌ
โ€ข ํ…Œ์ŠคํŠธ๋„ท์—์„œ ์ถฉ๋ถ„ํ•œ ์‹œ๊ฐ„ ๋™์•ˆ ์šด์˜
โ€ข ์ปค๋ฎค๋‹ˆํ‹ฐ ํ”ผ๋“œ๋ฐฑ ์ˆ˜์ง‘
โ€ข ์ „๋ฌธ ๋ณด์•ˆ ๊ฐ์‚ฌ ๋ฐ›๊ธฐ

๐ŸŽ“ ๊ณ ๊ธ‰ ์ฃผ์ œ: Diamond Pattern

Proxy ํŒจํ„ด์˜ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ ๋” ๊ณ ๊ธ‰ ํŒจํ„ด์ด ์žˆ์–ด์š”. ๋ฐ”๋กœ Diamond Pattern (EIP-2535)์ด์—์š”! ๐Ÿ’Ž

๐Ÿ”ท Diamond Pattern์ด๋ž€?

Diamond Pattern์€ ํ•˜๋‚˜์˜ ํ”„๋ก์‹œ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ(Facet)๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ์˜ˆ์š”. ๋งˆ์น˜ ๋‹ค์ด์•„๋ชฌ๋“œ์˜ ์—ฌ๋Ÿฌ ๋ฉด์ฒ˜๋Ÿผ์š”!

์ฃผ์š” ํŠน์ง•:
โ€ข ์ปจํŠธ๋ž™ํŠธ ํฌ๊ธฐ ์ œํ•œ(24KB) ๊ทน๋ณต
โ€ข ๋ชจ๋“ˆ์‹ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€๋Šฅ
โ€ข ํ•จ์ˆ˜๋ณ„๋กœ ๋‹ค๋ฅธ ๊ตฌํ˜„ ์ปจํŠธ๋ž™ํŠธ ์‚ฌ์šฉ
โ€ข ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์— ์ ํ•ฉ

๐Ÿ’ก Diamond Pattern ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค: ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋ธ”๋ก์ฒด์ธ์„ ํ™œ์šฉํ•œ๋‹ค๋ฉด:
โ€ข Facet 1: ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ
โ€ข Facet 2: ์žฌ๋Šฅ ๊ฑฐ๋ž˜
โ€ข Facet 3: ํ‰ํŒ ์‹œ์Šคํ…œ
โ€ข Facet 4: ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
โ€ข Facet 5: ๋ถ„์Ÿ ํ•ด๊ฒฐ

๊ฐ ๊ธฐ๋Šฅ์„ ๋…๋ฆฝ์ ์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋งค์šฐ ์œ ์—ฐํ•ด์š”! ๐ŸŽฏ

ํ•˜์ง€๋งŒ Diamond Pattern์€ ๊ตฌํ˜„์ด ๋ณต์žกํ•˜๊ณ  ๊ฐ€์Šค ๋น„์šฉ๋„ ๋†’์•„์š”. ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์ผ๋ฐ˜ Proxy ํŒจํ„ด์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•ด์š”! ๐Ÿ˜Š

๐Ÿ“š ํ•™์Šต ๋ฆฌ์†Œ์Šค ๋ฐ ๋„๊ตฌ

Proxy ํŒจํ„ด์„ ๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ๋ฆฌ์†Œ์Šค๋“ค์„ ์ถ”์ฒœํ•ด์š”! ๐Ÿ“–

๐Ÿ”— ๊ณต์‹ ๋ฌธ์„œ ๋ฐ ํ‘œ์ค€: โ€ข OpenZeppelin Upgrades Documentation
โ€ข EIP-1967: Standard Proxy Storage Slots
โ€ข EIP-1822: Universal Upgradeable Proxy Standard
โ€ข EIP-2535: Diamond Standard

๐Ÿ› ๏ธ ๊ฐœ๋ฐœ ๋„๊ตฌ:

โ€ข Hardhat Upgrades Plugin
โ€ข OpenZeppelin Defender (์ž๋™ํ™”๋œ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ด€๋ฆฌ)
โ€ข Tenderly (๋””๋ฒ„๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง)
โ€ข Slither (๋ณด์•ˆ ๋ถ„์„ ๋„๊ตฌ)

๐Ÿ“บ ํ•™์Šต ์ž๋ฃŒ:

โ€ข OpenZeppelin์˜ YouTube ์ฑ„๋„
โ€ข Ethereum.org์˜ Upgradeable Contracts ๊ฐ€์ด๋“œ
โ€ข Smart Contract Programmer์˜ Proxy ํŒจํ„ด ์‹œ๋ฆฌ์ฆˆ
โ€ข Patrick Collins์˜ Solidity ๊ฐ•์˜

๐Ÿงฐ ์œ ์šฉํ•œ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ

// ํ”„๋ก์‹œ ์ฃผ์†Œ์—์„œ ๊ตฌํ˜„ ์ฃผ์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ
const implementationAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress);

// ํ”„๋ก์‹œ ์ฃผ์†Œ์—์„œ Admin ์ฃผ์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ
const adminAddress = await upgrades.erc1967.getAdminAddress(proxyAddress);

// ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฒ€์ฆ (์‹ค์ œ ์—…๊ทธ๋ ˆ์ด๋“œ ์—†์ด ํ…Œ์ŠคํŠธ)
await upgrades.validateUpgrade(proxyAddress, NewImplementation);

// ๊ฐ•์ œ ์—…๊ทธ๋ ˆ์ด๋“œ (๊ฒ€์ฆ ๋ฌด์‹œ - ์œ„ํ—˜!)
await upgrades.forceImport(proxyAddress, Implementation);

// ํ”„๋ก์‹œ ์†Œ์œ ๊ถŒ ์ด์ „
await upgrades.admin.transferProxyAdminOwnership(newOwner);

๐ŸŽฏ ๊ฒฐ๋ก : ์–ธ์ œ Proxy ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

Proxy ํŒจํ„ด์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ๋ชจ๋“  ์ƒํ™ฉ์— ์ ํ•ฉํ•œ ๊ฒƒ์€ ์•„๋‹ˆ์—์š”. ์‹ ์ค‘ํ•˜๊ฒŒ ๊ฒฐ์ •ํ•ด์•ผ ํ•ด์š”! ๐Ÿค”

โœ… Proxy ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์€ ๊ฒฝ์šฐ: โ€ข ์žฅ๊ธฐ์ ์œผ๋กœ ์šด์˜๋  ํ”„๋กœ์ ํŠธ
โ€ข ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์žˆ๋Š” ๊ฒฝ์šฐ
โ€ข ๊ทœ์ œ๋‚˜ ์š”๊ตฌ์‚ฌํ•ญ์ด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ
โ€ข ๋ฒ„๊ทธ ์ˆ˜์ •์ด๋‚˜ ๋ณด์•ˆ ํŒจ์น˜๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ
โ€ข ๋Œ€๊ทœ๋ชจ ์ž๊ธˆ์ด๋‚˜ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒฝ์šฐ
โ€ข DeFi, NFT ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค, DAO ๋“ฑ
โŒ Proxy ํŒจํ„ด์„ ํ”ผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ: โ€ข ๊ฐ„๋‹จํ•œ ํ† ํฐ ์ปจํŠธ๋ž™ํŠธ
โ€ข ์™„์ „ํ•œ ํƒˆ์ค‘์•™ํ™”๊ฐ€ ์ค‘์š”ํ•œ ๊ฒฝ์šฐ
โ€ข ๋ถˆ๋ณ€์„ฑ์ด ํ•ต์‹ฌ ๊ฐ€์น˜์ธ ๊ฒฝ์šฐ
โ€ข ์ถ”๊ฐ€ ๊ฐ€์Šค ๋น„์šฉ์ด ๋ถ€๋‹ด๋˜๋Š” ๊ฒฝ์šฐ
โ€ข ๊ฐœ๋ฐœ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ
โ€ข ์ถฉ๋ถ„ํ•œ ํ…Œ์ŠคํŠธ์™€ ๊ฐ์‚ฌ๋ฅผ ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ

๐ŸŒˆ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

Proxy ํŒจํ„ด์€ ๋ธ”๋ก์ฒด์ธ์˜ ๋ถˆ๋ณ€์„ฑ๊ณผ ํ˜„์‹ค ์„ธ๊ณ„์˜ ์œ ์—ฐ์„ฑ ์‚ฌ์ด์—์„œ ๊ท ํ˜•์„ ์ฐพ๋Š” ํ›Œ๋ฅญํ•œ ํ•ด๊ฒฐ์ฑ…์ด์—์š”. ํ•˜์ง€๋งŒ "ํฐ ํž˜์—๋Š” ํฐ ์ฑ…์ž„์ด ๋”ฐ๋ฅธ๋‹ค"๋Š” ๋ง์ฒ˜๋Ÿผ, ์—…๊ทธ๋ ˆ์ด๋“œ ๊ถŒํ•œ์„ ๊ฐ€์ง„๋‹ค๋Š” ๊ฒƒ์€ ๊ทธ๋งŒํผ ์‹ ์ค‘ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๊ธฐ๋„ ํ•ด์š”. ๐Ÿฆธ

์žฌ๋Šฅ๋„ท์—์„œ ๋ธ”๋ก์ฒด์ธ ๊ฐœ๋ฐœ ๊ด€๋ จ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด, ์ด๋Ÿฐ ๊ณ ๊ธ‰ ํŒจํ„ด๋“ค์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ํฐ ๋„์›€์ด ๋  ๊ฑฐ์˜ˆ์š”. ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ๊ฐœ๋ฐœ์€ ๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด์„œ, ๋ณด์•ˆ, ๊ฒฝ์ œํ•™, ๊ฑฐ๋ฒ„๋„Œ์Šค๊นŒ์ง€ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ์ข…ํ•ฉ ์˜ˆ์ˆ ์ด๊ฑฐ๋“ ์š”! ๐ŸŽจ

์ด ๊ธ€์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ธ”๋ก์ฒด์ธ ๊ฐœ๋ฐœ ์—ฌ์ •์— ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ผ์š”. ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์˜ ์ง€์‹์ธ์˜ ์ˆฒ์—์„œ ๋‹ค๋ฅธ ์ „๋ฌธ๊ฐ€๋“ค๊ณผ ์†Œํ†ตํ•ด๋ณด์„ธ์š”! ํ•จ๊ป˜ ๋ฐฐ์šฐ๊ณ  ์„ฑ์žฅํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๋น ๋ฅธ ๊ธธ์ด๋‹ˆ๊นŒ์š”. ๐Ÿ˜Š

Happy coding, and may your contracts be forever upgradeable! ๐Ÿš€โœจ

V1 V2 V3 Continuous Evolution Your Smart Contract Journey
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

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