자바스크립트를 활성화 해주세요

Clean Code 4장: "주석" 정리

 ·  ☕ 13 min read

4장 주석을 읽으면서 내용에 대해 보충하거나, 개인적인 의견으로 반박하거나, 고민해 볼 부분에 대해 적어보려 한다.

4장에서는 본격적인 이야기 전 서론부터 주석에 대해 잘 해봐야 필요 악 정도라는 표현 등 매우 부정적으로 취급하고 있다. 프로그래밍을 처음 배울 때는 주석을 꼭 써야 식으로 교육받았던 것 같지만, 나도 경험이 생김에 따라 주석에 대한 관점은 이 책처럼 어쩔 수 없이 최소한으로 작성하는 것이라 생각한다.

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

보통 주석을 작성하는 이유는 코드가 더럽기 때문이다. 작성한 모듈이 헷갈리고 체계적이지 않다는 이유로 주석을 달곤 하는데, 코드를 정리하는 것이 더 낫다.

어질러진 코드에 변명처럼 주석을 다는 데 시간을 보내지 말고, 깨끗한 코드를 작성하란 뜻이다.

코드로 의도를 표현하라

그나마 주석을 잘 작성하는 의도는 왜 이런 코드/흐름을 사용하는가를 설명하는 것이라 생각한다. 하지만 책에서는 그 조차도 최대한 코드만으로 표현하라고 한다.

// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && 
    (employee.age > 65))
if (employee.isEligibleForFullBenefits())

해당 예시는 좋을 수도, 나쁠 수도 있다고 생각한다. 책에서 의도하는 대로 직관적인 함수 명으로 주석을 대체할 수 있다면 좋겠지만, 해당 코드의 내용처럼 함수로 구분해야 할 만큼 자주 나오거나, 내용이 복잡할까? 차라리 캡슐화를 더 보강하기 위해 멤버 변수에 직접 접근하지 않고 메서드로 추출했다고 하는 편이 더 논리적인 것 같다.

좋은 주석

일부 주석은 필수적이거나 도움이 된다. (물론 주석이 없는 것이 더 도움이 된다.)

법적인 주석

각 코드에 저작권 및 소유권에 관한 법률 정보를 작성해야 하는 경우가 있다. 오픈 소스의 경우 각 라이선스에 관한 경고문이라거나, 업무 상의 코드는 회사의 법적 소유권을 증명하기 위해 필요하다.

사실 좋은 주석이라기보단 불가피하게 보존해야만 하는 주석이라는 표현이 맞을 것 같다. (사업적으로는 영향을 줄 수도 있겠지만, 개발자에게는 거의 영향을 주지 못하기 때문에)

정보를 제공하는 주석

기본적인 정보를 제공하기 위해 작성된 주석을 예시로 들고 있다.

아래와 같이 추상 메서드의 반환 값에 대한 설명을 하는 주석을 보자.

// Returns an instance of the Responder being tested.
protected abstract Responder responderInstance();
protected abstract Responder responderBeingTested();

그런데 위의 예시와 같은 추상 메서드는 팩토리 패턴 계열이나, 싱글턴 패턴에서 인스턴스를 생성하거나 받기 위해 사용할 것으로 보이는데, getInstance() 식의 함수 명이 아닌 것도 이상하고, 고친 예시로 나온 responderBeingTested()도 명사구로 표현된 메서드라는 점에서 이상한 것 같다. (테스트 혹은 검사를 위해 추상 메서드를 사용하는 것도 이상하다는 생각이 든다.)

다른 예시로 정규표현식의 결과를 표현하는 주석을 보여주고 있다.

// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile(
    "\\d*:\\d*:\\d* \\w*, \\w* \\d, \\d*");
SimpleDateFormat timeMatcher = new SimpleDateFormat("kk:mm:ss EEE, MMM dd, yyyy");

