읽기 좋은 코드가 좋은 코드다/3 - 주석 달기

‘읽기 좋은 코드가 좋은 코드다’를 읽고 정리한 내용입니다. 문제가 될 시에는 삭제하겠습니다.

주석달기

설명하지 말아야 하는 것

주석으로 다는 내용에는 한눈에 보기에도 명확한 내용은 쓸모가 없다. 생성자나 변수의 타입에 대한 설명은 코드를 읽는 사람에게 어떤 추가적인 정보를 주는 것이 거의 없으므로 없는 것이 낫다.

마찬가지로, 코드에서 빠르게 유추할 수 있는 내용은 주석으로 달지 않는 것이 좋다. 간단한 내용의 코드는 오히려 읽는 사람이 코드를 보며 어떠한 일을 수행하는지를 유추해 내는 것이 더 빠르다.

1
2
// 전체 이름에서 성을 가져온다.
String firstName = fullName.split(',')[1];

이 코드는 fullName에 대한 사전지식이 있다면 주석을 보지 않고도 어떠한 내용을 하는 것인지를 빠르게 유추할 수 있기 때문에 주석을 다는 것에는 의미가 없다.

주석을 다는 대상들 중에 함수 이름이 불명확하거나 너무 짧은 경우에는 주석을 달기도 한다. 하지만 나쁜 이름에 주석을 다는 것보다는 이름을 바꾸는 것이 낫다. 함수 자체가 스스로 자신이 어떠한 일을 하는지에 대해 설명을 하고있으면 굳이 주석을 달 필요가 없기 때문이다.


설명해야 하는 것

자신의 생각을 기록하라

좋은 주석은 자신의 생각을 기록하는 것 만으로도 탄생할 수 있다고 한다. 코딩을 하는 시점의 자신과 시간이 흐른 뒤에 그 코드를 다시 보게되는 자신은 분명히 다른사람이다. 따라서 코딩을 하는 시점에서 어떠한 생각으로 코드를 이렇게 짯는지, 어떠한 점을 깨달았는지를 기록해 두는 것은 좋은 주석이 될 수 있다.

아래의 주석은 현재 이 코드가 엉망이라는 사실은 밝히고 다음 사람에게 어떠한 방식으로 코드를 수정해야하는지를 알려주고 있다.

1
//이 클래스는 점점 엉망이 되어가고 있다. 어쩌면 'ResourceNode' 하위 클래스를 만들어서 정리해야 할 지도 모른다.

아래의 주석은 개선해야 할 부분을 주석으로 알려주고 있다. 이는 주로 코드리뷰를 하는 중간에 많이 작성하게 되는 주석이다.

1
2
//TODO: 더 빠른 알고리즘을 사용하라.
//TODO: 이미지 포맷은 JPEG도 가능하여야 한다.

이런 경우에 주로 사용되는 주석들이 있는데 다음의 내용은 팀에 따라 다르게 사용될 수 있다.

  • TODO: 아직 하지 않은 일
  • FIXME: 오작동을 일으킨다고 알려진 코드
  • HACK: 아름답지 않은 해결책
  • XXX: 큰 문제가 있음

자신이 읽는 사람이 되어서 경고하라

주석을 다는 부분은 자신이 코드를 읽는 사람이라고 생각할 때 딱 보고 바로 이해되지 않는 위치에 주석을 달으면 된다. 읽는 사람이 마주칠 물음을 자신이 먼저 주석으로 대답하는 것이다.

또는 어떠한 함수에서 선조건이나 외부조건들이 있는 경우도 마찬가지다. 코드를 짜는 사람은 자신이 어떠한 조건일 경우에 이 함수에 오게 된다는 것을 알고있지만 코드를 읽는 사람은 그러한 사실을 알지 못한다.

1
2
//외부 서비스를 호출하여 응답을 받는다 (1분 후 타임아웃된다)
public void SendEmail(String to, String subject, String from);

큰 그림에 대한 주석

이 코드를 처음 보는 사람은 큰 그림을 이해하는데 어려움을 겪는다. 클래스 인입점이 어디이고, 어떠한 클래스들과 상호작용을 하는지를 알지 못한다. 따라서 이러한 내용을 주석으로 정리해 놓는 것은 큰 도움이 된다.

1
2
// 파일 시스템에 편리한 인터페이스를 제공하는 헬퍼 함수들을 담고있다.
// 파일의 퍼미션과 다른 자세한 세부 사항을 처리한다.

이렇게 클래스 단위로 큰 그림을 설명하는 것 이외에도 메소드 내부에서 간결하게 요약하는 것도 좋다.

