C# & VB.NET2009. 1. 19. 00:20

지난 번 Comb Sort를 소개했던 포스팅에서 말씀 드렸듯이, Bubble Sort, Comb Sort, 그리고 닷넷의 기본 소팅 알고리즘인 Quick Sort의 성능을 비교해보았습니다.

테스트 소스는 지난 번 소스에 Quick Sort를 호출하는 부분을 추가한 것 뿐입니다. 그리고 Quick Sort도 따로 구현한 것이 아니라 닷넷의 System.Array.Sort()를 호출하는 방식으로 했습니다.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SortingSample
{
   
class Program
   
{
       
static void Main(string[] args)
       
{
           
int dataCount = Convert.ToInt32 (args[0]);
           
string[] BubbleUnsorted = new string[dataCount];
           
string[] CombUnsorted = new string[dataCount];
           
string[] QuickUnsorted = new string[dataCount];
           
string[] BubbleSorted = new string[dataCount];
           
string[] CombSorted = new string[dataCount];
           
string[] QuickSorted = new string[dataCount];
           
Random RandObj = new Random();
           
for (int idx = 0; idx < dataCount; idx++)
           
{
               
BubbleUnsorted[idx] = RandObj.Next().ToString();
               
CombUnsorted[idx] = RandObj.Next().ToString();
               
QuickUnsorted[idx] = RandObj.Next().ToString();
           
}

           
BubbleSorted = BubbleSort(BubbleUnsorted);
           
CombSorted = CombSort(CombUnsorted);
           
QuickSorted = QuickSort(QuickUnsorted);
       
}

       
public static string[] QuickSort(string[] unsorted)
       
{
           
long begin = System.DateTime.Now.Ticks;
            
           
System.Array.Sort(unsorted);

           
long end = System.DateTime.Now.Ticks;
           
long duration = (end - begin)/10000;
           
Console.WriteLine("Quick Sort Data Count " + Convert.ToString(unsorted.Length) + " " + duration.ToString() + " milliseconds elapsed");

           
return unsorted;
       
}

       
public static string[] BubbleSort(string[] unsorted)
       
{            
           
bool swap;
           
long begin = System.DateTime.Now.Ticks;
           
do
           
{
               
swap = false;
               
for (int i = 1; i < unsorted.Length; i++)
               
{
                   
if (Convert.ToInt64(unsorted[i - 1]) > Convert.ToInt64(unsorted[i]))
                   
{
                       
string tmp = unsorted[i - 1];
                       
unsorted[i - 1] = unsorted[i];
                       
unsorted[i] = tmp;
                       
swap = true;
                   
}              
               
}
                
           
} while (swap);
           
long end = System.DateTime.Now.Ticks;
           
long duration = (end - begin)/10000;
           
Console.WriteLine("Bubble Sort Data Count " + Convert.ToString(unsorted.Length) + " " + duration.ToString() + " milliseconds elapsed");
           
return unsorted;
       
}

       
public static string[] CombSort(string[] unsorted)
       
{
           
int gap = unsorted.Length;
           
int swap;

           
long begin = System.DateTime.Now.Ticks;

           
do
           
{
               
if (gap > 1)
               
{
                   
gap = (gap * 10) / 13;
                   
if (gap == 9 || gap == 10)
                   
{
                       
gap = 11;
                   
}
               
}

               
swap = 0;

               
for (int i = 0; i + gap <= unsorted.Length-1; i++)
               
{
                   
if (Convert.ToInt64(unsorted[i]) > Convert.ToInt64(unsorted[i+gap]))
                   
{
                       
string tmp = unsorted[i];
                       
unsorted[i] = unsorted[i+gap];
                       
unsorted[i+gap] = tmp;
                       
swap = 1;
                   
}                  
               
}

           
} while (gap > 1 || swap == 1);

           
long end = System.DateTime.Now.Ticks;
           
long duration = (end - begin) / 10000;
           
Console.WriteLine("Comb Sort Data Count " + Convert.ToString(unsorted.Length) + " " + duration.ToString() + " milliseconds elapsed");
           
return unsorted;
       
}
   
}
}