정규표현식의 경우 “Write once, read never"같은 소리가 나오는 가독성 면에서는 좋지 않은 표현이니, 이를 설명하기 위해 어떤 형태의 문자열이 매칭되는지 설명하는 것이 좋은 예시라며 보여주고 있다. (예시의 정규표현식이 잘못된 패턴도 매칭시킬수 있다는 문제가 있다는 점은 일단 무시하겠다.)

설명에서는 SimpleDateFormat.format() 함수를 언급하면서 해당 기능을 처리하기 위한 클래스로 추출하는 것이 더 좋을 것이라고 언급을 하는데, 결국 어디선가는 이런 정규표현식에 대한 설명이 필요할텐데, 새로운 객체 내부로 주석에 대한 책임을 미루겠다는 것으로 밖에 안들린다.

그리고 정규표현식의 예시는 아래 의미를 명료하게 밝히는 주석에서 언급하는 것이 더 좋지 않았을까?

게다가 초반에 코드로 의도를 표현하라에서 예시로 보여준 것을 다시 반박하는 것 같다.

의도를 설명하는 주석

전적으로 동의한다. 무엇을 할 것인지는 함수/모듈 이름이 표현하고 있고, 어떻게 할 지는 코드의 흐름이 표현하고 있다. 왜 이런 식으로 수행해야 하는 지에 대한 설명은 함수/모듈 이름이나 코드의 흐름만으로는 표현하기 힘들다.

물론 구구절절 모든 코드 문단마다 이유를 작성할 필요는 없다. 갑작스레 나타난 코드 흐름이라던가, 직관적으로 이해되지 않는 코드에 대해 어떤 의도를 가지고 작성된 것인지 설명만 해도 충분할 것이다.

public int compareTo(Objcet o)
{
    if (o instanceof WikiPagePath)
    {
        WikiPagePath p = (WikiPagePath) o;
        String compressedName = StringUtil.join(names, "");
        String compressedArgumentName = StringUtil.join(p.names, "");
        return compressedName.compareTo(compressedArgumentName);
    }
    return 1 // we are greater because we are the right type
}
public void testConcurrentAddWidgets() throws Exception {
    WidgetBuilder widgetBuilder =
        new WidgetBuilder(new Class[](BoldWidget.class));
    String text = "'''bold text'''";
    ParentWidget parent =
        new BoldWidget(new MockWidgetRoot(), "'''bold text'''");
    AtomicBoolean failFlag = new AtomicBoolean();
    failFlag.set(false);

    // This is our best attempt to get a race condition
    // by creating large number of threads.
    for (int i = 0; i < 25000; i++) {
        WidgetBuilderThread widgetBuilderThread =
            new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
        Thread thread = new Thread(widgetBuilderThread);
        thread.start();
    }
    assertEquals(false, failFlag.get());
}

직관적으로 이해되지 않는 코드 흐름에 대한 설명을 위해 주석을 다는 것은 좋으나, 나라면 앞에 FIXMEHACK, XXX같은 태그를 붙였을 것이다. (아래 TODO 주석에서 다시 설명할 것이다.)

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

가끔씩 코드의 내용이 복잡하거나 가독성이 낮은 부분을 설명해주기 위한 주석도 좋다. (함수 인자로 들어가는 값이 복잡하거나, 결과 값이 직관적이지 않을 때) 원칙적으로는 복잡한 부분을 해소시켜서 코드만으로 의미를 알 수 있게 하는 것이 좋으나, 표준 라이브러리 안의 내용이라던가, 내부 코드를 교체할 수 없는 상황(외부 라이브러리 사용 등)에서는 이러한 부분이 도움이 될 수 있다.

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.PageB");

    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 < 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
}

앞에서 언급했듯, 정규표현식이야말로 대체 불가능한 복잡한 코드라고 생각한다.

결과를 경고하는 주석

