궁금한 주제를 찾아보세요

비트코인, 스테이블코인, 온체인 데이터처럼 주제로 검색하세요.

다시 읽을 이야기

저장한 글은 이 브라우저에만 보관됩니다.

해설기술·생태계

프록시 계약에서 constructor 대신 initializer를 쓰는 이유

Implementation constructor가 proxy storage를 초기화하지 못하는 이유와 initializer 탈취·재호출·상속 초기화 순서 방어를 설명합니다.

난이도 중급주제 카테고리와 개념 밀도 기준검토 정보AI 보조 초안 · 출처 목록 제공 · 주장별 대조 진행 중
배포 때 채워지는 구현 저장소와 외부 접근을 제한한 프록시 초기화 경로를 비교한 initializer 그림
주제의 이해를 돕기 위해 imagegen으로 제작한 AI 생성 개념 일러스트
먼저 읽는 핵심

Constructor 효과는 implementation storage에 남고 proxy state로 복사되지 않습니다.
Initializer는 proxy 문맥에서 한 번만 실행되도록 보호해야 합니다.
상속 parent initializer, implementation 잠금과 upgrade 재초기화를 버전별로 검증합니다.

Implementation의 constructor는 implementation 배포 때 implementation 자신의 storage에서만 실행되며 proxy storage를 초기화하지 않습니다. Proxy가 implementation code를 delegatecall할 때 owner·version 같은 proxy 상태를 한번 설정하려면 일반 함수 형태의 initializer가 필요합니다. 하지만 initializer는 constructor와 달리 호출 가능한 함수이므로 initialized flag와 권한을 원자적으로 설정하고 proxy 배포·upgrade transaction에서 즉시 실행해야 합니다. Implementation 자체도 초기화를 잠가 제3자가 ownership을 가져가는 경로를 줄여야 합니다.

두 주소의 storage를 구분합니다

Implementation I를 배포하며 constructor가 owner=deployer를 쓰면 I slot에 값이 저장됩니다. Proxy P가 I에 delegatecall해 함수를 실행할 때 SLOAD는 P slot을 읽으므로 P owner는 여전히 zero입니다. Constructor bytecode는 runtime code에 포함되지 않아 proxy가 나중에 재실행할 수도 없습니다.

Initializer는 I runtime code의 public 또는 external 함수지만 P를 통해 delegatecall하면 P storage를 씁니다. msg.sender도 proxy 호출자 기준으로 보존됩니다. P의 배포 transaction에서 initializer calldata까지 실행하면 주소가 공개된 뒤 누군가 먼저 initialize하는 창을 줄일 수 있습니다.

Initializer는 constructor의 이름을 바꾼 함수가 아니라, proxy storage에서 constructor 효과를 한 번 재현하는 권한 있는 상태 전환입니다.

JOBCOIN 해설

VISUAL GUIDE프로토콜 실행의 기본 구조
입력이 실행 규칙을 통과할 때 상태가 바뀌는 프로토콜 개념도
그림과 함께 짚어볼 본문 내용

그림은 이 주제의 공통 개념을 단순화한 설명입니다. 아래 항목에서 이 글의 구체적인 조건과 예외를 함께 읽어보세요.

  1. 두 주소의 storage를 구분합니다

    Implementation I를 배포하며 constructor가 owner=deployer를 쓰면 I slot에 값이 저장됩니다.

  2. 미초기화와 재초기화 위험을 나눕니다

    P가 배포됐지만 initialize가 별도 transaction으로 남으면 공격자가 먼저 owner를 자기 주소로 설정할 수 있습니다.

  3. 상속 초기화는 compiler가 자동 호출하지 않습니다

    일반 constructor에서는 compiler가 base constructors 호출을 관리하지만 initializer 함수는 개발자가 parent initializers를 호출해야 합니다.

AI로 제작한 개념도 · 실제 가격·거래 내역·통계가 아닙니다.

미초기화와 재초기화 위험을 나눕니다

P가 배포됐지만 initialize가 별도 transaction으로 남으면 공격자가 먼저 owner를 자기 주소로 설정할 수 있습니다. 반대로 initialized guard가 없으면 정상 초기화 뒤 다시 호출해 owner·token·oracle을 바꿀 수 있습니다. Guard flag 자체의 storage layout도 upgrade에서 유지돼야 합니다.

새 version에서 추가 모듈을 초기화하려면 versioned reinitializer가 필요할 수 있습니다. 이전 version을 다시 실행할 수 없어야 하고 새 initializer가 기존 parent 초기화를 중복 호출하지 않아야 합니다. ‘한 번만’과 ‘version당 한 번’을 구분합니다.