Colorized by: CarlosAg.CodeColorizer

 

배열의 개수를 파라미터로 넘길 수 있도록 코딩 했습니다. 그래서 테스트는 10개, 100개, 1000개 이렇게 10배씩 늘려나가는 방식으로 진행했고, 3번 해서 그 평균을 구했습니다. 결과는 이렇습니다. (단위는 millisecond 즉, 100분의 1초입니다)

  Bubble Sort Comb Sort Quick Sort
10 5 0 2
100 11 1 2
1000 804 18 5
10000 83904 282 42

확실히 Bubble Sort는 매우 매우 느립니다. ^^;; 그리고 Comb Sort는 100개 이하에서는 오히려 Quick Sort보다도 빠른데, 1000개 정도부터는 Quick Sort의 상대가 되질 않네요.

그래서 따로 Comb Sort 와 Quick Sort 만을 따로 비교를 해보았습니다. 이 두 가지 Sorting은 100000개와 1000000개도 테스트할 수 있었습니다. (Bubble Sort로는 도저히 이 숫자는 할 수가 없더군요)

 

  Comb Sort Quick Sort
10 0 2
100 1 2
1000 18 5
10000 282 42
100000 3831 526
1000000 49382 6418

그래도 빠르다고 생각했던 Comb Sort도 100000개 이상부터는 Quick Sort와 비교가 불가능한 성능을 보여줍니다. 정말 Quick Sort가 짱 이네요 ^^

 

Quick Sort에 대해서 자세히 알고 싶은 분들은 WikipediaQuick Sort 항목을 참조하시길 바랍니다. 오른 편에 있는 그림이 정말 Quick Sort의 모든 것을 보여주네요.

그리고 Reflector로 당연히 닷넷의 소스도 확인할 수가 있습니다. 아래 그림과 소스처럼 말이죠. ^^

internal SorterObjectArray(object[] keys, object[] items, IComparer comparer)
{
   
if (comparer == null)
   
{
       
comparer = Comparer.Default;
   
}
   
this.keys = keys;
   
this.items = items;
   
this.comparer = comparer;
}

internal void SwapIfGreaterWithItems(int a, int b)
{
   
if (a != b)
   
{
       
try
       
{
           
if (this.comparer.Compare(this.keys[a], this.keys[b]) > 0)
           
{
               
object obj2 = this.keys[a];
               
this.keys[a] = this.keys[b];
               
this.keys[b] = obj2;
               
if (this.items != null)
               
{
                   
object obj3 = this.items[a];
                   
this.items[a] = this.items[b];
                   
this.items[b] = obj3;
               
}
           
}
       
}
       
catch (IndexOutOfRangeException)
       
{
           
throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", new object[] { this.keys[b], this.keys[b].GetType().Name, this.comparer }));
       
}
       
catch (Exception exception)
       
{
           
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), exception);
       
}
   
}
}

internal void QuickSort(int left, int right)
{
   
do
   
{
       
int low = left;
       
int hi = right;
       
int median = Array.GetMedian(low, hi);
       
this.SwapIfGreaterWithItems(low, median);
       
this.SwapIfGreaterWithItems(low, hi);
       
this.SwapIfGreaterWithItems(median, hi);
       
object y = this.keys[median];
       
do
       
{
           
try
           
{
               
while (this.comparer.Compare(this.keys[low], y) < 0)
               
{
                   
low++;
               
}
               
while (this.comparer.Compare(y, this.keys[hi]) < 0)
               
{
                   
hi--;
               
}
           
}
           
catch (IndexOutOfRangeException)
           
{
               
throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", new object[] { y, y.GetType().Name, this.comparer }));
           
}
           
catch (Exception exception)
           
{
               
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), exception);
           
}
           
catch
           
{
               
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"));
           
}
           
if (low > hi)
           
{
               
break;
           
}
           
if (low < hi)
           
{
               
object obj3 = this.keys[low];
               
this.keys[low] = this.keys[hi];
               
this.keys[hi] = obj3;
               
if (this.items != null)
               
{
                   
object obj4 = this.items[low];
                   
this.items[low] = this.items[hi];
                   
this.items[hi] = obj4;
               
}
           
}
           
low++;
           
hi--;
       
}
       
