가시성

(visibility)

 

 

Solidity 언어의 가시성(visibility)은 Java나 C++에서의 public, private, protected와 같은 접근제어자(access control) 역할을 한다.

 

Solidity 언어에서 가시성(visibility) 키워드를 사용하는 목적은 스마트 컨트랙트(smart contract)의 "외부" 혹은 "내부"에 "공개"할 것인지 결정할 수 있도록 하는 것이다. 따라서, 스마트 컨트랙트의 상태변수들과 함수들 각각의 visibility가 어떻게 사용되었느냐에 따라 접근 범위가 달라지게 된다.

 

Solidity 언어에서 스마트 컨트랙트 내의 상태변수(state variable)와 함수(function)에 적용할 수 있는 visibility는 4가지가 있다. 그리고 상태변수와 함수에 적용되는 경우가 조금 다르니 주의해서 사용해야한다. 참고로, 4개의 visibility는 스마트 컨트랙트의 접근과 수정을 제한하도록 하는 것일뿐, 배포된 계약서의 내용은 누구나 모두 볼 수 있는 상태라는 점을 알아둔다.



Visibility 작성 위치

visibility 키워드는 상태변수(state variable) 타입 바로 뒤, 그리고 함수(function)의 입력 파라미터와 반환 파라미터 사이에 작성한다.

pragma solidity >=0.4.16 <0.7.0;
contract Joker {
 uint public data;
 
 function f(uint a) private pure returns (uint b) {
   return a + 1;
 }
 function setData(uint a) internal {
   data = a;
 }
}



Visibility 4가지 특징

1. external

external은 smart contract의 interface로 공개한다.

계약서의 해당 내용을 공개한다는 의미이며, 계약서의 외부(external)에서 사용하는 인터페이스(interface)라는 것을 표시하는 것이다.

 

특징

  • 상태변수(state variable)는 external 일 수 없다.

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   external uint x; // 에러 발생
}
  • 계약서 내부에서 사용할 경우 this를 사용해서 접근해야 한다.

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   uint x;
   
   function func1() external {
      x = 10;
   }
   function func2() public {
      func1(); // 에러 발생
      this.func1();
   }
}

contract Caller is Joker {
   function func() public {
       this.func1();
   }
}

 

2. public

public은 smart contract의 interface로 공개한다.

계약서의 해당 내용을 공개한다는 의미이며, 계약서의 외부(external), 내부(internal) 모두에서 사용하는 인터페이스(interface)라는 것을 표시하는 것이다.

 

특징

  • 상태변수가 public 이면, getter 함수가 자동으로 생성된다. 자동으로 생성되는 getter의 이름은 상태변수와 동일하다. 만약, 계약서 내부에서 사용할 경우 this를 사용해서 접근해야 한다.

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   uint public x1;
   uint internal x2;
   uint private x3;
   
   function func() public {
      this.x1(); // getter 호출
      this.x2(); // 에러 발생 (internal 상태변수는 getter 자동생성 x)
      this.x3(); // 에러 발생 (private 상태변수는 getter 자동생성 x)
   }
}

contract Caller {
   function func() public {
       Joker k = new Joker();
       k.x1(); // getter 호출
   }
}

※ 참고로, 일반적으로 getter와 setter는 프로그래밍 언어에서 객체 내의 변수값을 읽거나 변수값을 세팅하는 함수를 말한다. 각 언어의 프로그램 개발자들을 귀찮게 하는 함수 중 하나가 getter, setter 이다. 사용할 일이 많기 때문이다. Java 같은 경우는 IDE(ex, Eclipse, Android Studio)에서 클릭 몇번만 하면 자동으로 만들어 주기도 하고, 롬복(Lombok) 라이브러리를 사용해서 @(=어노테이션) 한 두개만 추가해주면 자동으로 민들어주기도 한다. 그런데, Solidity는 getter에 대해서는 자동으로 생성해주는 것이다.

 

3. internal

internal은 smart contract의  interface로 비공개한다.

계약서의 해당 내용을 비공개한다는 의미이며, 계약서의 내부(internal)에서만 사용하는 함수라는 것을 표시하는 것이다.

 

특징

  • 상태변수는 internal 이 기본값(default)이다.

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   uint x; // “uint internal x” 와 동일
}

contract Child is Joker {
   function func() public {
       x = 10; // x 접근
   }
}
  • 계약서 자신과 상속 받은 계약서만 사용할 수 있다. internal은 java언어의 protected와 비슷하다.

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   uint internal x;
}

contract Child is Joker {
   function func() public {
       x = 10; // x 접근
   }
}

contract Caller {
   function func() public {
       Joker k = new Joker();
       k.x = 10; // 에러 발생
   }
}

 

 

