잘 달린 주석은 좋다

그러나 주석이 필요 없는 코드를 짜는 것이 훨씬 좋다

 

주석이 불편한 이유?

코드를 유지, 보수하는 과정에서 주석까지 관리하고 다듬을 여유가 없다

>> 주석은 낡기 마련

 

코드의 수정이 많을수록 주석은 부정확할 확률이 높다

부정확한 주석을 사용할 바엔 아예 주석을 사용하지 않는 것이 좋다

 

 

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

주석이 필요한 상황? 코드의 품질이 나쁠 경우!

어수선한 코드를 주석으로 깔끔하게 정리할 시간에 그냥 코드 자체를 정리해라

 

 

코드로 의도를 표현하라

 

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

 

아래의 코드는 함수 이름만으로 의도를 표현하고 있다

많은 경우 주석으로 달려는 설명을 함수로 만들어 표현해도 충분하다

 

 

좋은 주석

어떤 주석은 필요하거나 유익하다

 

 

법적인 주석

 

때로는 회사가 정립한 구현 표준에 맞춰 법적인 이유로 특정 주석을 넣으라고 명시한다

각 소스 파일 첫머리의 저작권 정보, 소유권 정보 등

 

// Coopyright (C) 2003, 2004, 2005 by Object Mentor, Inc. All rights reserved,
// GNU General Public License 버전 2 이상을 따르는 조건으로 배포한다

 

정보를 제공하는 주석

 

때로는 기본적인 정보를 주석으로 제공하면 편리하다

 

// 테스트 중인 Respnder 인스턴스 반환
protected abstract Responder responderInstance();

위 주석은 추상 메서드가 반환할 값을 설명하고 있다

 

>> 함수 이름을 responderBeingTested 로 바꾼다면 주석이 필요 없긴 하다

 

// kk:mm:ss EEE, MMM dd, yyyy 형식이다
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

위의 주석은 코드에서 사용한 정규식이 시간과 날짜를 뜻한다고 설명한다

 

 

의도를 설명하는 주석

 

    public int compareTo(Ojbect o){
        if(o instanceof WikiPagePath){
            WikiPagePath p = (WikiPagePath) o;
            String compressedName = StringUtil.join(names, "");
            String compressedArgumentName = StringUtil.join(p.name, "");
            return compressedName.compareTo(compressedArgumentName);
        }
        return 1;   // 옳은 유형이므로 순위가 더 높다
    }

두 객체를 비교할 때 다른 어떤 객체보다 자기 객체에 높은 순위를 주는 코드

 

 

 

 

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

 

모호한 인수나 반환값의 의미를 명료하게 밝히는 주석이 유용

 

    public void testCompareTo() throws Exception{
        WikiPagePath a = PathParser.parse("PageA");
        WikiPagePath ab = PathParser.parse("PageA.PageB");
        WikiPagePath b = PathParser.parse("PageB");
        WikiPagePath aa = PathParser.parse("PageA.PageA");
        WikiPagePath bb = PathParser.parse("PageB.PageB");
        WikiPagePath ba = PathParser.parse("PageB.PageA");
        
        assertTrue(a.compareTo(a) == 0);        // a == a
        assertTrue(a.compareTo(b) != 0);        // a != b
        assertTrue(ab.compareTo(ab) == 0);      // ab == ab
        assertTrue(a.compareTo(b) == -1);       // a < b
        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
    }

인수나 반환값이 표준 라이브러리나 변경하지 못하는 코드에 속한다면 의미를 명료하게 밝히는 주석이 유용

>> 위험성이 높음, 주석이 옳은지 검증하기 어렵기 때문에

 

 

결과를 경고하는 주석

 

// 여유 시간이 충분하지 않다면 실행하지 마세요

public void _testWithReallyBigFile(){
	writeLinesToFile(10000000);
    
    response.setBody(testFile);
    response.readyToSend(this);
    String responseString = ouput.toString();
    assertSubString("Content-Length: 10000000", reponseString);
    assertTrue(byteSent > 10000000);
}

 

