· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Dev Workshop


본 문서의 배포를 금합니다. 배포를 금하는 이유는 아직 완성되지 않았기 때문이며, 배포수준으로 완성이 되면, 문서 라이센스를 재정리하여 공지하겠습니다. 현재는 본 사이트에서 열람만 가능합니다.




1. 개발 팀 사전 준비

1.1. 장비


  • 빌드 서버
  • CVS 저장소 서버 그리고 백업 저장장치
  • 각자의 개발 장비

빌드 서버란 개발도중 표준 바이너리를 만들기위한 표준이 되는 서버입니다. 이 서버는 지원 OS의 하위 버전으로 하여 생산되는 제품이 다양한 OS를 지원하도록합니다. CVS 저장소 서버는 버전 관리 서버를 말한다. cvs 서비스 방법으로 pserver 혹은 ssh 를 사용하도록 구성하면 되며, CVS 설치를 위한 문서를 참고하여 설치하도록합니다. 백업 저장장치는 다른 장비에 rsync 혹은 일별 tar 백업을 할 수 있는 장비를 말하며, 다른 장비가 여건이 안되면 동일 장비내에 다른 하드 디스크에 미러링을 할 수 있도록 준비합니다. 그리고 각자의 개발 장비에서 작업하도록 하지만, 정 여건이 안되면 위 세 장비는 하나가 될 수도 있을 것입니다.

1.2. 소프트 웨어


  • cvs (버전관리 프로그램, 클라이언트 서버 둘 다 지원)
  • viewcvs (web 기반 cvs repository viewer, 대용은 cvsweb)
  • indent (소스 beutifier)
  • cvsreport (cvs log reporter)
  • autotools (autoconf, automake, libtool)
  • doxygen (문서화 툴)

처음 프로젝트를 시작하는 그룹이라면 이들의 사용법을 각각 익혀두길 바랍니다.

1.2.1. indent

GNU Tool로서 [http]http://www.gnu.org/software/indent/indent.html 에서 구합니다. 가장 간단한 indent 는 소스를 .indent.pro 라는 옵션 파일 설정에 따라 들여쓰기를 바꿔주는 일을 합니다. 처음에는 여간 옵션들을 찾기가 힘들며, 팀이 원하는 모든 기능을 지원하지 않을 수 도 있습니다. 결과물이 또 어색할 수도 있습니다. 하지만 약간의 손질을 해서라도 조금 익숙해지면, 없는 것보다 훨씬 소스의 통일감을 줍니다. 이 프로그램은 CVSROOT의 commitinfo에서 연동하여 cvs에 소스를 저장할 때 강제로 들여쓰기를 통일 할 수 있게 합니다.

1.2.2. doxygen

[http]http://www.stack.nl/~dimitri/doxygen/ 에서 구합니다. 개발자중에 주석 관리 담당자를 한 명 선정하도록하여 doxygen을 익히도록하고, 이 사람은 나중에 최종 문서 산출물을 관리하는 역할을 하며 다른 사람들에게 교육을 하여 주석을 잘 달 수 있도록 고문관역할을 맡깁니다. 다른 개발자들은 doxygen의 주석 규칙을 익히도록 해야합니다. doxygen이 본인이 이용해 본 공개 툴 중에서는 가장 나은 출력을 보여줬습니다. 소스 주석은 팀에서 정하는 것도 좋지만, doxygen이 알아 들을 수 있도록 다는 것이 좋습니다. doxygen의 규약에 맞추도록하십시오. 그러면, 나중에 편리한 API 문서를 얻을 수 있을 것입니다.

1.2.3. cvs

[http]http://www.cvshome.org/ 에서 구합니다. cvs는 처음 개념이 어려워도 문서가 많이 있으므로 일단 따라 설치해봅니다. cvs 접속이 firewall 안에서 switching hub에 물려 작업하는 그룹이라면 굳이 ext 모듈로 ssh 를 사용하지 않아도 무방합니다. pserver는 간결하며 나름대로 빠른 장점이 있습니다.

1.2.4. viewcvs