초기화 실행 위치와 위험
실행 Storage 문맥 주요 위험
I constructor Implementation Proxy 미초기화
P→I initializer Proxy 선점·재호출
P constructor delegatecall Proxy calldata·실패 처리
Version reinitializer Proxy 순서·중복 parent call
I 직접 initializer Implementation 구현 소유권 탈취

상속 초기화는 compiler가 자동 호출하지 않습니다

일반 constructor에서는 compiler가 base constructors 호출을 관리하지만 initializer 함수는 개발자가 parent initializers를 호출해야 합니다. 다중 상속에서 같은 parent를 두 번 호출하거나 빠뜨리면 상태가 중복 설정되거나 zero로 남습니다. Linearization 순서와 각 parent의 onlyInitializing 보호를 확인합니다.

Library의 upgradeable variant는 constructor 대신 __Module_init 같은 함수를 제공할 수 있습니다. 일반 contract package와 upgradeable package를 섞으면 immutable·constructor 전제가 남을 수 있어 실제 source와 storage layout을 확인합니다.

  • Proxy 배포와 initializer를 한 transaction으로 묶습니다.
  • Initialized version flag의 slot을 보존합니다.
  • 모든 parent initializer와 호출 순서를 표로 만듭니다.
  • Implementation 배포 직후 초기화를 disable합니다.
  • Fork에서 재호출·선점·upgrade 초기화를 공격 계정으로 시험합니다.

Implementation 잠금도 proxy 권한을 대신하지 않습니다

Implementation contract의 initializer를 disable하면 누군가 I 자체의 owner가 되는 일을 막을 수 있습니다. 하지만 Proxy의 admin·UUPS upgrade authorization이 안전해지는 것은 아닙니다. 두 주소의 ownership과 callable functions를 따로 검사합니다.

Constructor에서 immutable을 설정하면 값은 implementation runtime code에 들어가 모든 proxies가 공유할 수 있습니다. Proxy별 설정이 필요한 값을 immutable로 두면 tenant 분리가 깨집니다. Constant·immutable과 proxy storage state를 설계 단계에서 분류합니다.

초기화 완료 증거를 네 단계로 남깁니다

Deployment receipt success만으로 owner·domain separator·roles가 올바르다고 결론 내리지 않습니다. Proxy address에서 getters와 raw slots를 읽고 initializer event, implementation slot과 admin 권한을 함께 확인합니다.

Upgrade 시에는 old state가 유지되는지, 새 fields만 설정되는지, initializer가 revert하면 implementation 전환도 원자적으로 revert되는지 테스트합니다. Separate upgrade와 init을 허용하면 중간 상태에서 호출 가능한 함수와 권한을 threat model에 넣습니다.

자주 묻는 질문

Implementation constructor에서 owner를 설정하면 proxy owner도 설정되나요?

아닙니다. Constructor는 implementation storage에 쓰며 proxy는 별도 initializer가 필요합니다.

Initializer를 internal로 만들면 되나요?

외부 entry initializer가 보호된 internal parent initializers를 호출하는 구조가 일반적입니다. 배포 흐름에 맞는 진입점은 필요합니다.

Implementation 초기화 잠금만 하면 충분한가요?

아닙니다. Proxy 초기화와 upgrade authorization, storage layout을 별도로 보호해야 합니다.

더 깊이 읽기

본문에서 다룬 개념과 확인 절차를 다음 글에서 이어서 살펴보세요.

참고한 원문 자료

자료 확인 기준일 2026.09.27
  1. OpenZeppelin Upgrades — Writing Upgradeable Contractsdocs.openzeppelin.com
  2. Solidity Documentation — Constructorsdocs.soliditylang.org
자료 대조 기록과 확인 범위
출처 수집
원문 링크 2개 제공
핵심 주장 대조
완료 근거가 아직 기록되지 않았습니다.
분야 전문가 검수
별도 완료 기록이 없습니다.

원문 링크와 자료 확인일은 글 전체의 주장 대조나 전문가 검수 완료를 뜻하지 않습니다. 별도 확인이 필요한 절차는 원문의 적용 대상과 최신 안내를 함께 확인해 주세요.

AI 활용 안내

이 글은 초안 구성과 자료 정리에 AI를 활용했습니다. 글에 표시된 출처와 기준일을 함께 확인해 주세요. 별도 검토 정보가 없다면 전문가 검수를 뜻하지 않습니다.

이해를 위한 정보 콘텐츠

이 글은 특정 자산의 매수·매도 또는 수익을 권유하지 않습니다. 자료의 발표 시점과 이후 변경 사항을 함께 확인해 주세요.

편집 원칙 보기 →이 기사 정정 제보 →