2018년 7월 31일 화요일

TSP 구현 - 2 (Memoization 추가)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <stdio.h>
 
using namespace std;
 
#define    INF 987654321
#define MAX_N 6
int N, Graph[MAX_N][MAX_N];
// memoization을 적용하기 위한 array 변수 입니다. 
int Memo[MAX_N][1<<MAX_N];
 
int solve(int pos, int visited)
{
    // memoization을 적용하여 검색 횟수를 줄입니다.
    if (Memo[pos][visited] != -1return Memo[pos][visited];
 
    if (visited == (1 << N) - 1)
        return 0;
 
    int ret = INF;
    for (int next = 0; next < N; ++next)
    {
        // 1. 방문 여부를 확인하고
        // 2. 간선 유무를 확인합니다. (단, full mesh-모든 간선 존재- 의 경우에는 항상 참입니다) 
        if (!(visited & (1 << next)) && Graph[pos][next])
        {
            int tmp = Graph[pos][next] + solve(next, visited | (1 << next));
            if (tmp < ret)
                ret = tmp;
        }
    }
 
    Memo[pos][visited] = ret;
    return ret;
}
 
int main()
{
    int tcCnt;
    freopen("tsp_input.txt""r", stdin);
    cin >> tcCnt;
    for (int t = 1; t <= tcCnt; ++t)
    {
        cin >> N;
 
        for (int i = 0; i < N; ++i)
        {
            for (int j = 0; j < 1 << N; ++j)
            {
                Memo[i][j] = -1;
            }
        }
 
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                cin >> Graph[i][j];
 
        int ans = INF;
        for (int i = 0; i < N; ++i)
        {
            int tmp = solve(i, 1 << i);
            if (ans > tmp) ans = tmp;
        }
        cout << "#" << t << ' ' << ans << endl;
    }
    return 0;
}
cs

TSP 구현 - 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <stdio.h>
 
using namespace std;
 
#define    INF 987654321
#define MAX_N 6
int N, Graph[MAX_N][MAX_N];
int solve(int pos, int  visited);
 
int solve(int pos, int visited)
{
    if (visited == (1 << N) - 1)
        return 0;
 
    int ret = INF;
    for (int next = 0; next < N; ++next)
    {
        if (!(visited & (1 << next)) && Graph[pos][next])
        {
            int tmp = Graph[pos][next] + solve(next, visited | (1 << next));
            if (tmp < ret)
                ret = tmp;
        }
    }
 
    return ret;
}
 
int main()
{
    int tcCnt;
    freopen("tsp_input.txt""r", stdin);
    cin >> tcCnt;
    for (int t = 1; t <= tcCnt; ++t)
    {
        cin >> N;
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                cin >> Graph[i][j];
 
        int ans = INF;
        for (int i = 0; i < N; ++i)
        {
            int tmp = solve(i, 1 << i);
            if (ans > tmp) ans = tmp;
        }
        cout << "#" << t << ' ' << ans << endl;
    }
    return 0;
}
cs
cs
예제 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
4
0 1 2 3
1 0 4 5
2 4 0 6
3 5 6 0
6
0 10 11 13 24 12
10 0 16 11 8 19
11 16 0 12 12 14
13 11 12 0 10 18
24 8 12 10 0 13
12 19 14 18 13 0
 
cs

2016년 11월 8일 화요일

fork된 child process에서 exit 문제

규모가 큰 프로젝트를 진행하다 보니 때로 예상하지 못한 SW 문제들로 고전하는 경우가 있다. 얼마전 저자가 담당하고 있는 라이브러리를 사용하고 있는 모듈(블록) 담당자로 부터 라이브러리 수정에 의한 SW 프로세스가 비정상 종료되는 문제를 전달 받았다.

언제나 그렇듯이 최종 수정측의 연관 담당자가 발생한 문제의 초도 분석을 진행하게 되므로 본 문제도 담당 모듈 담당자가 아닌 우리쪽 담당자에 의해서 최초 분석이 진행되었다. 로그 분석에 의하면 비정상 종료하는 프로세스는 fork된 자식 프로세스로 확인되었고, uct 파일과 map 파일 분석에 의해 프로세스 종료 시점의 call stack 정보도 확인할 수 있었다.문제의 원인을 간단히 요약해 보면 fork된 프로세스에서 전역 변수로 선언된 자원(mutex)에 접근하는 시점에 비정상 종료되었고 종료된 프로세스 코드에서 exit() 함수가 호출된 이후였다.

일반적으로 fork는 스레드 개념이 없던 시절에 만들어진 기능이기 때문에 thread-safe 하지 않다고 알려져 있다. 따라서 멀티스레드 환경에서 사용하려면 여러가지 고려사항을 체크할 필요가 있다. fork 함수에 대한 다음 표준 문서를 참조해 보자.
* 참조링크 http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html

요약해 보면 아래와 같다.

1) 멀티스레드 프로세스를 fork할 경우 fork를 호출한 스레드만을 포함하는 프로세스가 생성된다. 다른 스레드들은 생성되지 않는다.
2) 부모 프로세스의 생성되지 않은 스레드들이 할당한(allocation) 힙(heap)도 그대로 복사된다.
3) 부모 프로세스의 다른 스레드가 설정한 잠금(mutexes)이 그대로 복사된다.
4) 멀티스레드 환경에서 fork 수행시 파생되는 에러를 회피하기 위해 종료되거나 exec() 함수를 호출하기 전, 자식 프로세스는 async-signal-safe 함수를 호출해야 할 수 있다.