[http]http://viewcvs.sourceforge.net/ 에서 구합니다. 필자는 cvsweb 보다 python기반의 viewcvs를 선호하는데, 그것은 viewcvs에 있는 enscript와 cvsgraph 라는 두가지 확장을 통해서 syntax coloring, revision tree 를 보여주기 때문입니다. 이런 cvs viewer를 설치하여 공동작업하는 경우는 많은 확장성을 가져다줍니다.

1.2.5. cvsreport

[http]http://cvs.kldp.net/viewcvs/hserver/CVSROOT/cvsreport 에서 구합니다. cvs log를 메일로 알려주는 기능을 하는데, 바뀌는 소스의 viewcvs로 연동하여 diff URL을 보내줍니다. 이것은 관리자로 하여금 대충이나마 변경내용을 알 수 있도록 도와줍니다.

1.2.6. autotools

automake 가 가져다 주는 make framework을 이해해야합니다. automake 는

  • make all
  • make check
  • make dist
  • make install / uninstall
등을 이용하여 recursive build, recursive test, 배포 tar ball 만들기, install, uninstall을 제공합니다. 이런 기능을 익숙하게 사용하는데 약 두 달정도의 시간이 소요되지만, 인내를 갖고 익숙해지다보면, 소스가 점점 복잡해질 때 틀을 가지고 성장하는 모습을 볼 수 있을 것입니다.

여기까지 준비하는데 들어가는 시간 약 2~3일 입니다.

2. 개발 환경

2.1. 개발 팀 사전 공유 사항

2.1.1. 설치/삭제

패키지를 만들 생각이면, rpm, 선 package, HPUX depot 등에서 제공하는 설치 삭제를 추천합니다. 그렇지만 간단하게 할 생각이면, make install 이 제공하는 것도 하나의 방법이며, 이는 소스를 수반하므로 간단한 installer/uninstaller를 만들어 두는 것이 좋습니다. 설치/삭제 프로그램이 가장 먼저 만들 모듈입니다. 이것부터 하지 않으면 나중에 두 배로 힘들 것입니다. 그 이유는 개발과 테스트를 분리하는 과정에서 테스터에게 전달될 모습이 바이너리 단위로 넘어가면 절대 안되는 것이고, 인스톨본으로 넘기고 인스톨본에대한 테스트가 이루어져야하기 때문입니다.

설치 삭제를 스크립트로 만든다면, 다음과 같은 것을 준비하세요.

  • 파일명과 속성, 소유권한, MD5SUM 값이 들어있는 리스트 파일
  • 설치는 복사후 소유권을 바꾸도록합니다.
  • 삭제시에는 MD5 값이 바뀌지 않은 것들만 삭제하거나 아니면 강제 삭제하는 방향으로 합니다.

2.1.2. 로그 포맷

다음으로 중요한 것이 로그 포맷을 결정하는 것입니다. 이는 syslog를 쓸 수도 있고, 로그파일을 둘 수도 있습니다. 또한 로그 레벨을 둘 수도 있고, 로그 별 아이디를 둘 수도 있습니다. 어쨌든 이 로그를 남기는 공통 API를 먼저 만드십시오. 다음과 같은 것으로 요약할 수 있습니다.

  • 로그 On/Off
  • 로그 파일명 규칙 - 일별, 크기별
  • 로그 파일내 포맷
  • 로그 레벨별 출력 조절

2.1.3. 구성화일 포맷

일반적으로 구성화일은 전통적으로 Unix 형식이라면 Text 파일입니다. 윈도우에는 ini 파일형태가 쓰이다가 레지스트리로 바뀌었지요. 이 포맷을 공통으로 두어 만든다면 여러 모듈에서 사용할 것이며 개발팀의 자산이 될 수 있습니다.

  • 키는 중복 가능한가?
  • 값은 여러행으로 만들 수 있나?
  • 키와 값의 구분자는 공백, :, = 중 어떤 것으로 할 것인가?
  • 따옴표를 지원할 것인가?

2.1.4. 명령행 파서

