1. 개요
Code Conventions코딩 스타일이라 함은 기본적으로 소스 코드를 작성할 때의 불문율적인 편집 규약에 가깝다. 영어로는 코드 컨벤션이라 하며 현업에서는 영어 쪽으로 더 많이 지칭한다.
사실 기계가 이해하기엔 결과적으로 동일하지만, 코드를 작성하는 건 인간이기 때문에(비단 코드를 짠 본인이 아니라도) 열람 및 유지보수를 용이하게 하기 위해 어느 정도 통일된 스타일의 필요성이 생겨난 것으로 추정된다.
이러한 코딩 스타일은 코드 유지보수에 큰 영향을 끼치며, 좋지 않은 코딩 스타일에 익숙해질 경우 능률이 저하되거나 타인과의 공동작업에서 혼란이 가중될 수 있으니 참고하자. 특히, 코딩 스타일이 카멜레온마냥 변하는 것은 가장 안 좋은 습관이다.
디자인 패턴이 코딩 스타일의 유의어로 쓰이는데 굳이 차이점을 따지자면 디자인 패턴은 프로그램 설계도를 만들 때 프로그래머들이 자주 마주하는 문제들을 적절히 해결해주어 프로그램 성능을 올려주는 수학적 기호논리학적 이론이라면 코딩 스타일은 사람이 보기 편하게 작명하고 코드를 나열하는 잡다한 원칙이다.
이런 것 때문에, IT 회사나 아니면 웬만한 기업에서 진행하는 거대한 프로젝트에서는 미리 지켜야 할 코딩 스타일을 정의해놓기까지 한다. GitHub 오픈소스 프로젝트에서 이런 규약을 쉽게 볼 수 있다.
2. 괄호의 위치
2.1. 주요 유형
괄호의 위치에 따른 코딩 스타일에는 대표적으로 K&R[유래], BSD(Allman)[2], GNU 3개가 있다.각 코딩 스타일은 서로 장단점이 있으며,
대부분의 언어는 대표 코딩 컨벤션을 가지고 있다. 일반적으로 그 언어의 표준을 따르게 되며, 프로젝트별로 코딩 스타일이 정해져 있는 경우도 있다. 따라서 개인 프로젝트가 아닌 이상, 자기가 좋아한다고 해서 마음대로 코딩 스타일을 변경하기는 힘들다.
K&R |
|
여는 블록을 if와 같은 행에 배치한다. 코드 줄 수를 절약하여 한눈에 많은 코드를 볼 수 있고 수평으로 많은 코드를 작성할 수 있다. 흔히 C 계열 창시자들이 사용하던 스타일이다. Java 계열 Eclipse / 구글 C++[3], JavaScript[4] 등의 기본 포맷팅이다. 블록 내에 문장이 하나만 있을 경우에는 중괄호를 쓰지 않는 것이 특징이며, 문장이 하나인 경우에도 중괄호를 써주는 것은 One True Brace(1TBS or OTBS)라고 한다. |
BSD |
|
GNU의 블럭의 소속을 분명히 한다는 장점과 K&R의 수평으로 많은 코드를 작성할 수 있다는 장점을 가져와 결합한 스타일이다. 줄 수는 GNU 스타일만큼 늘어나지만 수평으로는 K&R만큼 빽빽하게 쓸 수 있다. Visual Studio에서의 기본 포맷팅이다. |
GNU |
|
블록을 if문 아래에 작성한다. 블록이 if에 속한 블럭임을 분명히 표시하여 구조가 잘 보인다. 허나 들여쓰기를 많이 해서 처리하기에 수평으로 많은 코드를 작성할 수 없다. 물론, 일반적인 들여쓰기를 공백 4자로 두고, 괄호의 들여쓰기는 2자로 두는 등의 방법도 존재한다. |
위 설명에서는 조건문과 반복문, 함수 등의 차이는 대충 뭉뚱그렸는데, 실제로는 조건문과 반복문, 함수 등에서 들여쓰기의 차이를 두는 경우도 있다.
K&R과 Allman을 합친 Horstmann 스타일이 따로 존재한다.
#!syntax cpp
int main()
{ while (x == y)
{ something();
somethingelse();
if (some_error)
{ /* the curly braces around this code block could be omitted */
do_correct();
}
else
continue_as_usual();
}
finalthing();
}
그 외 다양한 스타일은 위키 문서를 참조하자.
2.2. K&R과 BSD 비교 예제
같은 코드를 K&R과 BSD로 작성하고 비교해 보자.K&R |
|
BSD |
|
BSD 쪽이 좀 더 아래로 길어지는 것을 볼 수 있다. 코드를 종이에 뽑을 일이 있으면 이 문제는 좀 더 중요해진다.
한편, 무슨 일이 생겨서 저 반복문을 반복문이 아니게 해야 한다고 하자. BSD는 for문이 있는 줄만 통째로 지워도 된다. for문에 붙어 있던 중괄호는 단순 블록이 되어 코드에 영향을 미치지 않는다. 물론 변수 i가 for문의 조건식 안에서 선언되었다면 컴파일 에러가 발생하므로 주의.
BSD |
|
그런데 K&R 스타일에서는 그렇게 하면 여는 괄호만 지워지고 닫는 괄호는 안 지워진다. 그래서 리팩토링이 좀 귀찮아진다.
K&R |
|
사실 이 논란은 C/C++ 쪽에서 주로 일어나는 편이고, Java나 JavaScript, C# 등의 다른 프로그래밍 언어에서는 대부분 한 쪽으로 통일된 추세이다.
3. 변수/함수 명칭의 작성 스타일
식별자 명칭을 작성할 때 이름을 정의하는 것도 코딩 스타일 중 일부이다. 이 식별자에는 변수나 함수명, 타입명, 이름공간(네임스페이스) 이름을 모두 포함한다. 심지어 대형 프레임워크나 프로젝트에는 파일명에도 스타일을 적용하는 경우도 있다. 대표적으로 파일명으로 동적 라우팅을 하는 Next.js, Astro나 2차 확장자(*.service.ts, *.dto.ts 등
)로 코드의 종류를 결정하는 Angular, nestjs등이 존재한다.대표적인 표기법은 다음 6가지가 있으며, 헝가리안 표기법을 제외하고는 언어에 따라 적절히 혼합된다. 예를 들어 Java의 경우 변수, 메서드 등에는 카멜 표기법, 클래스 등의 타입에는 파스칼 표기법, 상수에는 대문자 스네이크 표기법이 권장되고 있다. C#은 Java와 비슷하지만 메서드에도 파스칼 표기법으로 쓴다는 점이 다르다. 파일명의 경우는 보통은 스네이크 표기법이 권장되고 있으나, Java, C# 등 일부 언어에서는 (대표) 타입명과 동일한 파일 이름을 권장하기도 한다.
3.1. 카멜 표기법(Camel Case)
camelCase
여러 단어를 연달아 사용할 때 각 단어의 첫 글자를 대문자로 적되, 맨 앞에 오는 글자는 소문자로 표기하는 것이다.
낙타의 등에 있는 혹과 같다고 하여 카멜(Camel) 표기법이라고 부른다. 표기에서도 볼 수 있듯 봉이 하나이기 때문에 단봉낙타 표기법이라고도 하며, 파스칼 표기법과 비교하여
lowerCamelCase
라고도 한다. 중간에 XML, JSON 같이 식별자 이름에 약자가 포함되는 경우는 해당 약자를 모두 대문자로 쓸 수도 있다.예:
parsedXMLElement
3.2. 파스칼 표기법(Pascal Case)
PascalCase
이 역시 연달아 오는 단어의 모든 앞글자를 대문자로 표기하는 것은 카멜 표기법과 같지만, 맨 앞에 오는 문자까지도 대문자로 표기한다.
카멜 표기법과 비교하여
UpperCamelCase
라고도 하며, 봉이 둘이기 때문에 쌍봉낙타 표기법이라고도 한다. 전술했듯 식별자의 특성에 따라 카멜 표기법과 파스칼 표기법을 적절하게 혼합하여 쓰는 작성 스타일이 대세로, 변수 정의에는 카멜 표기가, 타입 정의에는 파스칼 표기가 대세이다. 언어에 따라 전부 카멜, 전부 파스칼로 표기할 것을 권장하기도 한다.3.3. 스네이크 표기법(Snake Case)
snake_case
단어 사이에 언더바_를 넣어서 표기하는 것이다.
허나 한 단어에 언더바를 붙인
_apple
등의 명칭은 C++의 장래 예약어 확장을 위해 지양되고 있다. 자세하게는, 언더바를 사용한 후 바로 대문자로 시작하는(e.g. _Apple, _Banana, _Cucumber) 식별자나 인접한 언더바(_a_apple, _b_Banana), 또는 두 개의 언더바(\Apple, \banana)는 모든 스코프에서 지양된다. 언더바로 시작하는 모든 식별자는 전역 스코프에서만 지양된다. 언더바 바로 뒤의 문자를 대문자로 하면 Train_Case
, 소문자로 하면 spiral_case
로 불린다.언어 내의 식별자뿐만 아니라, 언어가 적힌 파일명을 작성하는 데에도 주로 사용된다. 파일명에 공백이 들어가면 터미널에서의 작업이 힘들기 때문.
3.3.1. Screaming Snake Case
SCREAMING_SNAKE_CASE
snake case와 비슷하게 단어 구분자로
_
를 사용하지만, 나머지 문자를 전부 대문자로 적는 표기법. MACRO_CASE
라고도 부른다.주로 상수 정의, 환경 변수 정의 등에 사용된다. 한글 등에는 없는 특징이지만 영어에서 문장을 전부 대문자로 적을 경우 굉장히 강조되어, 마치 화자가 소리치는 것 같은 느낌을 준다. 따라서 주로 경고의 의미가 있거나, 사용 시 주의해야 하는 경우 이렇게 표기하기도 한다.
상수 개념이 없는 파이썬 같은 언어에서 상수를 선언할 때에 권장되는 방법이기도 하다. const나 final 등으로 알아서 값 변경을 차단해주는 타 언어와 달리 파이썬 같은 경우는 실수로 값을 변경하지 않도록 그냥 개발자가 조심해야 하기 때문에, 일반 용도의 변수가 아님을 대문자로 적어 강조하는 것이다.
3.4. 케밥 표기법(Kebab Case)
kebab-case
snake case와 거의 비슷하지만 구분자로
-
를 사용한다. 단어들이 마치 케밥 꼬치에 꽂힌 것처럼 보인다고 해서 붙은 이름이다.현업에서 사용되는 언어들 중 케밥케이스를 공식 컨벤션으로 강제하는 언어는 사실상 없다. 오히려 불가능한 경우가 많은데, 컴파일러가 변수명의 '-'를 '빼기'로 인식하는 경우가 많기 때문. 주로 파일명 등을 공백 없이 작성할 때나 CSS 클래스명 등의 특수한 경우에 사용된다.
3.5. 헝가리안 표기법(Hungarian Notation)
접두어에 자료형을 붙인다. Windows API가 이 표기법을 사용한다.예시로
strName
, bBusy
, szName
등이 있다. 장점은 변수명만 봐도 자료형을 유추할 수 있어 가독성과 편의성이 향상된다는 것이다. 때문에 자료형 지원이 다양하지 않았던 시기에는 자주 사용되었다. 단점은 접두어만큼 변수명이 길어지기 때문에 사람에 따라서는 오히려 역으로 가독성이 깎일 수 있다는 것과, 개발 도중에 자료형이 바뀐다면 모든 변수명을 수정해주어야 하는 난감한 상황이 연출된다는 것이다.[5]과거에는 거의 표준 수준의 코딩 스타일인 시절도 있었지만 2020년대에는 예전에 비해서는 잘 사용되지 않는 스타일이 되었는데, 현대에는 자료형도 문서 데이터도 더 다양해졌고, IDE(통합 개발 환경)의 발전으로 변수 위에 마우스만 가져다 대도 타입을 곧바로 확인할 수 있게 되었기에 굳이 변수명까지 이중으로 표기해야 할 필요성이 줄어든 것이 크다. 자료형이 명백하게 제한되는 경우라 해도 보통은 다른 표기법으로 대체되는 경향이 크다.
자세한 내용은 헝가리안 표기법 문서 참고하십시오.
4. 들여쓰기
들여쓰기를 탭으로 하느냐 스페이스로 하느냐는 텔레타이프 터미널이 도입된 이래 개발자들의 유서 깊은(?) 논쟁거리 중 하나이다. 탭으로 들여쓰기를 하면 탭 키 한 번에 끝이고, 반대로 커서를 이동시킬 때도 화살표 키/백스페이스 키 한 번이면 되니 정말 간단하겠지만 IDE, 에디터가 다를 경우 각 시스템의 설정값에 따라 다르게 보일 수 있기에 스페이스로 들여쓰기를 하는 사람도 많다. 실제로 2013년 GitHub에 올라온 코드를 분석한 결과 2:1의 비율로 탭보다는 스페이스를 선호하는 모습을 보이고 있다. # 다만 최근의 IDE나 에디터들의 경우 탭 키를 눌러도 스페이스로 처리해 주는 기능이 있기 때문에, 이 문제는 요즘 들어서 크게 논란이 되지는 않고 있다. 특히 Python처럼 탭과 스페이스가 섞여있으면 오류를 뿜는 언어를 사용할 때는 이 기능이 매우 중요하다. 대신 요즘에는 들여쓰기를 두 번 하냐 네 번 하냐로 갈린다. HTML 쪽은 코드 라인이 길어지는 경향이 있어 두 번 들여쓰기를 선호하는 경우가 많다.
한편 탭과 스페이스의 들여쓰기법을 같은 소스 코드에 여기저기 썼다가는 코드에 혼돈의 카오스가 펼쳐지고, 팀 프로젝트일 경우 팀원들로부터 욕이란 욕은 다 먹게 될 가능성이 높으므로 제발 그러지 말자. 물론 어지간한 IDE에서는 코드 리포맷팅을 지원하므로 한 번 돌려주면 깔끔해진다.
[유래] Kernighan&Ritchie의 <The C Programming Language>에서 쓰였기 때문.[2] C언어에서 주로 Allman 이라 부른다. 한때 메일 서버계의 IE로 불리던 sendmail의 저자이며, 게이 프로그래머로 유명한 Eric Allman의 이름에서 따옴.[3] https://google-styleguide.googlecode.com/svn/trunk/cppguide.html[4] https://google.github.io/styleguide/javascriptguide.xml[5] 특히 컨테이너 계열에서 자주 일어난다. ArrayList 계열 컨테이너를 LinkedList나 다른 Set, Map, Hash 등의 컨테이너로 바꿀 때. 물론 리펙토링 기능이 있다면 그걸 쓰면 편하지만 그마저도 귀찮다.