본문 바로가기
Read Book/CleanCode

CleanCode 4장 주석

by I move forward every day. 2022. 9. 19.

출처: 인사이트

주석


  • 주석은 나쁜 코드를 보완하지 못한다.
  • 코드로 의도를  표현하라
  • 좋은 주석
  • 나쁜 주석
"나쁜 코드에 주석을 달지 마라. 새로 짜라."  -브라이언 w. 커니핸, P.J 플라우거

 

잘 달린 주석은 그 어떤 정보보다 유용하다. 그러나 우리에게 프로그래밍 언어를 치밀하게 사용해 의도를 표현할 능력이 있다면 주석은 필요 없다. 그러나 코드로 모든 의도를 표현하지 못해 때때로 주석을 사용하게 된다. 코드에 모든 생각을 담지 못한다면 그것은 자신에게 표현력이 부족하다는 것이다. 모든 진실은 코드에 담겨있다. 그렇기에 주석은 반가운 손님이 아니다.

 

 

 

주석은 나쁜 코드를 보완하지 못한다


코드에 주석을 추가하는 이유는 코드만으로 모든 것을 말하지 못하기 때문이다. 표현력이 풍부하고 깔끔하며 주석이 거의 없는 코드가, 주석이 많이 달린 어수선하고 복잡한 코드보다 훨씬 좋다. 더럽혀진 방이 왜 더럽혀져 있는지 설명할 시간에 깨끗이 치우는데 시간을 보내길 권장한다.

 

 

코드로 의도를 표현하라!


// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if ((employee.flags & HOURLY_FLAG) &&
	(employee.age > 65))
    

if (employee.isEligibleForFullBenefits())

분명 코드만으로 의도를 설명하기 어려운 경우가 존재한다. 하지만 위의 코드처럼 대다수의 경우는 몇 초의 시간만 투자한다면 코드로 의도를 표현할 수 있다. 주석으로 달려는 설명을 함수로 만들어 표현해도 충분하다.

 

 

좋은 주석


주석이 분명 필요할 때가 있다. 글자 값을 한다고 생각하는 주석 몇 가지를 알아보자. 그러나 잊지 않길 바란다. 가장 좋은 주석은 주석을 달지 않을 방법을 찾아낸 주석이다.

 

법적인 주석

회사에서 정립한 구현 표준에 맞춰 법적인 이유로 특정 주석을 넣으라고 할 때.

 

정보를 제공하는 주석

