솔리디티 v.0.5.2 스타일 가이드

Rachel Yeonju Kang
ReturnValues
Published in
15 min readJan 21, 2019

By Yeonju Kang, Technical Researcher at ReturnValues (yeonju.kang@returnvalues.com)

이 포스트에서는 이더리움 스마트 컨트랙트 작성 시 사용되는 언어인 솔리디티의 스타일 가이드에 대한 설명을 담고 있습니다. 스타일 가이드(코딩 스타일)을 따르지 않아도 코드에 문제가 없는 한 컴퓨터가 이해할 수는 있지만,
협업과 열람 및 유지보수를 위해 통일된 스타일을 준수 할 필요가 있습니다.

해당 포스트는 솔리디티 공식 문서(https://solidity.readthedocs.io/en/v0.5.2/style-guide.html)
의 내용을 따르고 있습니다. 특히 신입, 초보 개발자분들 중심으로 알기 쉽게, 가장 많이 쓰여지는 코드 위주로 가이드의 내용을 정리하였습니다.
코드 가이드의 내용은 업데이트에 따라 바뀔 수 있음을 미리 알립니다.

코드 Layout

들여쓰기

솔리디티에서는 들여쓰기 시 Space 키4번 쓰는것을 권장합니다.
또한 Tab 키와 Space를 같이 쓰는것은 피할 것을 권장하고 있습니다.

공백 Lines

상위 레벨 선언시에는 공백을 두 줄 넣습니다.

pragma solidity ^0.5.2;contract Apple {}contract Banana {}

컨트랙트 내 함수 선언시에는 공백을 한 줄 넣습니다.
그러나 추상적인 컨트랙트의 스텁(속이 빈) 함수와 같이 한줄 코드는 공백을 생략해도 좋습니다.

pragma solidity ^0.5.2;contract Apple {
function juice() public {
} function pie() public {

}
}

contract Banana {
function juice() public;
function cookie() public;
}

줄바꿈 및 규격

솔리디티는 파이썬의 PEP8 규약과 같이 한 줄에 작성할 수 있는 글자 수를
79 자 혹은 99자로 제한하고 있습니다. 매우 긴 코드를 작성할 시 가독성을 위해 다음과 같은 가이드라인을 제안하고 있습니다.

  1. 첫 번째 인자는 시작하는 괄호 옆에 작성하지 않습니다.
  2. 한 줄당 한번의 들여쓰기가 있어야 합니다.
  3. 각각의 인자는 각 줄의 같은 부분에 넣습니다.
  4. 종료의 ); 는 마지막줄에 따로 작성합니다.

매우 긴 함수를 호출한 예시입니다.

thisFuctionCallIsReallyLong(
longArgument1,
longArgument2,
longArgument3
);

그 밖에도 반환 값이 많은 함수선언, 인자가 많은 이벤트 선언 시 위와 같은 가이드 라인을 따르게 된다면 가독성이 훨씬 좋아집니다.

function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
) {
}event VeryLongAndManyArgs (
address sender,
address receiver,
bool isReady,
uint ammount,
bytes32 option
);
...emit VeryLongAndManyArgs (
sender,
receiver,
isReady,
ammount,
option
);

Layout 순서

스마트 컨트랙트의 작성 순서입니다.

1.Pragma 선언

2.상속 컨트랙트 선언

3.인터페이스 선언

4.라이브러리

5.컨트랙트 선언

pragma solidity ^0.5.2;import “./Owned.sol”interface {}
library {}
contract Testcontract is Owned {}

특히 import문은 항상 파일 최상단에 위치해야 합니다.

pragma solidity ^0.5.2;import "./Ownable.sol";
import "./SafeMath.sol";
contract Apple is owned {}
contract banana is SafeMath {
}

함수 순서

함수는 다음과 같은 순으로 같은 가시성의 함수끼리 그룹을 지어 작성합니다.
또한 그룹 내 view나 pure함수는 가장 마지막에 작성합니다.

constructor
fall back function
external
public
internal
private

pragma solidity ^0.5.2;contract Apple {
constructor() public {
} function A() external { } function B() external view { } function C() public { } function D() internal { }
}

구조 컨트롤

컨트랙트, 라이브러리, 함수와 구조체를 선언할 때 다음과 같은 괄호의 규칙을 따릅니다.

컨트랙트 선언과 같은 줄에서 여는 괄호를 적습니다.
여는 괄호와 컨트랙트명 사이에 한 칸 공백이 있습니다.
닫는 괄호는 컨트랙트와 같은 들여쓰기에서 마지막 칸에 작성합니다.

contract Apple {
struct AppleJuice {
uint8 liter;
bytes32 color;
}
}

