개발 환경을 갖추는 일은 성공적인 소프트웨어 개발을 위한 필수조건이다. 여기서 개발 환경이란 IDE 뿐 아니라 소스 저장소, 테스트 환경, 배포 툴, 문서화 도구, 소스 컨벤션, 관리 정책 등 소프트웨어 개발의 시작부터 끝까지 필요한 모든 것들을 포함한다. 많은 것을 제공하는 IDE를 사용하더라도 여전히 다른 많은 도구들과 연계해야 하며, 정책적인 부분의 결정 역시 개발자의 몫이다. 이런 소프트웨어 개발 환경이 잘 갖춰져 있어야 개발자들은 소모적인 작업에 노력을 들이지 않고 개발에만 집중할 수 있게 된다. 이 문서의 목적은 이러한 소프트웨어 개발 환경 구축에 필요한 것들을 간략히 소개하고 모범적인 Practice를 제공하는 것이다. 방법론과도 많은 부분이 겹칠 수 있으나 방법론보다는 좀더 기술적인 범위에 초점을 맞출 것이다. XP라면 첫번째 Iteration에서 해야할 일들이다.
아래의 목차는 중요도 순이 아니라 먼저 갖추는게 좋은 순서임을 참고하라. TableOfContents
커뮤니케이션 시스템
의사 결정 체계
의사 결정은 비단 소프트웨어 개발 뿐 아니라 모든 집단에서 공통적으로 중요한 문제다. 의사 결정 시스템은 관련되는 사람이 많은 프로젝트일수록 프로젝트의 진행 속도에 결정적인 영향을 미친다. 많은 사람이 공동 작업을 하다보면 결정을 내려야하는 일이 빈번하게 발생한다. 결정해야할 일이 발생했을 때 이것을 알아차린 사람이 그 내용을 관련되는 사람들에게 전달하고 사람들의 의견을 모아고 결정을 내리는 과정, 그 과정의 속도가 개개인의 작업 속도 이상으로 전체의 속도에 큰 영향을 끼친다. 자신의 작업에 무언가 결정이 되어야할 내용이 있는데 그 결정이 이루어지지 않아서 작업을 못하고 있다면 그 집단은 의사 결정 시스템이 문제가 있는 것이다. 따라서 무언가를 시작하기 전에 가장 먼저 해야할 일은 앞으로 의사 결정을 어떻게 할 것인가를 정하는 것이다.
어떤 식으로 의사 결정 규칙을 정할 것인가는 조직의 상황에 따라 다르다. 정치 교과서에서 배운 의사 결정 방식들이 도움이 될 것이다. 해당 문제에 관련된 사람이 각 파트별로 한 명 이상씩 참석한 상태에서 30분 이상의 토론 후 3분의 2 이상의 찬성일 경우 찬성으로 판정한다.와 같은 식이면 될 것이다. 관련자는 누구든지 의사 결정에 참여할 수 있어야함은 물론이다. 이런 의사 결정 체계는 명문화되어 있는 것이 좋다. 암묵적인 룰이 늘어나는 것은 좋지 않을 뿐더러, 중요한 것은 의사 결정을 원하는 사람이 누군가(소속 팀장, 상사 등)의 눈치를 보지 않고 의사 결정을 요구할 수 있어야 한다는 것이다. 명문화된 룰은 불만이 있는 사람이 정당하게 불만을 표출할 수단이 된다. 의사 결정의 필요성이 발생했을 때 의사 결정 요구가 빠르게 이루어질 수 있도록 CommunicationTrigger 등을 사용하는 것도 좋은 방법이다.
문서화 도구
문서화의 필요성은 재차 언급할 필요가 없을 것이다. 문서화에 있어 기억해야할 것은 두 가지, 필요 없는 문서를 만들지 않는 것이 첫째이고 필요한 문서는 쉽게 만들 수 있게 하는 것이 그 둘째이다. 첫째는 정책상의 문제이니 여기서는 논외고 여기서 논할 것은 둘째, 도구다. 가장 중요한 것은 문서를 써야하는 사람이 문서를 쓰고 다른 사람이 읽게 만들기까지의 비용이 작아야한다는 것이다. 읽는 사람이 편하게 읽을 수 있는 것도 물론 중요하지만 문서 쓰기가 귀찮아서 안 쓰게 된다면 다 소용 없는 일이다. 그렇다고 귀찮은 걸 억지로 하게 만든다면 생산성이 떨어진다. 결국 쉽게 쓰고 쉽게 고치고 쉽게 공유할 수 있는 도구가 필요하다. 더불어 버전 관리까지 되어야한다. 현재 이런 조건을 잘 충복하고 있는 것은 Nosmok:위키위키' 밖에 없는 듯하다. CVS 등의 SCM으로 워드 등을 공유하는 것보다는 이게 훨씬 간편하다.
어떤 위키엔진이 좋을까? 사실 위키 엔진은 간단하기 때문에 직접 만들어 써도 큰 부담이 아니다. 그러나, 이미 나와 있는 좋은 것들이 많다. Nosmok:위키엔진'을 참조하라. Tikiwiki 같은 복잡하고 다양한 기능을 가진 위키도 많지만 위키에 많은 기능이 필요하다고 생각되진 않는다. 모니위키 정도면 필요한 것은 다 갖추고 있고 국내에서 많이 쓰기 때문에 추천할 만하다. MoinMoin도 좋고 상용으로는 Confluence가 인기를 끌고 있다.
소프트웨어 형상 관리(SCM)
소프트웨어 형상 관리란 소스 버전 관리를 포함해서 product의 release 버전, 버그 추적, 기능 목록 관리 등등을 아우르는 것이다.
소스 버전 관리
소스 버전 관리 시스템은 CVS가 가장 대중적이다. 다른 버전 관리 시스템을 이미 사용하고 있는 것이 없다면 CVS가 가장 추천할 만하다. 물론 CVS보다 나은 것도 꽤 많다. 상용 소프트웨어를 비롯해서 CVS의 대체를 목적으로 개발된 Subversion도 있다. 그러나, 중요한 것은 버전 관리 시스템 자체의 기능보다도 얼마나 좋은 프론트엔드를 갖고 있는가, 그리고 IDE와의 통합이 얼마나 편리한가이다. 이 점에서 폭넓은 사용자층 덕분에 대부분의 IDE가 지원해주고 있고 ant, maven 등의 지원에 changelog 분석기 등의 여러 가지 툴이 많은 CVS가 유리하다. Subversion은 좀더 지켜 보아야 할 것이다.
소스 버전 관리는 단지 소프트웨어를 CVS로 선택하는 것만으로 끝나진 않는다. CVS를 어떻게 설치하고 설정할 것인지에 대한 자질구레한 기술적 결정들을 내려야하고 브랜치를 어떻게 사용할 것인가에 대한 정책도 세워야한다. 이 문서는 이런 결정을 내리는데 도움을 주는 것이 목적이며 CVS에 대한 기본적인 사용법은 설명하지 않을 것이다.
CVS 설치 및 설정
- CVS 서버
- 가급적이면 CVS 이외에는 별다른 부하가 걸리지 않는 서버가 좋다. 팀원이 많아질수록 CVS의 부하는 생각 이상으로 커질 수 있다. 주기적으로 changelog 통계 같은 것도 돌리고 하려면 큰 부하가 걸리지 않는 것이 좋다. 윈도우용 CVSNT가 있지만 추천하지 않는다. 계정 관리가 리눅스 서버보다 더 귀찮을 수 있다. 요즘 대세가 플랫폼 프리라고는 하지만 개발하는데 리눅스 서버 한 대쯤은 필수인 것 같다.
- 계정 관리 및 프로토콜
CVS 계정은 한 사람 당 하나가 주어지는 것이 좋다고 본다. CollectiveCodeOwnership 하에서도 여전히 개인 계정은 의미가 있다. 간혹 소스에 주석으로 작성자를 남기는 경우가 있는데 CVS를 사용한다면 이런 삽질은 불필요하다. 설정 방법으로 가장 편리한 방법은 cvs 그룹으로 개발자 각자에게 CVS 서버의 시스템 계정을 부여하는 것이다. 시스템 계정으로 만들면 ext+ssh와 pserver 모두 이용가능하고 pserver에서 별도 계정을 관리하지 않아도 된다. pserver에서 쓰는 패스워드 파일을 관리하는 것이 시스템 계정 관리하는 것보다 훨씬 귀찮다. 물론 루트 권한이 없다면 pserver를 쓸 수 밖에 없겠지만 루트 권한이 있다면 시스템 계정이 관리하기 가장 좋다. 또, 많은 경우에 개발자용 계정 외에 읽기 전용 계정이 필요할 수 있다. ext+ssh로는 권한 관리가 안되므로 이 때는 시스템 계정을 만들지 말고 패스워드 파일을 이용해서 pserver 계정을 만들어야한다.
브랜치 및 버전 정책
브랜치란 소스의 개발 방향이 여러 갈래로 나갈 때 이를 따로 관리하기 위한 방법이며, 중간 단계의 버전을 남기기 위해서도 사용된다. 일반적으로 Main trunc라 부르는 HEAD 브랜치는 소스의 가장 최신 버전을 담는다. product가 릴리스될 때는 항상 버전을 매기고 브랜치로 남겨두는 것이 좋다. 이를테면 개발자가 매일 매일 개발하는 소스는 HEAD에 커밋하고 어느 정도 개발이 완료되어서 릴리스를 할 때는 R1_0, R2_0_1 등과 같이 버전을 붙인 브랜치를 만든다. 이런 버전용 브랜치는 가급적 재수정을 하지 않는 것이 좋다. 개발 중에 소스가 두 갈래로 분기를 해서 개발해야할 때도 브랜치를 만든다. 이를테면, 오라클용 버전과 MySQL용 버전을 따로 만든다면 oracle, mysql 등과 같이 브랜치를 만들고 작업한다. 이들이 따로 릴리스가 된다면 ORACLE_R1_0, MYSQL_R3_1 등과 같이 브랜치 태그명을 정하면 좋다. 일반적으로 개발 중인 브랜치는 소문자로, 릴리스된 버전은 대문자로 태그 이름을 짓는 것이 구분이 편리하다. 하지만 실상 브랜치는 단기적으로만 쓰이는 것이 좋다. 위에서 oracle, mysql의 예를 들었지만 이런 문제로 브랜치를 나누는 것은 좋지 않다.
이슈 추적 관리
이슈 추적 관리는 두 가지가 있다. 하나는 고객(혹은 기획자)로부터 요구사항이나 버그 리포트를 관리하는 것, 또 하나는 개발자 자신이 product의 feature list를 관리하고 버그 추적을 하는 것이다. 둘다 하나의 소프트웨어로 관리할 수 있다. Issue Tracker, 혹은 Bug Tracker라고 불리는 product가 존재한다. 간단한 이슈 추적 관리 기능만 제공하는 것에서부터 gforge나 source cast처럼 아예 소프트웨어 개발 과정 전반에 걸친 collaboration system을 구축해주는 것까지 다양한 범위로 존재한다. 만약 멀리 떨어진 환경에서 인터넷으로 공동 작업을 해야한다거나, 오픈 소스 프로젝트를 한다면 대형 collaboration system이 좋을 수 있지만 대체로 CVS와 단순 Issue Tracker로 충분하다. 오픈 소스에도 상당한 품질을 가진 것들이 많이 나와 있다. mantis 정도면 국제화도 되어 있어 한글 메뉴도 나오고 설치하기도 쉽기 때문에 추천할 만하다. TUTOS도 괜찮다. trac처럼 위키를 제공해서 문서와 연동하기 좋은 것도 있지만 이슈 트래커 부분과 위키 부분 어느 쪽도 완성도가 높지 않다.
이슈 트래커에서 이슈 관리는 product의 릴리스 버전별로 하는 것이 좋다. 가능한 한 이슈 보고는 최초 발의자가 하는 것이 좋다. 고객 혹은 기획자에게는 보고 정도의 권한을 주고 개발자들에겐 최대한 관리자급의 권한을 주는 것이 좋다. 이슈 보고 및 관리에 대한 사람별 통계를 내보는 것도 재밌는 일이 될 것이다. 이렇게 이슈들을 관리한 내용은 release note를 만들 때도 유용하게 쓰일 수 있다.
다만, 플래닝의 용도로 이슈 트래커를 사용하는 것은 별로인 것 같다. 이미 어느 정도 굴러가는 프로젝트에서 새로 생겨나는 이슈들을 추적하기에는 괜찮지만 플래닝까지 포괄하긴 어렵다는 느낌이다. 하지만 XPlanner 등의 플래닝 도구로 이슈 트래킹을 커버하는 것은 괜찮은 것 같다.
개발 도구
개발 도구는 프로그래머의 생산성에 가장 결정적인 영향을 미치는 것이다.
프로그래밍 언어
프로그래밍 언어는 다분히 개발자의 취향에 의존한다. 여기서는 선택할 때 고려해야 할 조건들만 언급한다.
프로그래밍 언어 선택에 있어서 가장 먼저 고려해야할 것은 성능이다. 그러나, 이것은 선택조건으로가 아니라 제약조건으로 고려해야할 것이다. 하드웨어 접근이 엄청나게 많거나, 엄청나게 빠른 속도를 요하거나, 컴파일러 혹은 VM을 개발하는 일이 아니라면 어셈블러나 C를 써야하는 경우는 드물다. 가급적 객체지향 언어를 선택하라. 물론 앞의 조건이 걸린다면 C를 선택할 수 밖에 없다.
VM 기반 언어를 사용할지 말지도 중요한 문제다. 크로스 플랫폼은 이 선택에 있어서 중요한 문제가 아니다. VM 기반이 아닌 언어도 소스레벨에서의 크로스 플랫폼은 어느 정도 보장된다. 더 중요한 것은 실행코드의 보안 정도, 그리고 빠른 시작 속도의 필요성이다. 이런 경우는 native executable binary를 만들 수 있는 언어가 필요하다. 이런 경우가 아니라면 VM 기반을 사용하든 아니든 별 상관 없다.
다음 고려 대상은 언어에 대한 지원 정도이다. 사용하려는 부분에 충분한 API가 있는지, 좋은 IDE가 있는지, 관련 자동화 도구, 문서는 충분한지, 사용자 커뮤니티가 얼마나 많은지 등등을 검토하라. 실험적인 프로젝트가 아니라면 이런 부분에서 부족한 언어는 선택하지 않는 것이 좋다. Java, C++ 등은 이미 다방면에서 성숙된 API를 갖고 있고 수준 높은 IDE가 많으므로 이런 면에서 유리하다. 참고로 현재 언어의 사용 정도는 http://www.developer.com/lang/article.php/3390001 에서 볼 수 있다. Java, C, C++, PHP, Visual Basic, Perl, Delphi/Kylix, Python 정도의 순이다. Ruby가 최근 20위권에 올라왔다.
테스트 도구
테스트는 이제 프로그래머의 필수 스킬 중 하나이며, 좋은 테스트 도구를 갖는 것은 생산성과도 직결된다.
Acceptance Test
고객의 요구사항, 혹은 프로그램의 기능이 제대로 동작하는지를 테스트하는 도구. Acceptance Test에서 가장 중요한 것은 쉽게 테스트를 작성하고 보기 쉬운 리포트를 생성하는 것이다.
웹이라면 HttpClient와 정규식 등을 통해 검사해볼 수 있다. 잘 작성된 Acceptance Test들은 웹 서버의 모니터링용으로도 활용할 수 있다.
Unit Test
소프트웨어의 각각의 코드들이 정상적으로 동작하는지를 테스트하는 도구. 유닛 테스트에서 가장 중요한 것은 자동화가 되는 것이다. 자동화되지 않는 테스트는 자주 실행하지 않게 되어 테스트로서의 의미를 상실할 수 있다. 유닛 테스트를 작성할 때는 언제 실행시켜도 돌아가게 작성해야한다. 유닛 테스트에 대한 일반적인 이야기는 TestDrivenDevelopment 와 UnitTest 를 참조하라. 테스트 프레임워크로는 JUnit이 대표적이며 많은 언어들에 JUnit 클론이 있다. 그리고 MockObject가 있다. MockObject를 세트로 만들어 둔 것도 있고 즉석에서 MockObject를 만들 수 있는 MockFramework도 있다. 간단한 경우는 그냥 직접 만들어서 쓰는 것이 나은 것 같다. 파이썬 같은 언어에선 MockObject를 아주 쉽고 유연하게 만들 수 있다. MockFramework는 EasyMock과 JMock이 있다.
그리고 실제 테스트를 할 때 고민하게 되는 문제들이 몇 가지 있다.
- 테스트 소스와 실제 소스는 분리해서 관리하는 것이 좋다. 이를테면, 실제 소스는 src/java에, 테스트 소스는 src/test에 담는 것과 같은 식이다.
- 테스트 케이스의 이름은 뒤에 Test를 붙이는 것을 기본으로 지원하는 도구들이 많다. 이클립스, Maven이 그렇다.
- 실행 환경에는 테스트를 위한 클래스들과 자원들이 포함될 필요가 없다. 분리해도 동작할 수 있도록 구조화해야한다.
또 하나 중요한 것은 주기적으로 유닛 테스트를 실행시키고 결과를 수집해서 알려주는 환경을 구축하는 것이다. 이것은 개발자가 매일매일 소프트웨어의 품질이 어떻게 변해가는지, 버그가 발생하고 있는지 아닌지를 추적할 수 있게 해준다. 적절한 스케쥴러와 Ant, Maven 등의 자동화 도구, 메일 등을 연동하여 구축하라. 아직 이런 부분에서 범용적인 요구사항을 만족시킬 수 있는 도구는 많지 않으므로 직접 구축해보는 것도 좋다. CruiseControl을 참조하라.
Stress Test
스트레스 테스트는 주로 서버 사이드 애플리케이션에서 중요하다. 스트레스 테스트에서 측정해야하는 것은 보통 두 가지이다. 얼마나 많은 Concurrent Request를 견뎌낼 수 있는가(웹에서는 보통 Concurrent User라고 한다.), 그리고 각각의 애플리케이션이 얼마나 빠른 속도로 실행되는가(Throughput)이다. 이 둘은 모두 스트레스 테스트 도구에서 테스트 환경을 조절해가면서 측정할 수 있다. MS에서 나온 스트레스 테스트 툴이 있고 JMeter가 있는데 MS의 것이 더 안정적이고 정확한 것 같다.
통합 개발 환경 (IDE)
IDE는 개발 도구 중에서도 프로그래머의 생산성에 가장 큰 영향을 미치는 부분이다. 기본적으로 이 정도는 갖춰야 IDE라고 불러줄 수 있다.
- 빌드 및 실행의 자동화
- 문법 검사 목록
- 비주얼한 디버깅
- 소스를 보면서 한 줄씩 실행하기
- 변수값 보기, Watch, 실시간 표현식 평가 기능
- 메쏘드(함수) 호출 구조 (ex:자바의 Stack trace)
- 코드 에디팅시 컨텐트 어시스트 지원, 문법 하이라이팅 지원
- 프로젝트 관리
- SCM과의 비주얼한 연동
- API 문서의 자동 연동
vi의 집착에서 벗어날 필요가 있다.
지속적인 통합
ContinuousIntegration 은 소프트웨어 개발에서 소모적인 일을 줄이는데 가장 중요한 것이다.이 부분이 개발환경 구축에서 기술적으로 가장 많은 노력이 들어가고 가장 어려운 부분이다. 먼저 통합에 필요한 작업들을 정의하고 각각의 작업들을 One-stop으로 실행하고 가능한한 비주얼하게 결과를 볼 수 있도록 구성하라. 필요한 작업들은 대략적으로 다음과 같다.
build 컴파일 및 리소스 복사 및 변환 작업. IDE와 잘 조화되어야 하며, 어떤 버전이든지 연관 모듈들과 항상 통합 빌드할 수 있어야한다.
test 테스트를 쉽게 작성하고 실행시킬 수 있어야한다. 이는 주로 개발에서의 TDD와 배포 시의 검증을 위한 것이다.
test-report 테스트와는 별개의 테스트 리포트가 필요하다. 하루하루 개발의 진척 상황, 소스의 상태를 모니터링하기 위한 것이다.
xdoc javadoc, source xref 등의 각종 문서를 만드는 작업이다.
dist 배포판을 만드는 작업. build + test + xdoc이다.
deploy 서버 사이드 애플리케이션의 경우 서버에 직접 배치하는 것을 자동화하는 과정이 필요하다.
analysis 여러 가지 코드의 품질과 상태를 검사하고 리포트를 생성하는 도구. checkstyle, jdepend 등이 그 예다.
changelog CVS의 changelog, product의 changes 관리 등을 자동화할 수 있는 도구가 있으면 편리하다.
schedule 각종 리포트, nightly build & test 등을 위한 스케쥴러가 필요하다.
Ant와 Maven은 이런 작업을 도와주기 위한 툴이다. Ant는 당초 build & dist 정도의 목적을 달성하기 위해 출발했으나 현재는 상당히 넓은 범위로 확장되고 있고 Maven은 Ant를 한 단계 발전시켜 project management and project comprehension tool을 지향하고 있다. 그리고 오래전부터 사용된 Make가 있다.
개인적으로 이들 중 어떤 것도 만족스럽지 않다. Ant는 지나치게 definitive하게 문제를 해결하려 한다. 프로젝트가 복잡해지고 연관 프로젝트가 늘어갈수록 procedural한 코드가 필수적인데 Ant 자체에서는 이런 스크립트를 지원하지 않는다. 또한 dependency 문제를 개발자가 해결해야하며, Ant 스크립트 자체는 human readable과는 거리가 있으며 문법의 제한으로 리팩토링하기도 어렵다. 빌드 스크립트는 한 번 만들면 계속 쓰는 게 아닌가 생각하기 쉽지만 결코 그렇지 않다. 상황은 계속 바뀔 수 있으며 프로젝트의 어떤 부분이라도 쉽게 바꿀 수 있는 체제를 갖추는 것이 좋다.
Maven은 조금 낫다. Ant보다 더 definitive하면서 더 procedural한 코드를 작성할 수 있다. 작성해야하는 스크립트는 더 적으면서 유연성은 더 높다는 뜻이다. Maven의 리포팅 기능은 대단히 훌륭하다. 그러나, 이것보다 Maven이 가져온 가장 큰 변화는 dependency의 자동화된 관리이다. 단순한 설정만으로 dependency 문제를 상당히 깔끔히 해결할 수 있다. 이것은 데비안의 dselect가 rpm에 대해 갖는 장점과도 비슷하며, 젠투의 emerge가 dependency 관리를 통해 make를 효율적으로 사용하는 것과도 비슷하다. 그러나, 아직은 dependency가 jar에 중점을 두고 설계되어 다른 종류의 dependency에는 문제가 생길 수 있다. Maven은 Ant보다 유연성이 좋다. Maven의 메인 스크립트 언어인 Jelly는 XML 기반이지만 상당히 강력한 스크립트 기능을 가지고 있고 빌드 작업간의 의존성도 쉽게 걸 수 있기 때문에 어느 정도의 리팩토링도 가능하다. 그러나, 여전히 진짜 언어보다는 불편하며 이 점은 단순한 몇 가지 태스크를 추가할 때도 단점이 될 수 있다. 어차피 빌드 스크립트는 개발자가 개발하고 읽는다는 점을 감안하면 프로그래밍 언어가 사용되지 않을 이유는 별로 없다. Maven 역시 한 단계 더 도약이 필요하다. 또, Maven 역시 스케쥴러를 포함하고 있진 않기 때문에 이런 부분은 다른 소프트웨어를 사용하거나 직접 제작해야한다.
python 같은 언어(인터프리터, 고급 언어)에서는 이런 빌드 도구의 필요성이 상대적으로 적은 것 같다.
데이터베이스 관련 도구
sqlplus로는 부족하다. 비주얼 툴의 생산성을 얕보지 말라. 직접 만드는 것도 나쁘지 않다고 본다. 재정적 지원이 있다면 상용 툴이 나을 것이다.
모니터링 도구
[프로그래밍분류]