while (low <= hi);
       
if ((hi - left) <= (right - low))
       
{
           
if (left < hi)
           
{
               
this.QuickSort(left, hi);
           
}
           
left = low;
       
}
       
else
       
{
           
if (low < right)
           
{
               
this.QuickSort(low, right);
           
}
           
right = hi;
       
}
   
}
   
while (left < right);
}}

 

Posted by kkongchi
C# & VB.NET2008. 12. 26. 22:22

 

* 아래 덧글에서 보실 수 있듯이, exedra님 지적 때문에 일부 소스와 비교 데이터가 수정되었습니다. (2008-01-14)

 

회사 솔루션 코드 중에 VB로 된 Comb Sort 코드가 있길래, C#으로 재 작성해보았습니다.

그러면서 가장 기본적인 Bubble Sort와 성능 차이가 얼마나 되나 비교도 해 봤습니다.

Comb Sort의 내용에 대해서는 Wikipedia의 Comb_sort 항목을 참조하시면 됩니다.

 

코드는 아래와 같습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SortingSample
{
   
class Program
   
{
       
static void Main(string[] args)
       
{
           
int dataCount = Convert.ToInt32 (args[0]);
           
string[] BubbleUnsorted = new string[dataCount];
           
string[] CombUnsorted = new string[dataCount];

           
string[] BubbleSorted = new string[dataCount];
           
string[] CombSorted = new string[dataCount];

           
Random RandObj = new Random();
           
for (int idx = 0; idx < dataCount; idx++)
           
{
               
BubbleUnsorted[idx] = RandObj.Next().ToString();
               
CombUnsorted[idx] = RandObj.Next().ToString();
           
}

           
BubbleSorted = BubbleSort(BubbleUnsorted);
           
CombSorted = CombSort(CombUnsorted);
       
}
        
       
public static string[] BubbleSort(string[] unsorted)
       
{            
           
bool swap;
           
long begin = System.DateTime.Now.Ticks;
           
do
           
{
               
swap = false;
               
for (int i = 1; i < unsorted.Length; i++)
               
{
                   
if (Convert.ToInt64(unsorted[i - 1]) > Convert.ToInt64(unsorted[i]))
                   
{
                       
string tmp = unsorted[i - 1];
                       
unsorted[i - 1] = unsorted[i];
                       
unsorted[i] = tmp;
                       
swap = true;
                   
}              
               
}
                
           
} while (swap);
           
long end = System.DateTime.Now.Ticks;
           
long duration = (end - begin)/10000;
           
Console.WriteLine("Bubble Sort Data Count " + Convert.ToString(unsorted.Length) + " " + duration.ToString() + " milliseconds elapsed");
           
return unsorted;
       
}
        
       
public static string[] CombSort(string[] unsorted)
       
{
           
int gap = unsorted.Length;
           
int swap;

           
long begin = System.DateTime.Now.Ticks;

           
do
           
{
               
if (gap > 1)
               
{
                    
                   
gap = (gap * 10) / 13;
                   
if (gap == 9 || gap == 10)
                   
{
                       
gap = 11;
                   
}
               
}

               
swap = 0;

               
for (int i = 0; i + gap <= unsorted.Length-1; i++)
               
{
                   
if (Convert.ToInt64(unsorted[i]) > Convert.ToInt64(unsorted[i+gap]))
                   
{
                       
string tmp = unsorted[i];
                       
unsorted[i] = unsorted[i+gap];
                       
unsorted[i+gap] = tmp;
                       
swap = 1;
                   
}                  
               
}

           
} while (gap > 1 || swap == 1);

           
long end = System.DateTime.Now.Ticks;
           
long duration = (end - begin) / 10000;
           
Console.WriteLine("Comb Sort Data Count " + Convert.ToString(unsorted.Length) + " " + duration.ToString() + " milliseconds elapsed");
           
return unsorted;
       
}
   
}
}

Colorized by: CarlosAg.CodeColorizer

 