조건문, 반복문에서 소괄호 안에 들어가는 조건부의 내용을 작성 할 경우
앞뒤로 공백을 하나씩 추가합니다.

if (조건부) {}for (조건부) {}

조건문에서 else 와 else if 구문 작성시 if 문의 괄호 닫는 부분과 같은 줄에 작성해야 합니다.

if (x < 10) {
x += 1;
} else if (x > 5) {
x += 2;
} else {
x += 3;

함수선언

짧은 함수를 선언할 때 여는 괄호는 함수 선언과 같은 라인에 작성합니다.
닫는 괄호는 마찬가지로 함수 선언이 시작되는 부분에서 마지막 줄에 따로 작성해야 합니다. 여는 괄호를 작성하실때는 한 칸 띄고 작성해 주세요.

function add(uint a, uint b) public pure returns (uint) {
return a + b;
}
function add(uint a, uint b) public pure onlyowner returns (uint) {
return a + b;
}

또한 접근 제어자의 커스텀 키워드인 modifier의 경우(위의 onlyowner)에는 가장 마지막에 작성할 것을 권하고 있습니다.

또한 위에서 설명했던 것과 같이 코드 최대 길이 규격에 맞추어 긴 이름의 함수 및 많은 접근 제어자, 인자를 가진 함수는 다음과 같이 작성 할 수 있습니다.

function veryVeryLongNameOfFunction(
address x,
address y,
address z
)
public
onlyowner
returns (
address someAdderess,
uint longArg1,
uint longArg2
)
{
doSomething()
return ();
}

Mapping

매핑 선언시 인자 타입과 매핑 키워드는 띄어 쓰지 않습니다. 그 외 접근제어자, 매핑이름은 한칸 띄고 작성합니다.

mapping(uint => address) addressList;
mapping(address => uint) public favoriteNumber;
mapping(uint => mapping(bool => Data[])) public data;

//올바르지 못한 예시
mapping (address => uint) public favoriteNumber;

Array

배열을 의미하는 [] 와 배열의 값 타입은 띄어쓰지 않습니다. 매핑과 마찬가지로 접근제어자, 참조타입 및 배열명은 한칸 띄고 작성합니다.

uint[] x;
address[] public customerList;
//올바르지 못한 예시uint [] x;
address[]public customerList;

String

문자열을 작성할 때 따옴표가 아닌 쌍따옴표를 반드시 사용해야 합니다.

bytes32 myName = "KYJ";//올바르지 못한 예시bytes32 myName = 'KYJ';

Naming Style

혼동을 피하기 위해 다음과 같은 네이밍 스타일을 따르고 있습니다. 각각의 스타일의 명칭입니다.

b :단일 소문자 (single lowercase letter)
B :단일 대문자 (single upppercase letter)
lowercase : 소문자
lower_case_with_underscore: 언더바( _ ) 가 들어간 소문자
UPPERCASE : 대문자
CapitalizedWords: 이니셜이 대문자, CapWords 라고도 함
mixedCase : 첫번째 단어의 이니셜이 소문자로 시작되며 mixedCase 라고 합니다. 카멜표기법과 같습니다.
Capitalized_Words_With_underscore : 언더바가 들어간 CapWords

컨트랙트, 라이브러리

솔리디티에서는 컨트랙트, 구조체, 함수 및 변수의 이름을 다음과 같은 표기법으로 지정하기를 권유하고 있습니다.

컨트랙트 및 라이브러리의 이름은 CapWords 스타일로 지정합니다.

또한 컨트랙트, 라이브러리의 이름이 그 파일의 이름과 일치해야 합니다.

만일 한 파일에 여러개의 컨트랙트 혹은 라이브러리가 포함된 경우 가장 핵심인 컨트랙트 이름과 일치하게 해주는 것이 좋습니다.

pragma solidity ^0.5.2;// 라이브러리 파일명 Owned.sol
contract Owned {
address public owner;
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
}
// 컨트랙트 명 Congress.sol
import "./Owned.sol";
contract Congress is Owned, TokenRecipient {
//...
}

struct, event

구조체, 이벤트의 이름은 CapWords 스타일을 따릅니다.

struct Fruits {
bytes32 name;
uint256 price;
}
event AfterTransfer(address somebody, uint ammount);

함수, 인자 및 변수

함수이름과 함수 인자, 변수이름은 mixedCase를 따릅니다.

function simpleFunction(uint simpleArg) {
uint number;
...
}

Enum

열거형(enum)의 이름은 CapWords를 따릅니다.

enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } 

Modifier

사용자 정의 접근제어자는 mixedCase를 따릅니다.

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

Natspec