// Don't run unless you have some time to kill
public void _testWithReallyBigFile()
{
    writeLineToFile(10000000);

    response.setBody(testFile);
    response.readyToSend(this);
    String responseString = output.toString();
    assertSubString("Content-Length: 1000000000", respnseString);
    assertTrue(bytesSent > 1000000000);
}
public static SimpleDateFormat makeStandardHttpDateFormat()
{
    // SimpleDateFormat is not thread safe.
    // so we need to create each instance independently.
    SimpleDateFormat df = new SimpleDateformat("EEE, dd MMM  yyyy HH:mm:ss z");
    df.setTimeZone(TimeZone.getTimeZone("GMT"));
    return df;
}

첫 번째 예시의 경우, 수행 시간이 오래 걸린다는 점을 경고하고 있다. (테스트 자체가 효율적인지에 대한 의문은 들지만, 부하 테스트 등에서 필요할 수도 있으니 넘어가도록 하자.) 물론 책에서 언급하였듯 JUnit4 이후로는 @Ignore같은 어노테이션을 활용할 수 있다.

아래 TODO 주석에서도 설명하겠지만, 이런 내용의 경고를 강조하려면, 주석 앞에 NOTEWARN 등의 태그를 붙이는 것이 더 효율적이라고 생각한다.

TODO 주석

위에서도 몇번이고 언급했지만, TODO는 좋은 주석이라고 생각한다. 코드에 어떤 부분이 부족하고, 뭘 해야 할 지를 정확히 알려주기 때문이다. 게다가 TODO를 포함하여 자주 사용되는 주석의 태그들은 IDE에서 자동으로 하이라이트 해주고, 쉽게 찾을 수 있도록 도와주기 때문에 코드를 읽을 때도 도움이 된다 생각한다.

자주 사용되는 주석의 태그들은 아래와 같다.

  • TODO : 아직 해당 기능이 작성되지 않았지만, 코드 틀을 작성하느라 임시로 생성한 함수를 표현할 때
  • FIXME : 임시 방편으로 작성된 코드거나(하드 코딩), 수정이 필요하다고 논의된 코드에 메모를 남길 때
  • HACK : 문제를 회피하는 기법에 대한 메모, 직관적으로 이해하기 힘들지만 효율적인 코드에 대한 간단한 설명에 사용
  • XXX : 경고를 표현할 때 사용, WARN 등으로도 표현

위의 태그들은 공식적으로 지정된 규칙은 없지만, 어느 환경에서나 TODO, FIXME는 공통적으로 활용된다.

물론 더러운 코드를 작성해 놓고 TODO등으로 메모만 해놓고 방치하면 이런 주석 태그의 효과가 약해지므로, 남발하지 않는 것이 좋다.

TODOFIXME는 책에서 어느정도 설명이 되었으니, HACK을 적용하는 예시를 보여주도록 하겠다.

    // HACK: Get counts of packing payload without branch
#if 0 // unoptimized version
    pkt_cnt = size / PKT_PAYLOAD_MAX;
    if (size % PKT_PAYLOAD_MAX > 0)
        pkt_cnt += 1;
#endif
    pkt_cnt = (size + PKT_PAYLOAD_MAX - 1) / PKT_PAYLOAD_MAX;
    // HACK: Get counts of packing payload without branch
    // pkt_cnt = size / PKT_PAYLOAD_MAX;
    // if (size % PKT_PAYLOAD_MAX > 0)
    //     pky_cnt += 1;
    pkt_cnt = (size + PKT_PAYLOAD_MAX - 1) / PKT_PAYLOAD_MAX;

중요성을 강조하는 주석

아래와 같이 코드 흐름에서 강조를 해 주는 주석이 있다. (NOTE 등으로 강조를 해 주는 것이 좋다고 생각한다.)

String listItemContent = match.group(3).trim();
// the trim is real important. It removes the starting
// spaces that could cause the item to be recognized
// as another list.
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));

공개 API에서 Javadocs

잘 작성된 API 문서는 큰 도움이 된다. 사실 좋은 개발 방법은 구글(+스택 오버플로우)에 검색하는 것 보다 공식 문서를 보면서 작성하는 것이 더 좋다. (물론 방법을 모를 때는 어떤 문서를 봐야 할지 모르니 검색을 할 수 밖에 없지만 이미 사용할 함수/클래스를 알고 있을 때, 공식 문서가 가장 좋은 예시를 보여준다 생각한다.)