발췌문에 소개된 Async-signal-safe functions에 주목한다. _Exit() 와 _exit() 함수가 포함되어 있다. 정리해 보면 멀티스레드 환경에서 복제된 프로세스 종료시 exit() 함수 대신 Async-signal-safe 함수인 _Exit() 혹은 _exit() 함수를 호출해야 파생되는 에러를 방지할 수 있다는 의미이다.
* 참조링크 http://man7.org/linux/man-pages/man7/signal.7.html

같은 맥락에서 아래 unix programming guide 문서를 참조한다. 마찬가지로 복제된 프로세스에서 exit()가 아닌 _exit()를 호출하는 것이 왜 안전한 것인지 설명하고 있다. 특히, C++ 코드에서 전역 객체에 대한 소멸자 호출 문제를 언급하고 있음에 주목한다.
* 참조링크 http://www.unixguide.net/unix/programming/1.1.3.shtml

아래 그림을 통해 exit()와 Async-signal-safe 함수인 _exit()의 차이를 확인해 보자.
exit()는 _exit()와 달리 등록된 exit handler들을 호출하고, stdio buffer를 정리하는 것을 알 수 있다.

저자가 기술하고 있는 문제는 전역 객체의 소멸자가 호출하는 동작에 연관된 문제이다. 라이브러리 코드는 전역 변수로 글로벌 뮤텍스를 제공하였는데, 자식 프로세스가 종료(exit() 호출)되는 시점에 해당 자원을 해제 하기 위한 소멸자가 호출되면서 비정상 종료가 발생하였다.
C++에서 exit() 호출시 일어나는 동작을 정리하면 아래와 같다.
1. 전역 객체의 소멸자 호출(destructors of objects with static storage duration) 및 std::atexit 로 넘겨진 exit handler 호출
2. 입출력 스트림 비우기,flush and close
3. std::tmpfile 로 생성된 임시 파일 삭제
* 참조링크 http://en.cppreference.com/w/cpp/utility/program/exit
저자의 경우 문제를 요약해 보면 fork하여 프로세스가 복제되었을 때 이미 부모 프로세스에 의해서 글로벌 뮤텍스가 생성되어 있는 상태였고, 복제된 자식 프로세스에는 해당 뮤텍스에 대한 권한이 없으므로 접근시 문제를 일으키는 것이다(프로세스간 메모리가 공유되지 않으므로 복제된 프로세스에서는 해당 뮤텍스에 대한 현재 상태를 알 수 없음).
이로인해 파생되는 문제 현상으로 데드락(deadlock) 또는 본 경우와 같은 비정상 종료의 형태로 나타나는 것으로 확인된다.
* 데드락 케이스 : https://cppwisdom.quora.com/Why-threads-and-fork-dont-mix
* 비정상 종료 발생 케이스 : http://boost-users.boost.narkive.com/20fu9rBa/boost-recursive-mutex-destructor-call-in-child-forked-process