1000건을 Sorting했을 때 결과는 이렇습니다.

BubblevsComb

차이가 매우 크다는 것을 보실 수 있습니다. Comb Sort가 꽤 빠른 Sorting 알고리즘이라는 것을 알 수가 있습니다. 다음 번에 기회가 나면, .NET의 기본 Sort 알고리즘인 Quick Sort와 한 번 비교해보도록 하겠습니다.

 

Tistory 태그: ,,,
Posted by kkongchi
기타2008. 12. 26. 21:49

무려 9개월 간의 공백이 있었습니다. 도중에 일이 바쁘기도 했고, 몸이 살짝 아프기도 했으며, 신상에 조금 중대한 변화 – 업무도 그대로이고 근무하는 곳도 그대로이지만, 소속된 회사가 바뀐 – 가 있기도 했습니다. 그래도 결국 공백의 가장 큰 원인은 의욕 상실, 게으름 등등 내 안에 있었던 문제가 아니었나 싶습니다.

다시 시작합니다. 그리고 열심히 하겠습니다. 혹시라도 가끔 새 글이 없나 방문해주셨던 분들 – 없을 거라고 생각은 하지만 – 께는 감사드리면서, 다시 열심히 하겠다는 말씀 드립니다.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
   
class Program
   
{
       
static void Main(string[] args)
       
{
           
Console.WriteLine("New Beginning");
       
}
   
}
}

Tistory 태그:
Posted by kkongchi
.NET General2008. 3. 30. 22:39

최근에 현재 유지보수중인 솔루션의 에러 처리 로직 개선 작업을 하다가, 내가 아직도 Throw와 Throw ex의 차이를 모른다는 사실을 알게 되었다. -_-;; 그래서 구글신에게로 고고씽. ㅎ


역시 구글신께서 알려주신 Top 4개의 링크만으로 모든 궁금증이 풀렸다. 그 링크 목록은 다음과 같다.

Joteke's Blog - Difference: throw or throw ex?
Scott Dorman Blog - Difference between "throw" and "throw ex" in .NET
Mattness Encapsulated -  throw; vs. throw ex; Here's the difference!
BULLOG.NET - 즈믄의 C# 강좌 - throw  VS  throw ex


정리하자면..

  1. Throw는 이전 Exception의 모든 내용을 보존하지만, Throw ex는 보존하지 않는다. 따라서 단순히 이전에 발생한 예외를 전달만 한다면, Throw를 쓰는 것이 좋다.
  2. 새롭게 예외를 포장(?)한다면 - New ApplicationException등을 사용해서 - 이전 예외 객체를 innerException으로 같이 넘기는 것이 좋다.
Posted by kkongchi
MS-SQL2008. 3. 30. 21:39

"sp_dbcmptlevel" 내장 프로시저를 사용해서 MS-SQL 2005에서 DB 호환성 레벨을 변경할 수 있다.

사용 예제는 다음과 같다.

EXEC sp_dbcmptlevel AdventureWorks, 80;

DB 호환성 레벨에 대한 자세한 정보는 MSDN의 다음 링크를 참조하면 된다.

http://msdn2.microsoft.com/en-us/library/ms178653.aspx

Posted by kkongchi
기타2008. 3. 18. 01:02

최근 이 블로그 포스팅 작성 툴로 Windows Live Writer를 사용하고 있다. 물론 Live Writer 자체도 훌륭한 툴이지만, Colorized Code 플러그인과 MSDN 링크 플러그인 두 가지 기능을 쓰기 위해서 Live Writer를 쓴다고 해도 과언이 아니다.


Colorized code 플러그인은 코드를 삽입할 때 정말 유용하다. 아래 캡쳐화면처럼 코드를 상단에 입력하면 하단에는 그 코드를 Visual Studio처럼 보이게 해주는 태그를 생성해주는 방식인데, 물론 OK버튼을 누르면 바로 Windows Live Write 화면으로 포장된 텍스트가 입력이 된다. 이 기능없이 코드를 입력하려면 수동으로 일일이 각 단어의 색상을 따로 지정해 줬어야 했는데, 이 기능을 사용하면 전혀 그럴 필요가 없이 깔끔하고 보기 좋은 코드를 만들어 준다.