가장 흔히 getopt를 사용합니다. 이를 기반으로 모든 모듈이 공통적으로 처리할 수 있는 옵션들에 대해 기본적으로 파싱하여 처리할 수 있어야합니다.

2.1.5. PID file

프로세스 아이디 파일을 공통으로 사용하여 실행하는 데몬이 자동으로 기록할 수 있도록지원합니다. 이는 현재 실행 중인 데몬의 PID를 얻는데 사용되는 비용을 줄이고자 사용하는 방법입니다. 프로세스 실행/종료/재실행에 사용될 수 있습니다.

여기까지가 모든 프로젝트가 공통으로 사용할 수 있는 기본적인 환경입니다. 이미 정리되어 있고 팀의 문화로 만들어 있다면, 다음 프로젝트를 시작하는데 도움이 많이 될 것입니다.

2.2. 개발 절차 셋업


이 문서에서 다루지 않는 것은 산출물 리스트입니다. 예를 들면, 사양서, 설계서, DFD, ERD, API, UI Guide 등입니다. 그런 문서들은 적절하게 관리되어야하며 워낙 강조하므로 따로 더 강조하지 않습니다.

2.2.1. Coding Rule

코딩 규칙은 두 종류로 나뉩니다. 하나는 자동으로 스타일을 바꿀 수 있는 것 하나는 힘들여 코딩가이드를 따라야만하는 것입니다. 코딩 규칙은 너무나 개인 적인 성향이 강하게 나타나므로 팀이 개발 할 경우에 걸림돌이 될 수 있습니다. 대개 코딩 가이드는

  • 이름(파일, 함수, 클래스)을 어떻게 정할 것인가?
  • 주석을 어떻게 달 것인가?
  • 괄호의 위치는 어떻게 할 것인가?
  • 들여쓰기는 어떻게 할 것인가?
(TODO: 아무나 더 추가가능)

등의 커다란 주제로 나뉩니다. 그중에서 자동화 하거나 규칙을 어겼는지를 테스트하여 경고를 줄 수 없다면, 코드 리뷰를 하기전까지는 알 수가 없습니다. 과거에는 코딩 규칙만 정해놓고 따르지 않는 경우가 허다했습니다. 습관과 관계되었기 때문입니다. 하지만 요즘에는 code beautifier가 많기 때문에 마음대로 작성하고 코딩 규칙에 맞는 option을 선택하여 code beautifier를 돌려주면 됩니다.

구글로 code beautifier 혹은 source beautifier 로 검색하면 상당히 많은 검색을 얻을 수 있을 것입니다. 제가 즐겨쓰는 것은 GNU의 indent입니다. indent 자체에는 GNU style과 BSD style로 맞춰주도록 하는 옵션이 있어서, 많은 GNU tool이나 BSD tool들이 이런 유틸리티의 산출물 형태로 등록되는 것을 볼 수 있습니다.

코딩 규칙에는 파일 이름 정하는 것이나 함수 이름, 변수 이름등을 결정하는 것이 따라 갑니다. 이들 또한 ctags -x 를 사용하면 함수와 변수별로 정의된 위치를 파악할 수 있습니다. 함수,변수 이름 규칙은 반드시 정규표현식으로 결정할 수 있어야하고, ctags -x 의 출력물을 정규표현식으로 하나하나 조사하여 규칙 위반 여부를 리포팅하는 것도 코딩 규칙이 문서로만 존재하는 것을 막아 주게 됩니다.

2.2.2. 문서화 툴

문서화 툴을 선택하는 것은 주석을 어떻게 처리할 것이냐와 밀접한 연관성이 있습니다. 자동으로 문서화를 한다는 것은, 코드로부터 주석을 추출하고 이를 바탕으로 call-graph, class 상속관계, api 설명서 등을 만들 수 있기 때문입니다. 주석을 일일이 다는 코딩 규칙을 만드는 것보다 문서화 툴을 정하고 이 툴이 요구하는 대로 작성하도록하는 것이 이중의 수고를 막습니다. 주석따로 문서따로보다는 어느정도 공유하도록 만드는 것이 좋습니다.