다행히 복제된 프로세스에서 글로벌 뮤텍스에 대한 접근 동작이 전역 객체의 소멸자 호출에 한정되어 있어 다음 두 가지 해결책을 도출할 수 있었다.

- 전역 객체(싱글톤 객체였다)의 생성을 힙영역에 수행하고 소멸자에서 해당 메모리 해제 동작을 수행하지 않도록 하여 글로벌 뮤텍스 접근이 없도록 함(메모리는 프로세스 종료시 OS에 의해 회수됨).
또는
- exit() 대신 _exit(), Async-signal-safe 함수, 호출로 복제된 프로세스에서 전역 객체에 대한 소멸자 호출이 발생하지 않도록 함.

2016년 10월 2일 일요일

템플릿 함수에 배열의 크기를 전달하는 방법

우리 회사는 코드 리뷰 결과를 주기적으로 회신하여 코드의 품질을 제고하는 활동을 진행한다. 코드 리뷰어가 점검 결과를 전달하면 구현부서에서 이를 다시 검토하여 보완 여부를 결정하는 식이다.

한 번은 아래와 같은 템플릿 함수에 대하여 매개변수 T1, T2의 범위 체크(range check)를 요청받았다.
(코드는 샘플코드로 대체한다.)

template <typename T1, typename T2>  
 void update(unsigned short idx, T1 arr1[], T2, arr2[])     
 {                               
   if(idx == FA_1)  
   {                             
     arr1[3] = arr2[3];  // arr1과 arr2 배열의 범위를 알 수 없으므로 문제가 있는 코드입니다.
   }                             
   else if(idx == FA_2)                     
   {  
     arr1[2] = arr2[2];                   
   }                             
   else   
   {                             
     arr1[0] = arr2[0];                   
   }  
 }    
아마도 이 코드를 작성한 개발자는 매개변수의 타입 및 배열의 크기를 일반화 하려는 목적으로 함수 템플릿으로 기능을 구현한 것으로 추정할 수 있다.C/C++ 개발자들은 함수의 매개변수로 배열을 전달할 때 포인터 변수를 사용하기도 하는데, 이는 전달 받는 함수에서는 배열의 크기를 잃게 되므로 구현시 주의해야 한다.
(이러한 현상을 타입 디케이-Type Decay-라고 한다.)

이왕 배열과 포인터 얘기가 나옴 김에 다소 원론적인 질문을 던져보기로 한다.
C++ 에서 배열과 포인터는 어떻게 다른가 ?

첫째, 포인터는 변수이고 배열 상수이다. 쉽게 말해 아래와 같은 코드는 허용하지 않는다.

   int* ptr = NULL;   // ptr은 Pointer type
   int arr1[10] = { 0, };  // arr1, arr2는 Array type 
   int arr2[10] = { 0, };   
   
   ptr  = arr1;  // OK, Pointer는 변수
   arr1 = arr2;  // NOK, arr1, arr2는 상수이다.
둘째, 포인터가 가리키는 메모리의 크기는 동적으로 결정할 수 있지만, 배열이 가리키는 메모리의 크기는 선언할 때 정적으로 결정된다.

셋째, 배열을 함수의 매개변수로 전달할 때 배열 타입이 아닌 포인을 사용할 경우 배열의 크기는 전달할 수 없다. 포인터 타입으로 매개변수를 전달할 경우 배열의 크기 정보를 잃게 된다(아까 말했던 타입 디케이).
아래의 코드를 보자

 void funcP(int* arr)  
 {  
   printf("sizeof arr(%zu)\n", sizeof(arr));  // 결과는 '4'아니면 '8'
 }

 int main(void)  
 {  
   int arr1[10] = { 0, };  
   
   printf("sizeof arr(%zu)\n", sizeof(arr1)); // 결과는 '10'
   funcP(arr1);  
   
   return 0;  
 }  