4. private

private은 smart contract의 interface로 비공개한다.

계약서의 해당 내용을 비공개한다는 의미이며, 계약서 내부(internal)에서도 자신(private)만 사용하는 함수라는 것을 표시하는 것이다.

 

특징

  • 상태변수든 함수든 계약서 자신만 사용할 수 있다.

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   uint private x;
   function myFunc() private {
       x = 1;
   }
   function func() public {
       x = 10;
       myFunc();
   }
}

contract Child is Joker {
   function func() public {
       x = 10; // 에러 발생
       myFunc(); // 에러 발생
   }
}

contract Caller {
   function func() public {
       Joker k = new Joker();
       k.x = 10; // 에러 발생
       k.myFunc(); // 에러 발생
   }
}



Visibility 이해 1

앞에서 언급한 4가지 visibility를 이해하기 위해, 예제 코드 하나를 작성했다. 아래 Solidity 코드를 그대로 배포(deploy)하려한다면 에러가 발생할 것이다. 총 10가지 에러를 발생할 것인데 각각의 원인이 무엇인지 알아보면서 이해해보자.

 

  • 에러발생1 : 상태변수는 external 이 될 수 없다.

  • 에러발생2 : 상태변수는 external 이 될 수 없기때문에, 접근도 못한다.

  • 에러발생3 : 상태변수가 private 이기 때문에, 상속받았더라도 접근하지 못한다.

  • 에러발생4 : 함수가 private 이기 때문에, 상속받았더라도 접근하지 못한다.

  • 에러발생5 ~ 8 : 상태변수는 어떤 visibility도 외부에서 직접 접근하지 못한다.

  • 에러발생9 : 함수가 internal 이기 때문에, 자신 혹은 상속받은 계약서만 접근가능하다

  • 에러발생10 : 함수가 private이기 때문에, 자신만 접근가능하다

pragma solidity >=0.5.0 <0.7.0;
contract Joker {
   uint external x1; // 에러 발생1
   uint public x2;
   uint internal x3;
   uint private x4;   
   
   function func1() external {
   }
   function func2() public {
   }
   function func3() internal {
   }
   function func4() private {
   }
}

contract Child is Joker {
   function func() public {
       x1 = 10; // 에러발생2
       x2 = 10;
       x3 = 10;
       x4 = 10; // 에러발생3
       
       this.func1();
       func2();
       func3();
       func4(); // 에러발생4
   }
}

contract Caller {
   function func() public {
       Joker k = new Joker();
       k.x1 = 10; // 에러발생5
       k.x2 = 10; // 에러발생6
       k.x3 = 10; // 에러발생7
       k.x4 = 10; // 에러발생8      

       k.func1();
       k.func2();
       k.func3(); // 에러발생9
       k.func4(); // 에러발생10
   }
}



Visibility 이해 2

이번엔, visibility 중 public, private, internal 를 한번 더 이해하기 위해 예제코드를 작성했다. Joker에 있는 함수 중 public으로 작성된 setData와 getData는 어디서나 호출이 가능하지만, private과 internal로 작성된 함수들은 접근 가능한 영역이 제한되어 있기 때문에, 알맞지 않은 곳에 사용하면 에러가 발생한다. 참고로, 컴파일(compile)시 에러가 발생하므로, 에러발생 지점들을 제거해주어야 에러가 발생하지 않을 것이라는 점을 알아둔다.

pragma solidity >=0.4.0 <0.7.0;
contract Joker {
 uint private data;
 function setData(uint a) public {
   data = a;
 }
 function getData() public view returns(uint) {
   return data;
 }
 function increase(uint a) private pure returns(uint b) {
   return a + 1;
 }
 function compute(uint a, uint b) internal pure returns (uint) {
   return a + b;
 }
}

contract DC1 {
 function test() public {
   Joker c = new Joker();
   uint local;

   local = c.increase(7); // private 호출 에러
   c.setData(3); // public 호출
   local = c.getData(); // public 호출
   local = c.compute(3, 5); // internal 호출 에러
 }
}

contract DC2 is Joker {
 function test() public {
   Joker c = new Joker();
   uint local;

   local = c.increase(7); // private 호출 에러
   c.setData(3); // public 호출
   local = c.getData(); // public 호출
   local = c.compute(3, 5); // internal 호출 에러
   local = compute(3, 5); // internal 호출
 }
}



Visibility 버전별 특징

 

solidity version 0.5.x

함수(function)의 Visibility는 하나를 반드시 명시해주어야 한다.

 

solidity version 0.4.x

함수(function)의 Visibility를 명시하지 않으면 public을 기본값으로 가진다.