여러 문서툴이 존재합니다. automatic documentation tools 등으로 검색해보시기 바랍니다. 문서화 툴을 다른 용도로 이용하는 방법은 문서화 툴이 필요한 주석이 없는 것을 warning으로 처리해줄 때, 개발자들에게 필요한 주석을 달도록 지시할 수 있는 근거가 될 수 있습니다. 즉, 개발 팀의 대화를 툴을 사용해서 하는 것으로 해야지 코드를 보면 볼 수록 찾아내게 되는 방법으로 하면 생산성이 저하된다는 것입니다.

2.2.3. 코드 Skeleton

코드의 Template을 준비하는 것은 간단한 일이지만, 코드가 많아지면 많아질 수록 일종의 체계를 주기 때문에 좋습니다. 특히나 vim, emacs 등에서 제공하는 skeleton 을 이용한 새 파일 작성방법은 코드를 하나 만들 때 아주 유용합니다. 저는 vim을 주로 사용하므로 vim plugin을 소개하자면,


파일을 skel.c, skel.h 등으로 준비해두면 새로운 파일을 만들 때 자동으로 읽어오게 됩니다. 이런 skeleton이 제공하는 것은 크게 라이센스 조항, 포함 헤더, 전역 변수 선언부, 타입, 매크로 선언부, 전역 변수 선언부, export 함수 선언부, static 함수 전방 선언부 등이 있습니다. 이런 선언부들을 여러줄의 주석으로 강조하는 것도 한 방법입니다.

그리고, 중요한 것은 cvs 를 사용할 경우 파일 아이디를 넣어 주는 것입니다. 이것은 cvs 의 keyword 치환을 통해 일어나며, .h 파일과 .c 파일이 약간 다릅니다.

header 에서는

/* $Id: DevWorkshop,v 1.3 2012/04/04 01:57:34 kss Exp kss $ */


.c 에서는

static char id[] = "$Id: DevWorkshop,v 1.3 2012/04/04 01:57:34 kss Exp kss $";


GCC 만 쓸 것이라면,

static char id[] __attribute__((unused)) = "$Id: DevWorkshop,v 1.3 2012/04/04 01:57:34 kss Exp kss $";


등으로 표시 해둡니다. 이렇게 표시함으로서 cvs server는 commit 되어오는 파일에서 $Id: $ 부분을 날짜, ID, Revision 등을 섞어서 적당히 표현해줍니다. 자세한 것은 cvs keyword 로 검색해 보시기 바랍니다. 그리고, .c 파일에서 static 문자열로 주는 이유는 나중에 생긴 바이너리를 ident 라는 명령으로 확인해보면 어떤 소스가들어 갔는지를 확인해 볼 수 있기 때문에 디버깅 힌트를 줄 수 있습니다. ident는 indent와 혼동하지 마시기 바랍니다.

skeleton 마지막에

/*
   $Log: DevWorkshop,v $
   Revision 1.3  2012/04/04 01:57:34  kss
   113.160.52.170;;xvip87;;

   Revision 1.2  2004/12/04 12:17:45  kss
   61.74.173.192;;infiniterun;;

   Revision 1.1  2004/12/04 12:17:04  kss
   61.74.173.192;;infiniterun;;


 */


를 둠으로써, cvs commit 메시지를 소스에 계속 추가 할 수가 있습니다. 따라서 cvs를 잘 활용하면, 소스 변경에 대한 것을 따로 관리하지 않아도 cvs가 자동으로 치환해주게 됩니다. Log 는 주의해서 사용해야하는 것은 merge와 관련한 문제 때문에 소스 마지막에 둘 것을 권장하고 있습니다.

2.3. 개발 스타일

2.3.1. 테스트를 위한 구조

모듈화를 하는 목적은 그 모듈의 재사용성에 있습니다. 적절한 모듈화를 통해 얻을 수 있는데, 어떤 경우는 지나친 모듈화 또는 애매한 모듈화 때문에 모듈화의 의도가 흐려지는 경우가 있습니다. 이런 경우를 대비해서 사전에 생각해야할 것은 어떻게 테스트를 위한 모듈화를 늘 염두에 두라는 것입니다. 그렇게 되면 보다 적절한 모듈 구분이 생깁니다.