C/C++에서 배열의 정의는 다음과 같다.
http://www.cplusplus.com/doc/tutorial/arrays/


즉, 배열 변수를 선언하는 순간 해당 변수는 '타입'과 '크기'정보를 포함하게 된다.

이제 마지막으로 다시 처음의 코드로 돌아와서 코드 리뷰어의 요청사항을 어떻게 반영했는지 살펴보자.

 template <typename T1, typename T2, size_t N, size_t M>  
 void update(unsigned short idx, T1 (&arr1)[N], T2 (&arr2)[M])  
 // 배열 매개변수를 레퍼런스로 전달 받았다. 
 // 원래의 코드에서는 포인터로 전달 받았기 때문에 크기를 알 수가 없었음.
 // 이제 타입 디케이 현상 없음.
 {  
   if(idx == FA_1)  
   {    
     if(3 >= N || 3 >= M) return;  // 변수 범위를 체크합니다.
   
     arr1[3] = arr2[3];  
   }    
   else if(idx == FA_2)  
   {    
     if(2 >= N || 2 >= M) return;  // 변수 범위를 체크합니다.
   
     arr1[2] = arr2[2];  
   }    
   else   
   {    
     if(1 >= N || 1 >= M) return;  // 변수 범위를 체크합니다.
   
     arr1[0] = arr2[0];  
   }  
 }  
위 코드는 매개변수로 전달하는 배열의 크기가 다양할 경우 '코드 부풀림(Code Bloating)' 현상이 발생하지 않도록 주의해야 한다. 실제 코드에서는 한정된 크기의 배열에 한하여 해당 함수를 호출했기 때문에 문제없이 사용할 수 있었다.

2016년 9월 27일 화요일

Static Polymorphism(정적 다형성)

정적 다형성, 컴파일 타임 다형성, 템플릿. 일맥상통하는 의미를 가지고 있는 용어들이다. 반대되는 개념은 동적 다형성, 런타임 다형성, 동적 바인딩, virtual function 등이 되겠다.
다음의 코드를 통해 정적 다형성의 의미를 확인해 보자.


 struct TreeNode { TreeNode *left, *right; }; 

 class GenericParser {  
 public :  
   void parsePreorder(TreeNode* node)  
   {  
     if(node)  
     {  
       processNode(node);  
       parsePreorder(node->left);  
       parsePreorder(node->right);  
     }    
   }    
 private:  
   virtual void processNode(TreeNode* node) { }  // virtual function 입니다.
 };   
     
 class EmployeeChartParser : public GenericParser {  
 private :  
   void processNode(TreeNode* node)  
   {  
     cout << "Customized processNode() for EmployeeChart.\n";  
   }  
 };   
위 코드는 전형적인 가상 함수를 사용한 동적 다형성의 예이다.
익히 알려진 바와 같이 가상 함수 사용을 위해서는 vTable이 추가됨과 동시에 함수의 동적 바인딩에 따른 비용이 추가된다.
이 코드에 정적 다형성 기술을 사용하여 동적 다형성에 추가되는 비용을 제거하도록 해보자.
 
 struct TreeNode { TreeNode *left, *right; };  
  
 template<typename T>  
 class GenericParser {  // class template 입니다. CRTP 기술을 사용합니다.
 public :   
   void parsePreorder(TreeNode* node)   
   {    
     if(node)  
     {    
       processNode(node);  
       parsePreorder(node->left);  
       parsePreorder(node->right);  
     }  
   }  
 private :  
   void processNode(TreeNode* node)  // 가상 함수가 아닙니다.
   {  
     static_cast<T*>(this)->processNode(node);  // 상속 관계이기 때문에 casting을 통해 안전하게 
                                                // 하위 클래스의 멤버 함수를 호출할 수 있습니다.
   }  
 };   
     
 class EmployeeChartParser : public GenericParser<EmployeeChartParser> {  
 public :  
   void processNode(TreeNode* node)  
   {  
     cout << "Customized processNode() for EmployeeChart.\n";  
   }  
 };   