Java의 경우, Javadoc 기능으로 각 함수에 대한 문서화를 자동으로 수행해 주며, C/C++ 등은 doxygen 등의 도구를 활용하여 문서화를 자동 생성하기도 한다.

이런 문서화 도구에서 아래와 같은 내용들을 표현한다.

  • 설명: 짧은 설명(brief), 긴 설명(description)으로 해당 함수/클래스에 대한 역할, 수행 결과 등을 설명한다.
  • 인자: 함수의 인자, 클래스 생성 인자 등을 설명한다. (param이나 arg 등으로 표현되며, C/C++같은 경우는 포인터/레퍼런스를 통해 함수에서 값을 변경하는 경우도 있으므로 param[in], param[out] 등의 표기도 사용된다.)
  • 반환: 함수가 반환하는 값에 대해 설명한다. (return 등으로 표현)
  • 예외: Java같이 예외 처리를 자주 사용하는 언어는 거의 필수적으로 나온다. 해당 함수를 수행할 때 어떤 경우에 어떤 형식의 예외가 발생하는지 설명한다. (throws 등으로 표현)

나쁜 주석

위의 좋은 주석에 해당하지 않는 대부분의 주석이 나쁜 주석에 해당한다.

주절거리는 주석

기왕 주석을 써야 한다면, 시간을 들여서 꼭 필요한 중요한 주석만 작성해라.

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

어차피 코드에서 충분히 의도와 맥락이 설명되고 있다면, 같은 이야기를 중복하는 주석은 코드를 읽는 시간만 느리게 할 뿐이다.