요즘에는 @Ingnore 속성을 이용해 테스트 케이스를 끄기도 한다

구체적인 설명은 @Ignore 속성에 문자열로 넣어준다

@Ignore("실행이 오래걸린다.")

 

publi static SimpleDataFormat makeStandardHttpDateFormat(){
	// SimpleDateFormat 은 스레드에 안전하지 못하다
    // 따라서 각 인스턴스를 독립적으로 생성해야 한다
    
    SimpleDateFormat df = new SimpleDateFormat("EEE, dd mmm yyyy HH:mm:ss z);
    df.setTimeZone(TimeZone.getTimeZone("GMT"));
    return df;
}

주석이 아주 적절한 예시

프로그램 효율을 높이기 위해 정적 초기화 함수를 사용하려던 프로그래머가 주석때문에 실수를 면할 수 있다

 

 

 

TODO 주석

 

앞으로 할 일을 // TODO 주석으로 남기면 편리하다

 

// TODO-MdM 현재 필요하지 않다
// 체크아웃 모델 도입하면 함수 필요 X
protected VersionInfo makeVersion() throws Exception{
	return null;
}

 

최신 IDE에서는 TODO 찾아 보여주는 기능이 있어서 주석을 잊어버릴 걱정도 없다

>> 주기적으로 TODO 점검하며 주석 삭제 가능

 

 

중요성을 강조하는 주석

 

대수롭지 않다고 여겨질 뭔가의 중요성을 강조하기 위해 주석 사용 가능

 

String listItemContent = match.group(3).trim();
// 여기서 trim은 매우 중요, trim 함수는 문자열에서 시작 공백을 제거한다
// 문자열에 시작 공백이 있다면 다른 문자열로 인식하기 때문이다
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end());

 

 

 

나쁜 주석

위의 몇 가지 케이스를 빼고 절대다수의 주석이 이 경우에 속한다

 

 

주절거리는 주석

 

public void loadProperties() {
    try {
    	String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
    	FileInputStream propertiesStream = new FileInputStream(propertiesPath);
    	loadedProperties.load(propertiesStream);
    } catch (IOException e) {
    	// 속성 파일이 없다면 기본값을 모두 메모리로 읽어 들였다는 의미다.
    }
}

 

catch 블록에 있는 주석은 다른 사람들에게 의미를 전달하지 않는다

해당 주석의 답을 찾으려면 코드를 뒤져야 한다

이해가 안되어 다른 모듈을 뒤져야 하는 주석은 독자와 제대로 소통하지 못하는 주석이다

 

 

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

 

// this.closed가 true일 때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception {
    if (!closed) {
        wait(timeoutMillis);
        if (!closed) {
            throw new Exception("MockResponseSender could not be closed");
        }
    }
}

주석이 코드보다 더 많은 정보를 전달하지 못한다

읽기 쉽지도 않다

 

 

public abstract class ContainerBase
    implements Container, Lifecycle, Pipeline, MBeanRegistration, Serializable {

    /**
     * 이 컴포넌트의 프로세서 지연값
     */
    protected int backgroundProcessorDelay = -1;

    /**
     * 이 컴포넌트를 지원하기 위한 생명주기 이벤트
     */
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);

    /**
     * 이 컴포넌트를 위한 컨테이너 이벤트 Listener
     */
    protected ArrayList listeners = new ArrayList();

    /**
     * 컨테이너와 관련된 Loader rngus
     */
    protected int backgroundProcessorDelay = -1;

    ...

Tomcat 에서 가져온 위 코드를 보면

 

1. 쓸모없고 중복된 Javadocs가 매우 많다

2. 코드만 지저분하고 정신없게 만든다

3. 기록이라는 목적에 전여 기여하지 못한다

 

 

 

오해할 여지가 있는 주석

 

위의 waitForClose() 함수를 다시 보면

this.closed 가 true 여야 메서드는 반환된다