// 4~32자의 비밀번호는 4개 중 3개 이상 필요합니다(대문자
// 및 소문자, 숫자 및 특수 문자) 및 최대
// 2개의 동일한 연속 문자.
private static final String PASSWORD_REGEX = ^(?:(?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9]) + 
    (?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z]) + 
    (?=.*[^A-Za-z0-9]))(?!.*(.)\1{2,})[A-Za-z0-9!~<>,;:_=?*+#.”&§%°()\|\[\]\-\$\^\@\/] +
    {8,32}$

Pattern passwordRegex = Pattern.compike(PASSWORD_REGEX);

비밀번호를 만들 때에 필요한 조건 정보가 담긴 주석이다. 정규식을 하나하나 직접 알아내려면 꽤나 복잡할 것이다. 이런 때에 주석은 유용하다.

 

의미를 명료하게 밝히는 주석

일반적으로 인수나 반환 값 자체를 명확하게 만들면 좋겠지만, 인수나 반환 값이 표준 라이브러리나 변경하지 못하는 코드라면 의미를 명료하게 밝히는 주석이 유용하다.

public void testCompareTo() throw Exception{
    ......
    
    assertTrue(a.compareTo(a) == 0); // a == a
    assertTrue(a.compareTo(b) != 0); // a != b
    assertTrue(aba.compareTo(ab) == 0); // ab == ab
    assertTrue(a.compareTo(b) == -1); // a < a
    assertTrue(aa.compareTo(ab) == -1); // aa < ab
    assertTrue(ba.compareTo(bb) == -1); // ba < bb
    assertTrue(b.compareTo(a) == 1); // b > a
    assertTrue(ab.compareTo(aa) == 1); // ab > aa
    assertTrue(bb.compareTo(ba) == 1); // bb > ba
}

compareTo()에서 문자열을 비교할 때 기준값과 비교대상이 동일한 경우(0), 기준값이 비교대상보다 작을 경우(-1), 기준값이 비교대상보다 클 경우(1)를 반환한다. 이 부분에 대해 명료하게 알 수 없기에 주석을 사용하여 의미를 명료하게 밝힐 수 있다. 그러나 가장 최선인 주석을 달지 않고 해결할 더 나은 방법이 있는지 생각해보고 주석을 달도록 주의하자.

 

결과를 경고하는 주석

때로는 다른 프로그래머에게 결과를 경고해야 할 때가 있다. 특정 테스트 케이스가 돌아가는데 시간이 오래 걸린다면 해당 테스트 케이스를 꺼야 할 것을 권장한다.

 

TODO주석

TODO주석은 '앞으로 할 일'을 의미한다. 필요하다고 여기지만 당장 구현하기 어려운 업무들을 적어둔다. 누군가에게 문제를 봐달라고 요청할 때, 당장 좋은 이름이 떠오르지 않아 더 좋은 이름을 떠올려 달라 할 때, 앞으로 발생할 수정사항에 맞춰 코드를 고치라는 주의 사항을 남길 때에 유용하다. 그렇다고 TODO코드를 떡칠해서는 안된다. 해결할 수 있는 건 당장 해결하고 주기적으로 TODO 주석을 점검하여 없애도 괜찮은 것은 없애는 것이 좋다.

 

중요성을 강조하는 주석

대수롭게 여길만한 코드에 중요성을 강조하기 위해서도 주석을 사용할 수 있다. 예를 들어 공백 제거에 대한 부분이다. 문자열의 시작 부분에 공백이 있으면 다른 문자열로 인식된다던지 하는 부분이다.

 

 

나쁜 주석


대다수의 주석들은 나쁜 주석에 속할 가능성이 높다. 허술한 코드를 지탱하거나, 엉성한 코드를 변명하거나, 미숙한 결정에 대해 합리화하는 독백에서 크게 벗어나지 못한다.

 

주절거리는 주석

독자에게 정확한 정보전달을 하지 못하는 주석이다. 주석이 달려있음에도 불구하고 아무런 도움이 되지 못해 답을 알기 위해 다른 코드를 뒤져봐야 한다. 이런 주석은 바이트만 낭비하는 쓰레기일 뿐이다. 주석을 달기로 결정했다면 충분한 시간을 들여 최고의 주석을 달아줘야 한다. 

 

같은 이야기를 중복하는 주석

// orgName에 값이 비어있다면 전부 조회, 값이 있다면 orgName에 담긴 값을 포함한 orgName을 대소문자를 무시하고 조회해온다.
if(orgName.isEmpty()){
    resultList = cmsFileRepository.findAll(pageable);
}else{
    resultList = cmsFileRepository.findByOrgNameContainingIgnoreCase(orgName, pageable);
}

주석에 달린 내용이 코드의 내용을 그대로 중복한다. 이러한 코드는 주석을 읽는 시간이 코드를 읽는 시간보다 더 오래 걸릴 수 있다. 위의 주석은 코드보다 읽기 쉽지도 않으며, 주석이 코드보다 더 많은 내용을 제공하지도 못하고 있다. 이러한 주석은 코드보다 부정확해 독자가 코드를 대충 이해하고 넘어가게 만들 수가 있다.

 

의무적으로 다는 주석

함수를 만들 때에 Javadocs를 달거나 모든 변수에 주석을 의무적으로 달아야 한다는 규칙은 좋지 않다. 코드와 주석이 같은 이야기를 하는 괴물 같은 주석이 계속 탄생할지도 모른다. 이러한 주석은 코드만 헷갈리게 하고, 의무적으로 다는 주석에 잘못된 정보를 기술할 가능성만 생긴다.

 

이력을 기록하는 주석

20-05-26: ........를 추가..
20-07-07: ........를 변경...
20-09-03: ........를 수정..
20-12-25: ........를 추가...
21-03-14: ..........
21-11-26: ...........
22-03-25: ............
22-05-13: ...........

예전에는 이러한 방식으로 모듈을 편집할 때마다 모듈 첫머리에 변경 이력을 기록하고 관리하는 관례가 바람직했다고 한다. 그러나 지금은 소스 관리 시스템이 아주 잘되어있으므로 이러한 이력들은 혼란만 가중시킬 뿐이다. 제거하는 것이 좋다.

 

있으나 마나 한 주석

주절거리는 주석과 같은 말이다. 코드를 짜다가 뭔가가 잘 안 됐을 때에 주석으로 분풀이를 했다는 생각이 드는 주석들이다. 예를 들어 // 왜 이렇게 더러워!라는 식의 정말 쓸데없는 주석을 말한다. 그냥 대화할 사람이 없어서 주석이랑 대화라도 했다는 느낌이다.

 

함수나 변수로 표현할 수 있다면 주석을 달지 마라

// 전역 목록 <smodule>에 속하는 모듈이 우리가 속한 하위 시스템에 있는가?
if(smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))


ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

이 부분은 글쓴이도 이제까지 실수하고 있었던 부분이다. 코드를 한 줄로 작성하기 위해 변수에 굳이 담지 않고 작성했었다. 그러나 CleanCode를 읽으며 변수에 담거나 함수로 작성함으로써 의미를 표현할 수 있고, 표현해주는 것이 중요하다는 것을 깨닫는다. 물론 변수에 담지 않아도, 함수로 만들지 않아도 충분히 의미가 표현된다면 굳이 담을 필요는 없다. 중요한 것은 전달력이라고 생각한다.

 

위치를 표현하는 주석

============= Action =========================================
......
............
.............