MSDN 링크 플러그인은 아래 캡쳐처럼 네임스페이스나 클래스의 풀 네임을 입력하면 거기에 해당하는 MSDN의 링크를 찾아주는 플러그인이다. 사실 꼭 링크를 걸 필요는 없지만, 그래도 MSDN의 링크를 걸어두면, 독자들에게 조금이라도 더 보탬이 되지 않을까 해서, 글 속에 나오는 클래스나 네임스페이스는 이 기능을 이용해서 링크를 걸고 있다.



위 두 플러그인은 정말 내가 Windows Live Writer를 쓰는 이유와도 같은 기능들이다. 닷넷 개발 관련 블로깅을 하는 블로거라면 Windows Live Writer와 이 플러그인을 써서 좀 더 보기좋은 포스팅을 할 수 있을 것이다.


* 참고로 이 플러그인들은 Window Live Writer 플러그인 다운로드 페이지에서 다운받은 후 설치할 수 있다.

Posted by kkongchi
기타2008. 3. 13. 21:46

우리 솔루션의 고객 중의 하나가 며칠전 SSL(HTTPS) 환경에서 문제가 발생했다고 리포트를 해왔다. 그래서 나는 급하게 SSL 환경 테스트를 해야만 했다. 하지만, 우리 개발팀에는 아직 SSL환경이 구축된 서버가 없었고, 서버 인증서 또한 없었다. 인터넷을 뒤지다가.. Verisign에서 test용 서버 인증서를 발급해준다는 사실을 알게 되었다. 테스트 SSL 환경을 구축해야 할때, 쓸 수 있는 방법 중에 하나가 아닌가 싶다.

테스트 서버 인증서를 발급받을 수 있는 주소는 다음과 같다. 인증서 발급 절차와 함께, 자세한 설치 가이드까지 제공해주니까 매우 유용할 것 같다.

http://www.verisign.com/ssl/buy-ssl-certificates/free-ssl-certificate-trial/

Posted by kkongchi
C# & VB.NET2008. 3. 13. 13:14

가끔 우리 솔루션에서는 System.Runtime.Serialization.SerializationException이 발생한다. 문제가 발생한 상황이니까 예외가 던져지는 것은 당연하다. 하지만 문제는 실제 에러는 SerializationException이 아니라는 것이었다. 그리고 SerializationException은 다들 알다시피 객체가 Serialization/Deserialization하는 과정에서 나는 문제인데, 이 에러가 날만한 시나리오를 딱히 생각해낼 수는 없었다.

Stacktrace를 보니 이 예외는 솔루션 내부의 로깅 컴포넌트에서 던져지고 있었다. 로깅 컴포넌트는 에러나 문제가 발생했을 때 그 정보를 파일에 기록하는 역할을 하는 COM+ 서버 타입 컴포넌트였다. 문제가 생긴 함수는 다음과 같았다.

Public Function WriteException(ByVal userName as string, ByVal exception as System.Exception) as string

 
이 함수는 Exception객체를 받아서 그 예외 정보를 포맷한 뒤 파일에 쓰는 함수였다. 여기서 문제가 될만한 부분은 인자로 System.Exception 객체를 받는 부분뿐이었는데, 로깅 컴포넌트가 COM+ 서버 타입이기 때문에 Exception객체가 프로세스간 이동을 하기 때문이었다. 하지만 System.Exception 객체가 Serialization 과정에서 문제가 생긴다는 것은 사실 상상하기가 힘들었는데..
 
암튼 조사 끝에 이유를 알아내었다. 문제가 생긴 객체는 모두 System.Web.HttpException이었다. 그리고 이 System.Web.HttpException이 무슨 이유인지는 몰라도, .NET Framework 1.1에서는 Serializable로 마킹이 되어 있지 않다는 사실을 알게 되었다. 즉 Logging컴포넌트에서 HttpException을 인자로 받았는데, 이게 COM+ 서버 타입이라 프로세스 이동을 하려면 Serialization과정을 거치다가.. Serializable 마킹이 안 되어 있어서 Serialization작업을 못한 것이다. 그래서 SerializationException도 났던 것이고..
 