1
2
// 구입한 항목을 모두 찾는 함수
public List<Product> findAllProduct();

주석을 다는 경우에 어렵게 생각하지말고 자신이 이 코드를 보는 경우에 나오는 말을 그대로 옮겨 적으면 된다.

1
// 이런 제길, 이 리스트 안에 중복된 항목이 있으면 복잡해지잖아.

이제 이러한 내용에서 각각의 내용을 구체적으로 바꿔주기만 하면 되는것이다.


명확하고 간결한 주석 달기

주석은 간결하게!

주석은 명확하게, 최대한 구체적이고 자세하게 작성해야 한다. 다른 사람, 혹은 본인이 시간이 지난 뒤에 자신이 썻던 주석을 보게 된다면 어떠한 의도를 띄고 있는지 명확하게 알기 힘든 경우가 많다. 그래서 뜻을 파악하기 위해 시간을 들여 주석의 내용을 이해해야한다. 이러한 시간낭비를 줄이기 위해 주석은 최대한 간결하고 명확하게 그 뜻을 전달할 수 있어야 한다.

아래의 주석에서 가르키고 있는 그것은 데이터인지 아니면 캐시인지가 불명확하다. 이를 확인하기 위해서는 코드를 직접 따라가보는 수 밖에는 없다.

1
//데이터를 캐시에 넣어라. 하지만 그것이 너무 큰지 먼저 확인이 필요하다.

주석을 다는 경우에는 그것, 이것 등의 대명사는 제외하고 직접적으로 그 이름은 언급해주는 것이 좋다.

1
// 데이터를 캐시에 넣어라. 하지만 데이터가 너무 큰지 먼저 확인하라 or 데이터가 충분히 작으면, 이를 캐시에 넣어라

함수의 동작을 설명해라

함수 위에 주석을 다는 경우, 대부분은 두루뭉술하게 이 함수가 어떠한 역할을 하는지를 나타낸다. 이러한 내용은 함수가 어떠한 방법으로 해당 작업을 하는지에 대해 불분명하므로 필요에 따라서는 코드 전체를 봐야할 수도 있다.

아래의 주석과 같은 경우가 위에서 언급한 경우이다.

1
// 이 파일에 담긴 줄 수를 반환한다.

줄 수를 세는 방법은 여러가지가 있다. 따라서 그 중 하나의 방법을 사용했다면 그 방법에 대한 이야기를 주석으로 적어주는 것이 더 명확하다.

1
// 파일 내부에서 새 줄을 나타내는 \n의 개수를 센다.

또한, 동작을 설명하는데 있어서 좋은 예시를 보여주는 것은 어떠한 주석들 보다도 효과적일 수 있다. 하지만 이러한 주석을 다는 경우에 있어서 너무 간단한 예시는 오히려 혼란을 줄 수 있다.

1
2
// Strip("abba/a/ba", "ab")는 "/a/"를 반환한다.
private String Strip(String src, String chars) {...}

코드의 의도를 명시하라

앞에서 언급했던 것과 마찬가지로 주석은 코드를 작성하는 당시의 개발자가 생각했던 의도를 작성하는 것이 중요하다. 그렇지만 일반적으로 그런 주석들은 문자 그대로의 동작을 설명하는 것이 전부다.

1
2
3
4
5
6
7
8
void DisplayProducts(List<Product> products) {
	products.sort(CompareProductByPrice);
    
	// 리스트를 역순으로 반환한다.    
    for(List<Product>::reverse_iterator it = products.rbegin(); it != products.rend(); ++it) {
    	...
    }
}

이러한 주석은 아래의 코드의 동작만을 전달할 뿐이다. 개발자가 개발할 당시의 의도를 주석으로 적어주는 경우 조금 더 명확하게 어떠한 의도로 코드를 작성했는지가 전달되고 에러가 있는 코드의 경우에도 이를 조금 더 수월하게 찾아낼 수 있다.


요약

설명하지 말아야 하는 것

  • 코드 자체에서 재빨리 도출될 수 있는 사실
  • 나쁜 함수명과 같이 나쁘게 작성된 코드를 보정하려고 애쓰는 주석, 대신 코드를 수정하라
  • 주석에 대명사를 사용하지 마라

설명해야 하는 것

  • 코드가 특정한 방식으로 작성된 이유를 설명해주는 내용
  • 코드에 담긴 결함, TODO 또는 FIXME와 같은 내용으로 표시
  • 어떤 상수나 함수가 특정 조건, 값을 가지게 되는 경우
  • 함수의 동작을 최대한 명확하게 설명하라
  • 적당한 예시의 입출력 예시를 주석으로 설명하라