// Utility method that returns when this.closed is true. Throws an exception
// if the timeout is reached.
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 {
    /**
     * The processor delay for this component.
     */
    protected int backgroundProcessorDelay = -1;

    /**
     * The lifecycle event support for this component.
     */
    protected LifecycleSupport lifecycle = 
            new LifecycleSupport(this);

    /**
     * The container event listeners for this Container.
     */
    protected ArrayList listeners = new ArrayList();

    /**
     * The Loader implementation with which this Container is
     * associated.
     */
    protected Loader loader = null;
  
    /**
     * The Logger implementation with which this Container is    
     * associated.
     */
    protected Log logger = null;
  
    /**
     * Associated logger name.
     */
    protected String logName = null;
  
    /**
     * The Manager implementation with which this Container is 
     * associated.
     */
    protected Manager manager = null;
    
    /**
     * The cluster with which this Container is associated.
     */
    protected Cluster cluster = null;
 
    /**
     * The human-readable name of this Container.
     */
    protected String name = null;
  
    /**
     * The parent Container to which this Container is a child.
     */
    protected Container parent = null;
  
    /**
     * The parent class loader to be configured when we install a 
     * Loader.
     */
    protected ClassLoader parentClassLoader = null;
  
    /**
     * The Pipeline object with which this Container is 
     * associated.
     */
    protected Pipeline pipeline = new StandardPipeline(this);
  
    /**
     * The Realm with which this Container is associated.
     */
    protected Realm realm = null;
    
    /**
     * The resources DirContext object with which this Container 
     * is associated.
     */
    protected DirContext resources = null;

오해할 여지가 있는 주석

다른 나쁜 주석들은 불필요하고 괜히 관심을 끄는 주석이라면, 이런 주석은 말 그대로 나쁜 주석이다. 코드를 이해하는 것을 방해하기 때문이다.

의무적으로 다는 주석

/**
 * 
 * @param title The title of the CD
 * @param author The author of the CD
 * @param tracks The number of tracks on the CD
 * @param durationInMinutes The duration of the CD in minutes
 */
public void addCD(String title, String author, 
                  int tracks, int durationInMinutes) {
    CD cd = new CD();
    cd.title = title;
    cd.author = author;
    cd.tracks = tracks;
    cd.duration = duration;
    cdList.add(cd);
}

코드 작성할 때 주석에 대한 피로도를 높이는 주범이다. 모든 함수/클래스에 문서화 작업을 강제하면 불필요한 주석을 작성하게 만든다.

나의 경우 함수의 구조를 설계하다가 인자가 변경되는 경우, 해당 문서화 주석이 코드의 변화를 못 따라가는 경우도 있었다. (사실 함수 인터페이스를 변경하지 않도록 잘 고민해서 시작하는 것이 더 좋다는 것은 나도 알고있다.)

이력을 기록하는 주석

릴리즈 노트나 패치 노트에 언급할 내용을 주석에 작성하지 말자. 형상 관리 시스템을 잘 사용하면 다 알 수 있다. (코드 배포가 직접적으로 소스만 전달되면 모르겠지만, 이건 배포 방법이 구식이라는 점도 생각해봐야한다.)

있으나 마나 한 주석

당연한 걸 굳이 하나하나 설명하는 주석은 어떠한 도움도 주지 못한다. (기본 생성자라고 설명하는 주석이나, 뻔히 변수/함수 명으로 설명되는 것을 설명하는 주석)

무서운 잡음

return 0; // 0을 반환한다. 같은 주석. 보통 프로그래밍을 처음 배울 때 문법 자체에 익숙하지 않은데다 주석을 작성하라고 해서 그대로 작성하는 주석들은 특히 쓸모가 없다.

굳이 이런 주석이 의미가 있으려면, return 0; // 정상 종료 같이 코드상으로 바로 이해가 안 되는 흐름을 설명할 때겠지만, 이것도 return EXIT_SUCCESS; 같이 리팩토링하여 주석을 제거할 수 있다.

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

같은 이야기를 중복해서 표현하는 주석이나 다름없다. 함수를 통해 무슨 일을 하는지 설명하고, 변수를 통해 이 변수가 어떤 의도로 값을 저장하는지를 나타낼 수 있다면 주석은 불필요하다.

// does the module from the global list <mod> depend on the
// subsystem we are part of?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))
ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

위치를 표시하는 주석

소스 코드의 길이가 길어질 때, 역할 별로 코드를 구분하기 위해 구분자 같은 주석을 사용하기도 한다.

C#의 경우, #region ~ #endregion을 통해 코드의 구역을 나눠서, 에디터(비주얼스튜디오)에서 접어 둘 수도 있게 하는 기능을 제공하기도 한다.

닫는 괄호에 다는 주석

제일 간단한 해결법이 있다. 들여쓰기를 작작 하자. 들여쓰기 횟수가 많지 않으면 이 부분이 어느 부분을 닫는 괄호인지 고민할 일이 줄어든다.

예전 회사 코드에서는 4중, 5중 if문을 본 적이 있다. 아무리 생각해봐도 성능 효율이 아니라 그냥 생각나는 대로 작성한 코드였던 것 같다. if문 안에 조건 검사가 여러 차례 걸쳐서 일어난다면, && 등을 활용하자. 어차피 short circuit evaluation으로 인해 불필요한 조건 검사는 일어나지 않는다.

만약 if 안에 if-else가 연속해서 나타나는 상황이라면, 한두개 정도는 && 등으로 묶어도 되지만, 그 이상이 된다면 조건문을 해석하는 데 시간이 많이 걸리기 때문에, 이런 경우는 함수로 구분하여 작성하는 것이 좋다. (한 함수 안에서 if-else문의 중첩이 자주 일어난다면, 해당 함수가 너무 많은 맥락을 한번에 표현하려는 것이라고 볼 수 있다.)

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

이전 회사에서 이런 주석을 많이 봤는데, 솔직히 내 코드에 불만 있으면 자리로 찾아오던가로 밖에 안 들린다. 제대로 된 형상 관리 시스템을 사용하고 있다면 변경분마다 작성자 정보가 나타날 것이기 때문에 저자를 표시하는 것은 무의미하다.

그리고, 수정한 부분에 자기 이름을 남겨놓고, 잊고 있다가 나중에 다른 사람이 해당 코드 수정 이유에 대해 질문하러 찾아올 때, 모든 수정 사항과 그 이유를 기억할 수 있을까? 이런 주석은 남발할수록 서로에 대한 신뢰를 천천히 잃게 한다.

주석으로 처리한 코드

디버깅 중, 동작 확인을 위해 일부 코드 행을 주석처리 하곤 한다. 이런 실험을 끝냈으면 해당 주석을 남겨놓으면 안된다. 다른 사람이 보기에 이 주석 처리된 코드는 중요해서 남겨둔건가? 라고 생각해서 완전히 지우지 못할 수도 있다.

C/C++에서는 #if 0 ~ #endif 등의 전처리기를 이용해 주석처리를 하지 않고도 일부 코드를 비활성화 할 수 있다.

HTML 주석

보통 Javadoc같은 문서화 도구는 HTML 형태로 출력하기 때문에 강조나 문맥 구분 등을 위해 관련 주석 중간에 HTML을 삽입하기도 하는데, 코드를 읽는 입장에서 방해되는 요소다.

전역 정보

주석을 작성한다면, 해당 주석 근처의 코드만 서술해야한다. 너무 멀리 있는 내용을 서술하면 주석 및 코드를 이해하기 위해 계속 소스 코드를 오고 가며 읽어야 하기 때문에 불편하기 때문이다.

너무 많은 정보

이 코드가 왜 이렇게 되었는 지에 대한 과거 배경을 설명한다던가, 불필요할정도로 자세한 정보를 작성할 필요는 없다.

책에서는 base64 인코딩/디코딩 모듈을 테스트하는 모듈에서 base64 인코딩/디코딩 방법을 설명하는 주석을 보여주고 있다. 굳이 자세한 정보를 주고 싶다면, base64가 정의된 RFC 문서 번호, 해당 부분을 찾을 수 있는 목차 번호 정도만 제공되어도 충분하다.

모호한 관계

주석의 설명과 코드 사이에 간극이 있는 경우를 말한다.

  • 주석에서 의도는 한 부분만 설명하는데, 코드에서 여러 부분을 동시에 처리하는 경우
  • 수식이 복잡한데 의도만 설명해서 해당 수식을 이해하기 힘든 경우

함수 헤더

짧은 함수는 설명이 많이 필요하지 않다. 함수 이름만 잘 선택해도 주석이 불필요하다.

비공개 코드에서 Javadocs

이전의 좋은 주석에서 공개 API에서 Javadocs를 좋은 주석이라고 했다. 함수/클래스를 설명하는 주석은 분명 좋은 주석이다. 하지만 해당 주석의 대상 독자는 내가 작성한 코드를 재사용하는 프로그래머가 의도에 맞게 사용할 수 있도록 정보를 알리는 데 있다.

Javadoc 같은 도구는 코드를 작성하면서 문서화 작업을 동시에 처리할 수 있도록 도움을 준다. 그리고 아마 대부분의 프로그래머가 문서화는 필요하지만 귀찮다고 생각하고 있을 것이다. 그러므로 문서화는 꼭 필요한, 외부로 공개하는 코드에만 작성하는 것이 좋다. 비공개 코드에도 문서화 작업을 계속 하다 보면 문서화/주석 작성에 피로도를 느끼게 할 수도 있기 때문이다.

마치면서

사실 주석이 부정적으로 취급되는 가장 큰 이유는 코드의 변화를 주석이 잘 따라가지 못하는 편이기 때문이다. 처음 코드 작성시에는 클래스/모듈/함수 설계 등으로 인해 목적을 정하고, 코드 흐름에 대한 메모로 주석을 잘 작성하는 편이다. 하지만 시간이 지날 수록 버그를 수정하면서 바뀐 코드 흐름에 맞춰 주석이 변경되지 않거나, 그대로 남아있는 경우가 있다.


JaeSang Yoo
글쓴이
JaeSang Yoo
The Programmer

목차