.NET 1.1과 2.0의 System.Web.HttpException을 Reflector로 열어보면 다음과 같다.
 
1.1의 경우
DotNet1HttpException
 
2.0의 경우
DotNet2HttpException
 
.NET Framework 2.0에서는 Serializable 어트리뷰트가 있지만, .NET Framework 1.1에서는 없는 것을 볼 수 있다. 왜 1.1에서 없는지는 속시원한 설명을 제대로 찾지 못했다. 현재로서는 버그가 아닐까 추측만 될 뿐이다. 그리고 2.0이상부터는 이런 문제가 없는 것 같다.
 
우리 솔루션은 로깅 컴포넌트의 COM+ 유형을 Server Type에서 Library Type 으로 바꾸어서 해결했다. Library Type이라면 프로세스가 다르지 않을테고 프로세스간 객체 이동도 없을 것이니, 이 문제를 신경쓰지 않아도 되는 것이다.
 
다른 곳에도 이 케이스처럼 Exception객체가 프로세스를 넘어가는 시나리오가 있다면, 이 문제를 조심해야 할 것이다.
Posted by kkongchi
C# & VB.NET2008. 3. 9. 21:01
Tistory 태그: ,

메서드를 작성할 때에, Argument가 다 정상적으로 들어올거라고 가정해서는 절대로 안 된다. 반드시 다음과 같이 Validation 코드를 작성해서, 메서드의 가장 위에 둘 필요가 있다.


1. Argument 가 null인지 검사해서, null이라면 NullArgumentException을 던져야 한다. 아래 코드는 .NET Framework의 System.Windows.Annotations.Annotation 클래스의 WriteXml메서드의 가장 윗부분 코드이다.


public void WriteXml(XmlWriter writer)
{
   
if (writer == null)
   
{
       
throw new ArgumentNullException("writer");
   
}
}
 

2. 각 argument값 자체의 이상 유무를 검사한다.
기본적인 string 이라면, 전에 올렸던 글처럼 IsNullOrEmpty 메서드를 써서 검사하고, 다른 type이라면.. -_-;; 다양한 것을 다 여기서 다루기는 힘드므로.. 일단 적절하게 검사한다.  아래 예제는 위에서도 사용한 System.Windows.Annotations.Annotation 클래스의 WriteXml메서드의 일부분인데, 넘어온 XML의 내부 스트링들을 IsNullOrEmpty메서드로 검사해서, 없을 경우 채워넣는 코드이다.


  if (string.IsNullOrEmpty(writer.LookupPrefix("http://schemas.microsoft.com/windows/annotations/2003/11/core")))
   
{
       
writer.WriteAttributeString("xmlns", "anc", null, "http://schemas.microsoft.com/windows/annotations/2003/11/core");
   
}
   
if (string.IsNullOrEmpty(writer.LookupPrefix("http://schemas.microsoft.com/windows/annotations/2003/11/base")))
   
{
       
writer.WriteAttributeString("xmlns", "anb", null, "http://schemas.microsoft.com/windows/annotations/2003/11/base");
   
}
}
Posted by kkongchi
2008. 2. 25. 02:32
사랑하지 않으면 떠나라! - 10점
차드 파울러 지음, 송우일 옮김/인사이트


누군가의 블로그에서 알게 된 이 책을 읽으면서, 참 여러가지 생각이 들었던 것 같다. 나 자신 IT업계에 지난 수년간 몸담으면서, 이 책이 끊임없이 나에게 해오는 질문들에 대해서 제대로 대답하지 못하는 나를 뼈저리게 느꼈기 때문이었다.

이 책의 원제는 "My Job Went to India!"이다. OffShore 정책에 따라 많은 미국의 IT R&D Lab이 인도로 옮겨가게 되면서, 미국의 많은 개발자들은 실직의 두려움과 공포가 느꼈을 것이다. 그런 격변하는 상황 속에서 어떻게 자기 계발을 통해서 자신의 일자리 아니 자신의 자존심을 지킬 수 있는지가 저자인 차드 파울러가 이 책을 쓴 동기라고 말할 수 있겠다. 여담이지만, 나도 이 책에서 언급하고 있는 인도 방갈로르에 동료들이 있다. 그 친구들이 영어를 얼마나 잘하는지, 일은 또 얼마나 잘 하는지 나는 사실 잘 아는 편이다.