아마 다들 한 번씩 작성해봤거나, 본적이 있을수도 있다고 생각된다. 글쓴이는 frontend작업을 할때에 드물지만 한번씩 썼던 것 같다. 배너 아래 특정 기능들을 모아 두는 것이다. 너무 자주 사용하지만 않는다면 이러한 배너는 눈에 띄며 주의를 환기해준다. 정말 주의할 것은 너무 무분별하게 너무 자주 사용하지 말라는 것이다. 코드를 읽는 사람이 익숙해진다면 흔한 잡음으로 여기게 될 수 있다.

 

닫는 괄호에 다는 주석

try {
	for (){
    	while() {
        
    	}// while
    } // for
} // try
catch (... e){

}// catch

예전에 학원에 다닐 때, 그리고 백준 코테를 풀 때에 이러한 주석을 많이 사용했었다. 닫는 괄호에 주석을 작성해 해당 반복문들이 어디서 닫히는지 확인하는 용도로 사용했었다. 이러한 닫는 괄호는 중첩이 심하고 장황한 함수라면 의미가 있겠지만 우리가 추구해야 하는 함수에게는 필요하지 않으므로 닫는 괄호에 주석을 달아야겠다는 생각이 드는 함수라면 함수를 줄이려 시도해보자.

 

공로를 돌리거나 저자를 표시하는 주석

/* 글쓴이가 작업 */

누가 작업을 했는지 보고 찾아가서 물어보는 데에는 유용할 수 있다. 그러나 소스 관리 시스템이 누가 언제 무엇을 수정했는지 전부 기억하기 때문에 굳이 주석을 달아줄 필요가 없다. 

 

주석으로 처리한 코드

글쓴이도 이 책을 읽기 전에는 자주 했던 행동이다. 뭔가 기능을 만들어놓고 요구사항이 바뀌었을 때 아직 커밋 전이라면 주석으로 기존에 코드를 남겨뒀었다. 그러나 이러한 코드는 시간이 지나면 쓰레기가 되고 다른 누군가는 중요하기에 남겨둔 코드라 생각해 계속 지우지 않고 남겨둘 가능성이 있다. 나중에 다시 쓸 것만 같은 코드라면 한번 커밋을 해둬 기록을 남기는 것도 좋은 방법인 것 같다.

 

HTML 주석

주석에 HTML 문법을 남겨두는 것인데 아직 한 번도 HTML 주석을 본적이 없다. 사용하지 않도록 주의하자. 혐오 그 자체라고 표현할 정도다.

 

전역 정보

/**
* 적합성 테스트가 동작하는 포트: 기본값 8082
*/
public void set........

주석을 달아야 한다면 근처에 있는 코드에 대해서만 기술하는 게 좋다. 또한 시스템 전반적인 정보를 기술하는 건 좋지 않다. 예를 들어 함수가 동작하는 포트를 주석으로 달아 놓았을 때다. 해당 함수는 포트를 통제하지 못한다. 시스템 어딘가에 있는 포트를 제어하는 다른 함수를 설명하는 일이 될 뿐이다. 또한 포트 기본값을 설정하는 코드가 변하면 주석도 함께 변한다는 보장이 없다.

 

너무 많은 정보

주석에는 너무 많은 정보를 알려줄 필요는 없다. 해당 알고리즘이 시작된 역사부터 어떤 게 어떻게 더해지고, 변하고, 인코딩 되고, 정렬되는지 하나하나 전부 늘어놓는 정보는 불필요하다.

 

모호한 관계

주석을 달았다면 독자가 주석과 코드를 보고 무엇을 말하는지 확실히 알 수 있어야 한다. 인수가 두 개라면 첫 번째로 무엇이 들어갈지, 두 번째에 무엇이 들어갈지 무언가를 더해야 한다면 그것이 첫 번째 인수인지, 두 번째 인수인지 설명을 정확히 해야 한다. 이러한 주석은 역시나 코드만으로 다 설명하는 능력이 부족해서다.

 

 

마무리


제4장 주석을 읽으며 느낀 점은 주석을 달지 않아도 함수나 변수의 이름으로 생각보다 잘 설명할 수 있다는 것이었다. 그간 함수와 변수명을 짓는 내 노력이 부족했음을 깨닫는다. 주석은 나쁜 코드를 보완하지 못한다는 말이 와닿는다. 주석이 달려있어도 누군가가 수정하려면 코드를 읽어야 하는데 코드 자체가 나쁘다면 주석이 별 도움이 되지 않기 때문이다. 클린 코드를 읽다 보면 반성할 일과 깨닫는 것이 참 많은 것 같다.

'Read Book > CleanCode' 카테고리의 다른 글

CleanCode 6장 객체와 자료구조  (0) 2022.10.05
CleanCode 5장 형식 맞추기  (0) 2022.09.24
CleanCode 3장 함수  (0) 2022.09.15
CleanCode 2장 의미 있는 이름  (0) 2022.09.14
CleanCode 1장 깨끗한 코드  (0) 2022.09.14