천객만래 [千客萬來] (It has an interminable succession of visitors)

'주석'에 해당되는 글 19건

  1. 2010.11.01 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그
  2. 2010.11.01 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <c>
  3. 2010.11.01 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <code>
  4. 2010.11.01 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <example>
  5. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <exception>
  6. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <include>
  7. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <list>
  8. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <para>
  9. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <param>
  10. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <paramref>
  11. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <permission>
  12. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <remarks>
  13. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <returns>
  14. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <see>
  15. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <seealso>
  16. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <summary>
  17. 2010.10.29 [개발] C# 프로그래머 참조 - 문서 주석에 대한 권장 태그 <value>
  18. 2010.10.13 [IT/개발] [MSDN] 코딩기술 - 주석(comment)
  19. 2010.10.07 [it/개발] 버그 없는 깨끗한 프로그램 만들기

C# 프로그래머 참조  

문서 주석에 대한 권장 태그

http://msdn.microsoft.com/library/kor/default.asp?url=/library/kor/csref/html/vclrftagsfordocumentationcomments.asp

 

C# 컴파일러는 코드의 문서 주석을 XML 파일로 만듭니다. XML 파일을 처리하여 문서를 만드는 작업은 사용자의 사이트에서 구현해야 하는 세부적인 사항입니다.

태그는 형식, 형식 멤버 등과 같은 코드 구문에서 처리됩니다.

참고   태그는 네임스페이스에서 처리되지 않습니다.

컴파일러는 유효한 XML 태그를 모두 처리합니다. 아래 태그에는 사용자 문서에서 자주 사용하는 기능이 포함되어 있습니다.

1. 컴파일러는 구문을 검증합니다.

참고 항목

/doc(문서 주석 처리) | XML 문서

Posted by SB패밀리

C# 프로그래머 참조  

<c>

<c>text</c>

다음은 각 문자에 대한 설명입니다.

text
코드로 나타낼 텍스트입니다.

설명

<c> 태그는 설명에 있는 텍스트를 코드로 표시하는 데 사용합니다. 여러 줄을 코드로 표시하려면 <code>를 사용합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_c_tag.cs
// compile with: /doc:xml_c_tag.xml