CRTP(Curiously Recurring Template Pattern)는 템플릿 매개변수로 하위 클래스 타입을 받아 만들어진 부모 클래스로부터 다시 하위 클래스가 파생된 모습을 일컫는 용어다. 스캇 마이어스는 그의 저서 "Effective C++"에서 CRTP 대신에 "나만의 것(Do It For Me)" 이라는 이름을 제안했다고 하는데, CRTP의 성격을 잘 표현한 적절한 이름인 것 같다.

위 코드는 template과 CRTP 기술을 사용하여 vTable 생성과 함수의 동적 바인딩 비용을 제거하였다.
헌데 옥의티가 보인다. EmployeeChartParser 클래스의 processNode 함수의 접근제한자가 private에서 public으로 바뀐 것이 눈에 뜨인다. 즉, 객체의 캡슐화가 약해졌다. 이는 더 이상 processNode 함수가 가상 함수가 아니기 때문에 하위 클래스 함수의 접근제한자 변경이 동반된 것.

정적 다형성의 유익과 함께 클래스 캡슐화를 유지하기 위해 부모 클래스를 friend 선언하도록 하면 문제가 해결될 것 같다. 이제 마지막 코드다.

 struct TreeNode { TreeNode *left, *right; };  
   
 template<typename T>  
 class GenericParser {  
 public :   
   void parsePreorder(TreeNode* node)   
   {    
     if(node)  
     {    
       processNode(node);  
       parsePreorder(node->left);  
       parsePreorder(node->right);  
     }  
   }  
 private :  
   void processNode(TreeNode* node)  
   {  
     static_cast<T*>(this)->processNode(node);  
   }  
 };   
     
 class EmployeeChartParser : public GenericParser<EmployeeChartParser> {  
 private :  
   void processNode(TreeNode* node)   // 이제 private 함수로 선언할 수 있습니다.
   {  
     cout << "Customized processNode() for EmployeeChart.\n";  
   }  
   
   friend class GenericParser<EmployeeChartParser>;  // 부모 클래스를 friend 선언합니다.
 };  

2016년 9월 24일 토요일

SW 품질 개선, 그 시작점이란...

개발자로서 SW 문제를 대하는 바람직한 자세는 무엇인가?
아니, 구체적으로... SW 문제를 미연에 방지하기 위한, 그 궁극적 목표를 달성할 수 있는 해결책이란 과연 존재하는가 ?

10여년 대규모 제조업 회사의 SW 개발팀에 근무하면서 내 마음을 가장 무겁고 막연한 안개와 같이 뒤흔드는 주제는 '품질'이었다(관리자들의 가장 중요한 관심사이기도 하고 그들의 성에 차지 않을 때 실무자들을 진흙탕 속으로 몰아 넣기 가장 좋은 '구실'이 되기도 하는).

SW 품질 문제는 그 핵심을 찾기도 어려울 뿐더러 문제의 원인을 찾아냈다고 해도 올바른 개선 방향을 정하기 까지...(그것을 팀원들이 실천하고 업무에 정착시키기 까지)는 예측하기 힘든 정도의 비용, 시간과 돈과 노력, 이 투자되어야 한다. 

나는 SW 품질 문제를 분석하고 해석하는 큰 두 가지 관점이 있다고 생각한다. 

첫번째는 미시적인 관점으로 개발자 한 사람 한 사람의 역량과 자질의 문제로 접근하는 방식이다. 
두번째로 거시적인 관점인데 결국 SW 품질 문제는 경영자를 비롯한 주요 의사 결정권을 가지고 있는 사람들의 인식과 개발 프로세스의 문제로 바라보는 관점이다. 

10년간 개발부서에서 근무한 나름대로의 소회는 SW 품질은 미시적 요소보다는 거시적 요소에 더 많은 영향을 받는다는 것이다(최소한 벤처 회사와 같이 소규모가 아닌 대규모 개발조직에서는 대부분 그렇다). 다분히 개인적인 근거일 수 있지만, 개발자 한 사람이 개발조직에 끼치는 영향보다 조직의 문화와 분위기에 따라 개발자의 업무 방식이 영향을 받는 것을 수 없이 보아왔기 때문이다. 

