프로그래밍 강의/프로그래밍 정보

코딩 스타일?

wnsrn3436 2012. 1. 13. 20:12

 

코딩 스타일

 

코딩 스타일(Coding Style)이란 코드를 작성하는 방식에 대한 개인적인 기호라는 뜻이다. C/C++는 프리 포맷을 지원하며 코드의 형식에 대한 문법적인 제약이 없으므로 마음대로 작성할 수 있다. 개발자는 개성을 가진 인간이기 때문에 누가 작성하는가에 따라 코드의 모양이 조금씩은 달라질 것이다. 코드의 모양이야 어떻든 간에 컴파일러가 코드를 해석하기에 애매하지만 않으면 컴파일하는 데는 아무런 문제가 없다.

 

그러나 똑같은 내용의 소스라도 사람이 읽기 쉽고 구문 파악이 용이해야 이후 코드를 수정하기 편리하고 효율적으로 유지, 보수할 수 있다. 뿐만 아니라 소스의 구조가 잘 보이면 실수할 가능성이 낮고 골치 아픈 버그의 위험을 조금이라도 줄일 수 있다. 컴파일러는 기계이므로 스타일을 무시하지만 이 코드를 읽고 관리하는 사람은 코딩 스타일에 영향을 받으므로 코드의 작성 형태는 소스의 품질에 무시할 수 없는 중요한 요소이다.

 

코딩 스타일에 정답이라는 것은 없으며 가장 좋다고 소문난 스타일도 나와 맞지 않으면 쓸 수 없다. 어떤 스타일을 선택하든지 그것은 개인의 자유이므로 보기에 좋고 유지하기 편리한 스타일을 사용하되 단, 한 번 정한 스타일은 일관되게 지키는 것이 좋다. 여기서는 어떤 코딩 스타일이 있고 각각의 장단점은 무엇인지 객관적으로 비교해 보기로 한다. 남들은 어떤 식으로 코드를 작성하는지 구경해 보고 자신의 스타일을 만들어 보자.

 

중괄호 작성 스타일

 

C++, 자바, C# 언어를 흔히 { } 괄호 언어라고 부르는데 이는 이 언어들의 각 부분에 { } 괄호가 유독 많이 사용되기 때문이다. { } 괄호를 배치하는 형식은 소스의 전반적인 구조를 결정하는 가장 눈에 띄는 차이점이며 또한 개발자마다 각각의 스타일을 고집하는 대표적인 예이다. 여러 가지 스타일이 있지만 대표적인 세 가지 스타일을 들어 보면 다음과 같다. (if 문을 예로 들었는데 for, switch 등 대부분의 제어 구조에도 이 스타일들이 적용된다.)

 

if (조건)

    {

        명령

    }

if (조건) {

  명령

}

if (조건)

{

  명령

}

GNU

K&R

BSD

 

세 형식 모두 닫는 괄호는 별도로 한 줄에 작성하지만 여는 괄호의 위치와 블록 내의 들여쓰기 방식이 다르다. 어찌나 유명한 스타일인지 이름까지 붙어 있다.

 