/// text for class MyClass
public class MyClass 
{
   /// <summary><c>MyMethod</c> is a method in the <c>MyClass</c> class.
   /// </summary>
   public static void MyMethod(int Int1) 
   {
   }
   /// text for Main
   public static void Main () 
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<code>

<code>content</code>

다음은 각 문자에 대한 설명입니다.

content
코드로 표시할 텍스트입니다.

설명

<code> 태그는 여러 줄을 코드로 표시하는 데 사용합니다. <c> 태그는 설명에 있는 텍스트를 코드로 표시하는 데 사용합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

<code> 태그의 사용 방법을 보여 주는 예제는 <example> 항목을 참조하십시오.

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<example>

<example>description</example>

다음은 각 문자에 대한 설명입니다.

description
코드 예제에 대한 설명입니다.

설명

<example> 태그를 사용하면 메서드나 기타 라이브러리 멤버의 사용 방법에 대한 예제를 지정할 수 있습니다. 대개 이 태그는 <code> 태그와 함께 사용합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_example_tag.cs
// compile with: /doc:xml_ctag.xml

/// text for class MyClass
public class MyClass 
{
   /// <summary>
   /// The GetZero method.
   /// </summary>
   /// <example> This sample shows how to call the GetZero method.
   /// <code>
   ///   class MyClass 
   ///   {
   ///      public static int Main() 
   ///      {
   ///         return GetZero();
   ///      }
   ///   }
   /// </code>
   /// </example>
   public static int GetZero() 
   {
      return 0;
   }
   /// text for Main
   public static void Main () 
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<exception>

<exception cref="member">description</exception>

다음은 각 문자에 대한 설명입니다.

cref = "member"
현재 컴파일 환경에 있는 예외에 대한 참조. 컴파일러에서는 지정된 예외가 존재하는지를 확인하고 member를 출력 XML의 정식 요소 이름으로 변환합니다. member는 큰따옴표(" ")로 묶어야 합니다.
description
설명.

설명

throw할 수 있는 예외를 <exception> 태그에 지정합니다. 이 태그는 메서드 정의에 적용됩니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_exception_tag.cs
// compile with: /doc:xml_exception_tag.xml
using System;

/// comment for class
public class EClass : Exception 
{
   // class definition ...
}

/// <exception cref="System.Exception">Thrown when... .</exception>
class TestClass 
{
   public static void Main() 
   {
      try 
      {
      }
      catch(EClass) 
      {
      }
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<include>

<include file='filename' path='tagpath[@name="id"]' />

다음은 각 문자에 대한 설명입니다.

filename
문서를 포함할 파일 이름입니다. 파일 이름은 path로 한정될 수 있습니다. filename은 작은따옴표(' ')로 묶습니다.
tagpath
태그의 name을 찾을 수 있는 filename의 태그 경로입니다. path는 작은따옴표(' ')로 묶습니다.
name
주석 앞에 오는 태그의 이름 지정자입니다. name에는 id가 있어야 합니다.
id
주석 앞에 오는 태그의 ID입니다. ID는 큰따옴표(" ")로 묶습니다.

설명

<include> 태그를 사용하면 소스 코드의 형식과 멤버를 설명하는 다른 파일의 주석을 참조할 수 있습니다. 이렇게 하면 소스 코드 파일에 직접 문서 주석을 배치하지 않아도 됩니다.

<include> 태그는 XML XPath 구문을 사용합니다. <include>의 용도를 변경하는 방법은 XPath 문서를 참조하십시오.

예제

아래 예제는 여러 개의 파일로 구성됩니다. <include>를 사용하는 첫째 파일은 아래와 같습니다.

// xml_include_tag.cs
// compile with: /doc:xml_include_tag.xml
/// <include file='xml_include_tag.doc' path='MyDocs/MyMembers[@name="test"]/*' />
class Test
{
   public static void Main()
   {
   }
}

/// <include file='xml_include_tag.doc' path='MyDocs/MyMembers[@name="test2"]/*' />
class Test2
{
   public void Test()
   {
   }
}

두 번째 파일 xml_include_tag.doc에는 다음 문서 주석이 있습니다.

<MyDocs>

<MyMembers name="test">
<summary>
The summary for this type.
</summary>
</MyMembers>

<MyMembers name="test2">
<summary>
The summary for this other type.
</summary>
</MyMembers>

</MyDocs>

프로그램 출력

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>t2</name>
    </assembly>
    <members>
        <member name="T:Test">
            <summary>
The summary for this type.
</summary>
        </member>
        <member name="T:Test2">
            <summary>
The summary for this other type.
</summary>
        </member>
    </members>
</doc>

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<list>

<list type="bullet" | "number" | "table">
   <listheader>
      <term>term</term>
      <description>description</description>
   </listheader>
   <item>
      <term>term</term>
      <description>description</description>
   </item>
</list>

다음은 각 문자에 대한 설명입니다.

term
text로 정의할 용어입니다.
description
글머리 목록이나 번호 매기기의 항목 또는 term의 정의입니다.

설명

<listheader> 블록은 테이블이나 정의 목록의 머리글 행을 정의하는 데 사용됩니다. 테이블을 정의할 때는 머리글의 term에 대한 엔트리만 제공하면 됩니다.

목록의 각 항목은 <item> 블록으로 지정합니다. 정의 목록을 만들 때는 termtext를 모두 지정해야 합니다. 그러나 테이블, 글머리 목록 또는 번호 매기기의 경우에는 text의 엔트리만 제공하면 됩니다.

목록이나 테이블에 사용할 수 있는 <item> 블록의 수에는 제한이 없습니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_list_tag.cs
// compile with: /doc:xml_list_tag.xml 

/// text for class MyClass
public class MyClass 
{
   /// <remarks>Here is an example of a bulleted list:
   /// <list type="bullet">
   /// <item>
   /// <description>Item 1.</description>
   /// </item>
   /// <item>
   /// <description>Item 2.</description>
   /// </item>
   /// </list>
   /// </remarks>
   public static void Main ()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<para>

<para>content</para>

다음은 각 문자에 대한 설명입니다.

content
단락 텍스트입니다.

설명

<para> 태그를 <summary>, <remarks> 또는 <returns> 같은 태그 내에서 사용하여 텍스트에 구문을 추가할 수 있습니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

<para> 사용 예제는 <summary>를 참조하십시오.

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<param>

<param name='name'>description</param>

다음은 각 문자에 대한 설명입니다.

name
메서드 매개 변수의 이름입니다. name은 작은따옴표(' ')로 묶습니다.
description
매개 변수에 대한 설명입니다.

설명

메서드의 매개 변수 중 하나를 설명하려면 메서드 선언의 주석에서 <param> 태그를 사용해야 합니다.

<param> 태그의 텍스트는 IntelliSense, 개체 브라우저코드 주석 웹 보고서에 표시됩니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_param_tag.cs
// compile with: /doc:xml_param_tag.xml 

/// text for class MyClass
public class MyClass 
{
   /// <param name="Int1">Used to indicate status.</param>
   public static void MyMethod(int Int1) 
   {
   }
   /// text for Main
   public static void Main () 
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<paramref>

<paramref name="name"/>

다음은 각 문자에 대한 설명입니다.

name
참조할 매개 변수의 이름입니다. name은 큰따옴표(" ")로 묶습니다.

설명

<paramref> 태그를 사용하면 특정 단어가 매개 변수임을 나타낼 수 있습니다. 이제 XML 파일을 처리하여 이 매개 변수를 다른 방식으로 서식화할 수 있습니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_paramref_tag.cs
// compile with: /doc:xml_paramref_tag.xml 
/// text for class MyClass
public class MyClass
{
   /// <remarks>MyMethod is a method in the MyClass class.  
   /// The <paramref name="Int1"/> parameter takes a number.
   /// </remarks>
   public static void MyMethod(int Int1)
   {
   }

   /// text for Main
   public static void Main ()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<permission>

<permission cref="member">description</permission>

다음은 각 문자에 대한 설명입니다.

cref = "member"
현재 컴파일 환경에서 호출될 수 있는 멤버 또는 필드에 대한 참조입니다. 컴파일러는 지정된 코드 요소가 존재하고 출력 XML에서 member를 정식 요소 이름으로 변환할 수 있는지 확인합니다. member는 큰따옴표(" ")로 묶어야 합니다.
description
멤버 액세스에 대한 설명입니다.

설명

<permission> 태그를 사용하면 멤버 액세스를 문서화할 수 있습니다. System.Security.PermissionSet을 사용하여 멤버에 대한 액세스를 지정할 수 있습니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_permission_tag.cs
// compile with: /doc:xml_permission_tag.xml 
using System;
class TestClass
{
   /// <permission cref="System.Security.PermissionSet">Everyone can access this method.</permission>
   public static void Test()
   {
   }
   public static void Main()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<remarks>

<remarks>description</remarks>

다음은 각 문자에 대한 설명입니다.

description
멤버에 대한 설명입니다.

설명

<remarks> 태그는 형식에 대한 정보를 추가하여 <summary>에 지정한 정보를 보충하는 데 사용합니다. 이 정보는 개체 브라우저코드 주석 웹 보고서에 표시됩니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_remarks_tag.cs
// compile with: /doc:xml_remarks_tag.xml 
/// <summary>
/// You may have some primary information about this class.
/// </summary>
/// <remarks>
/// You may have some additional information about this class.
/// </remarks>
public class MyClass
{
   /// text for Main
   public static void Main ()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<returns>

<returns>description</returns>

다음은 각 문자에 대한 설명입니다.

description
반환 값에 대한 설명입니다.

설명

반환 값을 설명하려면 메서드 선언의 주석에서 <returns> 태그를 사용해야 합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_returns_tag.cs
// compile with: /doc:xml_returns_tag.xml 

/// text for class MyClass
public class MyClass
{
   /// <returns>Returns zero.</returns>
   public static int GetZero()
   {
      return 0;
   }

   /// text for Main
   public static void Main ()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<see>

<see cref="member"/>

다음은 각 문자에 대한 설명입니다.

cref = "member"
현재 컴파일 환경에서 호출될 수 있는 멤버 또는 필드에 대한 참조입니다. 컴파일러는 지정된 코드 요소가 존재하고 출력 XML에서 member를 요소 이름으로 전달할 수 있는지 확인합니다. member는 큰따옴표(" ")로 묶어야 합니다.

설명

<see> 태그를 사용하면 텍스트 내부에서 링크를 지정할 수 있습니다. 참고 항목 부분에 나타나는 텍스트를 지정하려면 <seealso>를 사용합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

<see> 사용 예제는 <summary>를 참조하십시오.

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<seealso>

<seealso cref="member"/>

다음은 각 문자에 대한 설명입니다.

cref = "member"
현재 컴파일 환경에서 호출될 수 있는 멤버 또는 필드에 대한 참조입니다. 컴파일러는 지정된 코드 요소가 존재하고 출력 XML에서 member를 요소 이름으로 전달할 수 있는지 확인합니다. member는 큰따옴표(" ")로 묶어야 합니다.

설명

<seealso> 태그를 사용하면 참고 항목 부분에 나타나는 텍스트를 지정할 수 있습니다. 텍스트 내부에서 링크를 지정하려면 <see>를 사용합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

<seealso> 사용 예제는 <summary>를 참조하십시오.

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<summary>

<summary>description</summary>

다음은 각 문자에 대한 설명입니다.

description
개체에 대한 요약입니다.

설명

형식 또는 형식 멤버를 설명하려면 <summary> 태그를 사용해야 합니다. 형식에 대한 설명을 보충하는 정보를 추가하려면 <remarks>를 사용합니다.

<summary> 태그의 텍스트는 IntelliSense에서 형식에 대해 표시할 정보를 가져오는 유일한 출처이며, 이 텍스트는 개체 브라우저코드 주석 웹 보고서에도 표시됩니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_summary_tag.cs
// compile with: /doc:xml_summary_tag.xml

/// text for class MyClass
public class MyClass
{
   /// <summary>MyMethod is a method in the MyClass class.
   /// <para>Here's how you could make a second paragraph in a description. <see cref="System.Console.WriteLine"/> for information about output statements.</para>
   /// <seealso cref="MyClass.Main"/>
   /// </summary>
   public static void MyMethod(int Int1)
   {
   }

   /// text for Main
   public static void Main ()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

C# 프로그래머 참조  

<value>

<value>property-description</value>

다음은 각 문자에 대한 설명입니다.

property-description
속성에 대한 설명입니다.

설명

<value> 태그를 사용하면 속성을 설명할 수 있습니다. Visual Studio .NET 개발 환경의 코드 마법사를 통해 속성을 추가하면 새 속성에 대한 <summary> 태그도 추가됩니다. 그러면 사용자는 직접 <value> 태그를 추가하여 속성이 나타내는 값을 설명해야 합니다.

/doc로 컴파일하여 문서 주석을 파일로 저장합니다.

예제

// xml_value_tag.cs
// compile with: /doc:xml_value_tag.xml
using System;
/// text for class Employee
public class Employee
{
   private string name;
   /// <value>Name accesses the value of the name data member</value>
   public string Name
   {
      get
      {
         return name; 
      }
      set
      {
         name = value; 
      }
   }
}

/// text for class MainClass
public class MainClass
{
   /// text for Main
   public static void Main()
   {
   }
}

참고 항목

문서 주석에 대한 권장 태그

Posted by SB패밀리

코딩 기술에는 소프트웨어 개발의 여러 가지 측면이 통합되어 있습니다. 일반적으로 코딩 기술은 응용 프로그램의 기능에 영향을 미치지는 않지만, 소스 코드의 가독성을 향상시키는 데 기여합니다. 여기서는 프로그래밍, 스크립팅, 태그 및 쿼리 언어와 같은 모든 형식의 언어가 고려됩니다.

여기에서 정의된 코딩 기술은 고정된 집합의 코딩 표준을 구성하는 데는 사용하지 않는 것이 좋습니다. 그보다는 특정 소프트웨어 프로젝트에 사용되는 코딩 표준을 개발하기 위한 지침으로 사용하는 것이 좋습니다.

코딩 기술은 다음 세 단원으로 구분됩니다.

이름

명명 스키마는 응용 프로그램의 논리적 흐름을 이해할 수 있는 가장 강력한 수단 중의 하나입니다. 이름에는 "어떻게"보다는 "무엇을"이 나타나야 합니다. 내부 구현을 노출시키는 이름을 사용하지 않으면 복잡성이 줄어든 추상화 계층을 유지할 수 있습니다. 예를 들어, GetNextArrayElement() 대신 GetNextStudent()를 사용할 수 있습니다.

올바른 이름을 선택하는 것이 어렵다는 것은 사용자가 항목의 목적을 더 정확하게 분석하고 정의할 필요가 있다는 것을 의미할 수 있습니다. 이름의 길이는 정확한 의미를 전달할 수 있을 정도로 길어야 하지만 너무 길어져서 의미가 모호해지면 안됩니다. 프로그램 상에서 고유 이름은 특정 항목과 다른 항목을 구분하는 용도로만 사용되며, 표현 이름의 기능은 사용자의 이해를 돕는 것입니다. 따라서 사용자가 이해할 수 있는 이름을 제공하는 것이 좋습니다. 하지만 선택된 이름은 해당 언어의 규칙과 표준을 준수해야 합니다.

다음은 명명 기술에서 권장되는 사항입니다.

루틴

  • 루틴 이름 AnalyzeThis()와 변수 이름 xxK8과 같이 주관적으로 해석될 수 있는 모호한 이름은 사용하지 않습니다. 이와 같은 이름은 추상화에 기여하기 보다 모호함의 원인이 됩니다.
  • 개체 지향 언어에서 클래스 속성 이름에 Book.BookTitle과 같은 클래스 이름을 포함시키는 것은 불필요합니다. 그 대신 Book.Title을 사용합니다.
  • 지정 개체에서 특정 동작을 수행하는 루틴을 명명하기 위해서는 CalculateInvoiceTotal()와 같은 동사-명사 방식을 사용합니다.
  • 함수 오버로딩이 허용되는 언어에서 모든 오버로드는 유사한 동작을 수행해야 합니다. 함수 오버로딩이 허용되지 않는 언어에서는 유사한 기능을 관련시켜 주는 명명 표준을 설정합니다.

변수

  • 해당하는 변수 이름의 끝에 Avg, Sum, Min, Max, Index와 같은 계산 한정자를 추가합니다.
  • 변수 이름에 min/max, begin/end 및 open/close와 같은 보완적인 쌍을 사용합니다.
  • 대부분의 이름은 연속되는 몇 개의 단어로 구성되므로, 대소문자를 혼용하여 쉽게 읽을 수 있도록 합니다. 또한 변수와 루틴을 쉽게 구별하기 위해 루틴 이름에는 각 단어의 첫 글자가 대문자로 시작되는 파스칼 대/소문자(CalculateInvoiceTotal)를 사용합니다. 변수 이름에는 첫 단어를 제외한 각 단어의 첫 글자가 대문자로 시작하는 카멜 대/소문자(documentFormatType)를 사용합니다.
  • 부울 변수 이름에는 fileIsFound와 같이 Is가 포함되어야 합니다. Is는 Yes/No 또는 True/False 값을 내포합니다.
  • 상태 변수를 명명하는 경우에는 Flag와 같은 구문을 사용하지 않습니다. 이 구문은 두 개 이상의 값을 가질 수 있다는 점에서 부울 변수와 다릅니다. documentFlag를 사용하는 대신 documentFormatType와 같이 더 정확한 이름을 사용합니다.
  • 단 몇 줄의 코드에서만 사용되는 임시 변수에 대해서도 의미 있는 변수를 사용하십시오. i 또는 j와 같은 단일 글자 변수 이름은 단기 루프 인덱스에만 사용합니다.
  • For i = 1 To 7과 같은 리터럴 숫자나 리터럴 문자열은 사용하지 않습니다. 그 대신 For i = 1 To NUM_DAYS_IN_WEEK와 같은 명명된 상수를 사용하여 관리와 이해를 쉽게 합니다.

테이블

  • 테이블을 명명하는 경우에는 이름을 단수로 표현합니다. 예를 들어, Employees 대신 Employee를 사용합니다.
  • 테이블 열을 명명하는 경우에는 테이블 이름을 반복하지 않습니다. 예를 들어, Employee라는 테이블에서 EmployeeLastName이라는 필드를 사용하지 않습니다.
  • 열 이름에 데이터 형식을 포함시키지 않습니다. 이렇게 하면 나중에 데이터 형식을 변경할 때 필요한 작업의 양이 줄어들게 됩니다.

Microsoft SQL Server

  • 저장 프로시저 앞에 sp를 붙이지 마십시오. sp는 시스템 저장 프로시저를 식별하기 위해 예약된 접두사입니다.
  • 사용자 정의 함수 앞에 fn_을 붙이지 마십시오. fn_은 기본 제공 함수를 식별하기 위해 예약된 접두사입니다.
  • 확장 저장 프로시저 앞에 xp_를 붙이지 마십시오. xp_는 시스템 확장 저장 프로시저를 식별하기 위해 예약된 접두사입니다.

기타

  • 가능하면 약어를 사용하지 않습니다. 다만 사용자가 일관성 있게 만든 약어는 사용합니다. 약어에는 단 한 가지 의미만 존재해야 하며, 각 약자에는 단 하나의 약어가 존재해야 합니다. 예를 들어, minimum의 약어로 min을 사용하는 경우 다른 곳에서 minute의 약자로 min을 사용해서는 안됩니다.
  • 함수를 명명하는 경우 GetCurrentWindowName()과 같이 반환되는 값에 대한 설명을 포함시킵니다.
  • 프로시저 이름과 마찬가지로 파일과 폴더 이름도 그 용도를 정확하게 나타내야 합니다.
  • ProcessSales()라는 루틴과 iProcessSales라는 변수와 같이 서로 다른 요소를 위해 이름을 반복 사용하지 않습니다.
  • 코드 검사 시 혼동을 방지하려면 요소를 명명할 때 write 및 right와 같은 동음 이의어를 사용하지 않습니다.
  • 요소를 명명하는 경우 철자가 자주 틀리는 단어를 사용하지 않습니다. 또한 color/colour 및 check/cheque와 같이 지역마다 철자가 다르다는 점도 알아 둡니다.
  • 데이터 형식을 식별하기 위해 입력 마크(예: 문자의 $, 정수의 %)를 사용하지 않습니다.

주석

소프트웨어 설명서는 외부와 내부 설명서의 두 가지 형태로 존재합니다. 사양, 도움말 파일 및 디자인 문서와 같은 외부 설명서는 소스 코드의 외부에서 관리되며, 내부 설명서는 개발자가 개발 시에 소스 코드 내부에서 작성한 주석들로 구성됩니다.

외부 설명서를 사용할 수도 있지만, 하드 카피 설명서를 잊어 버릴 수 있으므로 소스 코드 목록을 독자적으로 유지해야 합니다. 외부 설명서는 사양, 디자인 문서, 변경 요청, 버그 목록 및 사용되는 코딩 표준으로 구성되어야 합니다.

내부 소프트웨어 설명서의 한 가지 문제는 소스 코드와 일치하도록 주석을 관리하고 업데이트하는 것입니다. 적절한 주석이 추가된 소스 코드는 실행 시에 특별한 역할을 하지 않지만, 상당히 복잡하고 귀찮은 소프트웨어를 관리해야 하는 개발자에게 있어서는 상당히 중요한 역할을 합니다.

다음은 주석 추가 시 권장되는 사항입니다.

  • C#에서 개발을 수행하는 경우 XML 문서 기능을 사용합니다. 자세한 내용은 XML 문서를 참조하십시오.
  • 코드를 수정하는 경우에는 반드시 최신 주석을 코드에 추가하십시오.
  • 모든 루틴의 시작부에는 이 루틴의 목적, 전제, 제한 등을 나타내는 정확한 표준 주석을 추가하는 것이 도움이 됩니다. 정확한 주석은 이 루틴의 용도와 목적을 간략하게 설명할 수 있어야 합니다.
  • 코드 줄의 끝에 주석을 추가하지 않습니다. 끝부분에 주석이 있으면 코드를 읽기가 더 어렵습니다. 하지만 변수 선언에 대해 주석을 추가하는 경우에는 줄 끝에 주석을 추가하는 것이 좋습니다. 이 때 탭을 사용하여 모든 주석을 일렬로 정렬합니다.
  • 모든 줄에 별표가 사용된 혼란스러운 주석을 사용하지 않습니다. 그 대신 공백을 사용하여 주석과 코드를 구분합니다.
  • 입력 프레임을 사용하여 블록 주석을 둘러싸지 않습니다. 이렇게 하면 멋지게 보일 수는 있지만 관리가 어려워집니다.
  • 나중에 관리 작업을 수행하는 동안 혼동을 방지하려면 임시 주석이나 관련이 없는 주석을 완전히 제거한 후에 배포합니다.
  • 복잡한 코드 부분을 설명하는 주석이 필요한 경우 그 코드를 검사하여 이 주석을 다시 작성해야 하는지를 결정합니다. 가능하면 오류 코드를 제공하지 말고 다시 작성합니다. 일반적으로 코드를 단순하게 만든다고 해서 성능이 저하되는 것은 아니지만, 성능과 유지 관리성 사이에 균형을 유지해야 합니다.
  • 주석을 작성하는 경우 완벽한 구문을 사용하십시오. 주석은 코드를 모호하게 하는 것이 아니라 명확하게 하는 것입니다.
  • 나중에 따로 주석을 추가할 시간이 없으므로 코딩과 주석 작업을 동시에 수행하십시오. 또한 자신이 작성했던 코드를 다시 살펴보아야 합니다. 지금은 코드가 명확하게 이해되지만 6주 후에는 명확하지 않을 수도 있습니다.
  • 유머스러운 말과 같은 불필요하고 부적절한 주석을 사용하지 않습니다.
  • 주석을 사용하여 코드의 목적을 설명하십시오. 주석은 코드를 그대로 옮겨 놓은 것이 아닙니다.
  • 코드에서 쉽게 알아볼 수 없는 모든 내용을 주석에 추가합니다.
  • 반복되는 문제를 해결하려면 버그 수정과 작업 코드에 대해 주석을 사용합니다.
  • 루프와 논리 분기로 이루어진 코드에 대해 주석을 사용합니다. 이 주석은 소스 코드 사용자에게 도움이 되는 중요한 부분입니다.
  • 응용 프로그램 전체에서 일관적인 문장 부호와 구조로 일정한 스타일을 사용하여 주석을 작성합니다.
  • 공백을 사용하여 주석 구분 기호와 주석을 분리합니다. 이렇게 하면 색 표시를 사용하지 않고 코드를 보는 경우에도 쉽고 명확하게 주석을 볼 수 있습니다.

서식

서식은 코드의 논리적 구조를 명확하게 합니다. 소스 코드의 서식을 일관적이고 논리적인 방식으로 적용하면 자신이나 다른 개발자가 이 소스 코드를 해독하는 데 도움이 됩니다.

다음은 서식 기술에서 권장되는 사항입니다.

  • 들여쓰기의 표준 크기(예: 공백 4개)를 설정하고 이 크기를 일관적으로 사용합니다. 지정된 들여쓰기를 사용하여 코드부를 정렬합니다.
  • 하드 카피 형태로 소스 코드를 출판하려면 한 가지 글꼴을 사용합니다.
  • 중괄호 쌍이 함께 나타나는 경우에는 다음과 같이 여는 중괄호와 닫는 중괄호를 수직으로 정렬합니다.
    for (i = 0; i < 100; i++)
    {
       ...
    }

    여는 중괄호가 줄의 끝에 나타나고 닫는 중괄호가 줄의 앞에 나타나는 경우에는 다음과 같은 스타일을 사용할 수 있습니다.

    for (i = 0; i < 100; i++){
       ...
    }

    선택된 스타일을 소스 코드 전체에서 일관적으로 사용합니다.

  • 논리적 구조에 따라 코드를 들여쓰기합니다. 들여쓰기를 사용하지 않으면 다음과 같이 코드를 이해하기가 어렵습니다.
    If ... Then
    If ... Then
    ...
    Else
    End If
    Else
    ...
    End If

    코드를 들여쓰면 다음과 같이 코드를 쉽게 이해할 수 있습니다.

    If ... Then
       If ... Then
       ...
       Else
       ...
       End If
    Else
    ...
    End If
  • 소스 코드 편집기의 스크롤을 방지하고 하드 카피가 명확하게 표시되도록 하기 위해 주석과 코드의 최대 줄 길이를 설정합니다.
  • 코드의 목적이 변경되지 않는 범위 내에서 모든 연산자의 전후에 공백을 사용합니다. 예를 들어, 한 가지 예외로는 C++에서 사용되는 포인터 표기법이 있습니다.
  • 공백을 사용하여 소스 코드의 구조를 체계적으로 표시할 수 있습니다. 이렇게 하면 코드 "단락"을 만들 수 있으므로, 사용자는 소프트웨어의 논리적 구조를 쉽게 이해할 수 있습니다.
  • 하나의 코드가 여러 줄에 걸쳐 나타나는 경우, 다음 줄이 없으면 코드가 불완전하다는 사실을 명확히 하기 위해 연결 연산자를 각 줄의 끝에 추가합니다.
  • 한 줄에 두 개 이상의 문을 사용하지 않습니다. 한 가지 예외로는 C, C++, C# 또는 JScript 에서 사용되는 for (i = 0; i < 100; i++)와 같은 루프가 있습니다.
  • HTML을 작성하는 경우 태그와 특성의 표준 서식을 설정합니다. 예를 들어 태그는 모두 대문자로 설정하고 특성은 모두 소문자로 설정할 수 있습니다. 다른 방법으로는 XHTML 사양을 사용하여 모든 HTML 문서가 유효하도록 보장할 수 있습니다. 웹 페이지를 만드는 경우 파일의 크기가 문제될 수 있지만, 따옴표 붙은 특성값과 닫는 태그를 사용하면 쉽게 관리할 수 있습니다.
  • SQL 문을 작성하는 경우 모든 키워드에는 대문자를 사용하고 테이블, 열 및 뷰와 같은 데이터베이스 요소에는 대소문자를 혼용합니다.
  • 실제 파일 사이에서 소스 코드를 논리적으로 분할합니다.
  • 다음과 같이 각각의 주 SQL 절을 별도의 줄에서 작성하면 쉽게 코드를 읽고 편집할 수 있습니다.
    SELECT FirstName, LastName
    FROM Customers
    WHERE State = 'WA'
  • 길고 복잡한 코드부는 쉽게 이해할 수 있는 작은 모듈로 분할합니다
Posted by SB패밀리

버그 없는 깨끗한 프로그램 만들기
2007/02/28 01:18 출처 : http://www.winapiprogramming.com/

 필자에게 프로그래밍이 무엇이냐고 묻는다면 프로그래밍이란 "버그와의 끝없는 싸움"이라고 대답하고 싶습니다. 필자가 처음 프로그래밍을 접해본 것은 중학교 3학년 때인 1984년이었습니다. 친구 집에 놀러갔다가 접한 SPC-1000에서 베이직으로 간단하게 계산기를 만들어본 것이 처음이었습니다. 그 컴퓨터란 물건이 얼마나 부럽던지 반년 동안 아버지를 졸라서 고등학교 1학년 때 애플 II 컴퓨터를 샀고 몇몇 컴퓨터 잡지를 사서 소스를 아무 생각없이 입력한 것 말고는 입시 준비(?)에 시달리느라 제대로 프로그래밍을 해본 적은 없었습니다. 그러다가 대학에 들어와서 포트란, 파스칼, C등을 배우면서 좀더 본격적인 프로그래밍을 시작하게 되었습니다. 그 시절을 돌이켜보면 참 어떻게 그렇게 무식하게 (?) 프로그래밍을 할 수 있었는지 대단하다는 생각이 간혹 들기도 합니다.

그 시절을 잠깐 회상하자면 일단 무턱대고 컴퓨터 앞에 앉아서 프로그래밍을 시작하지만 넘치는 의욕에 비해 실력이 따르질 않아서 쉬운 숙제에도 쩔쩔매며 간신히 코드를 만들었습니다. 컴파일하면 에러가 잔뜩 !!. 간신히 에러를 잡고 나서 실행하면 프로그램이 죽던지 엉뚱한 결과가 나오고... 그러면 이제부터 디버깅이 시작됩니다. 일단 의심이 가는 곳을 대충 고친 다음에 먼저 돌려보고 안 되면 다시 의심가는 곳을 고치고... 코드가 누더기가 된 다음에야 간신히 코드는 돌아가고 원하는 결과가 나오기는 하는데 왜 동작하는지 본인도 알 수 없고...

아마 대부분의 프로그래머들이 비슷한 과정을 거쳤거나 앞으로 거치리라 생각됩니다. 100줄 이상의 프로그램이라면 프로그램에 버그가 없을 수는 없습니다 (아니 적어도 컴파일 에러라도 있습니다). 그렇기 때문에 좋은 프로그래머가 되려면 버그가 적은 프로그램을 작성할 수 있거나 버그를 빨리 발견할 줄 알아야 합니다. 이 글에서는 여러분들이 이러한 목표를 달성하는데 도움이 될 수 있도록 먼저 좋은 코딩 습관에 대해 살펴보겠습니다. 그리고나서 다음으로 실례를 중심으로 자주 접하게 되는 버그 상황과 디버깅 팁에 대해 알아보도록 하겠습니다. 참고로 여기서는 비주얼 C++을 바탕으로 설명을 진행하겠습니다.

 

1. 바람직한 코딩 습관

버그 없는 프로그램을 작성하는 가장 좋은 방법은 바람직한 코딩 습관을 몸에 배게 하는 것입니다. 저도 지금 생각해보면 처음 프로그래밍을 배울 때 누군가 옆에서 이러한 것에 대해 이야기해주는 사람이 있었다면 고생을 좀 덜하지 않았을까 하는 생각을 간혹 하곤 합니다. 물론 자신이 직접 깨닫는 것만큼 확실히 배우는 것은 아니고 그 당시 그런 조언을 이해할만한 수준이 아니었기 때문에 그런 것들을 들었다 해도 얼마나 습득할 수 있었을지 의문이긴 합니다만. 참 이런 이야기를 하다보니 제가 이제는 무슨 완벽한 프로그래머가 된 것처럼 되어버렸는데 저도 이런 코딩 습관을 확립하기 위해 지금도 노력하고 있는 중입니다.

자 그럼 제가 생각하는 바람직한 코딩 습관에 대해 하나씩 알아보도록 하겠습니다. 이제 읽어보면 알겠지만 여기에 무슨 눈이 번쩍 뜨이는 그런 이야기들이 있는 것이 아닙니다. 항상 그렇지만 모든 성공은 작은 습관과 집중력으로부터 비롯합니다.

 

1> 사용하는 프로그래밍 언어를 제대로 이해하자.

이 것은 초보 프로그래머에게는 아주 중요한 사항입니다. 프로그래밍을 처음 배우는 사람이라면 그 개념을 제대로 이해하지 못하는 경우가 허다합니다. 특히 C 언어를 배우는 사람이라면 포인터의 개념을 제대로 이해하지 못하는 경우가 많고 C++/자바 등의 언어를 배우면 클래스의 상속(Inheritance)이나 가상 함수(Virtual function)와 같은 개념을 이해하지 못하는 경우가 허다합니다. 이런 것을 이해하지 못하고 일단 프로그래밍에 뛰어들게 되면 당연히 많은 시간을 소비하기 마련이고 결과물을 만들어내지 못하는 경우도 많습니다. 아무리 시간이 없어도 한걸음 한걸음 배워나가는 것이 결국에는 시간을 단축하기 마련입니다.

프로그래밍 서적을 보고 공부할 때도 모니터 앞에 앉아서 지금 보고 있는 부분에 대해 실습을 해보며 공부하는 것이 최선의 방법입니다. 그렇게 하면 보다 더 이해하기가 쉽고 개발환경의 사용법에 대해서도 배워볼 수 있기 때문입니다. 그렇기 때문에 초보 프로그래머라면 프로그래밍 언어 관련 서적을 고를 때 예제 코드가 많이 있는지 또한 따라하기식의 구성이 잘 되어있는지를 살펴보는 것이 중요합니다. 즉 프로그래밍 언어는 머리로 이해하는 것도 중요하지만 손으로도 익혀야 한다는 것입니다.

top

2> 프로그래밍 중에는 프로그래밍에만 집중하자.

이 것은 초보 프로그래머 뿐만 아니라 이 시대의 모든 프로그래머에게 해당되는 이야기라고 할 수 있는데 한마디로 자신의 일에 대한 집중력을 키우라는 이야기입니다. 요즘 웬만한 컴퓨터는 인터넷과 연결되어 있습니다. 인터넷 매체와 메일, 메신저 등이 워낙 발달해 있어서 한 시간이라도 중단 없이 일한다는 것이 쉬운 일이 아닙니다. 이런 상황은 사실 프로그래머 뿐만 아니라 컴퓨터를 사용하는 모든 사람들에게 해당된다고 할 수 있을 것 같습니다. 다음과 같은 비슷한 경험이 있는지 한번 생각해봅시다.

코딩이 좀 막히면 머리를 식히기 위해 인터넷의 바다에서 뛰어들고 코딩이 잘 되어도 기분이 좋아서 인터넷의 바다에 뛰어들고... 또 요즘 메일 클라이언트는 모두 알림 기능이 있어서 메일이 도착하면 이를 알려주게 되어있습니다. 메신저는 어떠한가요 ? 툭하면 친구로부터 채팅 요청이 들어오고... 수다 떠느라 시간가는 줄 모르고...

의식적으로 노력을 해서 이러한 중단이 없도록 해야 합니다. 인터넷의 바다에 뛰어드는 시간대를 정해두는 것이 제일 좋은 것 같습니다. 점심 시간 직후라든가 하는 식으로 말입니다. 메일의 경우에는 새 메일이 도착했다는 알림이 와도 바쁜 경우라면 일단 그 일을 끝낸 후에 보는 식으로 한번 바꿔보기 바랍니다. 메일의 경우 같이 일하는 사람들간 의사소통 수단이기도 하기 때문에 알림 기능 자체를 꺼버리는 것은 좀 무리가 있는 듯합니다. 메신저 같은 경우는 집중이 필요한 시간에는 아예 로그아웃해버리는 것입니다. 아니면 약간 치사(?)하지만 상태를 "자리 비움" 혹은 "다른 용무 중" 같은 걸로 해두세요.

약간 이야기가 옆길로 새는 것 같긴 하지만 집중력에 관해 얼마 전에 제가 읽은 글이 있어서 참고로 여러분들께 소개해 드리고자 합니다. ("고도원의 아침편지"란 사이트에서 읽은 글입니다.)

밥을 먹을 때에는 밥먹는 일에 집중하고
청소할 때에는 온전히 청소하는 행위만 있어야 합니다.
그렇게 생각하고 말하고 행동하는 것을
달리 말하면, 집중력 또는 통일성이라고 합니다.
이 집중하는 태도와 노력을 통해
우리는 스스로 정화되기도 하고
안정되기도 하며
또 문제의 본질을 통찰하는
힘을 얻기도 합니다.


- 도법스님의 <<내가 본 부처>> 중에서

프로그래밍을 할 때는 프로그래밍에만 집중합시다 !! 인터넷으로 인한 이러한 중단이외에도 회사 생활을 막 시작한 초보 프로그래머의 경우에는 다음과 같은 것을 조심해야 합니다. 긴 업무 시간으로 인한 집중력 상실입니다. 이건 사실 개인의 힘만으로 해결할 수 있는 문제는 아닙니다만 아주 한국적인 근무 환경 하에서는 늦게 퇴근하는 것이 하나의 미덕입니다. 따라서 오랜 시간을 회사에서 보내려니 업무의 강도가 느슨해지기 마련입니다. 하루 근무시간을 8시간으로 생각하지 않고 10시간에서 12시간으로 생각하니까 할 일이 있어도 "저녁에 하지 뭐!" 이런 생각을 갖게 되고 커피마시고 담배피우며 잡담하느라 보내는 시간이 더 많아지게 되는 것입니다. 사실 이는 매니저 역할을 맡고 있는 사람이 해결해야할 문제입니다만 이런 상황으로 인해 집중력을 잃는 일이 발생할 수도 있다는 점을 알아두고 자신이 이런 증상을 보이거든 회사를 옮기던가(?) 자신을 채찍질하여 다시 집중력을 되찾기 바랍니다.

 

3> 주석을 많이 달자

주석을 많이 다는 것도 굉장히 중요합니다. 아주 복잡하고 거창하게 코드 흐름도를 그리라는 이야기가 아닙니다. 소스 코드 중간 중간에 설명을 자세히 달아놓으라는 이야기입니다. 그것만으로도 다른 사람이나 코드의 원저자 자신이 나중에 소스를 볼 때 큰 도움을 얻을 수 있습니다. 제 아무리 자기가 작성한 코드라고 해도 복잡하고 사연 많은 코드의 세부적인 내용은 몇 개월만 지나도 잊어버리기 십상이기 때문입니다.

귀찮아서 주석을 안 다는 사람들도 많습니다. 심지어 주석이 코드의 미관을 해친다는 이색적인 주장(?)을 펼치며 주석달기를 거부하는 사람도 본 적 있었습니다. 오 마이 갓 !! 주석을 다는 일은 습관이 되면 그리 어려운 일이 아닙니다. 또 주석을 달면서 작성하는 소스의 구성이나 흐름이 보다 더 명확해질 수도 있습니다. 예를 들어 C/C++이나 자바로 코딩을 하는 중이라면 먼저 //부터 입력하고 밑에서 할 일을 간단히 적고 시작하면 자신이 해야할 일이 좀더 명확해지고 나중에 주석을 보고 코드를 기억하거나 이해하기도 쉽기 때문입니다.

소스 파일을 하나 새로 만들면 그 앞에 다음과 같은 식으로 주석을 달기 바랍니다.

// -------------------------------------------------------

// 파일이름 : NewModule.cpp

// 설명 : 전체 프로그램에서의 이 소스 파일의 역할을 기술합니다.

// 노트 : 기억할만한 점이 있으면 기록합니다.

// 히스토리 : 생성 - 한기용, 2002.03.31

// Abc 함수추가 - 두기용, 2002.04.01

// -------------------------------------------------------

함수의 경우에는 함수마다 앞에 다음과 같은 함수 주석을 답니다.

// -------------------------------------------------------

// 함수이름 : Abc

// 설명 : 이 함수의 역할에 대해 기록합니다.

// 노트 : 기억할만한 점이 있으면 기록합니다.

// 인자 : 인자를 하나씩 설명합니다. 인자의 값을 누가 채워주는지도 명시합니다.

// [IN] int nCount

// [IN] char *pString

// [OUT] int *pResultCount

// 리턴값 : 리턴값의 타입과 의미에 대해 설명합니다.

// -------------------------------------------------------

번거롭게 느껴질지 모르지만 한번 몸에 배면 아주 좋은 특히 같이 일하는 사람들이 좋아하는 습관이란 점을 분명히 기억해두기 바랍니다. 문서화를 잘 하는 사람들은 어디를 가든 사랑(?)받습니다.

 

4> 새로 작성한 코드는 항상 디버거로 따라가 보자

처음 작성했거나 잘 동작하던 코드를 수정한 경우라면 컴파일이 제대로 된 후에 한번 디버거로 흐름을 따라가 보면서 생각하는 대로 동작하는지 살펴보는 것이 아주아주 좋습니다. 좀 번거롭게 생각되어서 이런 이야기를 그냥 듣고 넘길 수도 있는데 이를 습관으로 만들면 여러모로 오히려 편리합니다.

그냥 일단 돌려보고 제대로 동작하지 않으면 디버거로 따라가 보는 전략을 택할 수도 있는데 그것보다는 처음 한번은 일단 디버거로 따라가 보는 것이 여러모로 좋습니다. 디버거로 따라가 보면 결과에 나타나지 않는 에러들도 찾아낼 가능성이 있고 다른 아이디어가 나올 가능성도 더 높기 때문입니다.

>

top

5> 테스트 코드를 만들자

훌륭한 프로그래머라면 누구나 버그없는 빠르고 깔끔한 프로그램을 만드는 사람이라고 생각할 것입니다. 하지만 앞서 제가 언급한 것처럼 복잡한 프로그램을 작성하다보면 버그 없는 프로그램을 작성하는 것은 거의 불가능한 일입니다. 그렇기 때문에 제가 생각하는 좋은 프로그래머의 요건은 버그를 어떻게 빨리 발견해서 없애느냐에 달려있다고 생각합니다. 참고문헌 3을 보면 "Debugging the Development Process"라는 책이 언급되어 있습니다. 이 책은 제가 아주 감명(?)깊게 읽은 책으로 제대로 된 개발을 하는 방법론에 대해 다루고 있습니다. 시간이 된다면 한번 꼭 읽어보기를 권합니다. 이 책에서는 버그를 발견하고 이를 없앨 때마다 항상 다음 두 가지를 생각해보기를 권하고 있습니다.

 어떻게 했으면 이 버그를 내가 미연에 방지할 수 있었을까 ?

 어떻게 했으면 이 버그를 내가 아주 쉽게 찾아낼 수 있었을까 ?

이를 좀더 확장해서 생각하면 코드를 작성할 때부터 이걸 어떻게 테스트를 할 것인지 염두에 항상 두고 있어야 한다는 말입니다. 그래서 저 같은 경우는 프로그래머의 수준을 측정할 때 한 가지 척도로 그 사람이 테스트 프로그램을 작성하는지를 일단 봅니다. 자신의 코드를 테스트할 프로그램을 별도로 작성할 정도의 사람이면 일단 기본기가 아주 잘 되어 있는 사람이라고 볼 수 있기 때문입니다.

코드에 따라서는 테스트하는 것이 아주 힘든 경우도 있을 수 있습니다. 하지만 그걸 핑계로 테스트를 건너뛰지는 말기 바랍니다. 테스트할 방법을 계속 생각해보면 결국 어떻게든 그 방법이 떠오르게 되어 있습니다. 테스트하기 힘든 상황에도 그 방법을 생각해낼 정도의 사람이라면 아주 긍정적인 사람으로 인식될 것입니다.

결론적으로 무슨 코드를 만들던지 테스트할 방법을 생각하기 바랍니다. 프로그램 수행의 결과로 파일을 만들어내는 프로그램이라면 그 파일의 내용이 맞는지 체크하는 프로그램을 따로 만들어 볼 수도 있을 것이고 뭔가를 수행해서 화면에 출력하는 간단한 프로그램이라면 그 부분을 별도의 함수로 만들어서 다양한 입력을 주어서 올바른 출력이 나오는지 확인해볼 수 있을 것입니다.

또 실행이 오래 걸리는 프로그램이라면 중간 중간마다 실행 상태를 파일등에 기록해두도록 하는 것도 아주 좋습니다. 이런 용도로 사용되는 파일을 흔히 로그 (log) 파일이라고 합니다. 이때 그 시각도 같이 기록해두면 도움이 많이 될 것입니다.

6> 생각하는 프로그래머가 되자

무슨 코드를 작성하던지 어떻게 할 것인지 먼저 생각하는 습관을 갖기 바랍니다. 그리고 프로그램이 정상적으로 동작하고 원하는 결과를 낸다면 이에 만족하지 말고 더 빠르고 간단하게 처리하도록 개선할 방법이 없는지 자꾸 생각해봐야 합니다. 그 당시에야 일단 실력이 모자라고 개선하는데 시간이 걸리니까 답답하게 여겨질지 모르지만 결국에는 프로그래밍 실력의 발전에 가속이 붙을 것입니다.

사용자 인터페이스 프로그램을 만들고 있는 중이라면 사용자 입장에서 더 편리하게 사용할 수 있는 방법을 자꾸 생각해봐야 합니다. 물론 이 과정은 끝이 없는 과정이 될 수도 있기 때문에 뭔가 데드라인이 있는 일을 작업 중이라면 적정선에서 타협을 봐야 합니다. 이러한 타협은 회사의 운명이 걸린 상용 프로그램을 만들 때는 아주 중요합니다. 자꾸 개선하다가 오히려 에러를 더 낼 수도 있도 그로 인해 결과적으로 주어진 시간 내에 작업을 못 끝낼 수도 있기 때문입니다.

요약하자면 무슨 프로그래밍을 하건 간에 먼저 생각하는 습관을 들이라는 것입니다. 그리고 결과로 만들어지는 코드의 질을 높이기 위해 노력하라는 것입니다. 이런 자세가 습관이 된다면 자신이 만들어낸 코드에 대한 안정성과 높은 성능을 보장해줄 것입니다. 이는 프로 프로그래머가 가져야 할 의식이며 이것이 바탕이 된다면 다른 사람들도 모두 여러분을 믿을만한 사람이라고 높게 평가해줄 것임에 틀림없습니다. 사실 필자도 프로그래밍을 배울 때 이렇게 하지 못했습니다. 프로그래밍을 오래 하다 보니까 그런 자세를 처음부터 갖는 것이 중요하다는 생각이 든 것입니다.

 

2. 예로 살펴보는 버그와 디버깅 팁

이제부터 실질적으로 코드를 통해 자주 접하게 되는 버그의 유형에 대해 예를 들어 알아보기로 하겠습니다.

 

1> 부호 숫자 타입과 무부호 숫자 타입

C/C++에는 부호 숫자 타입과 무부호 숫자 타입이 다음과 같이 존재합니다.

타입 크기

부호 숫자 타입 크기

무부호 숫자 타입 크기

8비트

char (-128 ~ 127)

unsigned char (0 ~ 255)

16비트

short (-32,768 ~ 32,767)

unsigned short (0 ~ 65,535)

32비트

int (-2,147,483,648 ~ 2,147,483,647)

unsigned int (0 ~ 4,294,967,295)

32비트

long (-2,147,483,648 ~ 2,147,483,647)

unsigned long (0 ~ 4,294,967,295)

비주얼 C++에서 사실 int와 long은 모두 32비트라는 점에 유의하기 바랍니다. 초보 프로그래머들이 많이 실수하는 분야 중의 하나가 바로 부적절한 정수 타입을 사용하는 것입니다.

 먼저 사용되는 데이터 값의 범위를 확인하고 그에 맞는 변수를 선택해야 합니다. 위의 표를 보고 적절한 변수를 선택하면 됩니다. 위의 표에는 없지만 사실 __int64라고 해서 64비트 짜리 정수 타입도 있습니다. 사실 대부분의 경우 int로 충분합니다.

 만일 사용되는 데이터의 값이 음수가 될 수 없는 것이 분명하면 무부호 정수 타입을 사용하기 바랍니다. 예를 들어 int 대신에 unsigned int를 사용하란 이야기입니다.

 그런데 무부호 정수 타입을 사용할 경우에는 비교 연산시에 아주 주의해야 합니다. 예를 들어 다음과 같은 코드를 보면

unsigned int dwNumber1 = 100, dwNumber2 = 200;

if (dwNumber1 - dwNumber2 > 0)

AfxMessageBox("dwNumber1 is larger");

else

AfxMessageBox("dwNumber2 is larger");

당연히 if 연산이 거짓이 되어 두 번째 메시지 박스가 출력될 것 같지만 그렇지 않습니다. 이 if 연산은 참이 됩니다. 무부호 정수간의 연산 결과는 다시 무부호 정수가 됩니다. 무부호 정수에는 말그대로 음수가 없습니다. 따라서 이 연산은 항상 참이 됩니다. 위의 코드는 다음과 같은 식으로 변경해야 합니다.

if (dwNumber1 > dwNumber2)

AfxMessageBox("dwNumber1 is larger");

else

AfxMessageBox("dwNumber2 is larger");

즉 무부호 정수끼리 뺄셈을 하는 경우에는 아주 조심해야 한다는 것입니다.

 

2> 포인터 사용하기

C/C++에 처음 입문한 프로그래머들이 가장 어려움을 느끼는 영역 중의 하나가 바로 포인터라는 개념을 익히는 것입니다. 포인터는 메모리 주소를 가리키는 변수입니다. 여기에 메모리를 할당해서 유효한 메모리 주소를 갖게 하기도 하고 다른 변수의 주소를 가리키게 하기도 합니다. 즉 포인터 변수는 선언한 후에 바로 사용하면 안 되고 무엇인가가 유효한 메모리 영역을 가리키게 만들어야만 한다는 것입니다. 전산을 4년 전공하고 회사에 막 들어온 사람들 중 상당수가 이를 제대로 이해하지 못한 상태임을 여러 번 보았습니다. 그 중의 한 예는 다음과 같습니다.

void DoSomething(char *lpstrSource)

{

char *lpstrString;

strcpy(lpstrString, lpstrSource);

...

}

위의 코드를 보면 pString이란 포인터 변수를 선언한 후에 그걸 그대로 strcpy라는 함수에 사용하고 있습니다. 이 상태에서 pString이란 변수는 메모리의 아무 영역이나 가리키고 있습니다. 여기에다가 인자로 넘어온 pSource가 가리키는 문자열을 복사하려고 하면 당연히 프로그램이 뻗어버립니다. 이런 코드가 나오는 이유는 포인터라는 것의 개념을 이해하지 못했기 때문입니다. 그런 상태에서 strcpy 함수의 원형을 보고 그대로 변수를 선언하고 인자로 지정한 것입니다. 올바른 코드는 다음과 같습니다. 포인터에 메모리를 할당한 다음에 이 것이 제대로 할당된 경우에만 복사를 시도하는 식으로 변경해야 합니다.

void DoSomething(char *lpstrSource)

{

char *lpstrString;

lpstrString = (char *)malloc(strlen(lpstrSource)+1);

if (lpstrString)

strcpy(lpstrString, lpstrSource);

...

}

다시 정리하자면 포인터란 메모리 영역을 가리키는 변수이기 때문에 무엇인가 유효한 영역을 가리키도록 초기화해야만 사용할 수 있습니다.

참고 1. 메모리 할당

컴퓨터 세계의 함수 중에는 쌍으로 사용되는 것이 무지하게 많습니다. 대표적인 것이 바로 메모리 할당을 할 때 사용되는 malloc 함수와 더 이상 사용할 일이 없을 때 이를 운영체제에 반환해주는 free 함수입니다. C++에는 new와 delete가 있지요. 이것들의 쌍이 제대로 맞지 않으면 메모리가 조금씩 조금씩 부족해지다가 결국에는 바닥나게 되어 있습니다.

그런데 화장실 들어갈 때와 나갈 때 마음이 다르다고 처음엔 메모리가 필요하니까 할당해서 사용하지만 사후처리를 잊기 십상입니다. 뭐 간단한 프로그램에서야 메모리할당과 반환이 그다지 복잡하지 않지만 정말로 복잡한 프로그램에서는 이게 쉽지 않습니다. 서버 프로그램의 경우 아주 작은 양의 메모리가 반환되지 않는 경우에는 프로그램이 한두달 돌아야 그게 밝혀지는 경우도 있습니다.

그래서 자바(Java)와 닷넷(.NET)에서는 이런 메모리 반환의 책임을 프로그래머에게 넘기지 않고 시스템 레벨에서 처리합니다. 즉 사용되지 않는 메모리가 있으면 알아서 반환시켜버리는 것입니다. 이를 가비지 컬렉션(Garbage Collection)이라고 부릅니다. 프로그래머 입장에서 두손 들고 환영할 일이지요. 하지만 이 가비지 컬렉션을 해주는 프로세스가 주기적으로 동작해야 하고 메모리가 바로 반환되는 것이 아니라 시간이 걸리기 때문에 프로그램의 실행 속도가 전체적으로 좀 느려지게 됩니다.

 

3> 함수의 리턴값 체크

초보 프로그래머이건 노련한 프로그래머이건 간에 또 많이 하는 실수가 바로 함수의 리턴값을 체크하지 않고 그대로 코드를 작성하는 경우입니다. 리턴 값을 체크하려면 if 문이 들어가야 하고 이게 좀 귀찮은 일입니다. 또한 이게 많아지면 코드를 한눈에 보기도 힘들어집니다. 그래서 많은 프로그래머들이 별일 있겠냐 하는 마음에 함수 리턴값 체크를 하지 않습니다. 예를 들어 파일 I/O를 한다고 하면 다음의 코드처럼 리턴 값을 체크하지 않는 경우가 허다합니다.

// strFilePath가 읽어들이고자 하는 파일의 경로를 가리킨다고 하자.

CFile file;

char strHeader[256];

file.Open(strFilePath, CFile::modeRead);

file.Read(strHeader, 255);

이런 코드는 strFilePath가 가리키는 파일이 없거나 파일은 있지만 파일의 크기가 부족한 경우에 에러를 내게 됩니다. 사용된 함수의 리턴 값을 모두 체크하도록 코드를 수정하면 다음과 같습니다.

// strFilePath가 읽어들이고자 하는 파일의 경로를 가리킨다고 하자.

CFile file;

char strHeader[256];

bool bError = FALSE;

// CFile::Open 함수는 오픈이 성공하면 TRUE를 리턴한다.

if (file.Open(strFilePath, CFile::modeRead))

{

// CFile::Read 함수는 읽어들인 바이트수를 리턴한다.

if (file.Read(strHeader, 255) != 255)

bError = TRUE;

}

else

bError = TRUE;

if (bError) // 에러가 있으면

{

CString strError;

strError.Format("에러가 발생했습니다 - %d", GetLastError());

AfxMessageBox(strError);

}

수정된 코드를 수정전의 코드와 비교하면 아마도 조금 더 코드의 흐름을 이해하기 힘들다는 느낌이 들 것입니다. 하지만 대신에 훨씬 더 코드가 안정적으로 동작하게 됩니다. 참고로 수정된 코드를 보면 에러가 발생했을 때 GetLastError 함수를 부르는데 이 함수는 윈도우 운영체제가 제공해주는 함수로 방금 발생한 에러에 해당하는 에러코드를 리턴해주는 역할을 수행합니다. 이 값에 해당하는 설명 문자열을 보고 싶다면 비주얼 C++의 도구(Tools) 메뉴의 오류 조회(Error Lookup) 명령을 실행한 다음에 거기에 에러코드를 입력해보면 됩니다. 다음은 "오류 조회" 다이얼로그의 실행 화면입니다.


< 그림 1. "오류 조회(Error Lookup)" 다이얼로그의 실행 화면 >

가끔 테스트 프로그램을 작성하거나 시간이 없을 경우 방금 코드처럼 리턴 값을 일일이 체크하는 것을 생략하는 사람들을 많이 봤습니다. 코드를 작성하는 당시에는 나중에 제대로 검사하게 고쳐야지 하지만 그럴 만큼 부지런한 사람은 드뭅니다. 수정할 기회가 언제 올지 알 수 없기 때문에 무슨 프로그램을 짜던지 항상 최선을 다하는 것이 좋습니다. 즉 되도록이면 "나중에 고치지"라는 생각은 접어 두시기 바랍니다.

참고 2. try/catch

함수의 리턴값 체크가 번거롭다면 그의 대안으로 사용할 수 있는 것이 바로 C++의 try/catch 문법입니다. 비주얼 C++에서는 MFC 클래스에 대해서만 적용가능하다는 단점을 갖고 있긴 하지만 이를 이용하면 if 문을 이용해 함수의 리턴 값을 검사할 필요가 없기 때문에 코딩도 간단해지고 코드를 읽기도 좀더 쉬워집니다. "3. 함수의 리턴값 체크"에서 본 코드를 try/catch를 이용하도록 변경해보면 다음과 같습니다.

// strFilePath가 읽어들이고자 하는 파일의 경로를 가리킨다고 하자.

CFile file;

char strHeader[256];

try

{

file.Open(strFilePath, CFile::modeRead);

file.Read(strHeader, 255);

}

catch(CException e)

{

e.ReportError();

e.Delete();

}

try로 둘러싸여진 블록 내에서 에러가 발생하면 실행이 바로 catch 블록으로 넘어갑니다. 단 모든 에러가 다 try 블록에 의해 감지되는 것은 아닙니다. 에러가 발생한 경우 그것을 throw 키워드를 이용해 리턴하는 함수들에서만 에러가 감지됩니다. MFC 클래스들은 대부분 에러가 발생하면 CException이란 클래스로부터 계승된 각자의 에러 클래스의 개체를 throw하도록 되어있습니다. CFile 클래스의 경우에는 CFileException이란 클래스가 에러 클래스에 해당하며 CException으로 계승된 클래스입니다. 또 클래스는 아니지만 new로 메모리를 할당하는 경우에도 메모리가 부족하면 예외를 발생시킵니다. 즉 ,new를 이용해서 메모리를 할당하는 경우에는 try/catch를 이용한다면 굳이 메모리 할당이 제대로 되었는지 일일이 검사할 필요가 없다는 이야기입니다.

사실 try/catch/throw는 그 자체만으로 상당한 지면을 통해 설명해야 하기 때문에 여기서는 이런 것이 있다는 것을 알리는 정도로 끝을 맺겠습니다. 참고로 비주얼 베이직이나 자바, C# 등의 대부분의 현대적인 프로그래밍 언어들은 이런 방식의 에러처리를 지원합니다.

>

top

4> 컴파일러 경고 메시지에 신경쓰자

대개의 프로그래머들은 컴파일러가 내주는 에러 메시지에만 신경을 쓰고 경고(Warning) 메시지에는 둔감합니다. 그런데 이 경고 메시지를 눈여겨 보면 간혹 모르고 지나쳤을 버그를 잡는 경우가 있습니다. 조사에 의하면 버그를 발견하는데 드는 시간이 90%이고 이를 수정하는데 걸리는 시간은 10%에 불과하다고 합니다. 경고 메시지를 눈여겨 보면 적은 노력으로 버그를 발견할 수 있는 셈입니다. 다양한 경고 메시지 중에서 네 가지를 살펴보도록 하겠습니다.

 비초기화 변수 사용 경고

그 중의 대표적인 것이 바로 변수를 선언은 했지만 그 변수를 초기화하지 않고 사용하는 경우입니다. 예를 들면 다음과 같은 경우가 있습니다.

{

int nNumber1, nNumber2;

nNumber1 = 100;

nNumber1 += nNumber2;

nNumber2에는 어떤 값이 들어가 있을지 아무도 모릅니다. nNumber2에는 임의의 값이 들어가 있을 수 있고 이는 nNumber1의 값에도 영향을 주게 됩니다. 위의 코드를 컴파일하면 다음과 같은 컴파일러 경고 메시지가 나타납니다.

warning C4700: local variable 'nNumber2' used without having been initialized

컴파일러의 경고 메시지도 유의해서 보는 사람이라면 쉽게 nNumber2가 초기화되지 않고 사용된 사실을 알아차릴 수 있습니다. 사실 이런 종류의 에러는 컴파일러의 경고 메시지가 아니면 찾기가 그리 쉽지 않을 수도 있습니다. 그 이유는 위와 같은 결과가 프로그램의 실행 결과에 큰 영향을 끼치지 않을 수도 있기 때문입니다. 즉 모르고 넘어갈 수도 있다는 것입니다.

 함수 내에 값을 리턴하지 않는 플로우 존재

함수의 코드가 길어지고 그 안의 다양한 부분에서 값을 리턴해야 한다면 실수로 return문을 빼먹을 수가 있습니다. 이 경우에도 앞서 초기화 안 된 변수를 쓸 때와 마찬가지로 그 당시 스택에 있던 아무 값이나 리턴이 되고 또한 마찬가지로 프로그램이 실행되는데 별 지장이 없을 수 있습니다. 그래서 이 역시 좀 찾기 어려운 에러가 될 수 있습니다. 예를 들어 다음과 함수를 보겠습니다.

int ReturnCheckFunction(int i)

{

switch(iValue)

{

case 1: return 0;

case 0: return 1;

}

}

위의 함수를 보면 인자 iValue로 1,0이외의 값이 들어오는 경우 return 문이 존재하지 않습니다. 이 경우 스택에 있던 아무 값이나 리턴됩니다. 위의 코드를 컴파일하면 다음과 같은 경고 메시지가 발생합니다.

warning C4715: 'ReturnCheckFunction' : not all control paths return a value

 논리 판단문안에서의 치환문 사용

가끔들 많이 하는 실수 중의 하나는 if문이나 while 문과 같은 논리 판단문 안에서 == 대신에 실수로 =를 사용하는 것입니다. 이 에러도 경우에 따라서는 아주 찾기 힘든데 컴파일러의 경고 메시지를 눈여겨 보면 아주 쉽게 찾을 수 있습니다. 예를 들어 다음과 같은 코드가 있다고 합시다.

int iRet; iRet = CallSomeFunction(); if (iRet = 0) { ... }

위의 코드에서 if문을 보면 iRet의 값과 0을 비교한다는 것이 잘못 되어서 iRet에 0을 대입하고 있습니다. 이렇게 되면 이 if 문은 항상 거짓이 됩니다. 이 코드를 컴파일하면 다음과 같은 에러가 발생합니다. WARNING 4706: assignment withing conditional expression 반대의 경우로 =를 써서 변수에 어떤 값을 대입하는 경우에 잘못해서 등호 연산자인 ==를 사용하는 경우도 있습니다.

i == 0;

이 것도 컴파일이 됩니다. 단 컴파일러가 다음과 같은 경고 메시지를 내줍니다.

WARNING 4553: '==' : operator has no effect; did you intend '='?

항상 컴파일러가 내주는 경고 메시지에 꼭 신경쓰기 바랍니다. 몇십초 동안 잠깐 살펴보는 것으로 여러분이 몇 시간 혹은 며칠동안 고생하는 것을 방지해줄 수 있습니다.

 

5> BOOLEAN 타입

비주얼 C++에는 두 종류의 Boolean 타입이 존재합니다. 하나는 BOOL이고 다른 하나는 bool입니다. 이 두 타입은 모두 다른 타입으로부터 typedef를 이용해 재정의된 것인데 다음과 같이 정의되어 있습니다.

typedef int BOOL;
typedef byte bool;

이 두 가지 중에서 bool 타입을 항상 사용하기를 권합니다. 이 타입의 장점은 다음과 같습니다.

 크기가 작습니다. 위에서 볼 수 있듯이 BOOL 타입은 크기가 4바이트이고 bool 타입은 크기가 1바이트입니다. 즉, 커다란 배열을 사용할 일이 있으면 bool이 절대적으로 메모리의 낭비를 줄일 수 있습니다.

 안전합니다. BOOL은 사실 TRUE, FALSE이외에도 다른 값이 대입 가능합니다. 예를 들어 다음과 같은 코드를 보고 실행 결과를 예측해보기 바랍니다.

BOOL bRet = 100;

if (bRet == TRUE)

AfxMessageBox("bRet가 참입니다.");

else

AfxMessageBox("bRet가 거짓입니다.");

BOOL 타입은 원래 TRUE와 FALSE 둘 중의 한 값을 갖도록 되어 있지만 불행히도 위와 같이 bRet에 100을 대입하는 코드는 전혀 문제를 일으키지 않습니다. 그리고 FALSE는 0으로 정의되어 있고 TRUE는 -1로 정의되어 있습니다. 따라서 위의 if문에서 bRet의 값(여기서는 100이 됩니다)과 TRUE는 서로 다른 값이 되어버리기 때문에 두 번째 메시지 박스가 실행됩니다. 즉 BOOL 타입의 문제는 TRUE, FALSE 이외의 다른 값이 들어갈 수 있는 가능성이 있기 때문에 if 문을 어떻게 사용하느냐에 따라서 전혀 다른 실행 결과를 낳는다는 것입니다. 만일 위의 코드에서도 if (bRet)와 같이 사용했다면 if 문이 참이 될 것입니다.

반면에 bool 타입(혹은 BOOLEAN)은 true와 false라는 단 두 가지의 값만을 가질 수 있습니다. 만일 0이외의 값을 이 타입의 변수에 대입하여도 이 값은 true라는 값으로 바뀌어서 저장됩니다. 다음과 같은 코드를 보기 바랍니다.

bool bRet = 100;

if (bRet == true)

AfxMessageBox("bRet가 참입니다.");

else

AfxMessageBox("bRet가 거짓입니다.");

위의 if 문은 참이 됩니다. 앞서 설명한 것처럼 bool 타입에서는 0이외의 값은 true로 바뀌어 저장되기 때문입니다. 항상 bool 타입을 사용하기 바랍니다.

 

6> 함수 인자 유효성 체크

이것은 버그를 방지하기 위한 일종의 방법인데 자신이 만드는 함수의 선두 부분에서 주어진 인자가 맞는 범위에 있는지 항상 먼저 체크하는 것입니다. 이 방법은 특히 다른 사람과 사용할 함수를 만들거나 다른 사람이 만든 데이터를 사용하는 함수를 만들 때 아주 효율적인 방법입니다. 즉 자기가 예상하고 있는 데이터들이 들어오는지 사용에 앞서 한번 검사하는 것입니다.

방금 이야기한 것처럼 이 방법은 공동작업에 아주 쓸모가 많습니다. 사람마다 나름대로의 독특한 이해방식을 갖고 있기 때문에 한참을 이야기해서 함수 인자 및 데이터의 형식에 대해 토론을 하고 결론을 내려도 실제로 구현을 해놓고 보면 서로의 이해가 다른 경우가 많습니다. 이로인한 혼란을 막기 위한 방법 중의 하나가 바로 이 방법입니다.

예를 들어 어떤 소팅함수가 있는데 소팅 대상이 되는 것이 0부터 2048 사이의 정수라고 합시다. 그러면 그 함수의 선두 부분에 다음과 같은 검사 코드를 넣어두는 것입니다.

BOOL Sort(int *pnNumbers, int nCount)

{

// 검사 코드를 넣어둡니다.

if (nCount <= 0) // 소팅할 대상이 0보다 작으면 그냥 리턴합니다.

return FALSE;

for(int iIndex = 0;iIndex < nCount;iIndex++)

{

if (pnNumbers[iIndex] < 0||pnNumbers[iIndex] > 2048)

{

printf("%d 번째의 값이 이상합니다. - %d\n", iIndex, pnNumbers[iIndex]);

return FALSE;

}

}

// 실제 소팅 코드가 나온다.

....

}

위와 같은 식으로 코딩을 하면 많은 버그를 잡을 수 있습니다. 그런데 위의 코드를 보면서 속도가 느려지지 않을까 하는 걱정을 하는 사람도 있을 것입니다. 그런 걱정이 든다면 위의 코드를 디버그 모드에서 동작하도록 하면 됩니다. 원래 디버그 모드에서 개발을 하고 디버깅이 끝나고 나면 릴리스 모드로 컴파일해서 사용하는 것이 정석이니까요. 그렇게 할 수 없는 경우도 가끔 있습니다. 디버그 모드에서는 잘 동작하는 프로그램이 릴리스 모드에서는 잘 동작하지 않는 경우가 간혹 있습니다. 다시 본론으로 돌아와서 디버그 모드에서 위의 체크 코드를 활성화시키고 싶다면 #ifdef 조건부 컴파일 지시자와 _DEBUG 상수를 이용하면 됩니다. 이 상수는 디버그 모드로 컴파일되는 경우에 비주얼 C++ 컴파일러에 의해 정의가 됩니다.

bool Sort(int *pnNumbers, int nCount)

{

// 검사 코드를 넣어둡니다.

if (nCount <= 0) // 소팅할 대상이 0보다 작으면 그냥 리턴합니다.

return FALSE;

#ifdef _DEBUG

for(int iIndex = 0;iIndex < nCount;iIndex++)

{

if (pnNumbers[iIndex] < 0||pnNumbers[iIndex] > 2048)

{

printf("%d 번째의 값이 이상합니다. - %d\n", iIndex, pnNumbers[iIndex]);

return FALSE;

}

}

#endif

// 실제 소팅 코드가 나온다.

....

}

#ifdef 다음에 오는 상수가 #define문으로 정의된 것이면 여기서부터 #else 혹은 #endif까지의 코드는 포함되고 결과적으로 컴파일됩니다. 즉 디버그 모드인 경우에만 위의 검사 코드가 포함되는 것입니다. 릴리스 모드에서는 _DEBUG라는 상수가 정의되지 않기 때문에 위의 코드는 포함되지 않습니다. 참고로 #ifndef라는 것도 있습니다. 그 다음에 오는 상수가 정의되어 있지 않으면 참이 되는 것입니다.

다시 한번 말하지만 디버그 모드는 말그대로 개발시에 디버깅을 위한 컴파일 모드입니다. 이 모드에서는 컴파일러가 코드 최적화를 시도하지 않습니다. 그렇기 때문에 덩치도 크고 속도도 릴리스 모드에 비해 20-40% 가량 늦습니다. 따라서 상품화되거나 외부에 서비스를 하는 프로그램이라면 릴리스 모드로 컴파일되는 것이 필수적입니다. 참고로 비주얼 C++ 닷넷에서는 그림 2처럼 빌드(Build) 메뉴의 구성 관리자(Configuration Manager) 명령을 선택하면 이를 선택할 수 있습니다. 비주얼 C++ 6.0에서는 이 컴파일 모드를 결정하는 것은 Build 메뉴의 Set Active Configuration 명령에서 가능합니다.


그림 2. 디버그 모드와 릴리스 모드의 선택

7> 통일된 함수 실행 성공 리턴 값과 시작 인덱스 값

리턴 값이 있는 함수의 경우 성공을 나타내기 위해 사용하는 값을 통일하기 바랍니다. 예를 들어 어떤 함수에서는 성공을 나타내기 위해 0을 사용하고 다른 함수에서는 1을 사용하고 또 다른 함수에서는 -1을 사용한다고 하면 자신이 만든 함수라 해도 항상 코드를 다시 살펴보아야 하는 문제가 발생하며 이로 인해 버그가 발생할 수도 있습니다. 저는 항상 성공을 나타내는 리턴코드로 0을 사용합니다. 성공을 나타내는 함수의 리턴 값을 통일해서 사용하기 바랍니다.

시작 인덱스 값도 마찬가지입니다. 예를 들어 열명의 사람에 대한 정보가 있는데 이름이 주어졌을 때 그 사람에 해당하는 번호를 리턴하는 함수를 만든다고 합시다. 자 그럼 이 번호를 0부터 셀 것인지 아니면 1부터 셀 것인지 혼란이 오는 경우가 있습니다. 저는 이런 경우 항상 0부터 세며 꼭 주석이나 문서에 0부터 센다는 것을 명시합니다. C/C++의 배열 인덱스가 0부터 시작하기 때문에 C/C++에서는 무엇인가 숫자 정보의 시작을 0부터 하는 것이 자연스러운 경우가 많습니다. 자신만의 인덱스 시작값 시작 규칙을 만들어 두기 바랍니다. 만일 여러 사람이 같이 일을 한다면 다같이 통일하면 더욱 좋겠지요.


참고 3. 변수 표기법

프로그래머의 개인 취향에 따라 변수 이름을 만드는 방법은 참 다양합니다. 아무 생각없이 하는 사람도 많긴 합니다만. 윈도우 운영체제에서는 찰스 시모니(Charles Simony)란 사람이 만든 헝가리안 표기법이란 것을 사용합니다. 이런 이름이 붙은 이유는 이 사람이 헝가리 출신이었기 때문이었고 윈도우에서 이 사람의 표기법을 사용하게 된 것은 이 사람이 마이크로소프트의 초창기 멤버로 많은 일을 했기 때문입니다. 이 표기법은 변수 이름 자체에 그 변수의 타입에 대한 정보를 같이 주는 것이지요. 예를 들면 다음과 같습니다.

int nCount; // 정수 변수 앞에는 i나 n을 붙입니다.

DWORD dwCount; // DWORD 타입 앞에는 dw를 붙입니다.

bool bReturn; // Boolean 타입 앞에는 b를 붙입니다.

char *lpstrMessage; // 문자에 대한 포인터앞에는 lpstr 혹은 pstr을 붙입니다.

여기에다가 윈도우에서는 클래스 멤버 변수의 경우, 그 이름 앞에 m_를 붙이는 것이 일반적입니다. 저 같은 경우는 전역 변수의 경우 그 이름 앞에 g_를 붙입니다. 이렇게 하면 좋은 점은 변수를 보는 순간이 이게 어디 정의되었고 타입이 무엇인지를 알 수 있게 된다는 것입니다. 예를 들어 다음과 같은 코드를 보기 바랍니다.

m_nCount = nIndex +g_nMinimum;

위의 코드를 보면 m_nCount는 이 코드가 실행되고 있는 클래스내의 멤버 변수이고 nIndex는 로컬 변수 (흔히 말하는 auto 변수)이고 g_nMinimum은 전역 변수라는 것을 알 수 있습니다. 거기다가 모든 변수가 정수 타입이라는 것도 알 수 있습니다.


결론

지금까지 버그를 방지하거나 버그를 잡기 위한 여러 가지 방법들에 대해 알아보았습니다. 사실 제일 중요한 것은 빠르고 간결하며 정확한 코드를 만들겠다는 마음 자세가 아닌가 싶습니다. 이러한 자세가 바탕이 된다면 위의 방법들이 습관으로 만드는 것이 그리 어렵지 않을 것입니다. 사실 방법 하나하나는 대단한 것이 없습니다. 하지만 이것들이 합쳐지면 시너지 효과가 대단합니다. 이것들을 습관으로 만드는 것이 바로 버그를 없애는 프로그래밍의 시작이며 프로그래밍을 처음 배우는 단계에서 이를 습관으로 만들 수 있다면 여러분은 아주 뛰어난 프로그래머가 될 수 있을 것입니다.

참고문헌

  1. Steve Maguire, Writing Solid Code : Microsoft's Techniques for Developing Bug-Free C Programs, Microsoft Press, 1993
  2. Brian W. Kernighan and Rob Pike, The Practice of Programming, Addison-Wesley, 1999
  3. Steve Maguire, Debugging the Development Process : Practical Strategies for Staying Focused, Hitting Ship Dates, and Building Solid Teams, Microsoft Press, 1994
  4. Steve C McConnell, Code Complete : A Practical Handbook of Software Construction, Microsoft Press, 1993
Posted by keegan
Posted by SB패밀리