들어가며
소스 코드가 컴파일러나 인터프리터가 이해하고 실행하기 위해 작성되는 것이라면, 소스 코드 사이 사이의 주석은 사람이 읽고 코드를 이해 할 수 있도록 돕기 위해 작성된다. 세상에는 컴파일러를 위한 좋은 코드를 작성하는데 도움이 되는 많은 책들과 글, 심지어는 분석 도구들이 많이 나와 있지만 아쉽게도 사람에게 필요한 좋은 주석을 작성하기 위한 가이드라인은 상대적으로 적다.
먼저 소스 코드의 '주석(comment)'라는 주제에 대해 가장 인기 있는 글 중의 하나인 Visual Studio Magazine의 No Comment: Why Commenting Code Is Still a Bad Idea 를 살펴 보면, 주석에 대해 다음과 같이 이야기하고 있다.
- 주석을 작성하고 유지하는 것은 적지 않은 비용이 들어간다.
- 컴파일러는 주석을 확인하지 않으므로 올바른 주석인지 그렇지 않은지 확인 할 방법이 없다.
- 반면에, 코드에 관해서는 컴퓨터가 당신의 코드가 지시하는 바를 정확히 수행하고 있다는 것을 보증한다.
위의 이야기는 사실이다. 주석을 유지하는데는 적지 않은 비용이 들어가며, 가장 최악은 소스코드는 변경되었지만 주석에 대한 유지보수가 뒤따라가지 못해 오히려 주석이 소스 코드를 분석하는데 방해가 되는 경우를 필자는 종종 접했다. 하지만 위와 같은 너무 극단적인 이유로 주석을 달지 않는 것은 구더기가 무서워 장 못 담그는 것과 같다.
프로그램 작성 시 아무 생각 없이 많은 주석을 다는 것은 쉽다. 하지만 충분한 정보를 전달하는 좋은 주석을 추가하는 것은 어렵다. 심지어 주석을 많이 달았다고 해서 꼭 도움이 되는것만도 아니다. 나쁜 주석은 차라리 달지 않는 것만 못한 경우가 많다. 이번 포스트에서는 소스 코드를 이해 하는데 도움이 되는 좋은 주석을 달기 위한 규칙들에 대해 이야기 해보고자 한다. 다음은 주석에 관해 모두(..아니면 대부분이..) 행복해 질 수 있는 중간 타협점을 찾는데 도움이 되는 몇가지 규칙들이다.
규칙 1: 주석은 코드를 복제해서는 안된다.
규칙 2: 좋은 주석은 모호한 코드를 변명하지 않는다.
규칙 3: 명확한 주석을 작성할 수 없다는 것은 코드에 문제가 있다는 것을 의미할 수도 있다.
규칙 4: 주석은 혼란을 야기하는 것이 아니라 혼란을 없애야 한다.
규칙 5: 관용적인 사용에서 벗어나는 코드에는 주석을 사용하라.
규칙 6: 만일 코드를 복사해 온 것이라면 복사한 코드의 원본 소스에 대한 링크를 제공하라.
규칙 7: 가장 도움이 될 외부 참조에 대한 링크를 포함하라.
규칙 8: 버그를 수정했다면 주석을 추가하라.
규칙 9: 아직 완전하지 않은 구현을 표시하기 위해서는 주석을 사용하라.
이제 부터 이 포스트의 뒷부분에서는 이러한 규칙과 예제에 대해 설명하고 적용 방법과 시기에 대해 알아 보도록 하겠다.
규칙 1: 주석은 코드를 복제해서는 안된다
많은 주니어 프로그래머들은 강사들로 부터 주석을 많이 달것을 교육 받았기 때문에 때론 너무 많은 주석을 작성한다. 특히 인상 깊은 나의 경험중의 하나는 학생들이 각각의 닫힌 중괄호에 주석을 추가하여 어떤 블록이 끝나는지 표기하는 것을 보았다.
if (x > 3) {
…
} // if
또한 학생들에게 코드의 모든 라인에 주석을 달도록 요구하는 강사에 대해서도 들었다. 이것은 아~~주 초급자에게는 합리적인 교육 방법일 수 있지만, 이런 주석은 자전거의 훈련용 보조 바퀴와 같으므로 어느 정도 성장한 큰 아이들과 자전거를 탈 때는 제거하는 것이 합리적이다.
적절한 정보를 가지지 못하는 주석은 다음과 같은 이유로 마이너스 요소를 갖는다
- 시각적 혼란을 가중시킨다.
- 읽고 쓰는데 시간이 걸린다.
- 더 이상 유효하지 않을 수 있다.
일반적으로 나쁜 예는 아래와 같다.
i = i + 1; // Add one to i
위 주석은 코드에 대한 어떠한 유용한 정보도 제공하지 않으며, 심지어 코드가 수정 될 때 추가 유지 보수 비용이 발생한다.
아래 처럼 모든 라인에 주석을 다는 것은 이미 raddit에서 충분히 조롱거리가 되고 있다.
// create a for loop // <-- comment
for // start for loop
( // round bracket
// newline
int // type for declaration
i // name for declaration
= // assignment operator for declaration
0 // start value for i
주석은 코드에서 충분히 확인 할 수 있는 내용들을 복사해서 다시 작성 될 필요는 없다. 코드에서 충분한 정보를 제공하고 있다면 굳이 주석을 추가하여 중복 정보를 독자에게 제공 할 필요 없다.
규칙 2: 좋은 주석은 모호한 코드를 변명하지 않는다
주석의 또 다른 오용은 코드에 있어야 할 정보를 주석이 제공하는 것이다. 간단한 예는 한글자로 된 변수를 지정한 다음 주석으로 해당 변수의 목적을 설명하는 것이다.
private static Node getBestChildNode(Node node) {
Node n; // best child node candidate
for (Node node: node.getChildren()) {
// update n if the current state is better
if (n == null || utility(node) > utility(n)) {
n = node;
}
}
return n;
}
이런 경우 변수의 이름을 변경하는 것으로 주석을 제거할 수 있다.
private static Node getBestChildNode(Node node) {
Node bestNode;
for (Node currentNode: node.getChildren()) {
if (bestNode == null || utility(currentNode) > utility(bestNode)) {
bestNode = currentNode;
}
}
return bestNode;
}
As Kernighan과 Plauger는 The Elements of Programming Style에서 나쁜 주석을 달지 말고 코드를 다시 작성하라고 이야기 하고 있다.
규칙 3: 명확한 주석을 작성할 수 없다는 것은 코드에 문제가 있다는 것을 의미할 수도 있다
Unix 소스 코드에서 가장 악명 높은 주석은 복잡한 컨텍스트 스위칭 코드 위에 있는 "당신이 이것을 이해할 것이라고 기대하지 않습니다.(You are not expected to understand this)”다. Dennis Ritchie는 그 뒤에 "이것은 뻔뻔한 도전이 아니라 시험에 나오지 않을 것이라는 의미로 의도된 것이라고 설명했다. 심지어 그와 공동 저자인 Ken Thompson은 더 심각한 문제는 자신들도 이해하지 못했고("The real problem is that we didn't understand what was going on either.") 결국 나중에 다시 작성해야 했다고 한다.
이것은 Kernighan의 법칙을 생각나게 한다.
디버깅은 처음에 코드를 작성하는것 보다 두 배 더 어렵습니다. 따라서 코드를 가능한한 처음 부터 현명하게 작성한다면 이론상 디버그 할 만큼 똑똑하지 않은 것입니다.
코드를 독자들에게 이해 시키기 위해 이런 저런 군더더기 주석이 많이 필요하다면 그 코드는 심미적으로든 기능적으로든 아직 완벽히 제련되지 않은 코드일 가능성이 높다. 이런 경우 설명하기에 충분할만큼 이해하고 더 나은 방법으로 코드를 다시 작성해야 한다.
규칙 4: 주석은 혼란을 야기하는 것이 아니라 혼란을 없애야 한다
규칙 4에 대해 이야기 하기전에 Steven Levy’s 의 Hackers: Heroes of the Computer Revolution에 언급된 사례를 살펴보자.
[Peter Samson] 이 작성한 한 프로그램은 수백개의 어셈블리 언어의 명령어를 가지고 있었지만 1750라는 숫자를 가진 명령어 옆에 단 하나의 주석만이 있었습니다. 해당 주석은 RIPJSB였으며 사람들은 1750이 바흐가 죽은해임을 알때까지 그 주석이 무엇을 의미하는지에 대해 고민했어야 했습니다. Samson은 'Rest In Peace Johann Sebastian Bach'의 약자였습니다.
좋은 주석은 코드를 보는 다음 사람을 더할나위 없이 고맙게 하지만 위 사례는 전혀 그렇지 않다. 만일 주석이 독자들에게 혼란을 일으킬것 같다? 그렇다면 차라리 주석을 삭제해야 한다.
규칙 5: 관용적인 사용에서 벗어나는 코드에는 주석을 사용하라
다른 사람이 불필요하거나 중복된다고 생각할 수 있는 코드에 주석을 다는 것은 좋은 생각이다. 예를 들어 App Inventor의 코드를 살펴 보자.
final Object value = (new JSONTokener(jsonString)).nextValue();
// Note that JSONTokener.nextValue() may return
// a value equals() to null.
if (value == null || value.equals(null)) {
return null;
}
위 예제는 null을 리턴 할 수도, null과 동일한 값을 가진 value라는 객체를 리턴 할 수도 있는 예제다. 만일 주석이 없었다면 누군가 "단순화"하기 위해 위 코드를 수정할 충분한 가능성이 있다. 주석을 통해 오해 할 수 있는 코드가 필요한 이유를 적어 당신의 코드를 보는 독자의 시간과 불안을 줄이도록 하자.
주석을 달기 전에 코드에 설명이 필요한지 여부를 판단해야 한다. Kotlin을 배울 때 다음과 같은 안드로이 자습서의 코드를 본적이 있다.
if (b == true)
그리고 위 코드가 아래 처럼 변경 될 수 있는지 궁금했다.
if (b)
약간의 연구 후에 어글리 널 체크를 피하기 위해 Kotlin에서는 nullable boolean 변수가 명시적으로 true와 비교 된다는 것을 알았다.
if (b != null && b)
초보자를 위한 튜토리얼을 작성하지 않는 한 일반적인 관용구에 대한 주석은 포함하지 않는 것이 좋다.
규칙 6: 만일 코드를 복사해 온 것이라면 복사한 코드의 원본 소스에 대한 링크를 제공하라
대부분의 프로그래머는 때때로 온라인에서 찾은 코드를 사용한다. 소스에 대한 참조를 주석으로 달아 놓으면 미래의 독자가 다음과 같은 전체 내용들을 알 수 있다.
- 어떤 문제가 해결되고 있었는지
- 누가 코드를 제공 했는지
- 왜 이 솔루션이 권장 되었는지
- 주석 작성자가 어떻게 생각했었는지
- 여전히 잘 작동하는지
- 어떻게 개선 할 수 있는지
예를 들어 다음과 같은 주석을 살펴 보자.
/** Converts a Drawable to Bitmap. via https://stackoverflow.com/a/46018816/2219998. */
답변에 대한 링크를 따라가면 다음을 알 수 있다.
- 코드 작성자는 Stack Overflow의 상위 3%안에 드는 Tomáš Procházka다.
- 한 댓글 작성자는 이미 레포지토리에 통합된 최적화를 제공한다.
- 다른 댓글 작성자는 극단적인 경우를 피하는 방법을 제시하고 있다.
다음 주석과 대조해보도록 하자
// Magical formula taken from a stackoverflow post, reputedly related to
// human vision perception.
return (int) (0.3 * red + 0.59 * green + 0.11 * blue);
이 코드를 이해하려는 사람을 수식을 검색 해야만 한다. url을 붙여 넣는 것이 나중에 참조를 찾을 때 훨씬 빠르다. 일부 프로그래머는 자신이 코드를 직접 작성하지 않은 것으 부끄러워 말하기 꺼려 할 수도 있지만 코드를 재사용하는 것은 시간을 절약하고 더 많은 관심을 받을 수 있는 현명한 조치가 될 수 있다. 물론 이해하지 못하는 코드를 붙여 넣는 것은 안된다.
사람들은 Stack Overflow에서 많은 코드를 복사 한다. 보통 해당 코드는 저작자 표시가 필요한 Creative Commons 라이센스에 속한다. 참조 주석을 달아주는 것은 해당 요구 사항을 충족한다.
규칙 7: 가장 도움이 될 외부 참조에 대한 링크를 포함하라
물론 모든 참조가 Stack Overflow로 부터 나오는 것은 아니다.
// http://tools.ietf.org/html/rfc4180 suggests that CSV lines
// should be terminated by CRLF, hence the \r\n.
csvStringBuilder.append("\r\n");
표준 및 기타 문서에 대한 링크는 독자가 코드가 해결하는 문제를 이해하는 데 도움이 될 수 있다. 이 정보는 말도 안되게 디자인 문서의 어딘가에 있을 수도 있지만 잘 배치된 주석은 독자에게 언제 이것이 가장 필요한지, 어디에 가장 유용하게 쓰일 수 있을지를 알려 준다. 이 경우 링크를 따라 가면 RFC 4180이 RFC 7111에 의해 업데이트되었음을 나타낸다.
규칙 8: 버그를 수정했다면 주석을 추가하라
주석은 코드를 처음 작성할 때 뿐만 아니라 수정할 때, 특히, 버그를 수정할 때 추가되어야 한다.
// NOTE: At least in Firefox 2, if the user drags outside of the browser window,
// mouse-move (and even mouse-down) events will not be received until
// the user drags back inside the window. A workaround for this issue
// exists in the implementation for onMouseLeave().
@Override
public void onMouseMove(Widget sender, int x, int y) { .. }
주석은 독자가 현재 및 참조된 코드를 이해하는 데 도움이 될 뿐만 아니라 코드가 여전히 필요한지 여부와 테스트 방법을 결정하는 데 도움이 된다. 이슈 트래커를 참조하는 것도 도움이 될 수 있다.
// Use the name as the title if the properties did not include one (issue #1425)
어떤 라인이 수정되고 커밋 되었는지 찾기 위해 git blame을 사용 할 수도 있지만 커밋 메시지는 대체적으로 간략화 되기 쉽상이다. 그리고 가장 중요한 변경 사항은(예를 들어 fixing issue #1425)는 최근 커밋 목록에 존재하지 않을 가능성이 높다.
규칙 9: 아직 완전하지 않은 구현을 표시하기 위해서는 주석을 사용하라
때때로 알려진 버그가 있거나 구현이 완료 되지 않았는데 코드를 체크인 해야 하는 경우가 있다. 자신의 코드의 결함을 다른 사람에게 알리고 싶지 않은 유혹을 받을 수 있지만 TODO 주석과 같이 이러한 부분에 대해 명시적으로 표시하고 넘어가는 것이 좋다.
// TODO(hal): We are making the decimal separator be a period,
// regardless of the locale of the phone. We need to think about
// how to allow comma as decimal separator, which will require
// updating number parsing and other places that transform numbers
// to strings, such as FormatAsDecimal
이렇게 주석에 표준 형식을 사용하여 미구현 부분을 표시하면 기술 부채를 측정하고 해결하는 데 도움이 된다. 더 나은 방법은 이슈 트래커에 문제를 추가하고 주석에서 문제를 참조하는 것이다.
결론
위 예제들이 잘못된 코드에 대한 변명이나 나쁜 코드를 수정하지 않았음을 보여주었길 바란다. 주석은 다른 유형의 정보를 제공하여 좋은 코드를 보완한다. Stack Overflow의 공동 설립자인 Jeff Atweed는 "코드는 방법을 알려주고 주석은 이유를 알려줍니다(Code Tells You How, Comments Tell You Why)."라고 했다. 위에 언급된 규칙들을 따르면 귀하와 귀하의 팀동료들이 낭비할 시간과 좌절을 줄일 수 있을 것이다. 하지만 위 규칙들은 완벽한 것이 아니며 추가 의견이 있다면 언제든 제안해 주길 바란다.
부록 1. 같이 읽으면 좋은 글
- 개발 프로세스에 관한 소고
- Game API Design(Kgc 2010/배현직)
- The more open source, the better off we'll be
- 개발자로써 생산성을 키우기 위한 10가지 팁
이 포스트는 https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/ 의 내용을 한글로 번역한 내용이다.