예를 들어 SW 테스트 기간중 SW 문제점이 발견되었을 때 각 부서간 담당자의 반응은 보통 아래와 같은 형태였다. (이 예는 "HARD CODE 1장-프로젝트 부실관리"에서 발췌) 

  • 버그를 발견한 테스터에게 버그는 자신의 시간과 노력을 평가하는 잣대다. "고칠 가치가 없다니 도대체 무슨 말입니까?"
  • 기능을 구현한 프로그램 관리자에게 버그는 자신의 설계에 도전하는 반란이다. "버그를 고치려면 기능 자체를 뒤엎어야 합니다!"
  • 서비스 운영팀에게 버그는 지속적인 골칫거리를 뜻한다. "별 거 아니라고요? 새벽 세 시에 나와서 서버를 재부팅할 사람이 누구인 줄 아십니까 ?"
  • 개발자는 버그를 자기 나름대로 주관적으로 평가한다. "뭐, 그리 심각한 버그는 아니군요."
나 자신도 개발자로 오랫동안 일해 왔기 때문에 위의 예문이 때로는 내 모습의 일부였음을 고백하지 않을 수 없다. 이렇듯 복잡한 제품을 만들고 조직의 규모가 큰 회사일 수록 특히 실무자 '한 사람'의 노력으로 최선의 해결책을 찾고 사람들의 동의를 얻어내기란 정말 어려운 일이다(만약 그런 사람이 주위에 있다면 당장이라도 그에 맞는 권한과 직책을 부여해야한다). 경험상 프로젝트 일정이 점차 막바지에 이르고 그 진행 결과가 긍정적인 신호보다는 부정적인 신호들(빈번한 요구사항 및 설계 변경, 실무자들의 탈진과 피로감, 의사 결정권자들에 의한 잦은 의사 번복, SW 버그..)에 둘러싸였을 때 위와 같은 반응은 점차 자연스러운 것이 되곤 했다.

책의 저자는 "선별(triage) 회의"(쉽게 말하면 문제점 분석 회의 정도가 될 것 같다) 사례에서 지켜야할 다섯 가지 규칙을 소개한다. 책을 읽으면서 이것은 단순한 규칙이라기 보다는 SW 제품을 생산하는 조직, 특히 '개발 조직'에서 풀어야 할 "수 많은 문제들"을 어떻게 다루어야 하는가? 라는 질문에 "선별 회의"라는 국한된 주제를 통해 가능한 해결책을 암시해 주는 것 같다. (규칙은 결국 문제가 많고 혼란스러운 상황 속에서 그 빛을 발하는 것이 아니던가?)
  1. 문을 닫아라. - 협상은 비공개로 진행해야 참가자들이 솔직하게 흥정하고 타협할 수 있다는 의미
  2. 모든 결정은 팀 의사다. - 모두가 합의한 사안에 대해 조건 없이 결정을 지지해야 한다는 의미. 의사 결정에 참여한 구성원은 모든 결정을 자신이 내린 결정인 양 방어할 수 있어야 한다. 
  3. 참여 인원을 제한한다. - 머릿수가 많으면 회의가 늘어지고 사적인 감정이 얽히게 되어 결론이 쉽게 도출되지 않는다. 단호하고도 다양한 시각을 얻기 위해서는 부서당 한 명씩이 가장 적당함.
  4. 한 사람이 최종 결정을 내린다. - 팀이 사안에 합의하지 못할 경우 누군가는 최종 결정을 내려야 한다(이상적으로는 이런 상황이 생기지 않는 것이 좋다). 책에서는 PM(프로젝트 매니저)이 가장 적합하다고 설명함.
  5. 모든 결정은 '퀘이커(Quaker)'식 합의를 따른다. - 가장 중요한 규칙으로 합의가 어렵고 사적인 감정이 개입된 모임에서 만장일치를 기대하기는 어려우므로, '퀘이커'식 합의(반대자가 없다는 뜻)를 통해 누구도 반재하지 않는 결론을 찾아야 한다는 의미. 만장일치보다 훨씬 달성하기 쉬우며 훨씬 합리적인 결론이 나올때가 많다고 한다.