2.3.2. Real main, Test main

다음과 같은 코드들이 있다고 합시다.

file.c
-----

int file_send( const char * host, int port, const char * file )
{
    ....
}

int file_recv( char * buf, int size )
{
    ....
}

comm.c
------
#include "file.h"

void setup()
{
}

int main( int argc, char * argv[] )
{
    char buf[1024];
    setup();
    file_send( "localhost", 1122, "test.txt" );
    file_recv( buf, sizeof buf );
}

test_comm.c
-----------
#include "file.h"

void test_file_recv()
{
    assert(...);
}

void test_file_send()
{
    assert(...);
}

int main( int argc, char * argv[] )
{
    test_file_send();
    test_file_recv();
}



위 코드를 대략 읽게 되면, file.c 라는 모듈(?)이 하나 있고, 여기에 있는 함수를 실제 이용하는 comm.c 라는 main 함수 모듈과 test_comm.c 라는 테스트용 모듈이 있음을 알 수 있습니다. 아마도 comm 이라는 유틸리티의 일부일 것 같고, 그 main 함수는 comm.c에 있는 것인데, 이렇게 분리하는 것은, file.c의 내용을 comm.c 에 놓을 수도 있습니다만, 그렇게 하지 않고 분리했습니다. 혹자는 comm.c 에 main2 라고 넣어서 필요시마다 main과 이름을 바꿔가며 테스트할 것입니다. 하지만 그렇게 하는 것은 프로그램을 배울때나 하는 것이며 실전에서는 그렇게 테스트하면 나중에 필요한 테스트를 못하게 됩니다. 테스트 코드도 코딩의 메인이며, 항상 컴파일될 수 있는 상태를 유지해야합니다.

자, Real main과 Test main을 둘 수 있다는 가정하에 모듈로 나뉠 수 있음을 볼 수 있습니다. 이것을 좀더 발전시켜 생각한다면, 같은 테스트 그룹에 있는 것들 끼리 모아놓을 때 적절한 모듈화의 습관을 기를 수 있을 것입니다.

2.3.3. 부분 Archive

위에서 살펴본 것은 단지 main 함수만을 살펴 보았습니다만, 여기에 추가하여 더 생각해볼 수 있는 것이 있습니다. main 함수가 들어 있는 파일을 제외한 나머지 파일을 static library 즉, archive로 만들고, 이것을 main 함수가 들어 있는 것과 링크를 하라는 것입니다.

Makefile
--------

libcomm.a: file.o tcp.o udp.o serv.o
        ar cr libcomm.a $?

comm: libcomm.a comm.o
        gcc libcomm.a comm.o -o comm

test_comm: libcomm.a comm.o
        gcc libcomm.a test_comm.o -o test_comm



물론 autotool을 써서 진행하는 것을 권장합니다만, 예를 들어봅니다. 이렇게 부분 archive를 구하는 이유는 의미상있는 것인데, 만일 file.o 가 comm을 만들기 위해서 의존관계에 의해 컴파일되었다면, 엄밀히 말해서 test_comm을 위해서는 다른 file.o를 만드는 것이 옳습니다. 간혹, 하나의 실행 파일을 만들기 위해 컴파일 옵션이 달라진다면, 의도하지 않은 바이너리를 테스트를 할 수 있게 됩니다.

실제, autotool을 이용할 경우, 하나의 소스가 다른 바이너리 생성에 참여하게 된다면, 그리고 옵션이 달라진다면, 위의 경우 comm-file.o 라는 파일과 test_comm-file.o 와 같이 참여하게 될 바이너리를 prefix로 하는 오브젝트명이 사용됩니다. 이런 경우 테스트에 어려움이 있을 수 있기 때문에 같은 오브젝트를 테스트하기 위해서 부분 archive를 만들어 링크하는 것을 권장합니다.







sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2012-04-04 10:57:34
Processing time 0.0178 sec