하지만 이 책은 일자리를 뺏기고 지키고 이런 것이 중요하지 않다고 말한다. 즉, 일자리가 중요한 것이 아니라, 자신의 일에서 얼마나 즐거움을 느끼는가 그리고 얼마나 잘 하기 위해서 노력하고 있는가 이런 것에 중요하다고 말한다.

"일자리를 지키는 것이 여러분의 유일한 목적이라면 이 책은 별로 도움이 되지 않을지도 모릅니다. 대신 자신의 분야에서 탁월한 무언가가 되기 위해 노력한다면 이 책을 계속 읽어나가시기 바랍니다"
- 저자 서문에서

그런 의미에서 이 책은 무언가를 알려주는 책이 아니다. 거꾸로 이 책은 독자에게 계속해서 질문을 던진다. 당신은 진정한 전문가입니까? 당신은 유능한 프로그래머입니까? 당신은 다른 사람들에게 도움을 줄 수 있을만큼 지식을 갖추고 있습니까? 등등..

부끄러운 말이지만, 나는 거의 제대로 대답하지 못했다. 책을 읽는 내내 얼굴이 화끈거렸고, 후회가 들었다. 나는 정말 훌륭한 개발자도 아니었고, 다른 사람들에게 도움을 줄 수 있는 좋은 사람도 아니었고, 정말 진정한 개발자가 아니었던 것이다. 개발자인척 폼만 잡고 있었던 것이다.

나는 이 책을 읽고, 앞으로 아래 세 가지 방향으로 나를 발전시켜 나가기로 했다. 이 책을 몇 년 뒤에 다시 들었을 때, 이 책이 던지는 질문들에 대해서 자신있게 대답할 수 있도록 말이다.



넓고도 또한 깊어지자.

넓게 공부를 해야 한다. 현재 나는 MS 닷넷 기반 개발을 주로 하고 있지만, 자바나 루비같은 다른 최신의 기술들도 공부해야 한다. 개발자이지만, IT Staff들이 많이 사용하는 셸 스크립트나 Perl같은 것들도 공부해야 한다. 지식의 폭이 넓어질 수록, 내가 현재 사용하는 기술에 대해서 더욱 잘 이해를 할 수가 있다.

그리고 넓으면서 동시에 깊어야 한다. 여러 가지 공부를 한다고 해서 맛만 보는 수준은 되지 말아야 겠다. 흔히들 넓으면 깊지 못하고 깊으면 넓지 못하다고 말하지만, 내가 보는 한 그 말은 틀렸다. 넓어야 깊을 수 있고 깊어야 넓을 수 있을 것이다. 즉, 다른 기술들에 대한 넓은 이해를 바탕으로 해야, 현재 내가 주로 사용하는 기술에 대해서 깊게 이해를 할 수 있을 것이다. 또 반대로 내가 주로 사용하는 기술들에 대해서 깊게 이해를 하고 있다면, 넓게 공부하는 것이 좀 더 쉬워질 것이다.

"유능한 사람은 다양성을 찾는다. 그런 사람들은 새로운 것을 배우기 좋아하기 때문이다. 또한 이질적인 경험이나 환경을 통해 더 성숙하고 다재다능한 소프트웨어 개발자가 되기 때문이다"
- 04. 지성에 투자하라 중에서



남을 도우면서, 나 자신 또한 도와주자.