회사와 같이 비즈니스를 기반으로 한 조직에서 조직의 시작과 더불어 문화라는 것은 존재하지도 할 수도 없다. 그것은 결국 사람과 시스템, 시간과 프로세스의 '산물'일 수 밖에 없다. 조직에 영향력을 행사하는 사람들의 철학과 더불어 그들이 만들어 놓은 프로세스에 의해 조직이 운영되고 그 산출물로 문화가 생성된다. 그리고 그 문화는 그 조직에 속한 많은 사람들의 행동과 생각에 영향을 주는 요소가 된다. 
  
결론이다. 우리 모두는 건강한 개발 문화를 꿈꾸고 소망한다. 나의 꿈이기도 하다. 건강한 개발 문화란 무엇인가? 어쩌면 위의 "선별 회의" 사례에서 볼 수 있듯이, 우리가 '이상'이라 믿고 생각할 수 있는 합리적인 회의 문화를 정의하고 그것을 실천하는 배경에는 그 회사(조직)이 얼마나 '고객''비즈니스'에 집중하고 있는가에 달려있는 것은 아닐까? 최고의 제품, 품질이라는 것은 결국 '고객'과 '비즈니스'를 바탕에 두지 않고서는 도무지 그 실마리를 찾을 수 없는 미로와 같은 주제이기 때문이다. 

이 즈음에서 질문을 던져보자. 우리가 이 일을 하는 이유는 무엇인가? 
고객을 위한 것인가? 아니면 정체도 알 수 없는 그 누군가의 목표와 욕망을 채우기 위해서인가?

2016년 9월 21일 수요일

개발자가 갖추어야 할 9가지 기술

우연히 유투브에서 보게된 박종천님의 강연을 정리해 보았다.
개발자로서 여러모로 공감되고 동의하는 내용이 많다.

개발자가 갖추어야 할 9가지 기술 요약..

Hard Skills
1. Basic Knowledge(C++, OS)
2. Understanding for product
3. Development Cycle

1-5 years of development
  - Associate Software Engineer
  - Software Engineer