솔리디티의 컨트랙트에서는 이더리움 자연어 사양 포맷을 기반으로 한 주석의 작성이 가능합니다. ///로 시작하는 라인 혹은 /**로 시작하고 */로 끝나는 여러줄 주석에 추가로 작성할 수 있습니다. ‘@’가 붙은 doxygen스타일의 태그를 사용하여 작성하게 됩니다. 이러한 Natspec의 작성은 사용자들에게 자연어 설명을 보여줄 수 있게 되며 반드시 작성해야 하는 부분은 아닙니다.

pragma solidity >=0.4.0 <0.6.0;/// @author The Solidity Team
/// @title A simple storage example
contract SimpleStorage {
uint storedData;
/// Store `x`.
/// @param x the new value to store
/// @dev stores the number in the state variable `storedData`
function set(uint x) public {
storedData = x;
}
/// Return the stored value.
/// @dev retrieves the value of the state variable `storedData`
/// @return the stored value
function get() public view returns (uint) {
return storedData;
}
}

Natspec에서는 다음과 같은 스타일의 태그를 사용합니다. 모든 태그는 선택사항입니다.

‘@title’ : 콘트랙트의 설명하는 타이틀
‘@author’: 콘트랙트의 작성자의 이름
‘@notice’ : 콘트랙트 혹은 함수 기능에 관한 설명
‘@dev’ : 개발자의 추가적인 설명
‘@param’ : 매개 변수에 관한 설명, 반드시 매개변수명을 입력해야 합니다.
‘@return’ : 함수의 반환 값에 대한 설명

Tips

위와 같은 내용의 가이드를 잘 준수할 수 있게 도와주는 몇가지 모듈 및 플러그인을 소개하고자 합니다.

Ethlint(solium)

Ethlint는 솔리디티 코딩 스타일 및 보안 문제에 대해서 여러분의 코드를 분석하고 수정할 수 있게 도와주는 대표적인 모듈입니다.
이전에 작성된 스마트 컨트랙트 개발/ 테스트에 유용한 모듈 에서 소개 되었던 Solium과 동일합니다. 버젼 업데이트와 함께 이름이 Ethlint로 수정된 것 같습니다만 사용법은 동일합니다.

다음과 같은 명령어로 설치하실 수 있습니다.

npm install -g ethlint

또는

npm install -g solium

설치 이후 해당 프로젝트 디렉토리로 이동하여 다음 커맨드를 입력합니다.

solium --init

다음과 같이 입력하면 해당 컨트랙트 파일 혹은 폴더단위로 틀린 부분을 찾아내 줍니다. 파일 단위로 찾으실 땐 첫번째 줄의 명령어를 입력하고 -f 다음에는 솔리디티 파일명을, 폴더 전체에서 찾으실 때에는 두번째 줄의 명령어와 -d 다음 폴더명을 입력하세요

solium -f contract.sol 
solium -d contract/

이상이 있을 경우 다음과 같은 메세지가 나옵니다.

해당 부분을 수정하고 아무 이상이 없다면 다음과 같은 메세지가 나옵니다.

그 외에도 주석처리를 통해 linting을 생략하실 수 있습니다.

//특정 라인에서 생략contract Foo {
/* solium-disable-next-line */
function() {
bytes32 bar = 'Hello world'; // solium-disable-line quotes
// solium-disable-next-line security/no-throw, indentation
throw;
}
}

해당 파일에서 전부 생략할 경우에는 최상단에 다음과 같이 작성합니다.

/* solium-disable */contract Foo {
...
}

직접 터미널에서 Ethlint를 실행하는 방법 외에도 개발 시 VS Code를 쓰고계신 분들을 위해 VS Code에서는 솔리디티를 위한 다양한 플러그인을 제공하고 있습니다.
다음과 같은 플러그인들은 문법의 하이라이팅뿐만 아니라 컴파일러 지원과 더불어 Solium (Ethlint), Solhint (Solium과 같이 보안과 스타일 준수를 위한 오픈소스 프로젝트)의 기능을 추가로 제공하고 있습니다. 작성중에도 틀린 부분이 있다면 바로 코드에서 확인이 가능하다는 장점이 있습니다.

잘못된 부분이 있을 경우 바로바로 알 수 있어 편리합니다.

이상으로 솔리디티 스타일 가이드에 대해서 정리해 보았습니다. 이러한 가이드는 굉장히 기본적인 부분이기도 하지만 놓치기 쉬운 부분도 반드시 있을 것이라 생각합니다. 대부분의 스타일은 기존의 언어들과 차이가 크게 나지 않지만 솔리디티만의 특징도 있기 때문에 익숙하지 않은 분들에게 이와 같은 설명과 플러그인이 많은 도움이 되었으면 좋겠습니다.

--

--