나는 그동안 혼자서 개발만 하는 사람은 아니었다. SI업체에 있을 때에는 주로 개발 표준이나 코드 리뷰 등의 업무를 많이 맡았기 때문에, 각종 프로젝트에서 그래도 가장 다른 개발자들과 접촉이 잦았던 개발자 중의 하나였다. 그것은 그다지 쉬운 일은 아니었다. 사실 가장 편한 일은 자기 것만 하는 것이다. 하지만, 나는 그런 경험들을 통해서 정말 많이 배울 수가 있었다. 다른 개발자들의 질문에 대답해주면서, 나 자신 관련 기술들에 대한 이해를 좀 더 깊게 할 수가 있었다. 결국 남을 돕는 것은 나 자신을 돕는 것이었다. 현재는 그런 공통적인 일을 하는 팀이 아니라 혼자서 할 수 있는 일을 하고 있지만, 계속해서 내가 먼저 다가가서 다른 개발자들의 문제를 같이 고민해주고 같이 해결해주고 싶다. 그리고 그 모든 것들은 나에게 다시 돌아올 것이다. 더 크게..

"무엇인가를 정말 배우고 싶다면 그것을 다른 누군가에게 가르쳐 보라. 어떤 것에 대한 이해를 구체화하는데 가장 좋은 방법은 다른 사람들이 이해할 수 있도록 그것을 표현하는 것이다. 말을 한다는 것은 단순한 행동이지만 불분명한 사고를 다루는 데는 특효약이다."
- 13. 멘토가 되라 중에서

진정한 신뢰받는 전문가가 되자.


나는 사실 전문가여만 한다. 고객이나 PM 등이 나에게 프로그램에 대한 질문을 할 때에는 기본적으로 이 사람에게 물어보는 순간 모든 고민이 해결될 거라는 생각을 갖고 하게 될 것이다. 하지만 내가 그런 질문들에 잘 응대를 했던가 하는 반성을 하게 되었다.

일단, 관련된 기술 전체에 대해서 폭넓게 이해를 해야겠다. 닷넷 개발자이지만, 윈도우 서버에서 어떻게 우리의 서버 응용 프로그램들이 동작하는 지 알려면 서버나 네트워크에 대해서 알아야 한다. 오피스 애드인 프로그램에 문제가 있다면, 오피스의 동작원리나 애드인을 어떻게 처리하는 지에 대해서도 알아야 한다. 우리 제품 그 자체뿐 아니라 제품이 동작하는 환경 전체에 대해서 전문가가 되어야 한다.

그리고, 우리 제품의 가장 밑바닥에 있는 비즈니스 로직에 대해서도 알아야겠다. 내가 현재 개발하고 유지보수를 하는 제품은 기업 회계 소프트웨어이다. 하지만 솔직히 말하자면 나는 기업의 회계나 재무 분석에 대해서는 현재 백치나 마찬가지이다. 진정으로 전문가가 되기 위해서 나는 당장 회계 기초 책을 사서 공부할 생각이다. 그러지 않고서는 나는 내가 만든 소프트웨어로부터도 소외를 당하는 것이다.

"그렇다면 소프트웨어 분야에서 전문가는 어떤 사람이어야 할까? 어떤 상황이 닥치더라도 80%정도는 '그거요, 해봤습니다.' 라고 말할 수 있고 나머지 20%의 상황에서는 기존 지식의 깊이를 사용해 문제를 처리할 수 있는 사람을 원했다. 상위 수준 추상화를 다루면서 그러한 추상화 구현의 세세한 부분을 이해할 수 있는 사람을 원했다. 앞으로 부딪힐 수 있는 문제를 풀 수 있다거나 풀지 못한다면 최소한 도움을 요청할 만한 사람을 아는 사람이 필요했다."
- 06. 진정한 전문가가 되라 중에서



이상, 내가 앞으로 나의 계발을 위해서 할 것들을 정리해보았다. 다 정리해보니 사실 눈앞이 캄캄하다. 앞으로 놀기는 글렀다.^^ 하지만, 이제 더 이상 후회하지 않기 위해서, 더 이상 내 직업에 부끄럽지 않기 위해서 오늘부터 노력할 것이다. 아니 지금 당장부터 노력을 할 것이다. 매일 아주 바쁘겠지만, 기분이 좋다. 상쾌하다. 앞으로의 나의 진로와 미래가 기대된다. 두근두근거릴 정도로...


마이크로소프트 Hero 블로그
Posted by kkongchi