1. Basic Knowledge(C++, OS)
* Learn from school, workplace by yourself
- Mathmatics, Physics
- Programming language(C++, C#, Python)
- Algorithm, Data Structure, Design Patterns
- Graphics, Database, Networking, AI
- Game Engines(Unity)
- OS(Windows, Mac, iOS, Android)
- Tools(Source control, Visual Studio)

* 참고서적
C++ Coding Standards(코딩의 정석)

2. Understanding for product
* Product Analysis
* Power of Blizzard
- Everyone loves game
* Three types of restaurants
- Going for profit
- Try to make customer happy
- Love cooking

* 참고서적
Why Software SUCKS...(소프트웨어, 누가 이렇게 개떡같이 만든거야)

3. Development Cycle
* Requirements & Analysus
* Design & Modeling
* Implementation
* Test/Release
* Feedback/Update

* 참고서적
How Google Tests Software(구글은 소프트웨어를 어떻게 테스트하는가 : 구글의 테스팅 문화와 기법에 관한 인사이드 스토리)

Soft Skills
4. Project Management
5. Team Management
6. Process(Agile, Zero-Bug)

6-10 years of development
- Software Engineer
- Senior Software Engineer

* 참고서적
Debugging the Development Process

4. Project Management
* Why, What, How
* Triple Constraints
- Cost(Resource)
- Time(Schedule)
- Scope(Quality)

5. Team Management
* Forming, Stroming, Norming, Performing
- Trust & Knowledge
- Style : What, Why, How, What if ?
* Roles around the Team
- Product owner
- Producer, Artists, Designers, Engineers
(Artists   : Customer Attractor - 제품을 이쁘게 만들어 주는 사람,
Producer  : Helper - 의사소통 도와주는 사람,
Designers : Success Maker - 제품을 재미있게 기획하는 사람,
Engineers : Failure Preventer - 최선을 다해서 중간만큼 한다)
- Engineering Team Lead
(Project lead, Technical lead, People Manager)
* Clear role definition. Proper delegation

* 참고도서 : Teach What You Know

6. Process
* Better way to do it, Optimal way
- Detect failure, Prevent failure
* Project Management Process
- Waterfall, Agile/Scrum
* Development Process
- Development Cycles, Code Review
- The Joel Test: 12 Steps
( 1. Do you use source control  ?
 2. Can you make a build in one step ?
 3. Do you make daily builds ?
 4. Do you have a bug database ?
 5. Do you fix bugs before writing new code ?
 6. Do you have an up-to-date schedule ?
 7. Do you have a spec ?
 8. Do programmers have quiet working conditions ?
 9. Do you use the best tools money can buy ?
10. Do you have testers ?
11. Do new candidates write code during their interview ?
12. Do you do hallway usability testing ? )
* Your Own Evolving Process
* Project Management Maturity Model
1. Initial = Common Language
2. Repeatable = Common Process
3. Defined = Sigular Methdology
4. Managed = Benchmarking
5. Optimized = Continuous Improvement

* Process & People

* 참고도서 : HARD CODE

Business Skills
7. HR System
8. Business Management
9. Vision/Goals/Culture

* 10+ years of development
- Lead Software Engineer
- Technical Director, VP of Technology, CTO, CEO

7. HR System
* Hiring
* Performance Reviews
- Example : Software Engineer
. Productivity
. Professionalism(Reliability)
. Teamwork(Communication)
. Knowledge
. Functionality(No Defect)
. Implementation(Good Code)
. Design & Architecture

* Titles(Engineering)
* Rewards
* Education
- Example : Leaerning & Development
. Books, Conferences
. Internal seminars, EDGs
. College Programs, Tuition
. Side projects, Mentoring
. Individual Development Plan
. Studio Summit, Lunch Groups
. Internal mobility(Hiring cross-teams)
* Benefits

** 좋은 팀원을 만드는 방법
Attract(매력을 가지고 사람들을 오게 만들고) - Develop(훈련시키고 발전시킴) - Engage(융합시키는 과정)
** 개발자는 자신감을 키우기 위해 실력을 키워야 합니다.

8. Business Management
* Leading People, Manage Business
* Making money
  a certain way
  doing things you are comfortable doing
  and contrubuting to society
* ROI(Return over Investment)
* Capability, Strategy, Tactics, Finance, Economics, Marketing, Sales, CS, Operations, Change
* Balanced Scorecard
- Financial Perspective
- Customer Perspective
- Internal Process Perspective
- Learning and Growth Perspective
* Customer
- The company's customers
- The leadership
- The employees

9. Vision, Goals, and Culture
* Vision and Goals for the company
* Vision and Goals for the team
* Vision and Goals for you

* Example : Blizzard Mission Statement
"Dedicated to creating the most epic entertainment experiences...ever."

* Example : Blizzard Core Values
- Gameplay first
- Commit to quality
- Play nice; Play fair
- Embrace your inner geek
- Every voice matters
- Think globally
- Lead responsibly
- Learn & grow

** 정리
Hard Skills : Learn by Studying
* Basic Knowledge
* Understanding for product
* Development Cycle
Soft Skills : Learn by Experience
* Project Management
* Team Management
* Process
Business Skill : Learn from People
* HR system
* Business Management
* Vision/Goals/Culture

Sample Q&A
* Hard Skills
- What to study : Everything
- Growth : Performance review, Endless curiosity
* Soft Skills
- Successfule Developer : Talent, Practice, Chance
- How is it working in Blizzard : Experts & Collaborations
- Good Developer : Three stages(A, A+, B)
. 처음에는 팀장이 시키는대로 output을 만들어야 한다. -> A
. 신뢰관계가 형성된 이후 -> A+, B
* Business Skills
- How to learn business : Simulation & Communication
- Attitude to become CEO : Responsible for everything
* Base Skills
- Smart, Diligent, Good Will
- Communication Skill : Listen more, Win Less
        . 10번중에 8번 지고 2번만 이기면 됩니다.

** I don't divide the world into the weak and the strong, or the successes and the failures,
   those who make it or those who don't. I divide the world into learners and non-learners.
  - Benjamin R.Barber