아니면 타임아웃을 기다렸다가 this.closed 가 그래도 true 가 아니면 예외를 던진다

>> 주석에 담긴 살짝 잘못된 정보로 인해 this.closed 가 true 로 변하는 순간에 함수가 반환될거라고 생각할 수도 있다

 

 

 

의무적으로 다는 주석

 

모든 함수, 변수에 주석을 달아야 한다는 것은 어리석다

이런 주석은 코드를 복잡하게 만들고 거짓을 퍼뜨리며 혼돈과 무질서를 초래한다

 

 

/*

@param title CD제목
@param author CD저자
@param tracks CD숫자
@param durationInMinutes CD길이 (단위 분)

 */

public void addCD(String title, String author, int tracks, int durationInMunutes){
    CD cd = new CD();
    cd.title = title;
    cd.author = author;
    cd.durartion = durationInMinutes;
    cdList.add(cd);
}

 

 

이런 주석은 정말 아무런 가치가 없다

 

 

 

이력을 기록하는 주석

 

// * 12-nov-2001 : 버그수정
// * 05-dec-2001 : xx 클래스에 존재하는 오류 수정
...

소스 관리 시스템이 없을 때에는 이렇게 주석을 활용했지만, 지금은 그냥 쓰지말자

 

 

있으나 마나 한 주석

 

// 기본생성자
protected AnnulDateRule(){

}

// 월 중 일자
private int dayOfMonth;

전형적인 중복 정보를 전달하는 주석

 

 

 

닫는 괄호에 다는 주석

 

    try{
        while(true){
            ...
        }   // while
        
    }   // try
    catch(Exception e){
        ..
    }   // catch

중첩이 심하고 장황한 함수라면 의미가 있을 지 모르지만 작고 캡슐화 된 함수에는 잡음일 뿐이다

주석을 달 생각 하지 말고 함수를 줄일 생각을 하자

 

 

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

 

/* xx가 수정함 */

이런 주석은 쓸 필요가 없다

소스 코드 관리 시스템에 기록되어야 하는 정보

 

 

주석으로 처리한 코드

 

주석 처리된 코드는 이유가 있어 주석 처리 해놓았을 것이라고 짐작하게 만든다

이런 코드는 점점 쌓이고 질 나쁜 코드가 된다

 

 

 

HTML 주석

 

소스 코드에서 HTML 주석은 혐오스럽다

주석에 HTML 은 절대 넣지 말자

 

 

전역 정보

 

주석을 꼭 달아야 한다면 근처에 있는 코드만 기술하라

시스템의 전반적인 정보를 기술하지 마라

 

 

너무 많은 정보

 

주석에 흥미로운 역사나 관련없는 정보를 늘어놓지 마라

 

 

 

모호한 관계

 

주석과 주석이 설명하는 코드 사이의 관계가 명확해야 한다

 

// 모든 픽셀을 담을 만큼 충분한 배열로 시작(여기에 필터 바이트 더함)
// 그리고 헤더 정보를 위해 200바이트 더함

this.pngBytes = new byte[((this.width + 1) * this.height * 3)+ 200];

여기서 필터 바이트란 뭘까? +1과 관련이 있을까? *3 과 관련이 있을까? 아니면 둘다?

주석의 목적은 정보를 전달하는 것인데, 정보가 전달되지 않는다

코드를 설명하기 위한 주석을 설명하기 위한 무언가가 필요하다는 말 >> 안좋은 주석

 

 

함수 헤더

 

짧은 함수는 긴 설명이 필요 없다

 

 

 

'끄적 > Clean Code' 카테고리의 다른 글

Clean Code -6 오류 처리  (0) 2023.08.10
Clean Code -5 객체와 자료구조  (0) 2023.08.10
Clean Code -4 형식 맞추기  (0) 2023.08.10
Clean Code -2 함수  (0) 2023.08.09
Clean Code -1 의미있는 이름  (0) 2023.08.08

+ Recent posts