3.2 코드의 문서화
프로그래밍에서 문서화라 함은 보통 소스 코드 안에 작성된 주석을 의미한다.
주석은 코드를 작성할 때 코드가 어떤 일을 수행하는지 머릿속에 있는 것을 밖으로 표현할 좋은 기회이다.
주석은 어떤 내용이든 담을 수 있다. 단, 코드 자체에서 당연히 알 수 있는 내용은 피하는것 이 좋다.
3.2.1 주석을 작성해야 하는 이유
주석을 작성해야 하는 이유는 자명하다.
그러나, 내가 작성하는 코드에 주석이 필요한 이유가 무엇인지 생각해보자.
어떤 경우네는 주석이 왜 중요한지 완전히 이해하지 못한 상태에서도 프로그래머 스스로 주석의 필요성을 꺠닫기도 한다.
3.2.1.1 사용법을 설명하기 위한 주석
주석을 사용하는 한 가지 이유는, 사용자가 그 코드와 어떻게 연동할 수 있는지 설명하기 위해서다.
헤더파일에 공개된 각 함수 또는 메서드에 주석을 달아서 그들의 기능이 무엇인지 설명한다.
또는, 정규 양식으로 주석을 정의하여, 각 메서드의 기능, 파라미터, 리턴값, 그리고 발생 가능한 익셉션을 나열하기도 한다.
공개 메서드에 주석을 제공하면 두 가지를 달성할 수 있는데,
첫번째는 코드로는 표현하기 어려운 내용을 자연 언어로 기술할 수 있다.
에를들어, saveRecord()와 openDatabase()의 호출 이후 사용여부를 알 수 없지만, 주석은 다음과 같이 작성이 가능하다.
/*
saveRecord()
주어진 레코드를 데이터베이스에 저장
이 메서드는 openDatabase() 메서드가 호추로딘 적이 없는 상태에서 이용되면
DatabaseNotopendException을 발생시킨다.
*/
두 번째는 공개 메서드의 주서은 그 메서드의 사용법을 담을 수 있다.
C++언어는 메서드의 리턴 타입을 지정하도록 강제헌다. 하지만 그 리턴값이 무엇을 의미하는지 알 수 없다.
예를들어, saveRecord() 메서드의 선언은 리턴타입이 int 라는것 을 알려주지만, 사용자로서는 int타입이 뭘 의미하는지 알 수 없다.( int가 어떤 데이터를 의미하는지, 키인지 몸무게인지 어찌아는가!)
/*
saveRecord()
주어진 레코드를 데이터베이스에 저장한다.
파라미터 :
Record& rec : 데이터베이스에 저장할 레코드
리턴값 : int
저장된 레코드의 ID값
익셉션 :
DatabaseNotOpenedException: openDatabase() 메서드가 먼저 호출되지 않았을 때 발생
*/
어떤 때 는 범용 타입 이나 파라미터 리턴값을 이용해서 정보를 전달하기도 한다.
이때는 정확히 어떤 타입이 전달되는지 문서화 해야만 한다.
대부분의 에디터는 핫키 조합을 통해 자동으로 문구를 삽입할 수 있다. 기억해놓자.
3.2.1.2 복잡한 코드를 설명하기 위한 주석
좋은 주석은 인터페이스 정의 뿐만 아니라, 구현 코드 안에서도 필요하다.
사용자 입력을 받고 처리 결과를 콘솔에 출력하는 단순한 프로그램이라면 코드 전체를 읽어서 이해하는 데 아무런 문제가 없을것이다. 하지만 현실에서 전문적인 프로그램은 알고리즘으로 되어 있거나 고급 기술을 사용한 코드로 작성해야 할 때가 자주 있다.
이러한 코드는 단순히 코드를 읽는 것 만으로는 이해하기 힘들다. 예를들어보자
void sort(int inArray[], int inSize)
{
for(int i = 1; i < inSize; i++) {
int element = inArray[i];
int j = j - 1;
while( j >= 0 && inArray[j] > element) {
inArray[j+1] = inArray[j];
j--;
}
inArray[j+1] = element;
}
}
위와같은 코드가 작성되었다면, 초보자라고 할 때 무엇을 위해 동작하는 코드인지 이해할 수 없다.
사용된 알고리즘에 대한 설명을 주석으로 달면 훨씬 이해하기 쉬워진다. 이때 루프 불변 변수(loop invariant)를 명세화 하고 해당 코드 블록이 수행되는 동안( 루프 반복문 등 ) 만족되어야 할 조건도 함께 작성한다.
함수 앞에서는 전반적인 설명을 하고, 코드 안에서는 이해하기 어려운 라인들을 하나하나 짚어서 설명한다.
/*
삽입 정렬 알고리즘의 구현.
이 알고리즘은 배열을 정렬된 부분과 정렬 안 된 부분으로 나눈다.
1번 위치에 있는 각 항목이 검사된다. 배열 앞 부분은 이미 정렬된 부분이기 떄문에
순서가 맞는 위치를 찾을 때 까지 배열을 탐색한다.
배열의 마지막 항목까지 탐색하면, 배열이 모두 정렬되었기 떄문에
알고리즘을 종료한다.
*/
void sort(int inArray[], int inSize)
{
// 1번 위치에서 시작하여 각 항목을 검사
for(int i = 1; i < inSize; i++) {
// 불변 조건 : 인덱스 0에서 i-1에 이르는(경계포함) 항목들은 정렬상태이다.
int element = inArray[i];
// j는 이미 정렬된 배열의 위치를 표시
int j = j - 1;
// 정렬된 배열의 현재 슬롯이 항목보다 상위면
// 슬롯 뒤로 시프트 함
while( j >= 0 && inArray[j] > element) {
inArray[j+1] = inArray[j];
j--;
}
// 이 시점에서는 정렬된 배열의 현위치가 항목보다 크지 않기
// 때문에 현 위치가 새로운 위치가 된다.
inArray[j+1] = element;
}
}
이런 코드는 언뜻 보기에 지저분해 보이지만, 정렬 알고리즘에 익숙하지 않은 사람이 코드를 이해하기에는 훨씬 좋다.
어떤 회사에서는 라인 사이에 들어가는 주석을 싫어하기도 한다.
그런 경우에는, 간결하게 작성하되 함수 앞부분에 충실한 주석을 다는것 이 매우 중요하다.
3.2.1.3 메타 정보를 제공하기 위한 주석
코드 자체보다는 더 상위 수준의 정보를 주기 위한 목적으로 주석을 달기도 한다.
이러한 메타정보(정보에 대한 정보) 는 코드의 개별적인 기능을 설명하기보다는 코드가 생성된 배경에 대해 설명한다.
예를들어 개발 조직 내에서 해당 메서드를 작성한 원저자를 기록해두기를 원한다고 하자.
물론 별도의 문서에 기록할 수 도 있다.
/*
저자 : marcg
날짜 : 2011.04.12
기능 : PRD version 3, Feature 5.10
*/
int saveRecord(Record& rec)
{
if(!imDatabaseOpen) {
throw DatabaseNotOpenedException();
}
int id = getDB()->saveRecord(rec);
if (id == -1) return -1; // bug #142를 해결하기 위해 추가됨 - jsmith 110428
rec.setId(id);
// TODO: setId()가 익셉션을 발생시킬 경우 처리 필요 - akshayr 110501
return id;
}
위 코드에서는 메타정보의 몇 사례를 보여준다.
파일의 저자, 생성날짜, 버전정보와 함께,
인라인 주석을 통해 코드에 관련된 버그 번호와 나중에 주의해야할 사항을 기록한다.
하지만, 소스 코드 관리 솔루션을 사용하고 있다면, 이렇게 애써서 변경 이력을 주석으로 달지 않아도 된다.
CVS등과 같은 소스코드 관리 솔루션은 코드를 체크인 할 때 주석을 달 수 있다.
체크인은 각 변경 사항마다 따로따로 해야한다.
주석을 작성하느라 배보다 배꼽이 더 큰 상황이 있을 수도 있다.
팀원들과 가장 유용한 형태의 주석이 무엇일지 이야기 해보고 주석에 대한 정책을 정하는 것이 좋다.
'전문가를 위한 C++정리' 카테고리의 다른 글
3. 코딩 스타일 3.3 코드 분할 3.3.1 리팩토링을 통한 코드 분할 (0) | 2024.01.16 |
---|---|
3. 코딩 스타일 3.2 코드의 문서화 3.2.2 주석 작성 스타일 (0) | 2024.01.16 |
3. 코딩 스타일 3.1 보기 좋은 코드의 중요성 3.1.1 ~ 3.1.2 (0) | 2024.01.13 |
2. 문자열의 활용 2.1 동적 문자열 2.1.5 비표준 문자열 (0) | 2024.01.13 |
2. 문자열의 활용 2.1 동적 문자열 2.1.4 로우 문자열 리터럴 (0) | 2024.01.13 |