■ GNU 스타일: { } 블록을 if 문 아래쪽에 작성한다. if 문에 속한 하위 블록임을 분명히 표시하기 위해 블록을 통째로 안으로 들여 쓰고 블록 내의 명령도 { 괄호보다 하나 더 안쪽으로 들여쓴다. 가끔 명령을 { } 괄호와 같은 레벨에 들여쓰는 변형된 스타일을 쓰는 경우도 있다. 구조가 제일 잘 보이는 스타일이기는 하지만 들여쓰는 정도가 너무 심해서 수평으로 많은 코드를 작성할 수 없다는 것이 단점이다.

 

■ K&R 스타일: C언어의 창시자들이 흔히 즐겨 썼던 스타일이며 C++의 창시자인 스트로스트룹도 이 스타일로 문서를 작성한다. 블록을 여는 괄호가 블록 시작행의 끝에 있다는 점이 특이하며 명령은 블록 안쪽으로 들여쓴다. 조건절을 주석 처리할 때 약간 불편한 면이 있으나 if를 블록 시작으로 보므로 블록 구조 파악에는 큰 무리가 없다. 이 스타일의 가장 큰 장점은 여는 괄호가 한 줄을 차지하지 않아 수직으로 더 많은 코드를 볼 수 있다는 점이다.

 

■ BSD 스타일: GNU 스타일과 K&R 스타일의 장점만을 취한다. 여는 괄호가 별도의 줄에 작성되어 소스가 좀 더 길어지는 단점이 있지만 블록 구조가 더 잘 보이며 블록이 if와 같은 레벨에 있어 들여쓰기도 심하지 않다. 블록의 시작과 끝이 한눈에 들어오고 수평 위치가 같아 유지, 보수에는 가장 유리한 스타일이다.

 

세 스타일 모두 함수를 정의할 때는 여는 중괄호를 별도의 행에 따로 배치한다. if 문이나 for에 걸리는 명령이 하나뿐이라면 굳이 { }를 쓰지 않아도 상관없지만 확장성에 불리하기 때문에 일반적으로 권장되지 않는다. 이 세 가지 스타일 중 GNU 스타일은 상대적으로 인기가 없는 편이며 K&R 스타일과 BSD 스타일이 주로 많이 쓰이는 스타일이다. 두 스타일의 가장 큰 차이점은 { 괄호의 위치인데 블록끼리 중첩될 때 수직 길이에 많은 차이가 있다. BSD 스타일이 여는 괄호의 개수만큼 더 길어진다.

 

if (k == 5) {

     for (i = 0;i < 10;i++) {

          if (ar[i] == 10) {

              func(ar[i]);

          } else {

              proc(ar[i]);

          }

     }

}

if (k == 5)

{

     for (i = 0;i < 10;i++)

     {

          if (ar[i] == 10)

          {

               func(ar[i]);

          }

          else

          {

              proc(ar[i]);

          }

     }

}

 

수직 길이가 더 길어진다는 것은 한 화면에 많은 코드를 볼 수 없다는 뜻이며 아래위로 스크롤 해 가며 읽어야 하는 불편함이 있다. 또한 코드를 인쇄했을 때 용지를 너무 많이 낭비한다는 경제적인 이유도 무시할 수 없다. 이런 이유로 과거에는 K&R 스타일이 주로 많이 사용되었으나 요즘은 고해상도 모니터의 도움을 받을 수 있으므로 점점 BSD 스타일을 더 선호하는 편이다. 하드웨어 지원이 충분하다면 BSD 스타일이 모양은 가장 좋다.

 

들여쓰기

 

들여쓰기(Indentation)란 블록 앞쪽에 탭이나 공백을 넣어 상위 블록보다 더 안쪽에 배치하여 코드의 종속적인 관계를 표현하는 기법이다. 들여쓰기를 하지 않으면 소스의 논리적인 구조가 잘 파악되지 않으며 블록 구조를 수정할 때 실수할 가능성이 높으므로 반드시 해야 한다. 대부분의 편집기는 자동 들여쓰기를 지원하므로 편집기의 안내대로만 소스를 작성해도 큰 문제는 없다.

 

들여쓰기를 할 때는 탭이나 공백을 사용한다. 탭은 일정 수의 공백을 대신하는데 탭 하나에 몇 개의 공백폭을 사용할 것인가를 선택할 수 있다. 일반적으로 4가 가장 무난하지만 좀 더 시원스럽게 보이고 싶다면 8을 쓸 수도 있고 수평으로 너무 깊게 들여쓰기 싫다면 2를 쓸 수도 있다. 탭은 제어 코드 하나로 여러 개의 공백을 들여쓸 수 있어 문서 크기가 작아진다는 장점이 있지만 편집기에 따라 폭이 틀려져 문서가 쉽게 깨진다는 단점도 있다.

 

class Some

{

     int           value;      // 정수 멤버

     double         rate;        // 실수 멤버

     unsigned u;           // 부호없는 정수 멤버

};

 

탭폭이 4인 편집기로 멤버 변수명과 주석의 수직 위치를 보기 좋게 잘 정렬해 놓았다. 멤버의 타입, 이름, 주석이 일목 요연하게 파악되므로 아주 깔끔하다. 그러나 똑같은 소스를 탭폭이 8인 편집기로 열어 보면 모양이 완전히 바뀌어 버리며 탭폭이 같더라도 글꼴이 바뀌면 정렬 상태가 깨질 수도 있다. 탭 문자는 절대적인 폭이 없고 주변 상황에 따라 다음 위치가 결정되는 논리적인 포맷 지정을 하기 때문이다.

 

class Some

{

     int                    value;           // 정수 멤버

     double              rate;        // 실수 멤버

     unsigned u;                     // 부호없는 정수 멤버

};

 

편집기를 늘상 같은 걸로만 쓴다면 탭이 편리하지만 이것 저것 바꿔가며 쓴다거나 여러 사람이 같이 작업해야 한다면 탭은 문서의 모양을 일정하게 유지하는 역할을 하지 못한다. 그래서 탭 대신 공백을 사용하고 고정폭 글꼴을 사용하는 사람들도 있는데 이렇게 하면 편집기에 영향을 받지 않고 항상 일정한 모양을 유지할 수 있다. 그러나 공백은 커서를 이동할 때 이동 속도가 느리고 문서가 커진다는 단점이 있다. 대부분의 편집기는 들여쓰기에 공백이나 탭을 선택할 수 있도록 되어 있다.

 

일반적인 블록의 들여쓰기 방식은 대체로 한 수준 내려갈 때 한번 들여쓰는 것으로 통일되어 있다. for 반복문이나 if 조건문이 시작될 때 들여쓰고 끝날 때 다시 내어쓰면 블록의 중첩 구조를 쉽게 표현할 수 있어 별다른 변형이 없다. 그러나 switch 문의 들여쓰기 방식은 차이가 있는데 case를 switch보다 들여쓰는 경우가 있고 그렇지 않은 경우가 있다.

 

switch (value)

{

     case 1:

          명령;

          break;

     case 2:

switch (value)

{

case 1:

     명령;

     break;

case 2:

 

case를 들여쓰는 쪽이 보기에는 더 시원스럽지만 case 안쪽을 또 들여써야 하므로 깊이가 너무 깊어진다. switch가 두 번만 중첩되어도 4번 들여쓰기 되어 소스가 오른쪽으로 치우치는 경향이 있으며 이렇게 되면 소스 뒷부분을 읽기 위해 수평 스크롤을 해야 하므로 번거로와진다. 보통 case는 switch와 같은 레벨에 두고 case안쪽만 들여쓰는 것이 가장 무난하다.

 

소스의 오른쪽 끝을 어디쯤에 맞출 것인지도 중요한 결정 사항 중 하나인데 수평으로 너무 길거나 들여쓰기가 깊어지면 불편해진다. 과거 도스 환경의 영향으로 인해 통상 80컬럼 정도에 맞추는 것이 보통이다. 이 길이를 넘어서면 적당한 곳에서 강제 개행을 하는 것이 좋다. 편집 환경의 해상도가 좀 더 높다면 100컬럼 정도까지도 큰 문제는 없지만 그보다 더 길게 작성하는 것은 별로 바람직하지 않다.

 

빈 줄 사용 여부

 

빈 줄은 아무 것도 없이 개행만 하는 줄이지만 가독성 높은 소스를 작성하는데 중요한 역할을 한다. 함수는 개별적인 작업의 단위이므로 보통 빈 줄을 하나씩 넣어야 함수간의 구분이 용이하고 보기에도 시원스럽다. 단순 변수 선언문들은 붙여서 쓰지만 구조체나 클래스같이 덩치가 큰 타입 선언문들도 빈 줄로 구분하는 것이 좋다.

 

함수 내부에도 빈 줄이 종종 사용되는데 선두의 지역 변수 선언문과 본체 코드 사이에도 개행을 하는 것이 보통이며 함수 내부의 코드 덩어리 사이에도 빈 줄을 넣어 다른 작업 그룹임을 분명히 해야 한다. 너무 붙여 버리면 어디까지가 어떤 동작을 하는 코드인지 잘 파악되지 않아 코드를 읽기가 어려워진다. 보통 루프나 조건문 등이 새로 시작될 때마다 빈 줄을 하나씩 삽입하는 편이다.

 

이 책에서 작성한 예제들을 보면 함수 중간 중간에 삽입되어 있는 빈 줄을 흔하게 볼 수 있다. 예를 들어 소코반의 Move 함수를 보면 지역 변수 선언문과 방향에 따라 이동 거리를 조사하는 switch문 사이에 빈 줄이 있고 switch문과 실제로 이동을 하는 if 블록 사이에도 빈 줄이 있어 각 코드 부분을 논리적으로 구분한다. main 함수를 봐도 전역 초기화, 스테이지 초기화, 입력 및 처리, 게임 끝 판별 등의 큼직한 덩어리 사이에 빈 줄이 삽입되어 있다.

 

클래스 선언문 내부에도 액세스 지정자별로 빈 줄을 하나씩 삽입하여 멤버들의 그룹이 잘 보이도록 해야 한다. 또한 멤버의 용도에 따라 적당히 빈 줄로 그룹을 나누어 놓으면 보기에 훨씬 더 좋고 새로운 멤버를 추가할 장소를 찾기도 용이하다. 빈 줄이 너무 많으면 소스가 길어지는 단점이 있기는 하지만 적당한 여백이 없으면 소스가 너무 갑갑해 보이므로 필요한 곳에는 아낌없이 빈 줄을 넣도록 하자.

 

공백 사용

 

공백은 쉽게 말해서 스페이스 하나를 삽입하는 것인데 문법적으로 공백이 없더라도 컴파일러는 소스를 잘 해석하지만 적절히 띄워 놓으면 소스의 가독성을 높일 수 있다. 공백에 대해서도 일반적으로 적용되는 지침이 있는데 키워드와 여는 괄호 사이에는 공백을 넣는다. 예를 들어 if (...)이나 for (...) 등의 예에서 if와 여는 괄호, for와 여는 괄호 사이에 공백이 하나 들어간다.

 

그러나 함수와 괄호 사이에는 일반적으로 공백을 넣지 않는다. strcpy(...), printf(...) 등과 같이 함수와 인수열의 괄호를 바로 붙여 쓴다. 함수와 실인수 목록을 하나의 문장을 본다는 시각이다. GNU 스타일은 strcpy ( ... )과 같이 함수와 괄호 사이도 띄우고 첫 번째 실인수와 여는 괄호도 띄우는 것이 일반적인데 익숙하지 않은 사람은 다소 어색해 보인다.

 

이항 연산자와 피연산자 사이에도 공백을 넣는다. a = 1; 이나 a = b + c; 이런 식으로 말이다. a=1;처럼 딱 붙여 버리면 조금 갑갑해 보이며 a=b+c;도 읽기에 쉽지 않다. 그러나 예외적으로 . 연산자와 -> 그리고 [ ] 연산자는 좌우변이 굉장히 긴밀한 관계이므로 붙여 쓴다. obj.mem 이렇게 붙여서 쓰는 것이 일반적이고 obj . mem 이렇게 띄우는 사람은 거의 없다. *pi, a++ 등의 단항 연산자는 하나의 연산 단위이므로 공백을 두지 않는다.

 

a = b + c * d + 3 + 8;

a = arScore[3] * pRecord->Rate;

 

포인터 선언문의 경우 공백 위치가 애매한데 보통 다음 두 가지 스타일이 사용된다. 이 외에 * 앞 뒤로 공백을 다 넣는 스타일도 있기는 하다.

 

int *pi;

int* pi;

 

이 두 스타일은 똑같은 선언문이지만 의도하는 바는 약간 다른데 전자는 정수형의 포인터 변수라는 뜻이고 후자는 정수 포인터형 변수라는 뜻이다. C언어에서는 전자가 많이 사용되었고 C++언어에서는 후자를 주로 사용하되 정수형과 정수 포인터형을 한 줄에 같이 선언할 때 문제가 좀 있어 선언문 하나당 하나의 변수만 선언할 것을 권장한다. 함수 호출문이나 정의문의 인수 목록 사이에도 콤마 뒤에 공백을 넣는 것이 일반적이다.

 

함수 정의시: void func(int a, double b, char c);

함수 호출시: func(123, 4.5, 'Z');

 

여러 개의 인수들이 나열되므로 인수 목록 사이에 공백을 두지 않을 경우 인수간의 구분이 용이하지 않다. 비슷하게 배열이나 구조체 초기식의 초기값들 사이에도 가급적이면 콤마와 공백을 같이 넣는 것이 좋다.

 

명칭 작성법

 

변수나 함수, 타입의 이름은 모두 명칭으로 작성되는데 명칭의 첫 번째 요건은 다른 명칭과 구분되는 이름을 가져야 한다는 것이며 또한 표현하고자 하는 대상과 연관된 이름을 가져야 한다. 구분을 위해서는 길이가 너무 짧아서는 안되며 입력의 편의를 위해서는 너무 길어서도 안되는데 보통 3 ~ 10 자 정도가 적합하다. 명칭의 대소문자 구성이나 접두, 접미를 붙이는 방법도 다양한데 대표적으로 다음 세 가지 정도를 들 수 있다.

 

전부 소문자 : score, remaintime, callnextlink

어근만 대문자 : Score, RemainTime, CallNextLink

낙타형 : score, remainTime, callNextLink

 

이 외에 Remain_Time 등과 같이 단어 중간에 _를 넣는 방법도 있는데 타이프 하기에는 조금 불편하지만 읽기에는 가장 좋은 명칭이다. 그러나 밑줄로 시작하는 명칭은 표준이 장래의 예약어확장을 위해 금지하고 있으므로 사용하지 말아야 한다. 매크로 상수는 전부 대문자로 작성하는 것이 보편적이다.

 

함수와 변수, 타입 등에 따라 이름을 다르게 작성하는 경우도 있는데 변수는 모두 소문자로 함수는 첫 글자만 대문자로 작성하는 식이다. 이 외에 변수의 이름에 b, a, p 등의 접두를 붙여 이름만으로 타입을 분명히 알 수 있도록 하는 방법도 흔히 사용되는데 특히 포인터 타입의 경우 pPos, pTime 처럼 p를 붙여 놓으면 포인터인지 쉽게 알 수 있어 꽤 유용하다. 이름에 명칭의 자격과 타입 정보까지 같이 표현하자는 취지이다.

 

주석

 

소스에 설명을 붙이는 주석도 일종의 코딩 스타일인데 되도록 많이 다는 것이 좋다고 생각하는 사람도 있고 불필요한 주석은 오히려 지저분하다고 생각하는 사람도 있다. 과거에는 주석이 많을수록 더 쉬운 소스라는 견해가 일반적이었지만 요즘은 주석보다는 설명적인 소스를 작성하는 것이 더 바람직하다는 견해가 지배적이다. 즉, 다량의 주석보다 주석이 필요없을 정도로 읽기 쉬운 소스가 더 좋다는 뜻이다.

 

한 줄 주석은 //로 작성하고 여러 줄 주석은 //를 여러 번 쓰거나 /* */를 쓴다. 짧은 주석은 코드 옆에 바로 붙이는 것이 좋고 조금 긴 주석은 코드 위쪽에 붙인다. 한 줄로 작성하기 곤란할 정도로 설명해야 할 양이 많다면 주석으로 달아놓는 것보다 별도의 문서를 따로 작성하고 참고 문서를 주석으로 표시해 놓는 것이 좋다. 소스내의 주석은 텍스트밖에 표현하지 못하지만 참고 문서는 도표, 그림, 흐름드 등을 세밀하게 표현할 수 있어 더 상세한 설명서를 작성할 수 있다.

Comments