수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1691221
  • Today | 527
  • Yesterday | 628

6 Articles, Search for '.NET'

  1. 2007/06/15 Introduction to Windows Communication Foundation
  2. 2007/06/09 [닷넷 프로젝트를 정복하라] ④ 개발 방법론, 제대로 알고 쓰자
  3. 2007/06/09 [닷넷 프로젝트를 정복하라] ③ 더이상「꼼수」는 없다
  4. 2007/06/09 [닷넷 프로젝트를 정복하라] ② PM 김씨의 좌충우돌기
  5. 2007/06/09 [닷넷 프로젝트를 정복하라] ① 이래서 안되는거군!
  6. 2007/04/29 High-Performance .NET Application Development & Architecture
Programming/.NET2007/06/15 15:30

Introduction to Windows Communication Foundation

Introduction.

Windows Communication Foundation (WCF) is Microsoft's unified programming model for building service-oriented applications.  It enables developers to build secure, reliable, transacted solutions that interoperate with applications in different platforms.

Interoperability is the fundamental characteristic of WCF. The fundamental communication mechanism is based on Web Services specifications such as SOAP, XML, XSD, WSDL and newly established standards including the WS-* protocols. These specifications address several areas, including basic messaging, security, reliability, transactions, and working with a service's metadata.

WCF relies on WS-Policy and WS-Metadata Exchange to discover information about the communications partners. Reliable communication is essential for most situations (no duplicates messages), and WS-Reliable Messaging would be used to interact with many of the other applications in this scenario. WS-Security and the related specifications might also be used for establishing a secure channel. The specifications support the main security services such as authentication, integrity and confidentiality. WS-Atomic Transaction is very important for managing transactional context involving several transactional resources.

The key point is that WCF implements interoperable Web services, complete with cross-platform security, reliability, transactions, and other services. It also is transport neutral, protocol neutral, and format neutral. For example, services are free to make use of HTTP, TCP, named pipes, and any other transport mechanisms for which there is an implementation. It is possible the WCF-to-WCF communication to be optimized, but all other communication uses standard Web services protocols.

The architecture.

The architecture is based on layers. See on Figure 1.

사용자 삽입 이미지

Figure 1.

This architecture permits programmers to work at several levels. High-level programming leverages the service runtime and doesn't require developers to directly work with messages at all, unless they want to. At the other end of the spectrum, developers can program to the messaging layer and take charge of communication activities.

Contracts

Contracts define various aspects of the message system. The data contract describes how every business object to be on the wire is going to be persisted using XML. The message contract defines specific message parts (header and body) using SOAP protocols, and allows finer-grained control over parts of the message. The service contract specifies the actual method signatures of the service. The policy and binding contracts enable you to specify transport, security details, and other aspects that must be met in order to communicate with a service.

Service Runtime

The service runtime manages the execution of services. It is possible to be specified the run-time behaviors through attributes or configuration settings. For example, a service can indicate that its instances are not thread-safe. Throttling behavior allows you to put limits on the number of connections, sessions, and threads. Error handling behaviors control how errors are handled and reported to clients. Metadata behavior governs how and whether metadata is made available to the outside world. Transaction behavior enables the rollback of transacted operations if a failure occurs. Dispatch behavior is the control of how a message is processed by the WCF infrastructure. Parameter filtering allows preset actions to occur based on filters acting on message headers.

Messaging

The messaging layer illustrates the possible formats and exchange patterns of the data. WS Security channel is an implementation of the WS-Security specification enabling security at the message layer. The WS Reliable Messaging channel enables the guarantee of message delivery. The encoders present a variety of encodings that can be used to suit the needs of the message. The HTTP channel specifies that the HyperText Transport Protocol is used for message delivery. The TCP channel similarly specifies the TCP protocol. The Transaction Flow channel governs transacted message patterns. The Named Pipe channel enables inter-process communication. The MSMQ channel enables interoperation with MSMQ applications.

Activation and Hosting

The specific method by which a service is started is determined by its activation options. And services can be either self-hosted or hosted in the context of another application. The Windows Activation Service (WAS) enables WCF applications to be activated automatically when deployed on a computer running the WAS. Services can also be manually run as executables (.EXE files). A service can also be run automatically as a Windows service. And a service can also be run as a COM+ application.

Services from the Outside.

From the outside, a service is collection of endpoints and a service description. The description is the information necessary for interchange messages with the service. It is used WSDL, XML Schemas, WS-Policy (description of non-functional requirements such as security mechanisms, schedules availability, and quality of service), WS-Metadata Exchange (WS-MEX) for asking the service to describe itself.

The endpoint is a basic unit of communication in Windows Communication Foundation (WCF). It is the physical port. You interchange messages with a service through the endpoints. Each endpoint is made up of three elements: an address, a binding, and a contract. The endpoint's ABC, A stands for address, B stands for binding and C stands for contract.

The address is basically a URI. The format of the address is scheme://myservice.mydomain.com:port/path. For example for accessing a service using http protocol you must write the following address down http://myservice.mydomain.com:8080/myservice/financialservice.svc, or for accessing a service using tcp protocol by the port 2525 you must write the following address down

net.tcp://myservice.mydomain.com:2525/myservice/financialservice.svc.

The binding describes how a service communicates with the partners. It is a collection of details such as transport method, encoding format, reliability requirements, and security requirements. WCF creates a channel stack to satisfy the using binding.

The contract is a collection of operations that specifies what the endpoint communicates to the outside world. Each operation is a simple message exchange, for example a request message and an associated reply message that form a request/reply message exchange.

Conclusion.

This paper is intended to present the fundamental concepts of Windows Communication Foundation as the new programming model for service-oriented development. WCF implements new specifications adopted in standardization organizations such as OASIS, W3C, etc. And it is a good object model that really represents the concepts of the Service-Oriented Architecture.

".NET" 카테고리의 다른 글
  • Windows Workflow Foundation 호스팅에 대한 소개 (0)2007/07/23
  • Windows Workflow Foundation 규칙 엔진 소개 (0)2007/07/23
  • Introduction to Windows Communication Foundation (0)2007/06/15
  • High-Performance .NET Application Development &... (0)2007/04/29
  • 데이터 바인딩 어플리케이션 만들기 기초 (0)2007/01/11
2007/06/15 15:30 2007/06/15 15:30
Posted by webdizen
Tags .NET, WCF, Windows Communication Foundation
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3046

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Development2007/06/09 19:04

[닷넷 프로젝트를 정복하라] ④ 개발 방법론, 제대로 알고 쓰자

당신이 MSF/CD에 대해 고민해야 하는 이유

김상훈 (동명정보대학교 연구원)   2004/12/27 
    

연재순서?
1회. 이래서 안되는거군!
2회. PM 김씨의 좌충우돌기
3회. 더이상 꼼수는 없다
4회. 개발 방법론, 제대로 알고 쓰자

대부분의 PM은 자신이 맡은 프로젝트를 성공적으로 이끌기 위해서 개발 방법론을 도입한다. 다양한 개발 방법론 중 프로젝트의 규모와 크기, 형태, 그리고 조직 구성 등을 감안해 선택하고 따르게 되는데, 대부분의 닷넷 프로젝트는 MSF/CD 개발 방법론을 채택해 프로젝트를 진행하게 된다. 그렇지만 MSF/CD를 제대로 이해하고 있는 경우는 드물다. 이번 글에서는 MSF에 대해 이야기해보자.

처음에는 MSF(Microsoft Solution Framework)에 관련된 글을 쓰고자 했다. 사실 필자는 MSF라는 것이 신기하기만 하다. 최근 닷넷 개발 사례를 보면 개발 방법론으로 MSF/CD를 선택하는 프로젝트가 대부분인데, MSF라는 것이 정식적으로 국내에 소개된 것은 거의 없기 때문이다.

국내에서 발매된 MSF 관련 서적은 정보문화사에서 나온 책 한권이 전부라고 봐도 무방하고, 인터넷 컨텐츠 혹은 교육 등 여러 측면에서도 MSF는 다른 개발 방법론에 비해 자료가 턱없이 부족하다. 그럼에도 불구하고 MSF/CD 개발 방법론을 이용해 프로젝트를 진행한다는 프로젝트 제안서의 자랑스러운(?) 문구는 날이 갈수록 늘어가기만 한다.

“MSF/CD가 제대로 사용되는 것을 본 적이 없다”
세계적으로 볼 때 큰 활용 사례가 없는 MSF가 난데없이 닷넷 개발 방법론의 표준으로 자리잡으려 하는 것이 이상하기도 하려니와 객체지향 개발 플랫폼인 닷넷 환경에서 이벤트 지향적으로 분석하고 설계되는 MSF가 거의 붐인 것처럼 채택이 늘어나는 것이 의아하기도 하다. MSF에 대한 이해 없이 MSF/CD에서 작성하는 산출물 순서만 따라가는 개발 방법이 과연 성공할 수 있을지에 대한 의문도 크다.

프로젝트를 진행함에 있어서 어떤 개발 방법론을 선택하느냐가 중요한 것이 아니다. 그렇다고 아무 방법론이나 가져다 붙이면 된다는 것은 더더욱 아니다. 개발 방법론을 선택하는 작업은 신중에 또 신중을 기해야 한다.

어떤 개발 방법론을 채택했다가 중요한 것이 아니고 프로젝트에 도입될 플랫폼의 특성과 개발 기한, 개발 팀 구성, 개발 목표, 개발 범위 등이 모두 고려되어서 채택되어야 할 심각한 문제이다. 개발 제안서에 MSF/CD라고 이름만 거창하게 붙여놓는다고 해서 그 방법대로 진행되는 것은 아니기 때문이다. 필자가 참여한 몇 개의 프로젝트에서 MSF/CD가 제대로 사용되는 것을 본 적이 없다. 다음의 사례를 살펴보자.

MSF/CD를 도입한 사례
첫 번째, Object Interaction Diagram은 엑셀로 그리면 되나요?
닷넷 플랫폼으로 솔루션을 개발하게 된 한 업체는, MSF/CD를 도입하기로 결정했다. PM 김 씨를 제외한 내부 개발자들은 제대로 된 개발 방법론대로 프로젝트를 진행해 본 적이 없다. PM은 부랴부랴 개발자들에게 개발 방법론을 교육하기로 결정하고 MSF를 교육할 수 있는 기관이나 강사를 수소문했다.

MSF를 전문적으로 교육하는 기관은 찾지 못했고, 주위의 실력 있는 분석가들이나 설계자들의 말로는 설계 경험이 없는 개발자라면 UML에 대한 교육부터 실시하는 것이 좋을 것이라고 한다. PM 김 씨는 UML 교육을 실시하기로 하고 비싼 교육비를 투자해 UML을 교육하는 기관에 교육을 의뢰했다.

UML을 강의하러 온 강사는 래쇼날 로즈(Rational Rose)로 강의를 진행했고, 사흘간 교육에 참여한 개발자들은 UML을 사용한 설계 기법에 감동하는 것 같았다. 교육이 끝난 후 김 씨는 개발자들을 모아놓고 이번 프로젝트는 MSF/CD로 진행될 것이니 수료한 UML 교육에서 얻은 지식을 기반으로 MSF를 공부해보라고 지시했다.

개발자들은 인터넷을 뒤지고 책을 사보면서 열심히 공부했다. 김 씨는 꽤 만족했고, 개발 팀장에게 MSF/CD로 진행했을 때의 일정을 짜보라고 지시했다. 김 씨는 모든 산출물을 작성할 필요는 없으니 개발자 입장에서 빠르게 프로젝트를 진행할 수 있는 일정이 중요하다고 이야기했다. 며칠 뒤 김 씨가 받아본 일정은 다음과 같았다.


Business Context Diagram 7월 15일
Object Abstract Table 7월 20일
Class Diagram 7월 25일
… …


일정을 받아본 김 씨는 당황했다. “어떻게 Business Context를 설계하는 시간하고 객체를 설계하는 시간하고 같을 수가 있지? 그리고 Use Case가 왜 없는 거야? 데이터베이스 설계 방안은?” 김 씨는 일정을 작성한 개발 팀장을 불러서 의문점들에 대해 물어봤다. 개발 팀장의 답은 대강 다음과 같았다.


“다이어그램 그림 몇 장 그리는데 시간이 그리 오래 걸릴 리가 없잖아요?”
“Business Context Diagram의 도형이 뭘 뜻하는지 잘 몰라서, 일단 시작해놓고 보면 그 정도 걸릴 것 같아서요.”
“Class Diagram은 어차피 코드 작성해 나가면서 수정하는 거니까 대강 시간을 그 정도 잡았죠.”
“데이터베이스 설계 방안은… 나중에 페이지 설계가 나오면 그대로 해야죠.”


그리고 이어진 원망 같은 한 마디, “Object Interaction Diagram을 어떻게 그리죠? 엑셀로 그리면 되나요?” 이후 다른 대화는 계속됐다.


“Work Flow Diagram 작성은 누가 해?”
“전부 다 같이 하는 거죠”
“요구 분석에 따른 업무 분장은 안 하고?”
“몇 명 안 되는 개발팀에서 무슨 요구 분석에 따른 업무 분장을 해요?”


두 번째, “설계를 하지 않겠다는 뜻인가?”
필자는 모 대학 학사 행정 프로젝트의 기획 문서를 본 적이 있다. 이름만 대면 다들 알만한 큰 규모의 SI 업체에서 진행했는데, XXX-CBD라는 처음 보는 개발 방법론을 적용해 개발하겠다고 적혀 있었다.

교육 및 컨설팅으로 참여한 필자가 개발 방법론을 모른다는 것은 시쳇말로 ‘쪽팔리는’ 일이었고, 필자는 그 개발 방법론의 정체를 파악하기 위해 오만 서적과 인터넷 사이트들을 뒤져봤다. 하지만 그 미지의 개발 방법론의 정체를 파악하지 못했고, 결국은 업체의 과장님께 넌지시 물어봤다.


“XXX-CBD가 뭡니까?”
“아, 저희 회사에서 개발한 닷넷 개발 방법론입니다. XXX-CBD.NET이라고 부르는데 MSF/CD에 기반한 개발 방법론이죠”


필자는 그 개발 방법론에 대해서 알아보려고 그 회사의 홈페이지, 구글 등을 뒤져보고 개발 방법론을 정리한 문서를 찾아보려고 노력했으나 별 소득이 없었다. 결국은 HTML 한 페이지로 된 문서를 찾았는데 단지 개념적 설계·논리적 설계·물리적 설계라는 MSF/CD에서 이야기하는 3단계 스테이지밖에 없었고, 결론은 “특별한 개발 방법론이 아니고 MSF/CD를 그대로 사용하는구나”라고 생각할 수밖에 없었다.

MSF/CD가 그래도 괜찮은 개발 방법론이고, 회사 나름대로 개발 방법론도 정의하고 있으니 그 부분에 대해서는 그다지 걱정할 필요가 없지 않겠는가. 다른 회사의 경험 많은 분석가들이 정의해 놓은 개발 방법론에 대해 자세히 알 필요도 없을 뿐더러 왈가왈부하기도 어려운 입장이고 해서 2주로 예정된 교육만 잘 진행하면 별 어려움이 없을 거라고 생각했다.

교육을 하면서 느낀 것은 개발자들이 의외로 닷넷을 잘 모른다는 것이었고 MSF를 잘 모른다는 것이었다. MSF를 기반으로 하지 않은 MSF/CD는 이름을 그렇게 붙이는 것만으로도 이상한데, MSF/CD를 각 팀원에게 알아보라고 지시하고, 팀원들은 인터넷을 떠돌며 찾은 몇 개의 문서파일을 보고 산출물 리스트 작성 방법만 연구를 하는 것은 더욱 이상했다.

UML을 공부할 때 Use Case만 보더라도 제대로 표현하기 위해서는 몇 권의 서적을 읽고 많은 기간 연구를 해야 제대로 작성할 수 있는 법인데, 설계를 너무 가볍게 생각하는 것은 아닐까란 생각까지 들었다. 더욱 놀라운 것은 개발을 위해서 래쇼날 로즈를 구입한다는 것이었고, 개발을 위해서 래쇼날 로즈 전문 교육을 이수하도록 하겠다는 것이었다.


이상한데? MSF/CD 기반 개발 방법론인데 왜 래쇼날 로즈를 쓰지?


물론 Rose를 사용하면 절대 안 된다는 이야기는 아니지만, 당시에는 Rose의 C# 지원 도구가 발표되기 전이었고, Use Case라든가 Sequence, State 등의 다이어그램의 작성을 권하지 않는(아예 개발 방법이 틀리므로) MSF/CD에서는 가격에 비해서 그리 활용도가 높지 않을 것은 뻔한 일이므로, 엉뚱한 곳에 돈을 쓴다고 생각할 수 밖에 없었다. 나아가서 “설계를 하지 않겠다는 뜻인가?” 하는 생각까지 들었다.

얼마 후, 미국에서 객체지향을 전공한 설계 담당 프리랜서 직원이 고용됐다. 1개월 또는 2개월간 근무하면서 설계 산출물들을 작성할 것이라는데, 전체 학교의 모든 행정 시스템을 커버하는 시스템의 설계 산출물을 1명의 분석가가 한 달만에 다 작성하다니 말이 안 되는 일이었다.

필자와 프로젝트와의 인연은 거기까지였다. 그 후 어떻게 일이 진행됐는지는 자세히 알지 못하지만, 프로젝트가 막바지에 달했을 때 우연히 살펴본 프로젝트 산출물은 MSF/CD와는 거리가 멀었다. 그 산출물들이 XXX-CBD에 기반한 것이라고 말한다면, 필자는 그 방법론에 대한 문서를 열람한 적 없으므로 할 말 없지만, 더욱 기가 막힌 것은 설계 산출물의 내용과 작성된 프로그램은 완전히 다르다는 것이었다. 설계 따로 개발 따로의 전형적인 경우를 필자는 처음 봤다.

나중에 들은 개발 담당자들의 말은 더 기가 막히다.


“원래 그렇게 하는 거 아닙니까?”
“이론대로 다 하고 설계대로 다 하고자 하면 프로그램을 어떻게 짭니까?”


MSF는 개발 방법론이 아니다
MSF를 기반으로 하는 MSF/CD, MSF/EA 등을 공부할 때 명심해야 하는 것은, MSF는 개발 방법론이 아니라는 것이다. MSF는 이름 그대로 프레임워크이다. MSF는 솔루션을 작성하기 위한 모든 방법을 포함하는 개발 프레임워크를 지칭한다. MSF에는 프로젝트에 알맞은 팀을 구성하는 방법, 프로젝트의 라이프 사이클, 마일스톤 버전을 지정하고 버전 관리하는 방법, 프로젝트를 스타트하고 종료하고 리스크를 관리하는 방법 등이 포함되어 있다.

MSF는 마이크로소프트 내부에서 팀 단위로 개발을 관리하기 위해 쓰이던 기법이다. MSF는 개발을 관리하기 위한 교육, 검토, 팀 구성 모델 등을 모두 포함한다. MSF는 두 가지 모델과 다섯 가지의 훈련 사항들을 포함하고 있다.

사용자 삽입 이미지

<그림 1> MSF 모델과 훈련사항


◆ MSF Team Model : MSF에는 프로젝트를 진행하기 위한 팀 결성 모델이 들어 있다. 팀 모델은 메이저 프로젝트와의 연결을 위한 다른 팀과의 연결 고리와 프로젝트의 목표를 명확하게 할 수 있도록 팀을 구성할 것을 명시한다.

◆ MSF Process Model : MSF Process Model은 지정한 시간에 프로젝트를 완수하고 전달(deliver)하기 위해 프로세스를 조직하는 방법을 명시한다. 마일스톤 버전을 통해 위험 요소를 제거한 후 프로세스를 진행하도록 하고 있다.

◆ MSF Project Management Discipline : MSF에는 팀 단위 개발에서 프로젝트를 성공적으로 진행하기 위한 간소화되고 합리화된 프로젝트 관리 기법이 포함된다.

◆ MSF Risk Management Discipline : MSF에는 비용이 많이 소모되는 액티비티를 최소화하고 프로젝트의 진행 중 위험요소를 최소화하기 위한 위험요소 관리 기법이 포함된다.

◆ MSF Readiness Management Discipline : MSF에는 프로젝트를 성공적으로 수행하기 위해서 팀원들이 공통적으로 숙지해야 할 사항과 성공적인 수행을 위해 반드시 갖춰야 할 스킬을 관리할 수 있는 기법이 포함된다.


MSF를 다 설명한다는 것은 지면 관계상 불가능한 일이므로 MSF Team Model만 잠시 살펴보도록 하자. MSF Team Model은 계층 구조(Hierarchical Model)로 구성되지 않고 원형 구조(Circular Structure)로 구성된다. 각 구성 요소들은 같은 중요도를 갖고 프로젝트에 참여해야 하며, 여러 요소들이 동적으로 커뮤니케이션하며 프로젝트를 진행하는 구조를 지향한다.

사용자 삽입 이미지

<그림 2> MSF Team Model


MSF Team Model의 핵심은 커뮤니케이션에 있다. MSF Team Model은 프로젝트의 ‘보스’를 지정하지 않을 것을 권고한다. 프로젝트의 보스가 정해지게 되면 모든 팀들의 의사소통 창구가 하나로 통일되고 어떤 의사소통의 결과에 보스의 생각이 개입되며 자유로운 의사소통을 방해하는 요소가 될 수 있다. 보스가 의사소통 수단이 되면 팀원들간의 의사소통은 그만큼 줄어들게 된다.

프로젝트의 목표를 정확하게 파악하고 범위를 명확하게 하기 위해서는 팀원들간의 의사소통이 그만큼 중요함을 강조하는 모델이다. MSF Team Model은 각 팀이 대화를 하면서 각자의 책임을 규정하는 등의 프로젝트 진행이 자연스럽게 이뤄지게 된다는 것이 목적이다.

<그림 2>에서 보는 바와 같이 팀 모델이 조직됐다면 역할을 할당한다. 역할은 각 팀들의 기능적인 구획에서 반드시 수행해야 하는 일들을 팀별로 할당하는 것이다. 프로젝트에 ‘보스’가 없는 상황에서 할당한다는 것이 우리나라의 관습에는 그다지 어울리지 않는 형태일지 모르지만, MSF는 <그림 3>에서 조직된 팀들이 해야 할 일들을 명시한다.

사용자 삽입 이미지

<그림 3> 팀의 기능적 구획에 따른 역할 분담(Role Cluster)


MSF에서 각 역할은 드롭다운 형식으로 세분화된다. 여섯 개의 각 역할은 여러 개의 기능적인 분할로 나눠지며 각 기능 분할은 여러 책임 단위(responsibility)로 구성되고, 각 책임 단위는 여러 개의 작업(task)로 이뤄진다. 팀원의 구성에 따라 다르겠지만 하나의 책임 단위에 두 명 또는 그 이상의 개발자들이 투입되는 것이 정석이다.

사용자 삽입 이미지

<그림 4> MSF의 역할 배정 구조


MSF는 <그림 4>과 같이 역할 배정의 구조를 정하고 각 역할에서 어떠한 책임 단위를 구성해야 하는지를 명시한다. 또한 MSF에는 팀을 조직할 때 각 팀의 크기를 어떻게 결정할 것인지에 대한 명세를 포함하고 있다. MSF가 <그림 3> 또는 <그림 4>와 같이 팀 모델을 제시하고 있지만 각 조직들은 주어진 역할에 따라 복잡도, 크기, 위험 부담, 그리고 각 참여자들이 익히고 있어야 할 기술의 숙련도가 달라져야만 한다. 이를 위해서 MSF는 다음과 같은 방법을 제안한다.


◆ 각 구현 단위와 의사소통 단위, 낮은 등급의 프로세스 단위 등의 조직을 이용해 큰 팀을 작은 팀으로 분할한다.

◆ 다른 팀들을 이끌 수 있는 한 팀을 지정한다.

◆ 핵심이 되는 한 팀이 전체 프로젝트를 관리하도록 한다.


이렇듯이 MSF의 팀 모델은 프로젝트를 수행하기 위한 조직 구성의 모든 해법을 갖고 있다. 프로젝트 관리 경험이 있거나 CMM을 접해본 독자라면 이러한 조직 구성이 프로젝트를 성공적으로 이끄는 데 결정적인 역할을 한다는 것을 실감하고 있을 것이다. MSF의 팀 모델은 이 외의 팀 조직을 위한 다른 많은 요소들을 포함한다.

산출물 리스트만 따른다고 개발 방법론을 준수하는 건 아니다
앞서 살펴봤던 내용은 MSF의 Team Model의 20% 정도에 지나지 않는다. MSF는 팀 모델 이외에도 위험 관리, 프로젝트 라이프 사이클의 결정, 프로젝트를 시작하는 데 있어서 지켜야 할 사항, 프로젝트를 기획하는 방법, 솔루션을 개발하는 방법 등에 대한 모든 명세들을 총망라한다. MSF는 CMM에 못지 않을 정도의 규모와 상세를 갖고 있다.

MSF는 이러한 규약을 100% 준수하지는 못하더라도 비슷한 조직을 구성할 수 있는 능력이 있는 프로젝트팀에 적용될 수 있다. 사람들 모아놓고 MSF/CD라고 말한다고 무조건 MSF/CD대로 프로젝트를 진행할 수 있는 것은 아니라는 뜻이다.

MSF는 프로젝트의 진행에 필요한 모든 조건을 명시한다. MSF가 개발 방법론이 아니라 개발 프레임워크라고 불리는 이유가 여기에 있다. 개발 프레임워크는 개발 방법론을 적용할 때 그 기반이 되는 개발의 기본 방법들의 정의라고 할 수 있다. MSF를 파악하고 그 내용대로 조직을 정비하고 모델을 따를 수 없는 조직이라면 당연히 MSF 기반으로 만들어진 MSF/CD, MSF/AD 등을 개발 방법론으로 채택하는 것이 어려워지기 마련이다.

사용자 삽입 이미지

<그림 5> MSF Process Model


필자도 예전에 래쇼날 로즈 교육을 수강한 적이 있었는데, 객체지향 프로그래밍 교육이라기보다는 도구 사용법에 대한 교육이었다는 느낌이었다. 강사는 노트북에 준비해온 입력 산출물을 입력하고 산출물이 자동으로 작성되며 코드를 생성하는 시뮬레이션을 시연해줬다. 수강하는 학생들이 모두 감동했음은 자명하다.

하지만 문제는 툴의 성능이 아니다. 한 카피가 웬만한 고급 승용차 값을 호가하는 소프트웨어가 그 정도의 성능을 갖고 있는 것은 당연하다. 아무리 훌륭한 자동차를 가졌다 하더라도 운전을 못한다면 비용만 낭비한 셈이 된다. 훌륭한 도구를 갖고 있다면 그 도구를 훌륭하게 사용할 수 있는 능력이 있어야 한다.

그렇다면 MSF/CD는?
MSF/CD는 CBD가 솔루션 개발의 대세가 되는 무렵 마이크로소프트가 MSF에 따라 구성된 팀 모델과 프로세스 모델을 사용하며, 여러 MSF가 제시하는 방법들을 따라 컴포넌트 기반 솔루션을 구축하고자 할 때 사용하는 방법이다. 국내에 출간된 MSF/CD 서적을 봐도 MSF/CD의 장점은 프로토타이핑을 통한 빠른 개발 진척도 지원이라고 한다.

또한 MSF/CD는 다른 개발 방법론(예를 들면 RUP)들이 놓치고 있는 물리적인 설계를 잘 지원하도록 구성되어 있으므로 빠르고 정확한 개발을 진행할 수 있는 닷넷 기반 솔루션 개발에 가장 알맞은 방법이라고 MSF/CD를 소개한다.

하지만 그 한권의 책을 읽었다고 MSF/CD대로 프로젝트를 진행할 수 있는 것은 결단코 아니다. MSF/CD는 MSF를 소화할 수 있는 조직이 되어야 제대로 진행할 수 있다. MSF/CD의 산출물 리스트를 봐도 RUP에서 권장하는 산출물 리스트들과는 거리가 멀다. Use Case Diagram은 작성하지도 않도록 되어 있다.

조직의 구성이 트리 구조로 되어 있는(트리 형태의 계층 구조로 조직된) 프로젝트 팀에서는 팀원들간의 의사소통이 원활하지 않다. 또한 한 사람의 PM이 프로젝트를 책임지고 있는 경우라면 MSF/CD를 적용해 개발하는 것은 오히려 개발을 지연시키는 결과를 초래할 수 있다. MSF에는 4가지의 방법론이 포함되어 있다.


[1] MSF/AD(Application Development)
[2] MSF/CD(Component Design)
[3] MSF/ID(Infrastructure Deployment)
[4] MSF/EA(Enterprise Architecture)


4가지 설계 방법론과 개발 방법론은 모두 MSF를 기반으로 한다는 것을 잊어서는 안 된다. MSF는 2~3명의 소규모 개발팀부터 몇 백명의 대규모 개발팀까지를 커버할 수 있는 프레임워크라고 하는 것은 MSF를 잘 이해하고 이행하는 데서 비롯되는 것이다. 산출물 리스트만을 외우고 있다고 MSF가 되는 것은 아니다.

<그림 5>에서 볼 수 있는 MSF Process Model 또한 MSF/CD를 기반으로 개발할 때 프로세스 진행의 기반이 된다. MSF를 제대로 파악하지 못하고서는 MSF/CD를 제대로 사용할 수 없음은 당연하다. MSF의 여러 모델을 파악하지 못한 조직은 MSF/CD를 사용하는 것이 오히려 프로젝트가 실패하는 원인이 될 수 있다는 것이다.

MSF/CD가 영원할 것인가?
한 마디로 말하자면 “결코 그렇지 않다”라는 것이다. MSF/CD의 설계 원칙은 Event Driven이다. Workflow Process Model도 이벤트에 따라 작성되고 객체 상호 작용 또한 UP의 Sequence Diagram에서 익숙하던 시간의 흐름이나 객체의 상태 변화를 통해 나타내는 것이 아닌 이벤트의 발생에 따라 기술된다. MSF/CD는 컴포넌트의 작성과 상호작용에 너무 치중됐으므로 유연한 객체 구조를 작성하는 것은 오로지 개발자의 능력에 의존하는 결과를 낳기 쉽다.

MSF/CD는 MSF에 따라 마일스톤 소프트웨어의 작성이 우선되어야만 프로젝트 진행 과정에서 발생할 수 있는 위험 부담을 줄이고 일정을 지킬 수 있는 구조로 되어 있다. MSF/CD의 산출물 리스트대로 그림만 그리다가는 제대로 된 소프트웨어의 작성이 거의 불가능해지는 방법이 바로 MSF/CD이다.

여러 가지 이유로 MSF는 그리 오래 버틸 수는 없을 것 같아 보인다. CMM이 거의 표준이 되다시피 했고, MSF의 팀 모델이나 프로세스 모델이 CMM/CMI의 그것과 닮아 있지만 수정되어야만 할 것이다. 또한 닷넷은 전형적인 객체지향 개발 모델이므로 이벤트 지향적인 방법으로 설계하는 MSF/CD는 닷넷 개발에 어울리지 않는다는 것이 필자의 생각이다.

필자 역시 MSF/CD를 따라 프로젝트를 진행해 본 경험이 있지만, MSF/CD에서는 OOAD를 제대로 수행할 수 없고, 필자가 몸담은 조직을 MSF대로 재구성할 수도 없는 형편이라 객체지향 디자인시에는 개발자 개개인의 능력에 크게 의존할 수밖에 없었다. 다행이라면 마일스톤 작성을 통해 위험요소를 제거하며 프로젝트를 진행할 수 있었다는 것이 100% 성공적이지는 못하지만 그럭저럭 실패하지는 않을 수 있었던 이유였던 것 같다.

마이크로소프트가 다른 개발 방법론을 준비하고 있다는 소식이 여기저기서 들려오기 시작한다. 공식적인 채널을 통해서는 듣지 못했지만 마이크로소프트가 MSF를 포기한다는 것이 개발자들 사이에서 널리 퍼지고 있다. MSF/CD를 잘 알고 프로젝트 관리 이론과 UML 등을 잘 아는 개발자라면 누구나가 예측했을 법한 이야기일 것이다. 비주얼 스튜디오 2005를 셋업해보고 예제 프로그램을 몇 개 작성하다 보면 누구나가 신선한 충격을 느끼게 될 것이다.

프로젝트 관리에도 기본이 있다
필자는 C#을 교육할 때 교육 시간이 100시간이라면 추상화를 교육하는 데 60시간 이상을 할애한다. C#이라는 언어는 어떻게 변할지 모르지만 객체지향 이론과 추상화 이론, 타입 이론 등을 잘 알고 있다면 언어가 어떻게 변하든지 잘 적응할 수 있기 때문이다.

필자의 경우 C#이라는 프로그래밍 언어는 프로그램을 작성하기 위한 도구일 뿐, 프로그램을 작성하는 데는 추상화라는 도구가 가장 강력하고, 튼튼한 이론적 뒷받침이 좋은 프로그램을 작성할 수 있는 가장 바른 방법이라고 믿어 의심하지 않는다.

CMI와 SPICE를 공부해보면 프로젝트를 관리하는 기법에 있어서 MSF와 동일한 이론이 뒷받침되어 작성됐다는 것을 알 수 있다. 훌륭한 개발자가 되기 위해서는 튼튼한 이론적인 뒷받침이 되어야 함이 틀림없듯이 훌륭한 프로젝트 관리자가 되기 위해서는 프로젝트 관리 기법에 대한 튼튼한 이론적인 뒷받침이 있어야 한다.

전문 개발자나 프로젝트 관리 전문가가 되기보다는 Visio나 로즈, 파워포인트의 활용 전문가가 되고 싶다면 같은 개발 방법론(아마도 필자가 좋아하는 Nogada Technology 개발 방법론일 것이다)으로 산출물의 형태만 바꾸며 프로젝트를 진행해도 별 상관없다. 하지만 정말 성공적인 프로젝트를 진행하고 싶으면 프로젝트 진행 방법론을 잘 이해해야 한다.

또한 진행하는 프로젝트가 조선이나 건축 프로젝트가 아닌 IT 솔루션 프로젝트라면 프로젝트 관리 능력에 필적할 만한 IT 기술도 갖고 있어야 한다. 프로젝트 관리라는 산은 멀고도 험하기만 하다. @

* 이 기사는 ZDNet Korea의 자매지인 마이크로소프트웨어에 게재된 내용입니다.

"Development" 카테고리의 다른 글
  • [닷넷 프로젝트를 정복하라] ④ 개발 방법론, 제대... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ③ 더이상「꼼수」는... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ② PM 김씨의 좌충우돌기 (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ① 이래서 안되는거군! (0)2007/06/09
  • 소스 코드 공유를 위한 패턴 (0)2007/05/17
2007/06/09 19:04 2007/06/09 19:04
Posted by webdizen
Tags .NET, 프로젝트
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3045

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Development2007/06/09 18:54

[닷넷 프로젝트를 정복하라] ③ 더이상「꼼수」는 없다

김상훈 (동명정보대학교 정보기술원)   2004/11/23 
    

연재순서
1회. 이래서 안되는거군!
2회. PM 김씨의 좌충우돌기
3회. 더이상 꼼수는 없다
4회. MSF/CD에 대해 고민해야 하는 이유 <끝>

필자는 3년 전쯤 프란시스 포드 코폴라 감독의 ‘지옥의 묵시록 리덕스(Apocalypse Now Redux)’라는 영화를 본 적이 있다. 1979년에 만들어진 영화를 재편집해 3시간이 넘는 긴 영화로 만들어 재개봉한 이 영화는, 영화를 전공하지 않은 필자로서는 잘 모르겠지만, 볼 때는 지겨운 부분이 있긴 해도 보고나면 뭔가 남는 것이 있는(조금은 당황스럽지만) 좋은 영화였다.

필자는 객체지향 프로그래밍을 강의할 때 영화 대본의 예를 들어 객체지향 프로그래밍을 설명하곤 한다. 객체지향 프로그래밍을 하다 보면 객체(클래스가 아닌)를 정하는 일은 영화나 연극 작업에서 배역을 정하는 일과 거의 비슷하고, 지문, 소품, 대화 등을 객체의 상태, 메시지, 데이터로 생각하면 영화 시나리오를 쓰는 일과 프로그래밍을 위한 설계 작업이 거의 비슷하기 때문이다.

영화와 소프트웨어의 비슷한 점
객체지향적으로 문제를 분석하는 과정이 정형화되지 않은 사용자의 요구를 대본으로 옮겨 쓰면서 어떤 배역이 있는지를 분석하는 과정이고, 영화 대본이 배역(객체)끼리의 대화(메시지)이므로, 객체지향 프로그래밍을 위한 문제를 분석하는 과정(정규화하는 과정)은 소프트웨어의 개발 절차상 가장 시간이 오래 걸리는 작업이라고 볼 때, 정말 영화와 소프트웨어 사이에는 비슷한 점이 너무 많다.

사용자 삽입 이미지

<그림 1> 영화 작업과 흡사한 OOP


영화를 만드는 과정의 차이
한 편의 영화가 만들어져서 영화관에서 보게 되는 그 영화를 ‘프로그램’이라고 한다면, 영화를 만드는 과정, 즉 ‘영화 만들기’가 바로 ‘프로젝트’가 된다. 세계에서 영화를 가장 많이 제작하는 할리우드에는 바로 그 ‘영화 만들기’에 대한 엄청난 노하우가 있을 것이고 정형화된 과정도 있다. 필자의 개인적인 생각으로는 한국영화가 할리우드를 뛰어넘기 가장 어려운 부분이 바로 이런 부분이 아닐지 싶다. 얼마 전 ‘태극기 휘날리며’라는 영화를 보고 느낀 점은 “한국영화가 정말 많이 좋아졌구나”가 아니고 엉뚱하게도 “스티븐 스필버그가 영화를 과연 잘 만드는구나”였다.

필자의 그런 이야기를 들으면 사람들은 어쩔 수 없는 ‘제작비의 문제’라고 이야기를 하지만 그런 말에 동의할 수 없는 이유가 있다. 필자가 알기로 ‘태극기 휘날리며’의 제작비는 120억원이 넘었고(달러로 환산하면 1000만 달러가 넘는 큰 액수이다) 스필버그의 ‘라이언 일병 구하기’의 제작비는 6000만 달러라고 들었다. 주연 배우인 톰 행크스가 받은 출연료가 2000만 달러 남짓했었고, 국내 영화 스탭들의 인건비는 할리우드 스탭들의 수입과는 비교도 안 되는 수준이라면 할리우드 예산 6000만 달러와 한국 영화 예산 120억원은 거의 비슷한 수준으로 생각해도 되는 것 아닐까?

그렇다면 ‘태극기 휘날리며’를 만든 감독이 스필버그와 비교했을 때 그렇게 역량이 많이 떨어진다고는 생각할 수 없고, ‘태극기 휘날리며’에 출연한 배우들의 연기도 훌륭했고, 촬영기술도 ‘라이언 일병 구하기’의 초반 30분에 보여주는 카메라 흔들기와 개각도 촬영의 노하우를 아는 사람들이 비슷한 기법으로 촬영했다고 볼 때, 어떤 부분이 ‘라이언 일병 구하기’와 ‘태극기 휘날리며’의 차이를 만들 이유였는가를 생각해보면 ‘영화를 만드는 과정’이 어떠했느냐가 가장 큰 요소였다고 생각할 수밖에 없어진다.

‘태극기 휘날리며’라는 최종 산출물을 만들기 위한 프로젝트가 어떻게 진행되었는지 필자는 잘 알지 못하지만, 그 프로젝트가 잘 진행되었더라면 제작비를 조금 더 절감하면서 더 좋은 영화를 만들 수 있지 않았을까 하는 것이 필자의 견해이다. 영화를 좋아하고 영화에 대한 지식이 필자보다 월등한 독자들이 훨씬 많을 것이므로 영화에 대한 이야기는 여기까지만 하도록 한다(필자는 영화를 좋아하는 사람일 뿐, 영화 관계자가 아니고 IT 컨설턴트이므로 영화 지식의 무지에서 비롯된 여러 오류들은 널리 이해해 주리라 믿는다).

영화 이야기를 하는 이유
IT 프로젝트를 성공적으로 진행하는 방법에 관한 글에서 이렇게 영화 이야기를 장황하게 늘어놓는 이유는 필자가 10년쯤 전 서점에서 구입해 읽었던 한 영화 관련 서적에서 다음과 같은 글귀를 읽은 적이 있기 때문이다.


“감독은 영화를 완성하지 않는다. 적당한 시점에서 영화 만들기를 포기하는 것이다.”


이 말은 영화 ‘지옥의 묵시록’을 만들기 위해서 개인 재산을 모두 저당 잡히고 영화 만드는 과정에서 지게 된 빚을 갚기 위해 억지로 ‘대부 2’를 만들었던 프란시스 포드 코폴라 감독이 ‘지옥의 묵시록’을 만들면서 했던 말이다. 영화를 책임지는 사람은 바로 감독이고, IT 프로젝트를 책임지는 사람은 바로 PM이다. 영화 산업은 IT 산업과 비교해 훨씬 긴 역사를 가지고 있으므로 영화 만들기 내에서 세분화된 직책들로 구성되어 있다.

IT 프로젝트 역시 마찬가지다. 프로젝트를 성공적으로 이끌기 위해서는 세분화된 직책들이 구성되어 있어야 한다. 공들여 만든 영화가 비용과 상관없이 ‘벤허’가 되느냐 ‘조폭마누라’가 되느냐는 프로젝트의 진행에 달려 있다고 해도 과언이 아니다. 물론 영화계에는 오슨 웰즈나 쿠엔틴 타란티노와 같은 사람이 있어서 특별한 프로젝트의 관리 없이도 위대한 작품을 만들어내는 사람들이 있다.

IT 업계에도 찰스 시모나이나 빌 조이 같은 사람들이 있다. 하지만 ‘전함 포템킨’이나 ‘시민 케인’과 같은 영화보다는 ‘진주만’이나 ‘스파이더 맨’과 같은 영화들이 훨씬 많은 돈을 벌어들였다. IT에서도 그렇다. ‘불후의 걸작 프로그램’을 만들어 내는 것은 GNU나 Jakarta 같은 곳에 맡기는 것으로도 충분하다. SI 프로젝트에 투입되는 경우라면 독불장군이 되어 ‘시민 케인’을 만들기보다는 영화의 한 부분의 충실한 스텝이 되어 ‘스타워즈’를 만들어내는 것이 산업적인 측면에서 훨씬 더 올바른 경우임에 틀림없다.

왜 프로젝트 관리를 해야 하는가?
단순하게 조선소에서 배를 짓는 일에 대해서 생각해보자. 배는 인간이 만들어낼 수 있는 인공 건조물 중에 가장 큰 규모의 것이고 이런 어마어마한 규모와 예산이 투입되는 일을 프로젝트 단위의 계획 없이 진행하는 것은 불가능하다. 대우중공업이나 현대중공업 같은 대규모의 조선소에 가면 수만 명의 직원이 근무하고 있고, 수만 명의 직원이 수십 대의 배를 동시에 건조한다.

설계 부서에서 하루에 작도되는 도면의 양만 해도 어마어마하고, 건조 부서에서 하루에 생산되는 블럭의 무게만 해도 수만 톤을 넘는다. 조선소라는 공간이 제 아무리 넓다 하지만(실제로 대우중공업이나 현대중공업은 작은 시 규모의 면적을 가지고 있다) 제한된 공간을 가지고 있게 마련이고, 웬만한 아파트 한개 동 크기의 선박 블럭을 놓을 수 있는 장소에는 한계가 있다.

만약 설계 부서에서 도면을 제 시간에 작도해 건조 부서로 제 시간에 전달했지만, 건조 부서에서 지정한 시간 내에 도면대로 블럭을 완성하지 못했다면 다른 블럭을 건조하기 위한 장소의 확보가 어려워지고, 한 척의 배를 만드는 일정의 지연으로 인해 여러 척의 선박 건조 일정에 차질을 빚게 된다. 또한 설계 부서에서 며칠까지 도면 작업을 완료했다 하더라도 완료된 도면으로 블럭을 작업할 장소가 건조 부서의 인력이 없다면 설계와 건조가 탄력적으로 운영될 수 없게 된다.

이런 일정 조율은 설계 부서의 인원이나 건조 부서의 인원들이 전화 또는 회의로 정확하게 맞춰 진행될 수 있는 일이 아니다. 하물며 선박의 건조에는 설계 부서 또는 건조 부서뿐만 아닌 회계 부서, 자재 부서 등 수없이 많은 부서의 인원들이 동원되고, 동원되는 모든 인원과 자재 등의 자원(resource)이 유기적으로 결합되어 탄력적으로 운영되어야 한다. 투입되는 자원의 활용은 각 부서가 할 일이 아니고 각 부서를 유기적으로 결합해 탄력적으로 운영하는 관리 부서가 따로 필요하게 된다.

‘선택사항’이 아닌 ‘생존의 필수 요건’
Davidson Frame 박사는 그의 저서 “Managing Project in Organization”에서 다음과 같은 말을 했다.
“불확실성 시대의 생존 열쇠는 ‘탄력성’이며 어떤 변화에도 대처할 수 있는 프로젝트 마인드는 바로 ‘탄력성의 열쇠’이다. 이것은 이제 ‘선택사항’이 아닌 ‘생존의 필수 요건’이다.”


실제적으로도 조선소 또는 건설회사에서는 이러한 탄력적인 운영이 불가사의할 정도로 잘 운영되고 있다(필자는 조선소에서 근무해 본 적이 있다). 수만 톤의 배를 동시에 50대씩 건조하면서도 큰 불협화음이나 사고 없이 잘 진행된다. 역사가 오래된 조선이나 건설업 같은 경우에는 이러한 프로젝트의 관리 기법에 수백 년 전부터 도입되어 진행되고 있지만 역사가 얼마 되지 않은 IT의 경우에는 ‘프로젝트’라는 개념이 도입되기 시작하는 시점에 있다.

바로 프로젝트 개념의 관리와 가시성이 필요한 시점이 도래했다는 것이다(사실 배를 짓는 입장에서도 종이배를 짓거나 호수 위를 떠다니며 연인들의 사랑을 관리해 주는 나룻배 정도를 짓는 것이라면 프로젝트 관리가 별로 필요하지 않다. IT 프로젝트에서 프로젝트 관리의 중요성이 대두된 시점은 대 기업들이 회사의 관리에 IT를 도입하면서 시작되었는데, 이는 EJB나 닷넷과 같은 기업형 개발 시스템이 도입되는 시기와 일치한다). 프로젝트 관리에는 다음과 같은 개념들이 도입되어야 하고 이미 실천되고 있다.


[1] PMIS(Project Management Information System)
[2] EPM(EnterPrise management System)
[3] PPM(Project Portfolio System)
[4] EIS(Executive Management System)


프로젝트란 유일한 제품, 서비스, 프로세스, 결과, 계획을 생성하기 위해 수행되는 일시적인 행동이나 노력(A Project is a temporary endeavor undertaken to create a unique product, service, process, result, plan)을 말한다.

사용자 삽입 이미지

<그림 2> 프로젝트


프로젝트라는 것이 앞에서 이야기한 것이라면 프로젝트 관리라는 것은 다음과 같이 설명된다.

“프로젝트 요구사항 (Requirement)을 만족시키기 위하여 프로젝트 활동에 관련된 지식(Knowledge), 기술(Skill), 도구(Tools) 및 기법(Technology) 등을 적용하여 수행하는 제반 관리 활동”


프로젝트 관리를 위해서는 무엇이 필요한가?
프로젝트 관리를 위해서는 무엇이 필요할까? 한마디로 이야기하면 프로젝트 관리 조직(PMO: Project Management Office/PSO: Project Support Office)이 필요하다. PM이 제 아무리 능력이 뛰어난 사람이라도 유기적으로 다른 조직과 융합되어야 하는 프로젝트의 모든 부분을 감당할 수는 없다.

최근의 프로젝트들은 예전의 목표 지향적 관리보다는 미래 지향적 관리 기법이 필요하다. 예전의 목표 지향적 관리 기법이었다면 PM이 원가와 일정, 품질 관리를 통합하는 업무만으로 충분히 이룰 수 있었지만, 프로젝트의 규모가 커지고 탄력적으로 운영되어야 하는 현 시점에서는 목표 지향적 관리 기법이 그렇게 올바른 방법이 아님이 증명되었다.

사용자 삽입 이미지

<그림 3> 목표 지향적 관리와 미래 지향적 관리


<그림 3>에는 PMBOK(Project Management Body of Knowledge)라는 용어가 등장한다. 이는 미국 프로젝트 관리 협회인 PMI(Project Management Institute)에서 정의한 프로젝트 관리 분야의 범용적인 표준 체계를 말한다. PMBOK에 대한 내용은 책 여러 권의 분량으로도 부족하므로 관심 있는 독자는 PMBOK에 관련된 책을 구해서 읽어보기를 권한다.

전사적 입장에서 보는 프로젝트 관리를 위하여 CMMI를 도입하고자 한다면 한 사람의 PM이 감당할 수 없는 여러 일들을 해야 한다. 프로젝트 관리에서 이야기하는 프로그램이라는 것이 흔히 개발자들이 이야기하는 소프트웨어 프로그램이 아닌 서로 관련 있는 프로젝트들이 유기적으로 관리되는 프로젝트들의 그룹을 이야기하므로, 기업의 프로젝트(KFP나 ERP 같은 큰)를 탄력적이고 유기적으로 이끌어 가기 위한 다른 조직이 필요하게 된다. 그 조직이 바로 직접적으로 수익을 창출하는 조직이 아닌 조직들의 프로젝트를 관리하고 기업의 표준화를 이끌어나가는 조직인 PMO가 된다. 어떻게 보면 필요 없는 PMO 조직을 다른 부서에서 먹여 살리는 것 같은 느낌이 들지만 50인 이상의 조직에서는 반드시 필요한 조직이 된다.

PM 김씨의 고민 이야기
PM 김씨는 ERP 프로젝트를 2004년 9월에 시작해야 한다. 김씨는 얼마 전 마이크로소프트에서 개최한 세미나에 참석했었고, 마이크로소프트가 진행하고 있는 엄청나게 많은 제품에 기가 질려 돌아왔다. 특히 김씨를 힘들게 하는 것은 닷넷 프레임워크 2.0과 비주얼 스튜디오 2005에 관련된 새로운 정보였다. 김씨는 세미나에 참석해서 받은 비주얼 스튜디오 닷넷 2005를 컴퓨터에 셋업하고는 기가 질렸다. 이전 버전과의 호환성을 유지할 수 없는 것은 아니지만 새로운 개체가 엄청나게 많이 등장했다.

MDAC 9.0이 과거 2.7에서 이렇게 한 방에 버전업이 될 수 있는 건지 아니면 오타인지에 대한 구분도 가지 않는 상태이고, C#에는 Generic이 들어가서 이전 버전의 C#보다 훨씬 많은 프로그래밍 지식을 요구하고, 웹 프로젝트의 Code-Behind 방식은 사라졌다(김씨는 개발자가 아닌 PM이라 다른 업무도 많은 상태에서 사라진 것인지 숨은 것인지 알아내는 시간 투자가 부담스럽다).

닷넷은 자바처럼 여러 업체가 주도하는 기술이 아닌 마이크로소프트라는 한 기업이 주도하는 기술이고, 그런 이유로 기술의 변화가 급격하다. 자바는 이미 산업에서 많이 쓰이고 있으므로 자바 5가 발표되었다고 그 즉시 자바 5를 사용해야 되는 경우는 드물지만, 이제 도입하는 업체가 늘어나는 상황인 닷넷의 경우 새로 시작하는 프로젝트는 최신 버전을 사용해야 함이 당연하다(김씨의 경험으로는 기술의 설명 같은 것은 클라이언트들이 들어주지도 않는다. 클라이언트들은 최신 기술이 무조건 좋은 기술인 줄로만 안다. 전산을 조금 안다는 클라이언트일수록 이 현상은 더욱 심각하다).

어떤 버전으로 진행해야 할까?
팀원이 그렇게 많지 않은 조직에 근무하는 김씨는 고민에 싸이게 되었다. 기존의 1.1 버전으로 프로젝트를 시작할 것인지, 내년 3월이면 발표된다는 2.0 버전으로 프로젝트를 시작해야 할 것인지에 대한 고민이 우선 클 뿐더러, 2.0으로 결정했다 해도 2.0에 포함된 기술들에 익숙하지 않은 팀원들의 교육 문제는 어떻게 해결할 것이며, 산출물의 형태는 어떤 식으로 변경되어야 하고, 회사의 기존 프로젝트 산출물들을 어떻게 새 버전에 맞추어 개량해야 할 것인지의 문제에 직면했다.

기업에 PMO 조직이 있다면 김씨의 이런 고민은 말끔히 해결된다. PMO 조직이 김씨의 고민거리를 몽땅 맡아주기 때문이다. CMM에 따라 정비된 조직이라면 다음과 같은 프로젝트 오피스 조직들을 운영하고 있을 것이다.


[1] PSO(Project Support Office)
[2] PMO(Project Management Office)
[3] PMO(Program Management Office)


이와 같은 프로젝트 오피스 조직들은 다음과 같은 업무들을 맡아서 진행한다.


[1] 훈련/교육, 소프트웨어 도입 및 표준 템플리트 등의 형식으로 프로젝트 매니저에게 지원 기능을 제공한다.
[2] 일부 실제적인 프로젝트 결과를 책임진다.
[3] 표준 템플리트는 절차서, 범위 명세서, WBS, 액티비티에 대한 전반적인 일정 내용을 지칭한다.
[4] 프로젝트 관리 지침 및 프로젝트 헌장 등의 각종 절차서를 개발하고 제공한다.
[5] 회사의 PMIS를 구축한다.


PMO 조직은 예상 문제점을 사전에 파악해 분석하고, 이에 대한 대응조치를 제공해 프로젝트가 실패할 확률을 줄여줄 뿐만 아니라 팀원들의 프로젝트 목표의식을 분명히 해줌으로써 CMP(Communication Management Plan)를 명확하게 하고 이해 관계자간의 간섭 사항을 고려해 업무 수행을 원활하게 해준다. 절차를 명확하게 명시해 줌으로써 논리적이고 체계적인 업무 처리를 할 수 있게 해줌은 물론이다.

완벽하지 않다고 해서 실패한 프로젝트는 아니다. 하지만 실제 상황을 이야기해 보자. CMM, 물론 좋은 이야기다. CMMI, SPICE 모두 다 좋은 이야기이긴 하지만 하청의 하청인 영세 업체에서는 꿈꾸기조차 어려운 이야기이다. CMMI 레벨을 획득하기 위한 비용이 만만치 않을 뿐더러 PMP를 두 명 이상 유지하는 것은 현재 회사의 규모로 볼 때 만만한 일이 아니다. 영세 업체의 입장에서 볼 때 PMO 조직을 운영하는 것 자체가 무리이고, 비용 관리에서 일정 관리, 팀원의 교육까지 모두 PM이 책임져야 하는 상황이라면 모든 일을 PM이 다 하는 것은 불가능한 일이 된다. 이런 경우 적당한 시점에서 타협해야 한다.


[1] 프로젝트의 전반적인 방향에 대해 파악하는 데 있어 기존의 자료를 최대한 활용한다.
1차 또는 2차 하청 업체이거나 또는 전체 프로젝트의 특정 모듈만을 작성해야 하는 경우라도 프로젝트의 전반적인 방향이나 목적에 대한 파악 없이는 불가능하다. 의욕적인 PM이나 개발자의 경우 모든 파악을 능동적으로 구성하려 하지만 시간적이거나 인력적인 여건에서 불가능한 경우가 대부분이다. 이런 경우 최대한 클라이언트 또는 원청 업체를 졸라서 최대한 많은 리소스를 확보하고 참조하는 것이 좋다.

[2] 프로젝트 헌장을 정확하게 파악한다.
프로젝트 헌장이라는 것은 프로젝트의 존재를 알리는 공식적인 문서를 의미한다. 이 문서에는 사업의 요구사항, 제품 기술서, PM이 자원을 사용할 수 있는 권한에 이르기까지의 사항들이 기술된다. 프로젝트 헌장이 작성되기 전에 PM으로 임명됐다면 다른 이야기가 될 테지만, 그렇지 않다면 헌장을 정확하게 파악해 자신이 해야 할 일과 해서는 안 되는 일에 대한 파악이 무엇보다 중요하다.

[3] WBS 리스트를 작성하고 시작한다.
개발이 끝나고 나면 실제 동작하는 프로그램보다 산출물이 훨씬 더 중요시되는 경우가 대부분이다. 특정 개발방법론을 선택하고 개발에 착수했을 경우 작성되는 WBS의 분량은 실로 엄청나다. 투입되는 개발자의 대부분이 프로그램을 작성하는 것보다 산출물을 작성하는 것이 더 어렵고 시간이 많이 소요된다고 이야기한다. 프로젝트를 관리하는 사람의 입장에서는 팀원들의 요구를 잘 파악하는 것이 중요하고 필요 없는 산출물을 작성하고 있는 것은 오히려 프로젝트 진행의 발목을 잡는 일이 된다. WBS 리스트는 클라이언트 또는 원청 업체와 상의해 필요 없을 것 같은 산출물은 아예 작성하지 않는 것이 좋다.

[4] 일정을 작성할 때는 비 프로젝트 휴가를 반드시 포함한다.
일정을 작성할 때 휴일을 고려하는 것은 당연하다. 비 프로젝트 휴가를 포함하라는 말은 예상치 않은 곳에서 발생할 수 있는 결원 가능성에 대해서 반드시 고려하라는 말이다. 어떤 모듈이 내일까지 완성되는 줄로 알고 있었다가 담당 개발자가 예비군 훈련 등의 일로 인해서 2~3일씩 지연되는 경우가 많다. 기업간의 프로젝트는 일정으로 이야기되는 경우가 대부분이어서 예기치 못한 일정 지연으로 낭패를 당하는 경우가 허다하다. 일정에 플러스마이너스 15 정도의 여유를 두는 것이 오히려 진행 속도를 빠르게 하는 결과를 가져온다.


실패하지 않은 포기를 위한 몇 가지 충고
프로젝트를 처음 시작할 때는 누구나가 야심찬 계획을 세우게 된다. 산출물도 바로 작성하고 포트폴리오도 정리하고 코드도 예술 같은 코드를 써서 인쇄해 벽에 걸어놓고 싶은 마음이 들게끔 하고 싶은 것이 당연하다. 하지만 그런 야심찬 계획들은 대부분 진행 도중에 난관을 만나게 된다.

난관에 부닥치면 결정의 기로에 서게 되는데, 난관을 타개하고 애초 생각한 모습대로 진행하느냐 또는 적절한 시점에서 타협해 몇 가지를 포기하느냐 하는 결정을 내려야 한다. 대부분의 경우 적절한 시점에서 타협하게 된다. 그리고 그 타협이 돌아가는 프로그램을 만들게 되고 프로그래머를 과로사의 위협에서 구해낸다.

한마디 덧붙여 이야기하면, 프란시스 포드 코폴라는 악몽과도 같았던 프로젝트 ‘지옥의 묵시록’으로 칸 영화제 황금 종려상을 수행했고, 1979년 아카데미 시상식 8개 부분에 노미네이트되어 두 개 부분을 수상했다. 완벽하지 않지만 실패하지 않은 포기를 위한 몇 가지 충고를 하면 다음과 같다.


[1] 팀원들을 ‘No Man’으로 만들라.
클라이언트와 팀원들 사이의 대화 통로를 아예 봉쇄하는 것이 가장 바람직한 길이라고 극단적으로 말하고 싶다. 하지만 그렇다면 PM이 할 일이 너무 많아지게 되므로 적당한 대화 수단을 제공하되, 클라이언트의 요구사항을 팀원이 절대로 “해드리겠습니다”라고 말하지 않게 훈련시키는 것이 중요하다. 프로젝트 요구사항에 포함되지 않은 어떤 요구를 구두로라도 승락하게 되면 구두 계약이 되어 프로젝트의 분량이 엄청나게 늘어나는 경우를 종종 봐왔다. 다음 버전업에서 해야 할 작업을 팀원의 “예” 한마디 때문에 모듈을 모두 수정하는 경우도 가끔은 있다.

[2] 프로젝트 매니저는 프로젝트에 직접 참여하지 말라.
프로젝트 매니저가 의욕이 넘치는 사람이고, 개발 기술을 잘 아는 사람이라면 직접 프로젝트에 참여하여 개발 일부분을 담당하는 경우가 있다. 개발을 담당한다는 것은 프로젝트 관리 업무를 포기하겠다는 말과도 같다. 프로그램 코드를 작성하는 업무와 프로젝트를 관리하는 업무는 본질적으로 틀려서 얽힌 코드를 푸는 작업을 하다가 일정 관리 작업으로 다시 돌아오는 데는 시간이 제법 걸리게 된다. 프로젝트 매니저가 일정을 작성하지 않으면 개발자들이 적당히 알아서 다음 프로그램을 작성할 것이라는 생각은 버리는 것이 좋다. 일정이 비게 되면 개발자들은 100% 논다.

[3] 제대로 알지 못하는 기술을 사용하는 프로젝트에는 참여하지 말라.
통계에 의하면 1시간을 강의하기 위해서는 3시간을 공부해야 한다고 한다(재판 판례에 따른 이야기다). 물론 강의와 개발은 엄연히 다른 이야기이지만 새로운 기술을 익혀서 개발에 직접 이용한다는 것은 많은 노력과 시간을 요구한다. 프로젝트의 성패 여부는 일정 관리와 시간을 어떻게 사용하느냐에 있다고 봐도 과언이 아니므로 팀원의 교육과 훈련 등을 맡아서 관리해주는 조직 또는 사람이 따로 있지 않은 프로젝트 관리자의 입장에서는 프로젝트를 맡지 않는 것이 경력에도 도움이 되고 회사의 입장에서도 이익이 되는 경우가 훨씬 더 많다. ‘따고 보자’의 시대는 지나갔다.

[4] 두 마리 토끼는 두 마리 호랑이가 쫓아야 한다.
두 마리 토끼를 쫓다가 저녁을 굶는 호랑이 이야기를 많이 듣고 살지만, IT 일처럼 이런 경우가 딱 들어맞는 경우가 드물다. “이것도 하고 저것도 한다”는 것은 관리자 또는 개발자의 업무를 두 배로 늘이겠다는 이야기 밖에는 되지 않는다. 특히 한국 개발자들의 특성이 하루 만에 할 수 있는 일이라면 밤샘하면 할 수 있는 일을 생각한다는 것은 큰 병폐 중의 하나가 아닐 수 없다. 물론 일정이 빡빡하면 사흘쯤 밤샘하는 것은 당연한 일(?)이라고 생각하는 것도 좋지만 과중한 업무를 감당하는 기간이 길어지게 되면 업무에 대한 집중도가 떨어지고 실수를 많이 하게 된다. 특히 디버깅 기간 중에 실수를 저지른다면 감당할 수 없는 일이 발생할 수도 있다. 한 마리 토끼를 착실히 쫒아서 반드시 잡는 것이 프로젝트를 성공하는 가장 큰 방법이 된다.

지극히 상식적인 프로젝트의 수행
결혼정보회사에 근무하는 친구의 말을 들으니 3년 전쯤에는 프로그래머라는 직업의 선호도가 아주 높았는데, 날이 갈수록 떨어지고 있다고 한다. 이유인 즉 급여 수준에 비해서 하는 일이 너무 많아 가정적이지 않고 전체적으로 성격들이 완벽주의자라 피곤한 성격의 소유자들이 많고, 또한 꼼꼼하고 나쁘게 말하면 째째하게까지 보이는 사람들이 많아서라고 한다.

프로젝트를 시작했으면 완벽하게 마무리하고 싶은 것이 개발자들과 관리자들의 본성이지만 당연히 완벽하게 마무리되는 경우는 드물다. 특히 자존심 강한 엔지니어들의 세계에서는 누구나가 완벽하게 프로젝트를 마무리짓고 싶어 하지만 적당한 시점에서 욕심을 다스리는 것이 프로젝트를 성공적으로 이끄는 요인이 될 수도 있다.

이번 연재에서는 프로젝트를 수행하는 데 있어 책임을 회피하거나 작업량을 줄일 수 있는 방법, 즉 ‘꼼수(?)를 권장하는 듯한 냄새가 풍기는 듯하다. 하지만 이번 연재는 어디까지나 100점 만점에 95점을 기준으로 했을 때라는 것을 알아주었으면 하다. 일정을 지키기 위해 산출물의 내용과 프로그램의 내용이 따로 노는 프로젝트라도 성공한 프로젝트라고 말하는 것은 아니기 때문이다. 모 CF의 내용처럼 ‘상식에서’ 이야기했을 때의 적당한 시점을 이야기 하는 것이기 때문이다.

하지만 이런 ‘꼼수’가 통하지 않는 시대가 가까운 미래에 오게 될 것 같다. 미국에서는 2006년부터 CMMI 레벨 3을 획득하지 못한 기업은 국책 프로젝트에 참여할 수 없다는 법률이 정해졌고 국내에서도 그런 움직임이 활발하다. 하지만 우리나라 사람이 항상 그렇듯이 언제나 빠져나갈 구멍은 있게 마련이고 그 빠져나갈 구멍의 한도에서 병폐도 생길 것이 확실하다. 프로젝트를 성공적으로 이끄는 방법은 ‘지극히 상식적인 프로젝트의 수행’에서 리소스를 잘 파악해 적당한 시점에 적정 리소스를 투입하는 데 있다. @

* 이 기사는 ZDNet Korea의 자매지인 마이크로소프트웨어에 게재된 내용입니다.

"Development" 카테고리의 다른 글
  • [닷넷 프로젝트를 정복하라] ④ 개발 방법론, 제대... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ③ 더이상「꼼수」는... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ② PM 김씨의 좌충우돌기 (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ① 이래서 안되는거군! (0)2007/06/09
  • 소스 코드 공유를 위한 패턴 (0)2007/05/17
2007/06/09 18:54 2007/06/09 18:54
Posted by webdizen
Tags .NET, 프로젝트
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3044

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Development2007/06/09 18:41

[닷넷 프로젝트를 정복하라] ② PM 김씨의 좌충우돌기

김상훈 (동명정보대학교 정보기술원 연구원)   2004/10/25 
    

연재순서
1회. 이래서 안되는거군!
2회. PM 김씨의 좌충우돌기
3회. 더이상 꼼수는 없다
4회. MSF/CD에 대해 고민해야 하는 이유 <끝>

김씨는 대학기관에서 근무하며 강의를 한다. 어느 날 김씨는 닷넷 기반의 프로그램을 작성하는 업체와 산학협력 프로젝트로 연계해 수행하는 프로젝트의 PM을 맡기로 결정됐다. 원래 일 욕심이 많은 사람이라 누군가 일을 시키면 ‘안 한다, 못 하겠다’라는 말을 하지 못하는 사람이며, 이번에도 평소에 하던 업무도 적지 않은데 프로젝트의 PM까지 맡기로 했다. 김씨는 하루에 8시간으로 예정되어 있는 강의를 진행하면서, 프로젝트 진행 상황을 점검하고 관리와 개발까지 하기로 했다. 특히 김씨가 근무하는 기관이 산학협력으로 프로젝트를 진행하는 건 처음이었고, 프로젝트의 진행은 한 마디로 ‘좌충우돌’이었다. 이번 연재에서는 김씨가 프로젝트 경험을 통해 성공적인 ‘프로젝트를 이끌 수 있는 방법’에 대해서 알아본다.

계약 내용을 명확하게 알아야 한다
진행되는 프로젝트는 시작부터가 복잡했다. 프로젝트의 책임자가 두 번이나 바뀌고, 우여곡절 끝에 김씨가 프로젝트를 책임지게 됐고 사공이 많은 탓에 배는 산으로 갔다. 프로젝트는 애초에 윈도우 플랫폼에서 동작하는 CMS로 계획됐다. 김씨는 다이렉트X 9.0 SDK를 이용하고 XML을 사용해 컨텐츠를 검색하는 식의 동작을 하는 소프트웨어를 계획했다. 하지만 프로젝트를 시작하기 전 CMS 프로젝트는 나중에 시간 나면(?) 하는 프로젝트로 변경되고, 당장 진행해야 되는 프로젝트는 처음 계획했던 프로젝트와는 전혀 상관없는 닷넷 기반의 웹 응용 프로그램으로 변경됐다. 김씨가 처음 들어본 서울의 모 업체와 산학협력 프로젝트로 진행하기로 됐고, 김씨는 협정의 내용을 정확하게 알지 못했다. 협정서에는 단순히 ‘시제품의 개발을 김씨가 근무하는 기관에서 책임진다’는 내용만을 포함하고 있었다.

김씨는 나중에 협정서를 다시 읽어봤지만, 협정서에는 틀림없이 ‘시제품의 개발’이라고 명시돼 있었다. 김씨는 협정서를 작성한 팀과의 회의를 통해 시제품이라는 것의 범위를 명확하게 할 필요가 있다고 이야기했고, 협정서를 작성한 팀에서는 그 ‘시제품’이라는 것은 마일스톤 버전 소프트웨어를 의미한다고 이야기했다. 김씨는 시제품이라는 것이 프로토 타입도 아닌 마일스톤 소프트웨어라는 것이 조금 의아하기는 했지만, 개발팀과 의논해 시스템이 닷넷 플랫폼에서 동작할 때 고려될 수 있는 위험요소가 제거된 마일스톤 버전의 소프트웨어를 개발하기로 하고 일정을 작성했다. 김씨는 마일스톤 버전을 정하고, 3단계까지의 마일스톤(사용자 단위의 보안이 적용되는 여러 컴포넌트 기반 소프트웨어의 작성에 있어 프로젝트 위험 요소가 제거된 버전) 버전을 2개월 동안 진행하는 것으로 일정을 작성했다. 김씨는 엔터프라이즈 디자인 패턴들을 적용하고, Front Controller를 사용하지는 않지만 차후에 완전한 프레임워크 기반으로의 발전을 위해 MVC 패턴을 적용하도록 하고, 데이터베이스와의 접속에 있어 동시 접속자 기준의 커넥션 풀링에서의 안전을 지켜낼 수 있는 마일스톤 버전 소프트웨어의 개발을 진행했다.

사용자 삽입 이미지

<표 1> 김씨가 작성한 마일스톤 버전 리스트


프로젝트는 그럭저럭 순조롭게 진행됐고, 김씨는 꽤 만족할 만한 사용자 인증 프레임워크를 개발해 인증 프레임워크를 사용하는 라이브러리를 작성하는 형식으로 진행했다. 김씨는 실제로 동작하는 소프트웨어를 작성하는 프로젝트라기보다 연구 과제 프로젝트를 수행하듯이 프로젝트를 수행했고, 계획대로만 진행된다면 최초의 쓸만한 닷넷 기반 프레임워크가 개발될 것 같아 흡족해 했다.

2차 마일스톤 버전을 끝내고 결과에 만족하고 있을 무렵, 협력 업체의 사장이 격려차 부산에 내려왔고, 당연히 뒤따르는 회식에서 여러 이야기를 나누던 도중 동석하신 부장이 다음과 같은 말을 꺼냈다.


“개발이 끝나면 QA는 어떻게 해주나요?”


QA? 품질 보증? 마일스톤 소프트웨어에도 품질 보증이 필요한가? 김씨는 가만히 생각해보니 마일스톤 소프트웨어에도 품질 보증이 필요할 것도 같았다. 김씨가 세운 계획대로 프로젝트가 순조로이 진행되어 예측할 수 있는 대부분의 위험요소가 제거됐다고 해도, 김씨 혼자서 위험요소가 제거됐다고 주장하는 것 밖에 안 될 수 있다는 생각이 들었다. 김씨는 위험요소가 제거됐고 소프트웨어가 제대로 조립됐다면 조건을 만족하는 하드웨어에서는 제대로 된 동작을 보증할 수 있다고 이야기했다.


“소프트웨어가 제대로 동작할 거라고는 말씀을 드릴 수 있죠. 그런데 소프트웨어를 조립하는 건 우리가 하는 일이 아니고 귀사에서 하는 일이라서…. 필드에서 동작하는 소프트웨어에 대한 QA는 제가 담당할 일이 아니지 않은가요?”


그러자 이번엔 부장님께서 의아해했다. 뭔가 다른 내용으로 이야기하고 있다는 듯한 표정이었다.


“조립이라뇨? 시제품을 개발하기로 했잖아요?”
“그렇죠. 협약서에는 시제품이라고 명시돼 있던데요.”
“시제품을 조립할 필요가 있나요?”
“마일스톤 소프트웨어를 그냥 사용할 수는 없잖아요”
“?”


김씨는 이쯤에서 뭔가 잘못됐다고 느꼈다. 이상한데? 협정서의 내용이 잘못됐나? 아니면 협정을 체결한 팀과 업체간의 의사소통에 무슨 문제가 있었나? 김씨는 거기에서 이야기를 마무리짓고 다음 날 대외협력팀과 회의를 가졌다. 회의 결과 마일스톤 버전 소프트웨어 개발 이야기는 우리끼리 이야기였고, 업체에서는 완전히 동작하는 소프트웨어를 개발해 준다는 의미로 받아들였다고 결론을 내렸다. 기간이 꽤 지나서 알게 된 사실이었기 때문에 김씨는 일정을 급하게 재조정할 수밖에 없었다. 일정을 재조정하고, 우선 보기 좋으라고 붙이던 디자인을 필드에서 동작했을 때 손색이 없는 디자인으로 고쳐나가기 시작했다. 처음에 계획했던 일정은 완전히 틀어졌다.

일주일쯤 후 업체에서 받은 메일에는 5장 짜리 요구사항 분석서가 첨부돼 있었고, 요구사항 분석서는 특정한 개발 방법론이나 모델링 규칙에 따라 작성된 요구사항의 정의가 아닌 시스템이 필드에서 동작하기 위한 각 클라이언트의 요구사항을 항목별로 맵핑한 엑셀 문서가 전부였다.

김씨가 프로젝트를 진행하며 계획했던 야심찬 꿈은 온데 간데 없어졌다. 김씨는 며칠 남지 않은 일정을 조정하여 항목 하나하나를 구현하는 데 힘을 기울였다. 다행히 설계된 프레임워크와 라이브러리를 조합하면 되는 일이어서 일정이 많이 지연되지는 않았고, 프로젝트는 그럭저럭 굴러갔다.

프로젝트를 처음 관리해 보는 김씨는 큰 교훈을 얻었다. 근무하는 회사가 CMM 레벨을 보유하고 있고, PMO 조직이 구성돼 있어서 프로젝트를 수행하기 전 요구사항들을 파악해서 투입되는 경우가 아니라면 감히 PM이라는 직함을 달아서는 안 되는 것이고, 혹시라도 그런 식으로 프로젝트를 관리하게 된다면 다른 사람의 말을 듣고 적당히 짐작해서는 안 되는 일이다. 프로젝트의 PM이라면 프로젝트에 관계된 모든 사항들과 일정들을 꼼꼼하게 정리하고 체크하는 것이 관건이고, 그러자면 세심한 주의가 필요하며 웬만하면 몇 가지 일을 겹쳐가면서 프로젝트를 관리하겠다는 생각은 버려야 한다.

그리고 또 한 가지. 김씨가 좋아하는 외화 시리즈 ‘X-파일’에 나오는 말인데, 프로젝트를 진행하며 명심해야 할 두 마디 격언. 진실은 저 너머에 있고(Truth is out there), 프로젝트를 순조롭게 진행하고 싶으면 아무도 믿어서는 안 된다(Trust no one). 프로젝트의 진행은 개발자/회사/PM간의 대화로 진행되는 것이 아니고, 작성되는 문서로 진행된다. 오버한다 싶을 정도로 명확히 대화하고 문서로 꼼꼼이 작성하고 보관하는 것이 성공적인 프로젝트를 진행하는 길이다.

협업하는 개발자와 업체간 용어를 통일하라
아주 당연한 이야기인 것 같지만 용어가 통일되지 않으면 프로젝트가 순조롭게 진행되지 않는다. PM 김씨는 프로젝트를 진행하면서 시스템이 그렇게 큰 규모가 아니기 때문에 특별히 용어를 정리할 필요가 없다고 생각했다. 인증 프레임워크의 개발은 김씨 혼자서 진행했고, 여러 가지 추상화 기법을 동원해 공통화하고 캡슐화했기 때문에 프레임워크를 기반으로 하는 라이브러리를 개발할 때 특별한 용어에 대한 정리가 필요 없을 거라고 생각했다. 하지만 라이브러리를 작성하고 프로토타이핑을 할 때, 김씨는 용어를 정리하지 않은 것이 아주 중대한 실수였음을 깨달았다.

교수 관련 라이브러리를 작성하는 개발자 윤군에게는 “한 학기 동안 진행되는 강의”가 Course였고, 학생 관련 라이브러리를 작성하는 개발자 길군에게는 “한 학기 동안 진행되는 강의”가 Lecture였다(갑자기 윤군과 길군이 왜 나왔는지 궁금한 사람은 지난 호를 찾아보기 바란다). 데이터베이스 모델링은 김씨가 했고, 대학과 교육기관에서 강의 하는 것이 주 업무인 김씨는 한 학기 강의는 Course, 한 강의중의 각각의 수업은 Lecture라는 이름의 테이블을 만들고 1:N 관계의 테이블들로 모델링했다. 테스트에 필요한 데이터는 삽입하지 않은 상태였고, 따라서 윤군의 라이브러리에서 강의 리스트를 보여주는 프로시저는 Course 테이블에서 데이터를 select하고, 길군의 라이브러리에서 강의 리스트를 보여주는 프로시저는 Lecture 테이블에서 데이터를 select했다. 각 라이브러리를 따로 떼놓고 보면 강의 계획서를 작성하고 수정하는 로직을 포함하는 윤군의 라이브러리는 제대로 동작하고 각 강의에 대한 계획을 보기만 하면 되는 길군의 라이브러리 또한 제대로 동작했다.

사용자 삽입 이미지

<그림 1> 용어 정리가 제대로 되지 않음으로 오동작하는 라이브러리


테스트 데이터가 삽입되어 있지 않기 때문에 보여지는 데이터가 정확한 데이터인지 아닌지를 판단할 수도 없었다. 두 라이브러리를 조립하고 테스트 데이터를 삽입한 후 테스트 관련 라이브러리를 조립했을 때, 김씨는 페이지에 표시되는 데이터가 이상하다는 것을 느꼈다. 출석부도 제대로 동작하지 않았고, 조립하는 라이브러리의 모든 데이터는 상황에 맞지 않은 데이터를 출력했다.

김씨는 밤을 새워가며 이유를 찾았고 결국은 길군의 라이브러리가 Course 테이블에 아닌 Lecture 테이블에서 데이터를 패치한다는 것을 찾아냈다. 길군의 라이브러리를 수정하는 데는 꼬박 3일이라는 시간이 걸렸고, 김씨의 일정표에서 학생 라이브러리에 대한 FS는 1일 밖에 주어지지 않았으므로, 전체적으로 2일이라는 일정이 미뤄지는 결과를 가져왔다.

길군이 프로그램을 잘못 작성했다고 볼 수 없는 것이(길군은 객관적으로도 꽤 뛰어난 닷넷 개발자다), 요구사항 분석은 협력 업체에서 맡기로 협정이 되어 있었고, 김씨는 아무런 요구사항을 전달 받지 못한 채 프로젝트에 착수 했다. 모든 다 요구사항을 3명의 개발자가 다 파악하기에는 시간이 턱 없이 부족했다. 김씨는 궁여지책으로 개발 기간에 요구사항 분석 기간을 포함시켰고, 라이브러리에 포함되는 프로시저들을 조각조각 내어 차후에 분석되는 요구사항을 쉽게 반영할 수 있도록 작성하자는 방침을 세웠다. 그리고 모든 객체들은 Abstract Factory 패턴을 사용하기로 하고, 관련 객체들은 모두 공통 타입으로 이끌어 내어 추상화해 차후에 다른 객체가 추가되더라도 리플렉션(reflection)을 사용해 라이브러리를 사용할 수 있도록 한다는 궁여지책을 내놓았다. 하지만 궁여지책은 궁여지책일 뿐, 요구사항이 완전히 정리되지 않은 상태에서 작성된 소프트웨어는 제대로 동작할 리 없다.

부랴부랴 김씨는 밤을 새워가며 Model Dictionary를 작성했다. 모든 용어를 다 정리하기에는 시간이 모자랐고, 결국은 혼동을 일으킬 여지가 있는 용어들만 정리했다. 김씨는 나흘간 두 시간 취침해 일을 했고, 이틀 동안 혼수상태에 빠져 프로젝트 일정은 지연될 수밖에 없었다.

사용자 삽입 이미지

<그림 2> Model Dictionary


앞에서 이야기했듯이 용어의 정리는 개발자 사이에서나 PM 등의 내부 인력에서만 필요한 것이 아니다. 애초에 협정서를 작성하던 단계에 있어서 협정서에 표시된 시제품이라는 용어의 정리만 명확했었다면 프로젝트는 좀 더 원활하게 진행될 수 있었을 것이다. 김씨가 처음 듣기에 시제품이라는 말은 웬지 ‘Prototype’이라는 말 같이 들렸고, 협력 업체에서는 ‘’판매 가능한 제품‘이라고 인식했다. 프로젝트에 관계하는 모든 사람들이 최근의 소프트웨어 공학 지식을 다 알고 있을 리는 만무하고 마일스톤이라는 말을 누군가가 꺼낸다면 모른다고 말하기도 그렇고, 뜻이 뭔지는 모르겠고 해서 적당히 자기 생각대로 해석해서 진행하게 되는 경우가 많다.

프로젝트를 진행하기 위해서는 용어의 정리가 반드시 필요하고, 그 용어는 혼자만 알아서 되는 것이 아니라 프로젝트에 참여하는 모든 사람과 모든 업체들의 공통된 동의가 필요하고, 모든 사람이 다 알 수 있는 공통적인 용어를 사용하는 것이 중요하다. 추상화라는 것이 프로시저나 클래스를 작성할 때만 적용할 수 있는 기법이 아니고, 프로젝트를 관리하고 진행하는데 있어서도 강력하게 적용될 수 있는 기법이다. 그리고 중요한 또 한 가지, 용어를 정리하고 모두가 용어에 동의했으면 반드시 문서로 남기고 확인을 받아야 한다. 김씨과 같은 기관에 근무하는 모 선배 개발자가 다음과 같은 감동적인 말을 한 적이 있다.


“강사는 강의로 말을 한다.”
그렇다면 “프로젝트는 문서로 말을 한다.”


개발자들의 수준을 파악하라
요구사항이 정확히 정리되지 않은 상태에서 프로젝트를 진행한 김씨는 궁여지책을 하나 내놓았다. Abstract Factory 패턴을 사용해 차후에 추가될 수 있는 기능들을 라이브러리로 분리하고, 리플렉션을 사용해 라이브러리를 동적으로 로드할 수 있는 기법을 사용하기로 했다. 개발자들도 모두 동의하고 파악된 요구사항들을 기준으로 프로그램을 작성했다. 프로젝트가 그렇게 진행되던 도중 급기야 라이브러리를 동적으로 로딩해야 되는 경우가 발생했다(사실 새 요구사항이 발생하면 라이브러리의 소스를 수정하고 다시 컴파일했지 새로 발견되는 요구사항 때문에 리플렉션을 사용하는 경우는 드물다).

크리스탈 리포트의 사용을 위해서 데이터셋을 생성하고, 형식화된 데이터셋을 사용해야 하는데, 형식화된 데이터셋을 사용하는 가장 좋은 방법은 데이터셋 객체를 직접 생성하는 것이 아니고 닷넷 프레임워크 SDK의 XSD.EXE 툴을 사용해서 형식화된 데이터셋을 생성해낸 후, 데이터 셋을 채우는 방법이다. 하지만 프레임워크에서 사용하는 데이터베이스 에이전트 라이브러리는 단순 래퍼일 뿐 형식화된 데이터셋을 생성할 수 있는 방법이 없었다. 여기서 김씨가 생각해 낸 방법이 Factory Method 패턴을 데이터베이스 에이전트에서 사용하여 XSD.EXE를 사용해서 생성한 클래스를 라이브러리로 만들고, 매개변수에 따라서 라이브러리를 동적으로 로딩하게 하자는 것이었다.

김씨는 개발자들에게 데이터베이스 에이전트의 수정을 맡기고 다른 일을 진행했다. 하루가 지난 후 수정을 맡은 개발자가 부시시한 얼굴로 내려와서는 질문을 했다.


“C# 디자인 패턴 책에는 안 나와서 자바 디자인 패턴을 봤는데요, 자바에서 클래스가 뭐하는 놈입니까?”
“객체의 타입을 스트링으로 받아서 객체의 인스턴스를 생성하는 놈인데 닷넷에서는 리플렉션을 사용하면 됩니다.”
“저… 리플렉션이 뭡니까?”


이럴 수가. 김씨는 당연히 개발자들이 리플렉션 정도는 사용할 수 있는 거라고 생각했다. 리플렉션을 모른다면 Activator도 당연히 모를 테고, Type 객체를 사용할 수가 없게 된다. 그러면 의도된 대로 프로그램을 작성할 수 없게 된다. 그렇다고 데이터베이스 에이전트의 수정을 김씨가 할 수도 없었다.

결국 김씨는 개발자들을 모아놓고 저녁 11시에 시작해 새벽 3시까지 닷넷 프레임워크에서 어셈블리를 조작할 수 있는 개체들의 집합인 리플렉션을 강의할 수밖에 없었다. 아예 처음부터 김씨가 작성하기로 했다거나 아니면 다른 방법을 택했다면 또는 유스 케이스를 아예 포기하는 것이 현명한 선택이었을 수도 있었다.

PMO 조직이 있는 큰 조직에서의 프로젝트라면 이러한 위험 요소는 모두 파악이 되어 교육을 실시하거나 다른 방법을 택하는 프로세스가 있을 테지만, 불행히도 3인의 개발자가 진행하는 4개월짜리 프로젝트에 PMO 조직이 있을 리가 없다. 프로젝트 관리 기법을 적용하는 데도 영세 프로젝트나 소규모 프로젝트에서는 한계가 있다. 김씨는 이점을 간과했었다. 소규모 프로젝트를 진행하고 있다면 프로젝트를 관리하는 사람의 입장에서는 개발자들의 수준을 파악하는 것이 무엇보다 중요한 일이 될 수 있다.

신기술, 어려운 기술만이 능사가 아니다
우여곡절 끝에 프로젝트는 종료됐고, 종료된 프로젝트의 소스코드와 설계 산출 문서들을 협력 업체에 전달하고 몇 시간 정도 구조를 설명한 후 김씨는 한 숨 돌리고 다른 방향으로 진행할 준비를 했다. 그런데 며칠 후 협력업체에서 받은 연락으로는 김씨가 작성한 소프트웨어가 못쓸 수준의 소프트웨어라는 것이었다.

자신의 기술에 꽤 자부심을 느끼고 있는 김씨로서는 도저히 용납 못할 일이었다. 이유인 즉 전달한 요구사항에서 10% 정도 밖에 구현되지 않은 소프트웨어로 납품하는 것은 불가능하다는 것이었다. 한 마디로 말하면 1000원 짜리 소프트웨어를 만들 줄로만 알았는데 100원짜리 소프트웨어를 만들었다는 것이었다. 김씨는 이해할 수가 없는 것이 김씨의 기관에서 특정 클라이언트에 납품할 수 있는 소프트웨어를 작성하기로 한 것도 아니었고, 프로토타입으로 작성한 웹 응용 프로그램에서 보여지는 동작이 전체 요구사항에서 10% 정도인 것이지, 프레임워크나 라이브러리의 동작으로 볼 때 80% 정도는 구현되어 있었기 때문이다. 김씨가 만든 소프트웨어 게시판은 다음과 같이 동작한다.

사용자 삽입 이미지

<그림 3> 게시판 동작

사용자 삽입 이미지

<표 2> 협력업체에서 전달받은 문서


구현 퍼센트에 대한 판단이 김씨의 기준에서는 프레임워크나 라이브러리에서 구현돼 있는지의 여부였고, 협력업체의 판단은 요구한 문서에서의 O표로 표기됐는지의 여부였다. 김씨의 기준으로 본다면 70%가 맞고, 협력업체의 기준으로 본다면 10%가 꼭 틀리다고는 할 수 없는 일이었다.

흥분한 김씨는 협력업체의 개발자들과 가진 회의에서, 표에서 X로 표기된 부분을 페이지로 작성해서 협력업체에 기능 구현 여부를 10%에서 70%까지 올리는 데는 사흘 정도면 충분하다는 것을 보여주기 위해 시스템을 가져다 놓고 시연을 했다. X 다섯 개가 O으로 바뀌는 데는 불과 10분도 걸리지 않았고, 피차간에 궁색한 변명만 하게 되는 경우가 발생했다.

협력업체에서는 한 클라이언트와 계약을 해서 2개월 후 프로그램을 납품해야 하는데, 김씨가 만든 프로그램을 사용할 수 없겠다는 의사를 전했다. 이유는 프레임워크 방식으로 개발해 본 경험이 있는 사용자가 없고, 프레임워크를 이해하고 라이브러리를 파악하는 데 소요되는 시간보다 협력업체의 개발자들이 처음 짜는 것이 납품 기일을 맞추는 데 유리할 거라는 것이었다. 그 의견에는 다들 동의했다.

김씨는 아무 말도 하지 않았다. 소프트웨어 산업이 어떻게 되려고 저 모양이냐는 생각이 들긴 했지만, 구태여 그런 말로 분위기를 망치고 싶지는 않았다. 모든 요구사항이 반영된 프레임워크 기반의 소프트웨어라면 패키지 형태의 배포가 가능하고, 공들여 한번 작성해 놓으면 다음부터는 클라이언트의 요구사항에 따라 조립해서 판매만 하면 될 텐데 솔루션을 판매하는 업체가 SI식의 개발을 고집하는 것이 답답하기만 했다. 차라리 김씨도 프레임워크 기반 소프트웨어가 아닌 다이렉트 액세스 방식의 소프트웨어를 페이지 단위로 개발해서 500페이지 정도의 뷰를 갖는 소프트웨어를 개발했더라면 개발자들이 이해하기도 쉬웠을 테고 협정대로 납품되어 동작하는 소프트웨어를 만들 수 있었을 거라는 생각도 했다. 하지만 김씨는 그런 방식으로 소프트웨어를 개발할 생각은 추호도 없다. 혹시 SI 일을 하게 된다면 모를 일이지만.

새로운 기술이나 신기술 또는 어려운 기술만을 사용해서 프로그램을 작성하는 것이 반드시 능사는 아니라는 결론을 내릴 수도 있을 것 같지만 김씨는 그렇게는 생각하지 않는다. 문제는 계약의 문제였다. 잘 작성된 문서는 잘 개발된 소프트웨어만큼이나 중요하다는 것을 명심하라.

김씨는 협력업체에 소프트웨어를 넘기면서 소스코드와 다이어그램, ER-D 등 몇 장의 문서만을 넘겨줬을 뿐 제대로 된 조립 매뉴얼이나 라이브러리 문서 등은 작성하지도 않았고 넘겨주지도 않았다. 김씨가 라이브러리를 작성한 것은 10%가 아님을 증명하기 위한 문서 작성이 전부였고, 프로젝트가 종료된 지금도 김씨는 문서를 완전히 작성하지 않았다.

김씨의 궁색한 변명에 따르면 협력업체의 개발자와 이야기할 때 구조를 설명했고, 협력업체의 개발자는 충분히 이해하겠노라고 이야기했다는 것이다. 개발에 참여한 개발자 중의 한명이 협력업체에 직원으로 채용됐으므로 라이브러리의 내용은 다 파악하고 있는데 굳이 개발 방법이나 라이브러리 문서, 매뉴얼 등은 작성할 필요가 없었다고 판단했기 때문이라고 한다. 하지만 협력업체의 개발자는 구조와 라이브러리의 조립법을 제대로 이해하지 못한 것 같았고, 협력업체에 직원으로 고용된 개발자는 다른 업무에 투입됐으므로 소프트웨어를 제대로 이해하고 사용할 수 있는 사용자는 드물었다. 개발자들 대부분이 이번에 비주얼 베이직 또는 ASP 개발자 출신이어서 객체지향 프로그래밍이나 CBD를 제대로 알지 못하는 상황에서 정리된 문서의 부재가 가져오는 개발의 어려움은 엄청난 것이었다. 개발자들의 수준은 둘째라고 하더라도 제대로 된 문서를 작성하고 소프트웨어와 함께 전달됐더라면, 좀 더 나은 프로젝트의 진행을 볼 수 있었을 것이다.

프로젝트는 적당한 시점에서 포기하는 것?
프로젝트를 진행하면서 가장 중요한 요소는 성질대로 되지 않는다는 것을 몸으로 이해해야 한다는 것이다. 김씨는 부산 출신 남자답게 성격이 괄괄하고 다혈질이다. 요구사항을 파악해 주기로 한 날짜가 지나도 협력업체에서 아무 말도 없자(당연히 협력업체에 연락해서 좋게 풀어야 할 상황임에도 불구하고) 프로젝트를 임의대로 진행했다. 그것이 프로젝트가 원활하게 진행되지 않은 가장 큰 이유인데, 프로젝트가 소프트웨어를 작성하기 위한 것이라고 해도 결국은 사람 대 사람의 일이다. 일이 뜻대로 진행되지 않아서 기분이 상하거나 성질을 돋운다고 해도 성질을 참고 원활하게 일을 풀어나가는 것이 성공적인 프로젝트를 진행하는 가장 지름길이 되지 않을까 싶다.

그리고 프로젝트를 진행할 때는 no man이 되어야 하는 것은 무엇이든 중요하다. 기관 대 기관 또는 업체 대 업체 사이에서의 Yes는 구두 계약이 된다. 무심코 얻어먹은 점심 한 끼로 ‘Yes’라고 대답했다가는 개발자 자신뿐만 아니라 개발자가 몸담은 기관까지 타격이 전해지는 수가 있다. 조심에 조심, 조심을 거듭해야 한다.

어떤 영화감독은 “영화는 완성되는 것이 아니다. 감독이 적당한 시점에서 영화를 포기할 뿐이다”라고 말했는데, 프로젝트의 진행도 이와 일맥상통하는 점이 있다. 세상에 완벽한 프로그램이라는 것은 존재하지 않는다. 개발자와 프로그램 매니저가 지정된 기간과 지정된 비용에 맞게 적당한 시점에서 작성을 포기하는 것이 바로 프로젝트다. @

"Development" 카테고리의 다른 글
  • [닷넷 프로젝트를 정복하라] ④ 개발 방법론, 제대... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ③ 더이상「꼼수」는... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ② PM 김씨의 좌충우돌기 (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ① 이래서 안되는거군! (0)2007/06/09
  • 소스 코드 공유를 위한 패턴 (0)2007/05/17
2007/06/09 18:41 2007/06/09 18:41
Posted by webdizen
Tags .NET, 프로젝트
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3043

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Development2007/06/09 18:03

[닷넷 프로젝트를 정복하라] ① 이래서 안되는거군!

김상훈 (필자) ( 마이크로소프트웨어 )   2004/10/11  


연재순서
1회. 이래서 안되는거군!
2회. PM 김씨의 좌충우돌기
3회. 더이상 꼼수는 없다
4회. MSF/CD에 대해 고민해야 하는 이유 <끝>

프로젝트를 처음 시작한다는 것은 개발자의 입장으로서 참 가슴 설레는 일이 아닐 수 없다. 프로젝트를 시작할 때는 누구나가 다 원대한 목표를 가지고 시작하게 된다. 하지만 프로젝트 종료 날짜가 다가오면 애초의 원대한 목표는 간 곳이 없고 누가 이 코드들을 보고 욕이나 하지 않을까 걱정하게 되는 경우가 허다하다. 물론 가장 큰 이유는 개발 플랫폼을 잘 이해하지 못했다거나 기술을 제대로 사용할 수 있을 만큼 숙달되지 않아서 그런 경우겠지만, 그런 기술적인 요소 외에 다른 엉뚱한 돌발 상황이 발생해 프로젝트의 발목을 잡는 경우가 허다하다. 이번 연재에서는 프로젝트를 진행할 때 고려해야 하는 부분에 대해 생각해 보기로 하겠다.

김씨가 근무하는 대학의 기관에서는 닷넷 기반에서 동작하는 중간 규모 정도의 프로그램을 작성하는 프로젝트를 서울의 한 업체와 산학협력 프로젝트로 연계해 수행하기로 결정했다. 그리고 닷넷 기술을 4년 동안 강의했고, 닷넷 기술로 여러 컨설팅을 수행한 경험이 있는 김씨에게 프로젝트가 맡겨졌다. 김씨는 최근에 수행되는 여러 닷넷 프로젝트가 ‘닷넷답게’ 수행되지 않는다며 한탄하던 사람 중 한 사람이어서 자신이 수행하게 된 이번 프로젝트를 정말 ‘닷넷 프로젝트의 모범이 될 수 있는’ 레퍼런스급 소프트웨어로 만들어보자는 결심을 하고 프로젝트를 수행하게 된다(DVD로 치면 ‘반지의 제왕’급의 소프트웨어를 만들어보자는 결심).

그동안 강의를 위해, 또는 다른 목적을 위해 공부하고 실험했던 기술들을 제대로 한번 써먹을 수 있는 기회가 왔음을 반가워하며 프로젝트에 써먹을 수 있는 기술들을 정리하고 개발에 착수할 준비를 했다. 김씨는 전체 소프트웨어의 보안을 관리하는 모듈(보안 모듈)의 구성대로 보안 등급을 관리하는 모듈, 보안 등급에 따라 구성되는 게시판 모듈, 시험 모듈, 강의실 모듈 등의 설계를 머리 속에 그리며 실제 개발을 담당할 개발자들과 토의를 시작으로 개발에 착수했다.

윤군의 개발 이야기 - 프로젝트는 결국 노가다인가?
대학에서 컴퓨터공학을 전공하고 소규모 프로젝트에 여러 번 참여한 경력이 있는 윤군은 프로젝트 매니저(PM) 김씨의 아이디어를 주의 깊게 들었다. 프리젠테이션 자료 준비도 하고, 빔 프로젝트까지 빌려서 브리핑해 주는 김씨의 열정에도 감동을 받았다. 여러 프로젝트에 참여를 했었어도 ‘몇 페이지’를 작성하느냐가 관건이었던 개발 업무에서 벗어나 제대로 된 프레임워크 기반에서의 개발을 해볼 수 있겠다는 기대에 마음이 부풀었다. 대학 4학년때 꽤 잘 가르친다는 교육기관에서 1년간 닷넷을 배우기도 한 윤군은 공부한 기술들을 제대로 써 먹을 수 있는 프로젝트에 참여할 수 있겠다는 희망에 부풀었다. 윤군이 들은 PM 김씨의 계획은 이러했다.

[1] 전체 시스템을 파악한 결과 시스템에는 여섯 단계의 사용자 인증이 필요하다.
[2] 시스템에는 각 사용자별로 다르게 동작하는 게시판이 여러 개 필요하다.
[3] 설문조사/블로그/메시징 등의 시스템이 필요하다.
[4] 전체 시스템에 통용되는 범용 강의실 시스템이 필요하다.
[5] 학사정보를 관리할 수 있는 시스템이 필요하다.
[6] 시험을 온라인으로 실시할 수 있는 시스템이 필요하다.

PM 김씨는 전체 시스템을 완전한 컴포넌트 기반 시스템으로 구성하겠다고 했고, 프로젝트의 진행도 MSF/CD를 제대로 사용해 보겠다고 했다. 실질적인 개발에 착수하기 전, PM 김씨는 윤군에게 <그림 1>과 같은 다이어그램을 보여주었다.

사용자 삽입 이미지

<그림 1> 전체 시스템의 Business Context Diagram


TIIT 프레임워크는 PM 김씨가 개발하겠다고 했다. TIIT 프레임워크 Arda(반지의 제왕을 좋아하는 PM 김씨가 톨킨의 소설 『실마릴리온』을 읽고 감동받아서 붙인 이름이라고 한다)에는 Data Access Block이 포함되어 있고(마이크로소프트의 DAL을 쓰는 것이 어떨까 생각했지만 김씨는 끝까지 고집을 부렸다. 이 시스템은 MS-SQL 서버에서만 동작할 게 아니고 오라클이나 액세스에서도 동작 가능하도록 구성되어야 하기 때문에 마이크로소프트가 제공하는 DAL로는 적당하지 않다는 것이 김씨의 고집이었다. 결국은 김씨의 고집에 따르기로 했다), 전체 웹 응용 프로그램의 설정을 담당하는(Web.Config 파일을 관리하는) 컴포넌트들을 작성할 것이라고 했다.


“이 컴포넌트가 언제 나옵니까?”
“일주일 후!”


윤군은 일주일을 놀기로 했다(?). 윤군이 일주일을 놀아야 하는 이유는 전체 시스템의 어떤 컴포넌트나 페이지라도 보안 인증이 없이는 작성될 수 없기 때문이었다. 일주일 동안 MVC 같은 디자인 패턴들을 공부하면서 시간을 보낸 윤군은, 일주일 후에 김씨가 작성한 보안 인증 컴포넌트의 소스코드와 클래스 다이어그램을 받아보았다. 컴포넌트 이름이 TIIT.Arda.Trainman인데(영화 ‘매트릭스’를 좋아하는 김씨가 3편에 등장하는 현실세계에서 매트릭스로의 인증을 담당하는 프로그램의 이름에서 따온 이름이란다), 컴포넌트는 Visitor 패턴을 응용해 작성되었고, 여섯 가지 타입의 클래스를 정의해서 인스턴스의 타입을 검사해 각각 다른 인증을 보여주는 방식이다. 보여준 클래스 다이어그램은 다음과 같다.

사용자 삽입 이미지

<그림 2> 사용자 인증 컴포넌트의 클래스 다이어그램

그리고 김씨는 작성된 사용자 인증 컴포넌트를 사용해 다중 수준의 보안 등급을 유지하며 동작하는 범용 게시판 컴포넌트의 코드를 보여주었다. 게시판 컴포넌트(이 컴포넌트의 이름은 Hihava인데, 김씨의 여자친구가 ‘미래소년 코난’을 좋아해서 붙인 이름이라고 한다)의 모든 객체들은 IVisitor 인터페이스의 서브 타입인데, IAcceptor 타입의 사용자들이 게시판 객체를 매개변수로 Accept() 메쏘드를 호출하면 각 객체의 Visit 메쏘드가 Acceptor의 타입에 따라 다른 동작을 하여 각 사용자별 기능 제한을 주는 다중 수준의 보안을 유지할 수 있는 소형 프레임워크 구성이라고 한다. 김씨는 이 프레임워크의 이름을 TIIT 프레임워크라고 이름을 붙였다.

사용자 삽입 이미지

<그림 3> 인증 컴포넌트를 사용하는 범용 게시판 컴포넌트의 클래스 다이어그램

윤군은 전에 일하던 개발 회사에서 게시판을 관리하고자 할 때 일반 사용자용 게시판과 관리자용 게시판을 따로 만들었던 기억이 났다. 그 게시판은 일반 사용자/회원/관리자의 세 등급으로 나누어져 있었는데 일반 사용자는 글을 읽을 수만 있고 회원은 글을 쓸 수 있고 관리자는 모든 권한을 다 가지는 형식의 동작을 구성했다. 일반 사용자가 액세스할 수 있는 페이지, 사용자가 로그인하면 사용자의 등급에 따라 다른 페이지로 리다이렉트(redirect)하는 형식으로 여러 페이지를 구성했는데, 김씨의 말대로라면 한 페이지에서 객체의 상호 작용에 따라 모든 동작이 한 페이지에서 구현되는 형태의 프로젝트를 구성할 수 있을 것 같았다.

평소에 디자인 패턴에 관심이 많아 여러 책들을 읽으며 디자인 패턴을 공부한 윤군은 1시간 정도의 설명을 듣고 프레임워크를 이해할 수 있었다. 일주일간 Iterator 패턴과 Visitor 패턴에 대해 조금 더 상세히 공부했던 터라 윤군은 프레임워크의 동작 방식을 완벽하게 이해할 수 있었고, 작성된 프레임워크가 코딩 양과 페이지의 수를 많이 줄여줄 수 있을 것이라고 믿게 되었다.

학사 관리 쪽의 모듈 개발을 맡은 윤군은 TIIT 프레임워크의 보안 인증 시스템을 기반으로 학사 관리 모듈을 작성하기 시작했다. 현재 페이지에 액세스하고 있는 모든 사용자의 등급을 알아내기 위하여 다음과 같은 코드를 삽입하였다.


학사 관리 쪽의 FAQ 페이지를 작성하던 윤군은 FAQ 중 글을 보여주는 페이지를 작성하는데 다음과 같은 몇 줄의 코드만으로 전체 작업이 이루어진다는 것에 감동받았다.



Q&A를 만드는 작업 역시 비슷한 동작으로 가능했다. 다음 코드는 Q&A의 리스트를 보여주는 페이지이다.


고민과 짜증에 한숨 내쉬는 윤군
윤군은 TIIT 프레임워크가 꽤 잘 만들어진 프레임워크라고 생각하며 개발 작업을 계속했다. 그러나 페이지의 수가 늘어나자 윤군은 슬슬 짜증이 나기 시작했다. 모든 페이지마다 저 코드를 꼭 써야만 하나, 페이지에서 저런 코드를 쓰지 않고 단순히 HttpContext만 사용해서 현재 사용자와 사용하는 객체에 대한 동작을 이루게 할 수는 없을까 등등 고민이 늘어만 갔다.

윤군은 PM 김씨와 그 점에 대해서 이야기를 했다. “TIIT 프레임워크가 하는 일이 조금 더 많으면 페이지에서 저런 귀찮은 코드를 일일이 쓰지 않아도 하부 프레임워크가 다 알아서 해줄 수 있는 프레임워크를 구성하는 것이 바람직하지 않겠느냐”고 건의했다. 하지만 PM 김씨는 한마디로 안 된다고 일축했다. 윤군은 다음과 같은 코드로 동작하는 프레임워크를 만들 수 있을 것이라고 이야기했다.


윤군은 닷넷 프레임워크의 HttpModule 객체를 사용하면 충분히 구현할 수 있다고 이야기하며, 프로젝트가 끝나고 배포 단계에 이르렀을 때 좀 더 손쉬운 배포와 쉬운 커스터마이징을 위해서는 복잡하지만 페이지에서의 코드를 줄여주는 이와 같은 방식이 올바를 거라고 이야기했다. 윤군은 자신의 생각이 당연이 바람직하다고 생각했다.

PM 김씨의 말은 이랬다. “현재 TIIT 프레임워크를 설계하고 코드를 작성하고 동작시키는 데만 일주일이 걸렸다. 이와 같은 코드로 동작 가능한 프레임워크를 작성하려면 한 달, 아니 제대로 만들려면 6개월 정도는 소요될 거다. 전체 프로젝트의 기간이 3개월 남짓인데, 프레임워크를 개발하는 데 그만한 시간이 소요되어서는 프로젝트를 기간 내에 끝낼 수가 없게 된다. 현재 프레임워크도 그걸 고려하지 않은 바는 아니지만, HttpModule을 사용해서 구성할 수 있는 Front Controller 프레임워크의 안정성이나 위험 요소가 제거된 상태가 아니기 때문에 기간 내에 끝내야 하는 프로젝트에서 그런 모험을 할 수 없다.”

윤군도 그 말에는 곧 동의했다. 프로젝트가 끝나면 혼자서라도 Front Controller 프레임워크를 작성해 보겠다고 다짐하면서 페이지들을 작성해나갔다. 개발하는 도중에 틈틈이 MVC 모델을 자세히 살펴보고 HttpModule 객체의 활용 가능성을 검토해나갔다. 하지만 작성하는 페이지가 30페이지가 넘어가자 윤군은 또 짜증이 나기 시작했다.

모든 페이지마다 사용자 인증 컴포넌트의 메쏘드를 호출해야 한다는 것이 마음에 들지 않았다. 다른 문제는 디자이너가 작성한 페이지에 프로그램을 붙여나가는 일이었다. 웹 디자이너가 닷넷 프레임워크를 잘 이해하고 있을 리는 만무하고, 웬만한 경력의 개발자도 제대로 이해하지 못하는 MVC2 모델을 고려해 디자인하고 있을 리가 없었다. 예를 들면 페이징이 필요한 모든 데이터 리스트는 DataGrid 컨트롤을 사용해 DataGrid 컨트롤의 페이징 기능을 사용하기로 결정했는데, 디자이너의 디자인은 그렇지 않기 때문이었다.

사용자 삽입 이미지

<그림 4> News 디자인

프로젝트는 결국 노가다야?
닷넷 환경 이전의 ASP였다면 이와 같은 디자인대로 작성하는 것이 별 문제가 되지 않겠지만, 닷넷 환경에서 IDataReader 타입의 Read() 메쏘드를 일일이 호출하며 쓴다는 것은 말도 안 되는 이야기다.

윤군은 Hihava 컴포넌트에서 몇줄의 코드로 전달받은 데이터 소스를 디자인과 같이 보이게 하기 위해서는 HTML과 CSS 기술이 훨씬 중요하다는 것을 깨닫게 된다. 디자인과 같이 페이지를 보이게 하는 것이 분명히 기술적으로 어려운 것은 아닌데 태그 하나 잘못 붙임으로 해서 전체 페이지가 뒤틀려 보이게 되고, 데이터 그리드 컨트롤의 각 행에 점선을 긋기 위해서는 두꺼운 CSS 사전을 뒤져서 스타일 시트에 클래스를 추가해야 함을 깨달았다.

학교나 집에서 연습삼아 작성하던 프로그램에는 디자인이 필요 없었지만, 실제 프로젝트에서는 디자인대로 프로그램을 작성하는 것이 아주 중요하다는 사실을 깨달았다. 그 작업은 기반 컴포넌트를 개발하는 작업보다 훨씬 손이 많이 가고 지겨운, 정말 ‘노가다’성 작업이었다. 윤군은 결국 Front Controller 프레임워크 개발의 꿈을 접으면서 새삼 깨달았다. ‘프로젝트는 결국 노가다일 수밖에 없구나.’

프로젝트를 진행하는데 단순 무식 노동 집약적 작업을 하지 않으려면? 프로젝트에 참여하는 모든 사람들이, 개발자고 디자이너고 PM이고 관계 없이 기반 플랫폼을 잘 이해해야 하고, 잘 만들어진 프레임워크가 필요하다는 것. 프레임워크 기반 작업의 가장 큰 단점은 프레임워크를 학습하는 시간이 많이 소요된다.

TIIT 프레임워크 같은 경우에는 디자인 패턴을 이해하지 못하면 제대로 사용할 수 없는데, 윤군의 주위에는 Visitor 패턴 하나라도 제대로 이해하고 있는 사람이 드물다. 윤군은 PM 김씨가 항상 이야기하는 “대한민국의 닷넷 프로젝트가 제대로 수행되지 못하는 이유”를 이해할 수 있을 것 같았다.

길군의 개발 이야기 - 개발자와 디자이너
PM 김씨, 개발자 윤군과 같이 프로젝트에 참여하고 있는 개발자 길군 또한 프로젝트의 진행 중에 슬슬 짜증이 솟구치기 시작했다. 강의와 교수 쪽의 모듈 개발을 맡은 길군은 설계대로 컴포넌트 개발을 어느 정도 진행한 후 개발 일정에 맞추기 위해 페이지에서 컴포넌트를 사용해 페이지를 만들어내는 작업에 착수했을 때였다. 길군은 디자이너가 작성해놓은 페이지가 이쁘다고는 느끼면서도 개발자의 입장은 전혀 고려하지 않은 디자인이라고밖에 생각할 수 없었다.

항상 빌 게이츠를 ‘우리 회장님’이라고 부르며 마이크로소프트를 너무나 좋아하는, 회식할 때마다 웹 사이트에서 본 스티브 발머 동영상에서 발머가 뛰어다니면서 외치며 ‘디벨로퍼’를 건배 구호로 외치던 PM 김씨와 오래 생활한 탓인지? 길군도 회장님의 저서를 항상 들고 다니며 읽었다. 『생각의 속도』에서 읽은 다음 글귀가 길군을 감동하게 했다.

나의 주된 염려는 웹 페이지의 구성이 너무 복잡하고, 웹 페이지들끼리 서로 일관성이 없으며, 그래픽을 너무 화려하게 처리한 나머지 다운로드되는 속도가 느려서 실제로 필요한 정보에 접속하는데 시간이 너무 오래 걸린다는 것이었다.

“물론 우리 회사 웹 페이지의 전체적인 모양새는 나아졌으나, 우리가 의도한 기준에는 미치지 못한다고 생각합니다. 인터넷에 대해 MS가 지대한 관심을 가지고 있다는 것을 사람들에게 보여주려고 했던 의도를 제대로 반영하지 못하고 있습니다. 우리 회사의 전반적인 웹 사이트는 여전히 매우 취약합니다. 어지러운 색상의 그래픽이 쓸데없이 많이 들어가 있습니다. 마치 훌륭한 웹 페이지를 평가하는 척도가 필요한 정보를 얻는 기능에 있는 게 아니라 웹 페이지가 다운로드되는 동안 멀찍이 떨어져서 감상하는 데 있다고 생각하고 만든 것 같아요. 회사로서는 한 페이지에 가능한 한 많은 정보를 담으려고 노력하는 신문의 1면 편집자와 같은 생각을 소유한 사람이 필요합니다. 한 페이지를 보면 또 다음 페이지로 넘어가야 되도록 만들어 사용자들을 웹 페이지의 미로 속에서 헤매게 만들어서는 안 됩니다”

회장의 이러한 말들에 감동받은 길군은 현재 디자인에 컴포넌트의 루틴을 붙여서 처리하는 작업이 너무 한심하다고 생각되었다. 강의 모듈을 개발하는 과정에 강의 정보를 보여주고 수정하는 사용자 정의 컨트롤을 개발하며 길군은 현재 디자인이 프로젝트의 일정을 지연시키는 가장 큰 요소라는 극단적인 생각까지 하게 되었다. 얼마 전 컴포넌트를 설계하면서 Factory를 잘못 작성하는 바람에 Factory가 생성해 반환하는 객체와 반환받은 객체가 다른 곳을 참조하도록 하는 어이없는 실수를 저질러 하룻밤을 꼬박 새운 길군은 기껏 디자인에 컴포넌트 로직을 붙여나가는 작업이 훨씬 힘든 작업일 이유가 없다고 생각하고 고민하다가 결국은 성질을 못 참게 되는 지경까지 이르렀다.

늘어만 가는 길군의 고민과 근심
개발팀에서 나이가 제일 많은 길군은 성질대로라면 디자이너에게 당장 디자인 작업은 제쳐두고 닷넷 프레임워크에서 System.Web.UI 네임스페이스 부분이라도 공부하라고 이야기하고 싶었다. 그러나 디자이너는 내부 개발팀원이 아닌 협력 업체에서 파견된 외부 업체의 직원이었고, 유일한 여성 팀원이었으므로 모든 사람의 갖은 총애를 받는 인력이었기에 함부로 말할 수도 없었다. 길군은 시간이 지날수록 부아가 치밀기 시작했다.

디자이너 박양은 박양 나름대로 화가 나 있었다. 만리타향에 한 달씩 파견나와 있는 것도 서러운데, 파견지의 근무 환경이 원래 직장과는 너무 달랐다. 생전 처음 보는 이상한 포맷을 주면서 이대로 디자인하라고 해놓고는 그대로 디자인해 주면 또 닷넷 플랫폼의 특성을 제대로 살리지 못하는 디자인이라며 화를 낸다. 생전 처음 보는 양식을 들이밀며 이게 UI Specification이라며 그 많은 페이지들을 다 작성하라는 이상한 일을 시키지를 않나(개발자라는 사람들이 포토샵을 사용해서 그림에 쓰여진 글자 하나 고치지를 못하는 수준이다), 작업 좀 하려면 무슨 버튼을 만들어 달라, 어디에 무슨 글씨를 고쳐 달라, 어디에 무슨 내용이 들어가 있으니 고쳐 달라 등 만리타향에 귀양 온 듯한 서러운 기분이 드는 것이었다.

길군은 드림위버로 작업된 asp 파일을 넘겨받고는, 파일의 Indentation이 아예 안 되어 있어 일일이 탭으로 지정해야 하는 작업에 진저리가 났다. 한 두 페이지도 아니고 ASCX 파일을 만들어 메뉴를 재사용하는 코드도 전혀 들어가 있지 않은 페이지를 열어서 일일이 작성하다 보니 화가 났다. 특히</table> 태그를 하나 빠뜨림으로 해서 전체 페이지의 레이아웃이 무너져 빠진 태그를 찾아내는 데만 두어 시간 정도 허비하고 나서는 현재 작업에 깊은 회의를 느끼기 시작했다.

PM 김씨는 길군의 이런 속사정을 아는지 모르는지, 컴포넌트 수정은 우선 미루고 페이지부터 작성하라는 닥달만을 하고 있고, 페이지에 로직을 붙여나가는 작업은 기반 프레임워크가 아무리 코드를 줄여준다고 해도 성질나는 일이었다. 길군은 참고 또 참으며 일정을 지키기 위해 코드를 작성했다. PM 김씨가 대문짝만하게 인쇄해 벽에 붙여놓은 ‘Be the Miracle’이라는 개발 구호가 원망스럽게 느껴졌다.
결국 길군은 개발 일정을 지키지 못했고, 현재 건강 상태가 별로 좋지 못한 이유도 있지만 정말 ‘별 것 아닌 것 같은’ 디자인대로 UI를 수정하는 작업 때문에 일정을 지키지 못한 것 같아 화가 났다. 평소 일정을 못 지키는 것을 견디지 못하던 길군은 결국 사고를 저지르고야 말았다.

PM 김씨는 어느 날 아침 출근해서는 디자이너 박양이 출근하지 않았음에도 별로 신경쓰지 않았다. 사람이 살다보면 어쩌다가 아플 때도 있는 법이고, 김씨의 평소 신조인 ‘아프다고 전화할 수 있는 체력이 남아있으면 출근한다’는 신조에 따라 박양도 그렇겠거니 하고 신경쓰지 않았다. 그런데 해가 질 무렵이 되고 퇴근 시간이 다 되어서도 박양이 출근하지 않자 김씨는 의아심이 들기 시작했다. ‘이상하네 왜 출근을 안 할까?’ 여러 회사에서 근무해 본 김씨의 생각으로는 파견 업체에서 근무하면서 무단결근을 한다는 것은 회사간의 관계로 볼 때도 이해할 수가 없는 일이었다.

상처받은 박양, 길군의 깨달음
사건의 전말은 이러했다. 길군과 같이 근무하는 윤군과 김군의 말에 따르면, 전날 개발팀이 다 모여서 술을 마셨는데, 길군과 박양 사이에 약간의 다툼이 있었다는 것이다. 길군은 박양에게 개발자와 디자이너가 프로젝트를 진행하면서 지켜야 할 여러 가지에 대해서 이야기했고, 결론은 디자이너가 개발 플랫폼을 이해하지 않으면 프로젝트가 제대로 수행되지 못한다는 이야기였다.

박양은 자신의 전공은 개발이 아니고, 자기는 프로그래밍을 별로 배우고 싶은 생각도 없고, 웹 디자인이라는 게 쉬워 보여도 생각할 것이 아주 많다며, 작성한 디자인대로 프로그래밍하지 못하는 프로그래머가 무슨 대단한 능력을 가진 프로그래머냐고 팽팽히 맞서서 대들었다(길군이 나이가 훨씬 많으므로)고 한다. 심성은 곱지만 욱하는 성질을 가지고 있는 길군은 참지 못하고 몇 마디 언성을 높였는데, 몇 마디 말의 요지는 이러했다고 한다.

“프로젝트 수행의 주체는 무조건 개발자다. 디자이너의 임무는 개발자의 코드를 조금 더 예쁘게 보이도록 하는 데 지나지 않는다. 그러므로 시키면 시키는 대로 하라.”

박양은 마음에 큰 상처를 받았고, 반쯤 찬 맥주잔을 앞에 놓고 펑펑 울었다고 한다. 김군의 말에 따르면 아주 서럽게 울더란다. 그 후로 박양은 출근을 하지 않았고, 김씨의 일정에 따르면 이번 주 안에 UI 작업을 마무리하고 다음 주에 컴포넌트 정제 작업에 들어가려는 일정에 커다란 차질이 생길 수밖에 없었다. 미치고 펄쩍 뛸 일이 아닐 수 없어서, 김씨는 박양에게 전화를 해서 구스르고 달래고 협박하고 회유하고 갖은 수를 다 썼다.

하지만 박양은 말을 듣지 않았다. ‘말을 듣지 않는 게 당연하지. 전화로 말을 들을 사람 같았으면 애초부터 무단결근 같은 건 하지 않았을 테니까.’ 김씨는 이해할 수가 없었다. 아무리 기분 나쁜 일이 있다고 해도 파견 나온 회사에서 무단결근까지 하고 일 못하겠다는 파행을 저지르다니. 결국은 협력 회사의 사장까지 나서서 문제를 해결했다. 일주일쯤 지난 후, 디자이너는 다시 출근을 했고, PM 김씨는 모든 일정을 재조정해야 했다. 김씨는 이런 어이없는 일 때문에 일정이 늦춰지고 재조정되는 일이 발생한다는 것에 화가 나서 일정을 새로 잡는 데 시간이 꽤 걸렸고 전체적으로 보름이 넘게 일정이 지연되는 결과를 가져왔다.

개발자 길군은 자기 나름대로 ‘벙어리 냉가슴’을 앓고 있었다. 김씨가 자기를 조용하면서도 으슥한 데로 불러서 린치(?)라도 가하지 않을까 불안했다. 또한 김씨가 자기를 불러서 한마디쯤은 할만한 데 아무 말도 없는 것이 더 불안하고 미안했다. 일주일 후 재출근한 디자이너 얼굴 대하는 것도 영 머쓱했고, 웬만하면 박양에게 일을 부탁하지 않으려고 노력했다. 꼭 필요한 디자인이라도 길군은 서툰 포토샵을 실행해서 아이콘을 만들고 붙이고 했다. 개발 속도가 떨어지는 건 당연하다. 하지만 자기가 저지른 사고로 인해 일이 이렇게 됐으니 불평 한 마디 못하고 전보다 더한 밤샘 작업을 계속했다.

길군은 느낀 바가 컸다. 프로젝트를 제대로 진행하려면 성질부터 죽여야 되는구나. 말 한 마디 잘못했다가 일정이 보름씩이나 지연되는 결과가 발생하다니. 길군은 한때의 분함을 참으면 하루가 편안해진다는 옛 성현의 말이 한 마디도 틀림이 없음을 깨달았다.

하지만 PM 김씨한테도 섭섭한 점이 없지 않았다. 애초에 PM이라는 사람이 나서서 이런 경계를 좀 그어줬어야 하는 것 아닌가. 다른 바쁜 일 있다고 하루에 한두 번쯤 개발실에 얼굴 비추는 PM 김씨한테 관리 잘못이 더 큰 게 아닌가 생각했다. ‘PM 김씨도 아마 그렇게 생각하기 때문에 길군에게 아무 말 못했던 것이 틀림없다.’

뛰어난 기술만이 프로젝트를 성공적으로 이끄는 요소가 아니었다. 프로젝트를 성공적으로 이끌려면 기술적인 요소 이외에도 관리해야 할 문제가 아주 많다는 것. 특히 팀원들 간에 불화가 발생하면 개발 일정이 한 달씩 지연되는 건 일도 아니란 것을 느꼈다. 그 일이 있은 후, 길군은 김씨와 술을 마시면서 이런 저런 이야기를 했다.

“개발하면서 주의해야 되는 일에 대한 내용을 적은 책은 없습니까? 기술 서적이야 넘쳐나지만 실제 프로젝트를 수행해 보니까 기술만 뛰어나다고 무조건 프로젝트가 성공하는 건 아니네요. 물론 기술 없이는 프로젝트를 시작할 수도 없겠지만…”

김씨는 책을 한권 읽어보라고 했다. 책 제목이 『Programatic Programmer』라는 책인데, “담배 빼앗아가는 동료를 비난하지 말라”는 내용까지 있다고 한다. 책을 한번 읽어 보고는 싶은데 인터넷 서점에서 검색해 봐도 없었다. 또 물어보니 원서란다. “개발 1등급을 받는 업체가 한국에 하나도 없는 이유가 거기에 있구나.” 길씨는 원서라도 사서 읽어 봐야겠다고 생각한다.

“프로젝트라는 게 정말 산 너머 산이구나”
PM 김씨는 또 다른 고민이 생겼다. 김씨가 생각하는 현재 솔루션은 MileStone 버전이라고 생각하는데 협력 업체에서는 완전한 판매 가능한 솔루션이라고 생각하는 모양이다. ‘3개월 만에 판매 가능한 솔루션을 만들라고 했으면 시험적으로 프레임워크를 작성해 기반 개발을 시도하지 않았을 텐데.’ 김씨는 “기간을 단축해서라도 개발을 해야 하는지 아니면 접어야 하는지 고민에 빠졌다.

PM 김씨는 책임 연구원으로 근무하는 선배 개발자이자 스승으로부터 공부 똑바로 안 하고 서툰 일만 한다는 꾸중까지 들었다. 다음 주에 이런 일들로 여럿이 모여서 회의를 진행하기로 했다. 김씨는 ‘그동안 손놓았던 소프트웨어 공학 책을 주의 깊게 읽어보고 회의에 참석할 필요가 있다’고 생각했다. PM을 처음 해보는 김씨는 딜레마에 빠진다. “정말 개발만 잘 한다고 되는 일이 아니구나.” 일이 마음먹은 대로 안 풀리니까 자신의 실력에 대해서도 의심이 가기 시작한다. “프로젝트라는 게 정말 산 너머 산이구나.” @

"Development" 카테고리의 다른 글
  • [닷넷 프로젝트를 정복하라] ③ 더이상「꼼수」는... (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ② PM 김씨의 좌충우돌기 (0)2007/06/09
  • [닷넷 프로젝트를 정복하라] ① 이래서 안되는거군! (0)2007/06/09
  • 소스 코드 공유를 위한 패턴 (0)2007/05/17
  • 소프트웨어 개발과 디자인 패턴 (0)2007/05/17
2007/06/09 18:03 2007/06/09 18:03
Posted by webdizen
Tags .NET, 프로젝트
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3042

Leave your greetings.

[로그인][오픈아이디란?]

Programming/.NET2007/04/29 11:52

High-Performance .NET Application Development & Architecture

Introduction

It has always been a goal of project architects to plan an effective strategy from the ground up in regards to an new application. All relevant factors are taken into consideration with respect to the application, from its design and layout to a functional website infrastructure. Pre-.NET strategies and design guidelines still effective now were developed with Microsoft's DNA (Distributed interNet Application) platform. This model successfully served the purpose of architecting N(any number of)-Tier (levels) applications. In its basic sense, as in most robust, distributed applications, you'll architect 3 main layers or Tiers: presentation, business rules and data access.

Now, each independent layer within itself, is another issue altogether. Each one has its own methodologies regarding its perspective best practices. In other words, many factors will determine its proper design, efficient data access and visually pleasing presentation. That is why each Tier, with respect to the overall success and health of your application, should be maximally optimized. So in light of such, we'll go through each Tier and dependent of your situation, offer the very best methods resulting in one robust, secure and high-performance distributed application.

One issue that is slightly bemused is that of Performance and Scalability. However similar these may sound in end-result, none could be further from the truth. Performance is a matter of responsiveness, how well a application performs, as our General .NET Best Practices section below will help you capitalize on. Scalability is another subject slightly paralleling performance only within the confines that it has to be high-performance for all users and not affect anyone else nor the tie up the server for that matter. Highly-scalable applications do their best to absolutely minimize network traffic, and server-side database interaction. By these measures then, could scalability be in collaboration with performance.

At any rate, as aforementioned, architecting a robust application involves the time-tested, structurally sound methodology in having an application made up of separate, easily maintainable components that would require modification only once to effect site-wide changes. This, among other things, creates harmony among IT teams, guaranteeing no one will interfere with or break someone else's work. The parts individually, in this sense, are on par to the sum total. To digress, how many audiophiles would buy a all-in-one stereo unit, rather than various individual high performance components? Right, I think none would.

Anyway, before proceeding on, this article assumes you have some basic .NET and or development experience. Still, it's important to realize that this article is a codified manual of tips and optimization techniques for the more or less experienced developer and, or expert looking for more ways in maximizing an application. Beginners may find it hard in following some sections. Nevertheless, I have made ample effort to direct beginners to any outside sources where more in-depth information is available, should one require it, and recommended.

In any case, we'll begin.

Planning

Planning is the first stage all developers embark on - what to do, how to do it or the best way to do it. Proper planning is key in determining that all aspects of the application are architected correctly and efficiently, and the right direction is chosen. Once this is established, the actual development begins to take place. From this point on you'll begin to deal with the application at all levels and its individual components, and how each requires individual optimization. Thus as a whole it becomes one solid, cohesive application.

Good robust, application architecture insists on it being functional, responsive, secure and user friendly. I'll briefly point out that when presenting an finished application, it must be faultless. People like a speedy application with complementing visually appealing elements. Most users will never fully appreciate all the sophistication it took behind the scenes to make the application work, they initially focus on its design and layout. Although, I won't demonstrate web site and graphics design, I felt it a point needing attention, even thought this article will not demonstrate such. So keep this in mind, that for a fully robust, cohesive application, design and back end do go hand-in-hand, not productively but ultimately. So all parts must be on equal level in making it an cohesive application. Again, even thought this article is really more for .NET project architects, it's a fact that some developers out there simply do it all. For some more info of project planning read - Web Application Development - A Guide to Success.

That aside, prior to dealing with the three archetypal levels inherent to "N-Tier," we'll be discussing some conventional information regarding application and server security. After that, we'll dive right into offering general .NET Best Practices. Then move on to error handling, and further deal with some common .NET errors you may encounter, and finally look into performance testing.

As a final note, .NET's documentation offers an absolutely exhaustive collection of information which is the reason why you'll notice numerous links throughout this article directing you to more in-depth information where applicable and necessary.

Now let's get started with .NET security, and rest assured that our application is being built on a secured platform, and that all security precautions were taken and are intact.

Application/Server Security

Security in .NET, and really in any application, is and should be of the highest importance. It is critical that at all times your application be unbreakable and tamperproof, as much as possible, from anything. Anywhere from someone fiddling with your query string to server / data access authorization to preventing SQL Injection attacks.


As the topic of security is really too vast and complex to be dealt with here in any great detail, we'll offer some generalized pointers and tips to set you off in the right direction with a good overview of the various security methods available. Oh, btw, work closely with your Network Admin, make sure you're both on the same page.

Security in .NET works with two types of concepts:

  1. Authentication : Confirms users identity and credentials in allowing them access, through either valid Windows Accounts or IIS, that include methods such as certificate (SSL), Windows (NLTM or Kerberos), Forms, and Passport authentication.
  2. Authorization : Allows or denies file or url access to a given user based on certain criteria, or on ACL (Access Control List) settings in Windows. Authorization parallels .NET's users and roles settings within its web.config file's <authorization> element node. Note all authorization is always done after authentication.

With which introduces us to the following kinds of security implementations.:

IIS Security

IIS (Internet Information Services) is the first line of defense for application security, since all requests must go through here first. This method of security rests solely on setting certain elements found in IIS's Directory Security / Anonymous Access and Authentication Control tab. This verifies IP/Domains and user accounts via ACLs, that are preset Windows or Active Directory User Accounts. Incidentally, ACL access is verified whether or not anonymous access is enabled. Disabling Anonymous Access will bring up the logon dialog box, that would require you a valid Windows account, unless you're on the same domain.

Generally, for any fully public sites you should leave all default settings to manage Anonymous Access . Windows (Windows Integrated Security) handles this, whereby it assigns its own IUSR_computername account the privilege, and all users of this site fall under the Guest group account.

Moreover, you could also disable anonymous access and integrate Basic Authentication (with disabled Windows Integrated Security) that presents the user with the common dialog-driven box to enter in their credentials. Basic security mode further allows control via a domain controller, though this should not be the sole method in doing such. At any rate, Basic Authentication is the most frequently used security measure because this works well among all browsers, and is commonly used in concert with SSL (Secure Sockets Layer) technology for added security.

Another IIS security measure, though not widely used since all browsers do not support it, is Digest Authentication . This was Microsoft's way of remedying one issue that has befallen Basic Authentication which was sending passwords in plain text, and the solution here sends a digest (Hash encryption) in place of the actual password.

Finally, is Integrated Windows Authentication or NTLM challenge/response protocol. This increased security method, since it doesn't work with Netscape, is not really suited for the Internet, but rather a fully IE-inherent Intranet.

Now that we've got a grasp on the IIS's security options, IIS has many more options within its properties that are adjustable for not only additional security measures, but even performance.

.NET/IIS Permissions

Moving on now, .NET allows programmatic determination of NT group permissions as well, using two .NET (principal objects) classes : WindowsPrincipal (automatically created when using Windows authentication) or GenericPrincipal (customizable event using the WindowsAuthentication_OnAuthenticate event (within the Global.asax file) that occurs during authentication, used with None Authentication mode in web.config):

// With users being members of a Windows account group on a domain: 
if (User.IsInRole ("Domain\\Group")) {
...
// Or via Windows built-in account groups users get added to:
if (User.IsInRole ("BUILTIN\\Administrators")) {
...

Or through user identity and role, including web.config's case-sensitive credential information, as discussed in the next section containing Forms Authentication, and through Windows Authentication:

using System.Security.Principal;
if ((User.Identity.Name == "authorizedUsername") && (User.Identity.IsAuthenticated == true)) {
AllowAccess();
}

Finally, you could also implement the WindowsIdentity Class to validate a Windows user account, using the GetCurrent method:

using System.Security.Principal; 

WindowsIdentity User = WindowsIdentity.GetCurrent();

if ((User.Name == "Domain\\User") && (User.IsAuthenticated == true)) {
AllowAccess();
}

Note: Anonymous Access, as well as impersonation, should be disabled to initiate these kinds of Role-Based Security inquiries, and Authentication Mode should be set to Windows in your web.config file. Otherwise, you'll retrieve .NET's IUSR_machinename user account info - "Domain\ASPNET", or simply nothing at all, due to the enabled impersonation.

Moreover, employing this works as well for applying file authorization through the file's Security Permissions - right-click Properties settings, and URL Authorization.

So now that we've got a good overview on identifying permissions, and since we're on the topic of IIS, we'll briefly discuss some general IIS optimizations that will help our server's overall efficiency and performance.

General IIS Optimizations

  • Make sure Mappings under Home Directory-->Configuration-->Application Configuration has Cache ISAPI Applications enabled, as well as Buffering and other Cache Options under Options
  • Also enable HTTP compression found under the Service tab
  • Adjust the Performance Tuning of your server to an amount of anticipated site activity or hits you may expect to get, and enable Process Throttling in preventing your site from overusing CPU processing, which may indicate an external attack of some kind. Both these adjustments can be made under the Performance tab, within the Web Site's Master Properties
  • Finally, enable performance settings for all your sites under the Default Web Site Properties-->Server Extensions tab, and adjust the performance list box for the appropriate amount of pages that you may have on your site

Web.Config or ASP.NET Security

.NET incorporates this configuration file that is capable of doing a great deal when it comes to security, among other things. For starters, it's the perfect place to store all your appsettings or database connection strings that'll be accessed in your application. Furthermore, everything regarding your application's security can be stored here within its perspective nodes, from allowing specific users and groups access to a particular file location path or multiple location paths to storing account credentials, examine specific HTTP access, and standard Authentication.

A typical web.config configuration file

<configuration> 
      <appSettings> 
        <add key="myDataBase" value=id; _=pw; Data=myDS; _=myCat; Enlist=false;" /> 
      </appSettings> 
      <location path="securePage.aspx"> 
        <system.web="None | Windows | Forms | Passport"/> 
          <forms forms="securepage" loginUrl="login.aspx" /> 
          <credentials passwordFormat="Clear | MD5 | SHA1"> 
            <user name="Jimmy" password="hashed Password"/> 
          </credentials> 
          </authentication> 
          <authorization> 
            <!-- General application authorization -->
            <allow verbs="POST" users="Jimmy, Lumi" roles="Administrators" /> 
            <allow verbs="GET" users="Peter" roles="Debugger Users" /> 
			<!-- URL Authorization --> 
            <allow users="domain1\user, domain2\group" /> 
            <!--Deny anonymous or All Users--> 
            <deny users="? | *" /> 
          </authorization> 
        </system.web> 
      </location> 
      <!--For a second secure page--> 
      <location path="securePage2.aspx"> 
        <!-- Other configuration settings--> 
      </location> 
<configuration> 

As seen, the web.config file is capable of many security measures, from public access to Passport authentication. We'll now discuss them in turn:

<authentication mode="None | Passport | Windows | Forms "/>

Impersonation

.NET allows general, public access through impersonation, where IIS uses its own IUSR_computername account process token to control authentication and authorization. As far as strict security is concerned, this is not really it. That is why it best serves all public sites, and is set in the web.config file like so:

<system.web="true" /> 
</system.web>

The identity key element holding the attribute impersonate, set to true is the equivalent of IIS's Anonymous access, but directly affects your .NET application here more, since it helps overcome any " Access Is Denied " problems with .NET on a public site.

And due to this, your SQL connection string would be this below, providing both your application and SQL Server share the same domain or trusted domains. Otherwise you'll receive the Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON' error.

"Data Source=myDS; Initial Catalog=myCat; Enlist=false; Trusted_Connection=yes"

For more info on .NET Impersonation, readImplementing Impersonation in an ASP.NET Application.

Default/None (Custom) Authentication (IIS's Anonymous Authentication)

The default setting of no authentication or None mode , is typically for public sites (impersonation). When included for this purpose in your configuration file, does two things, helps performance since no extra runtime processing is needed and could allow customized authentication to take effect, whether application wide via the global.asax or HTTP modules, working with the AuthenticateRequest event.

Passport Authentication

As noticed in the authentication key element above, one such technique, different among the others as I'm sure most have seen is .NET Passport authentication mode . This service offered by Microsoft, is a somewhat of a one-stop shopping methodology, enabling you a unified access across many applications.

Windows Authentication (IIS's Integrated Windows / Digest Authentication)

Next, as aforementioned in the IIS section above, is Windows Mode , the default security method, as it indeed relies on Windows Authentication in IIS for its authentication, prior to .NET gaining control. Furthermore, because of its nature it is best suited for Intranet sites.

In the web.config file, the authentication node is set for ACL Windows authorization, and is also based on the elements contained within the authorization nodes for which users, roles or groups are allowed or denied access, or URL authorization. Additionally, within these nodes you may even specify what type of HTTP action is allowed, using the verbs key, and setting it for either POST, GET, HEAD, or DEBUG.

Here we've set Role-Based Security within the following web.config's <allow> element key setting, and in this instance it'll allow only Admins and Debuggers access, and deny everyone else:

<configuration> 
<system.web="Windows" /> 
  <authorization> 
	<allow roles="Administrators, Debugger Users"/> 
	<deny users="*"/> 
  </authorization> 
</system.web> 
</configuration>

Forms Authentication

And finally, there is Forms (Cookie based) Authentication Mode , where your dealing with security based upon criteria captured through a form on your web page. This could be Forms-based (using configuration credentials) or Windows/Role-Based, with account credentials set in place and stored in a database, XML file, text file, or even a remote Web Service, rather than solely in an ACL list. Incidentally, this mode enables good flexibility for creating more personalized sites.

Forms Authentication is your best bet for Internet security-based applications such as an E-Commerce site, since it supports all browsers, is fully customizable, and works well with other security additions such as SSL. Incidentally, cookie size limit with IE5 and higher browsers, has no limit.

Here is an example of Forms-Based Authentication using the FormsAuthentication Class ,

if (FormsAuthentication.Authenticate (userName, userPassword)) { 
//Redirect user back to original URL once logged in or do whatever
FormsAuthentication.RedirectFromLoginPage (userName, boolean);
} else {
Response.Redirect ("login.aspx");
}

with the web.config file holding the user's credentials and authorization:

<authentication mode="Forms"> 
	<forms name=".ASPXAUTH" loginUrl="/login.aspx"> 
	  <credentials passwordFormat="userName" password="encryptedPassword"/> 
	  </credentials> 
	</forms> 
</authentication> 
<authorization> 
	<!--Deny all unauthenticated users-->
	<deny users="?"/> 
</authorization> 

Forms authorization as seen, is controlled via cookies (name attribute) that you name within the forms element key, and the loginUrl or login page to use. This is useful for any pages requiring strict security measures, whereby all unauthorized users get sent to your login page to enter their credentials in a form, and must match the Forms Authentication Credentials before access is granted.

Alternatively, you can utilize the same FormsAuthentication class technique above using a database housing users and roles, as well as utilizing the FormsAuthenticationTicket Class. This method of course has its advantages since a database could store as many credentials as you need. Additionally, if you so choose, you could even store credentials within an XML file, providing you set ACL permissions on its location.

Now, as far as encrypting passwords is concerned, its significance cannot be really stressed enough. It's fortunate however, that it is incredibly easy to do for the above credentials using the (get ready for a long one) FormsAuthentication.HashPasswordForStoringInConfigFile Method. This method uses two parameters, one for the password itself, and the other for the type of hash algorithm you require, SHA1 or MD5:

string encyptedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Text, "encryptionMethod");

Furthermore, within the web.config file you could add any specific authenticated page and allow certain user's access, based on specific HTTP method and deny access to all all or specific non-authenticated user's. Subsequently, by choosing Forms Authentication as a custom security measure, then bear in mind that Anonymous Authentication should be enabled in IIS.

Read Forms Authentication in .NET's documentation for more info.

Miscellaneous

Now aside from these aforementioned security measures, I'll discuss a few miscellaneous encryption-enabling configuration settings helpful in further securing your application.

If you're concerned about the security of any viewstate data being examined or manipulated in any way, then set enableViewStateMac (MAC stands for message authentication code) property to true, as demonstrated below, and individually within each .aspx page's @Page Directive - EnableViewStateMac="true" as well. This property hash encrypts the viewstate and ensures that it has not been altered in any way. It stores this information in the machine.config file's <machineKey> node element. Luckily, this is already the default in .NET, phew!

Further encryption of any viewstate, or forms authentication cookie data could be physically set within the web.config by adding - <machineKey validation="3DES" /> , .NET's Triple-DES (3DES) encryption or any other encryption method you prefer.

As for sites dealing with file uploading, we both know limits have to be set. Users obviously can't upload to their hearts content, and bog down the server. So setting .NET's HTTP runtime maxRequestLength parameter, for 2 megabytes as demonstrated below, ensures that any larger files are declined.

<system.web="true" enableViewStateMac="true" /> 
	<machineKey validationKey="autogenerate | value" decryptionKey="autogenerate | value" 
		validation="SHA1 | MD5 | 3DES" /> 
	<httpRuntime maxRequestLength="2048" /> 
</system.web>

ADO.NET Security

In all database applications you're dealing with a form of some kind that will be accessed by a user who will enter information to be verified against a database or other means, before any access or data is allowed. Protecting information or access in this situation isn't too difficult a concept, it's simply a matter of storing (preferably encrypted info) into a database, then validating this on login. For more info check out - Using MD5 to Encrypt Passwords in a Database. Almost certainly your site will also include a searchable form for users to enter search criteria in obtaining data.

Aside from these somewhat innocent scenarios, and by virtue of creating forms that enables users the ability to submit information to your application, you're left vulnerable to a malevolent technique exploited by hackers known as SQL Injection . This is a security threat whereby SQL statements or other malicious code, through form fields, are injected in addition to your code and executed, and can cause great damage to your data, whether they retrieve, delete confidential information or simply cause havoc among it!

To prevent SQL Injections you should:

  1. Validate all entered criteria via regular expressions for any mischievous keywords or text. Furthermore, apply HTML Encoding to any inputted text. Fortunately, version 1.1 of the .NET framework, offers superior validation, whereby if any user enters certain un-encoded HTML syntax or characters, .NET will raise an error. Read Request Validation - Preventing Script Attacks for more info.
  2. Use parameterized Stored Procedures (Sprocs) for your queries
  3. Create custom error pages so no one could inadvertently retrieve any data or server information from non-custom error pages.

Some added security measures when working with ADO.NET are:

  1. If caching data, store critical info server-side via session state or with the Cache object. I discussed this in light of a real-world example in my article Drilldown Datagrid Searching with ASP.NET, and read more about the various method for storing state.
  2. SQL server access should be performed through Windows Integrated Security, not "mixed mode," and never leave the default "sa" username and blank password as is, set a good username and password instead, which lead us to number 3.
  3. If possible create a trusted connection to SQL with the Integrated Security set to SSPI (Security Support Provider Interface), as this eliminates the need in storing any user id's and passwords.
    "Data Source=myDS; Integrated Security=SSPI; Enlist=false; Initial Catalog=myCtlg" 

    However, you may encounter - Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON,' where remedying this is to have your Windows Account be a Domain Account and share the same or trusted domains with IIS and SQL Server.
  4. Encrypt your connection strings in your web.config file, or other critical data elsewhere, and then later decrypt them, via the CryptoStream class in your page code, when you need to use them. Also dependent on the size of your data, whether large or small will determine Symmetric or Asymmetric Encryption.
  5. Finally, never hard-code anything, like any of the aforementioned, in your client-side pages.

Code Security

This security measure deals with setting permissions on projects, files, code and resources from anything unauthenticated. The CodeAccessPermission class comes in to play in allowing administrators the power to delegate certain types of access to various resources, thus ensuring authorized access only. It works alongside the trust element key within the web.config file in setting code access security (Full is the default):

<trust level="Full | High | Medium | Low | Minimal" originUrl="url"/>

Read Secure Coding Guidelines for the .NET Framework and Secure Coding Guidelines for more info .

Furthermore, as this also applies to all aspects in this article, if you really, really need the utmost security for very critical data, you do have a couple of nice options:

  1. Retrieve sensitive information and settings using serialization , that converts data to bytes for any type of transmission, that later gets deserialized on the other end.
  2. Directly reference an object's (dll/assembly) metadata (binary info about your dll) and other information or invoke methods contained within it during run time via Reflection using the System.Reflection namespace

Look at ASP.NET Security for more info and if you got the time read Microsoft's 608 page - Building Secure ASP .NET Applications treatise. Furthermore, peruse ASP.NET Architecture as well.

Now that we've dealt with security as an good overview, we now can move on to the actual components involved in making our application's architecture robust and high-performance. We'll first begin looking at some best practices that will determine general application health, before we discuss the best methods with each Tier.


General .NET Best Practices

The best practices to be discussed stem from real-world .NET application use, and have proven themselves very viable and sound in producing very scalable, high-performance applications. Since I'll be dealing with .NET programming a la hand coding and web forms, those interested in strict Visual Basic.NET optimizations should see - Performance Optimization in Visual Basic .NET for more info. Nevertheless, the best practices discussed here, generally apply to VB.NET as well. Moreover, all the tips offered here are general application page optimizations.

  • Right at the top of your .aspx page's @ Page directive, it is always important to disable any features you won't need or use. For example, set EnableSessionState to false on any pages not requiring use of session state, as well as Debug, Trace and EnableViewState. For strongly-typed error-free compilation, Strict and Explicit should be set to true to enforce early binding and variable declaration. Moreover, always set the page's Buffer to true for nice performance boosts, and application-wide within the web.config file's system.web nodes as well - <pages buffer="true"/> .
    <%@ Page Language="C#" Debug="false" Strict="true" Explicit="true"
        Trace="false" EnableSessionState="false" EnableViewState="false"
        Buffer="true" %>

Note, all of the above attributes are applicable to an .ascx user-control's @ Control directive, except for Buffer, Trace and EnableSessionState.

  • On Page_Load set Response.BufferOutput to true and Response.Flush at some later key point in your code.
    void Page_Load(Object sender, EventArgs e) { 
    
        Response.BufferOutput = true; 
    
        //...... 
    Response.Flush(); }
  • Utilize Page caching by incorporating the @ OutputCache directive to speed up page display by storing this page is memory. You start by specifying, in seconds, the duration for the cache to remain in memory, your particular request scenario and its location. Other customizable request attribute settings include VaryByCustom, VaryByHeader, VaryByParam and VaryByControl , and for the Location attribute (not supported in User Controls or .ascx files) you're allowed - "Any | Client | Downstream | Server | None."
    <%@ OutputCache Duration="7200" VaryByParam="value" Location="Any" Shared="True" %>
    Output caching could also be enabled with the HttpCachePolicy Class. Not only that, but the OutputCache directive include-syntax also fully applies to Fragment Caching or caching server user controls (.ascx) files separately as well, and works along the exact same lines. However, one distinct advantage this has over standard Page output caching, is that the user control file supports the Shared attribute setting, whereas the .NET Web Forms page (.aspx) does not.

    Therefore, by setting your server user control's Shared attribute to true, you in effect allow this control to be shared among all your pages, as opposed to creating separate cached versions of it. As you can probably guess, you'll end up with more available memory and even better performance!

    Now that's not all, caching your code-behind's source file is very different from what we've seen. To cache your code-behind control you'd declaratively implement the duration attribute in your source file's metadata like so:

    [C#]
    [PartialCaching(7200)] 
    
    public class myClass : UserControl { 
    
       //Your code here 
    }

    [VB]
    <PartialCaching(7200)> _ 
    
    Public myClass : Inherits UserControl 
    
       'Your code here 
    End Class
    Reference PartialCachingAttribute Classfor more info.
  • Aside from Page Caching, Data Caching is extremely important, and what you tend to do once you present data to the client. How have you developed the page in question in light of scalabilty and performance? If the user is returned 10,000 records that will assuredly require paging, will your application keep re-hitting the database each time or does it take advantage of .NET's data caching methods?

    I have written two articles that cover such topics in much greater detail .NET Data Caching and Drilldown Datagrid Searching with ASP.NET. I'll refer you to these two articles for all the information you'll need. As for more generalized data access, we'll be discussing this in greater detail in our Data Layer section, and reveal different scenarios and the best data retrieval method to be used for the best performance.

  • Check for Postback on Page_Load to avoid unnecessarily hitting the server when the page is posting back to itself:
    void Page_Load(Object sender, EventArgs e) { 
    
       if ( !IsPostBack ) { 
    
         //...rest of code here 
    } }
  • Don't unnecessarily use server-side web server controls for simple tasks where standard HTML controls would suffice.
  • Use .NET's StringBuilder class for all your string writing/creation and manipulation needs.
  • When it comes to your code and values try to minimize boxing and unboxing of values, as this can cause a performance hit.
  • Be very paranoid when it comes to any text you'll be accepting through forms, or query strings. Unless imperative and not practical although easier, utilize POST primarily instead of GET, for any data requests or passing among pages.
  • Use <%@ Page AspCompat="true" %> only if it's an absolute must where you require pre-.NET apartment-threaded VB6 COM object compatibility. It truth, it's best that you simply make the effort to fully migrate your application completely to .NET, as this is better in the long run.
  • Particular settings within your configuration file - web.config , prove useful in improving application performance. First of all, set all pages to buffer, as mentioned. Second, if your site is fully public, setting authentication to None mode further helps, as well as disabling site wide debugging. No public pages or applications should ever have debug mode enabled.
    <system.web="None" /> 
       <pages buffer="true" /> 
       <compilation debug="false" /> 
    </system.web> 
  • Take advantage of .NET's machine.config file processModel attributes. They have much to do with the robustness of the application as it's the server and how well it lives that will further epitomize your application. The old days of rebooting IIS for component registration or recycling and so forth are long gone with all of .NET's way cool abilities contained within this one file. Heck, this file even helps with automatic crash recovery! Read Configuring the ASP.NET Worker Process through the <machine.config> file for some good tips.
  • When compiling any classes in a code behind scheme or as a data object, outside of VS.NET, use /optimize thus assuring smaller, more efficient runtime code:
    (csc/vbc).exe /t:library /out:c:\inetpub\wwwroot\bin\sf.dll sf.(cs/vb) /optimize

Within VS.NET, you could enable optimizations through your Project Solutions properties, under Configuration Properties-->Optimizations.

Furthermore, you could utilize .NET's Native Image Generator (Ngen.exe) to cache your components or applications (.exe) within the GAC (Global Assembly Cache) for faster start-up times:

ngen myComponent.dll/exe
  • When possible and applicable, utilize Asynchronous Calls for added scalability to your application, anywhere from I/O actions to ASP.NET Web Forms.
  • Always, always remember to close, null and dispose of all your objects. Even though .NET's garbage collection scheme is magnificent, do it as well nevertheless.
  • For maximum readability, always properly indent your code.
  • And finally, add comments to your code. .NET comment tags are <%-- Code --%> , whereas dependent on the language you've chosen, VB uses an apostrophe ' to comment out code and C# uses slashes // . This practice ensures not forgetting certain logic or program flow when the time arises. We don't always remember why we coded what we did or how, do we?

Now that we've gotten some great performance enhancing tips for our standard .NET pages, we'll proceed to the N-Tier portions, and individually deal with each one.

Directory Structure

This should be a simple enough concept, separate all key components in their own folders: directories like: includes, images, bin, info, documents, stylesheets, scripts, etc. You get the idea, what a nightmare to have your entire application in one folder, yikes! Furthermore, for stronger security measures, never place your site's folder in your server's root folder.

Also working with a source code control software such a Microsoft's Visual Source Safe goes a long way in protecting code. Many editors available now from HomeSite to Dreamweaver integrate this. Furthermore, Source Safe promotes versioning as well, just like .NET.

Presentation / Business Layer

N-TIER

The presentation layer is just that the presentation of the application via a User Interface (UI). It encapsulates our HTML code and any controls or components that make up our entire application. In classic ASP this page would look like a unpleasant interspersion of code, or spaghetti code as it's known. Simply put, this means server, client and HTML code all mixed together without a care in the world.

This approach is messy, problem prone and doesn't follow .NET's Object-Oriented approach. Our .NET page in comparison will be cleanly laid out, and lightly coded. Why? Well, again because of .NET Platform model. How? With .NET's code-behind . Code-Behind is, as its name implies, the method where all your commonly used code is placed in another file, "behind" the page, in a dll or vb/cs source file, that get's inherited via your page's @ Page Directive. Alongside this benefit, there are performance advantages of writing code this way, since .NET automatically pre-compiles these controls!

The page's @ Page Directive inheriting code-behind is thus:

<%@ Page Inherits="MyNamespace.ClassName" Src="source file" %>

The source file would look something like this:

          

// Page inherits code-behind source file

using System;
using System.Diagnostics; //For Tracing, Performance Monitoring
using System.Drawing; // For Graphics, Datagrid Colors
using System.Web; // For HttpContext
using System.Web.UI; //For Literal Controls
using System.Web.UI.WebControls; // For Datagrid

namespace MyNamespace {

// All your variables, properties here

public class ClassName : Page { //Inherits Page Class

// All your code here }

}

Above we imported any necessary libraries we might need. Next we created our namespace, and within this our class, which is what our Page's @ Page Directive will inherit for code-behind to work. However, if you prefer for whatever reason, you could simply create a standalone class in your code-behind file, whereby the Page Directive's Inherits attribute is the class name alone, instead in our example where it is "MyNamespace.ClassName."

Also notice that our class inherits the Page class, because this is what constitutes our code-behind interaction with our caller page.

Alternatively, if you compile your source code into a dll,

(csc/vbc).exe /t:library /out:c:\inetpub\wwwroot\bin\sf.dll sf.(cs/vb) /optimize

you would then import it and instantiate it, like so:

          

// Import It

<%@ Import Namespace ="MyNamespace" %>

// Then Instantiate It

[C#]

ClassName variableName = new ClassName();

[VB]

Dim variableName As New ClassName()

Another method that implement's code-behind methodology are pagelets or .NET's new improved include files/ Web Server User Controls , or .ascx files . These files expand on the content (UI)/code separation by including commonly used code, in nicely stored controls, again just like include files in classic ASP, but certainly more powerful. How? Well, they for one they can assign and pass parameters, not to mention handle events!

Apart from the more conventional application of user controls, and normal code-behind modules, yet another instance is in creating reusable, Custom Server Controls . Basically these are new, custom built, super-duper controls, acquiring their functionality from inheriting its target control's base class, and expanding on it. Creating one works along the same lines as the above source code, except instead of a Page code-behind file inheriting the Page Class in that example, you'd inherit your particular control's base class. Take for example creating a custom datagrid control:

          

namespace MyNamespace {

// All your variables, properties here

public class ClassName : DataGrid { //Inherits DataGrid Class

// All your code here

}

}

After you compile your source file, you'd then register your assembly for use in your page like so:

          

<%@ Register TagPrefix="myControl" Namespace="myNamespace" Assembly="myDll Name" %>

Then implementing it is as easy as:

          

<myControl:myNamespace ID="MyControl ID" runat="server" property1 = "false" _

property2 = "Red" OnClick="Click Method Name" />

Notice how (providing your source code includes this) your custom control could accept and set, parameters and or properties, and handle events, as we've mentioned. Moreover, this could also apply to standard code-behind user controls as well! Custom control creation tends to get more advanced, and the coding skill requirements jump up a few notches. At any rate, a user controls' more common usage stems, more or less, in reusable site code, i.e. a page's header and footer for instance.

Below demonstrates a typical, albeit, bare bones .aspx page. It has a header and footer, whereby the respective code is separated between two web server controls or modules - header.ascx and footer.ascx. All your page would need to do is register them, and place them in the page accordingly:

          

// The control's first get registered at the top of the page


<%@ Register TagPrefix="Header" TagName="Top" Src="header.ascx" %>
<%@ Register TagPrefix="Footer" TagName="Bottom" Src="footer.ascx" %>

// Then get placed in your page where needed

<body>

<Header:Top ID="Header" runat="server" />

<Footer:Bottom ID="Footer" runat="server" />

</body>

Man, .NET rocks! Now that's a nice, clean presentation I think! But wait, that's not all, your web server controls themselves could also further inherit code-behind code! In this case the inherit/src members are implement this time via the control's @ Control directive.

// Header.ascx
          

<% @ Control inherits="ClassName" src="source file" %>

// Your code here

And your control's code-behind file is identical to your page's code behind file, except the control doesn't inherit the Page class, but rather the UserControl class, since it's a control that's calling this file.

          

// User Control's code-behind source file


using System;
using System.Web.UI;
using System.Web.UI.WebControls;
public class ClassName : UserControl {

// Your code here

}

Additionally, if you compile your code-behind file as a dll with a namespace that looks like this:

          

namespace ClassName {

public class myClass : UserControl {

// Your code here

}

}

Then there are now two ways for including it for use in your page. You could inherit it as follows:

          

// Inherit it

<%@ Control inherits="ClassName.myClass" src="source file" %>

Or you could import it and instantiate it as demonstrated earlier. Still yet another cool feature in .NET is the ability for the global.asax file to inherit code-behind as well!

<%@ Application Inherits="MyApp" %> 

For more info on the fine art of OOP Programming, it'll be worth your while in taking the time to thoroughly read - The Quick and Dirty .NET Guide to C#/VB Object-Oriented Programming.

As a final afterthought, since a majority of your initial code within your pages and source file will interact on your code's Page_Load method, you may find that aside from this common function, you may to need to process something even before this or something else as well. Well then have a look at these six additional Page methods, that are contiguous to your Page's event handlers:

  • Page_Init - for pre-Viewstate processing
  • Page_PreRender - for pre-Page rendering tasks.
  • Page_DataBind - for when the Page binds data
  • Page_Dispose - for when the Page get's disposed
  • Page_Error - for any errors on the Page
  • Page_Unload - for code to run on Page unloading or exit

So the above are simply just something to keep in mind. Now you should have a real good idea on creating a clean Presentation Layer with the use of web server controls.

Business Rules/Logic Layer:

This layer is quite simple, as it involves all specific and particular business rules and requirements to any given company. It enforces rules pertaining to data that is retrieved from a database, i.e. mathematical, monetary, formatting, etc.

These rules are stored as methods in this layer separately from other components, thus any changes effective to this app would take place only once. Again, these are functions or methods capable of anything required by your application, to effect all particular business practices. Therefore, in light of efficient N-Tier design, you really do want to avoid placing any business logic in your presentation layer, and here is where it would all go.

Data Access Layer

The data layer, on the other hand, is where we'll be covering various scenarios, and the best methods to use for the best performance and results. This layer encapsulates and compartmentalizes all our data access code within nice, clean components, that interact with our database. Also, keep in mind that the following tips apply to non-Tier standalone applications as well :-)

Incidentally, take the time and prep yourself with An Introduction to ADO.NET for a good look at ADO.NET, with v1.1 features as well, prior to diving into this section. Furthermore, although this section is named Data Access, it could nevertheless include XML as a viable data source. Therefore, read Reading, Storing and Transforming XML Data in .NET for a good look into this methodology.

Now to start, some quick tips off the top dealing with both aspects of data, SQL and ADO.NET. We'll divide these here solely to offer optimal means in each for prime data access. This involves both efficient database design and queries, and employing proficient ADO.NET techniques in retrieving already polished data.

SQL

  • Normalize your data for maximum efficiency and faster performance.

  • When creating new SQL tables always consider the best data types to employ with the type of column data you'll be storing.

  • Index your tables using SQL's Create Index command and or Query Analyzer's Index Tuning Wizard, assuring that your databases are finely tuned.

  • Use SQL Stored Procedures for all your data access, as SQL Server compiles them for all future uses. Furthermore, in SQL, straight and narrow is the best method in writing SQL Stored Procedures. Never overcomplicate your procedures with unnecessary or excessive temporary tables, server-side cursors , as these do create performance bottlenecks. You're better off creating subqueries off a base query and or with a join (avoiding left joins).

  • Never name your stored procedures with a "sp_" prefix, as SQL will interpret this as a system procedure.

  • Create SQL Views for added clarity and security.

  • Include SET NOCOUNT ON in all your stored procedures, as this diminishes network traffic by eliminating the need for SQL in always returning how many rows the procedure affected.

  • Use sp_executesql to execute any standard non-Sproc SQL statements in your code, gaining Stored Procedure-type benefits and reducing overhead:

    sp_executesql N'Select * from table' 

    and even within a Stored Procedure, instead of strictly EXECing any query strings:

    DECLARE @sqlQuery nvarchar (100)

    SET @sqlQuery = N'SELECT * FROM database.dbo.table'

    EXECUTE sp_executesql @sqlQuery



  • Finally, optimize SQL Server itself to maximize performance. Read SQL Server Settings Optimization Tips for more info.



    ADO.NET


  • Create all your database access routines as generic, versatile objects, rather than client-side repeated-code methods. All your UI should do is interact with these components, and not have to work out any details. The methods aforementioned in the Presentation Layer apply here as well in creating components and controls.

  • For all intents and purposes, the golden rule for data access is as follows: If you want to page data or provide your application with functionality use a Dataset as the preferred method of disconnected data, otherwise use a Datareader for all your data retrieval. For XML users this would translate to an XmlReader , and StreamReader for text files, both equivalent as that of a DataReader for their respective file types.

  • Use the correct managed data provider for your particular database, ex. System.Data.SqlClient for SQL Server , System.Data.OleDb for Access, System.Data.OracleClient for Oracle , etc.

  • Use Strongly-Typed Datasets over the standard, common un-Typed ones when possible, as this yields better performance. A generated typed Dataset is basically an early bound class inheriting the Dataset base class, as opposed to the typical late bound, non-inheritable Dataset. Thus, it allows for greater flexibility in dealing with your data. In turn, you'll be dealing with easier to read code, whereby you can access fields and tables by customizable names, instead of the conventional collection-based way. There's a good article here on DNJ that you should definitely read called Using Typed DataSets . Here you'll find all you need to know to get you going.

  • As mentioned briefly in the General .NET Best Practices, take full, and I mean full advantage of .NET Caching, as this will significantly boost performance and greatly diminish any persistent database interaction. However, and an important however is, if you decide on caching your data from within your data object, then conventional caching methods won't apply. Within the confines of components, and its interaction with the caller page, these requests happen via an HTTP request. Therefore, caching within your component can only be implemented by using the HttpContext.Cache Class property, part of .NET's Page class.

  • Use parameterized Stored Procedures along side .NET's Command Class Prepare() method, that caches your query for all future SQL Server uses.

    objCommand.Prepare()

  • Make chunky calls to your database rather than smaller, chatty calls. It's better to group all similar, associated calls in one SQL Server access. In other words, use one solid connection to retrieve as much as you can, as opposed to multiple ones.

  • Avoid using Universal Data Link ( UDL ) files for OleDb connections as these can cause potential performance hits. .NET's SQLClient managed data provider does not support this, as prior versions of SQL Server did.

  • Take advantage of connection pooling (whereby all your connection strings are identical) by storing all your connection strings in your web.config file. Additionally, if your not enlisting any transactional procedures, include enlist=false ; to your database's connection string for added performance.

    Example:

    <configuration>

    <appSettings>

    <add key="myDatabase" value="User ID=id; Password=pw; Data Source=datasrc; _
    Initial Catalog=dbCat; Enlist=false;"
    />

    </appSettings>

    <configuration>

and call it from your page, code-behind source file or component like so:

            

[C#]

ConfigurationSettings.AppSettings["myDatabase"].ToString();

[VB]

ConfigurationSettings.AppSettings("myDatabase").ToString()

  • Finally, remember to close, clear and dispose of all your data objects no matter what. If you would like to further confirm that your database connection is indeed closed, you would write:

    if (dbConnection.State != ConnectionState.Closed) { dbConnection.Close(); }

Common ADO.NET Scenarios

  1. You want to retrieve one single value or item:

    Use the command class's ExecuteScalar method.

    object dbValue = objCommand.ExecuteScalar();

    Remember, this method always returns an object that you have to convert/cast to your specific type.

  2. You need to retrieve just a single row of data instead:

    Use the ExecuteReader's SingleRow Command Behavior

  3.               

    objDataReader = objCommand.ExecuteReader(CommandBehavior.SingleRow);



  4. You want to retrieve multiple database rows and automatically close your connection:

    Use a data reader.

    objDataReader = objCommand.ExecuteReader(CommandBehavior.CloseConnection);


    Not fast enough, then read all data sequentially as a data stream via ExecuteReader's SequentialAccess Command Behavior. Note that although obvious, I should point out all fields are to be read and access in sequential order starting from 0, dependent on your SQL query columns order.


    objDataReader = objCommand.ExecuteReader(CommandBehavior.SequentialAccess); 


    Still not enough? Read all fields by index position or strongly-typed accessor method, instead of field name


    string value = objDataReader(0);

    or try

    string value = objDataReader.GetString(0);



    *The GetString is one of the Datareaders type specific members to read columns thus reducing any runtime conversions.

    And get your results with your preferred method of datareader field reads:


    while (objDataReader.Read() == true)
    {

    Response.Write (objDataReader(0));

    }



  5. You'd like to open a database connection, read your data, then close it all pretty quickly without too much code

    This can be accomplished only in C#, with the using statement.

    using (SqlConnection string) {


    // Do database work


    } // Now connection is automatically closed



  6. Best method to retrieve values from a Stored Procedure, instead of looping through rows with a datareader:

    objCommand.ExecuteNonQuery();


  7. Want even better functionality when working with SQL Server? Then use Microsoft's new Data Access Application Block (DAAB) 2.0 . .NET introduces the SqlHelper class that dramatically cut's down development time by allowing you to execute commands against a SQL database, with Sprocs, all within a few lines of code (about 75% less code than you would typically need)!

    The example below demonstrates how incredibly easy it is to implement a Datareader to connect to and query a SQL database, whether using simple SQL syntax or a SQL Parameter, and all with one line of code!

    <%@ Import Namespace="System.Data" %> 
    <%@ Import Namespace="System.Data.SqlClient" %>
    <%@ Import Namespace="Microsoft.ApplicationBlocks.Data" %>

    <script language="C#" runat="server">


    void Page_Load (Object Source, EventArgs E) {

    string strConn = "server=(local);uid=sa;pwd=;database=northwind;";
    int categoryID = 1;


    //Using a SQL Query
    //Connection String, Command Type (Text), and SQL Query

    SqlDataReader objDataReader = SqlHelper.ExecuteReader_
    (strConn, CommandType.Text, "SELECT * FROM Products");



    //Using a Sproc
    //Connection String, Command Type (Stored Procedure), and Parameter
    SqlDataReader objDataReader = SqlHelper.ExecuteReader_
    (strConn, "getProductsByCategory", categoryID);


    while (objDataReader.Read() == true) {

    Response.Write (objDataReader[1].ToString() + "<BR>");

    }


    //Close and clear our object
    objDataReader.Close();
    objDataReader = null;

    }

    </script>



    That's amazing, considering this would usually be accomplished with roughly more or less 10 lines of code!

    The SQLHelper class used here provides the same amazing robustness for the Dataset. Not only that, but you're allowed transactional and Dataset database updating, as well as exception management!

    Now to implement this class into your project, you can do this one of two ways. One, as a private assembly by simply xcopying the Microsoft.ApplicationBlocks.Data.dll into your project's bin folder, or two, as a Shared Assembly whereby you install the dll into the Global Assembly Cache using .NET's gacutil.exe command line utility while noting that it has to be assigned a Strong Name. This can be accommodated using the Assembly Linker (Al.exe) utility program. Finally, within Visual Studio.NET, it's all a matter of simply referencing the DLL, by importing into your project.

    This sure is a cool new class, huh? I think so too. Download the Data Access Application Block for .NET v2 here.

So there you have most ADO.NET data access scenarios that you'll run into while creating your application. Pretty sure you've got enough now to get you moving.

Now that we've gotten our Tiers taken care of, we'll discuss methods that'll ensure us from having our application blow up in our faces - Error Trapping and Handling.

Error Trapping & Handling

Error handling is an essential consideration in all applications. You always want to make sure users aren't scared half to death with some unintelligible error message. Furthermore, you never, ever want any server-side errors ever shown in any way to the client, ever!

Not only that, but you'd want to catch any errors that may in effect break your application. Usually employing typical if-then conditionals are the best way to go in dealing with standard errors and controlling program flow. Still, at times prior to critical code execution, you may need further protection, and better overall error handling. However, overusing try/catch exceptions can decrease system performance!

.NET offers its try/catch error handling , where you'd first issue "try" to run the following code, capture any errors that may occur, and display the exception message, then take any appropriate steps to remedy it. Moreover, this error code block further attaches a finally to itself for any final code to execute:

          

try {

myDataGrid.Databind()

} catch (Exception e) {

Response.Write ("The following error occurred :" + e.Message);

} finally {

myDataGrid.CurrentPageIndex = 0

}

Of course, using finally is not mandatory, and doesn't require it being included. For more info read - Handling and Throwing Exceptions and Best Practices for Handling Exceptions.

Further VB error objects available is the On Error Statement, capable of resuming next and going to a preset error handling label via Goto.

Alternatively to assigning custom error pages through IIS's Custom Errors tab, another such method involves our famous web.config file. Invoking the try/catch error blocks do well for code errors, but server errors? Nope, not these. Here we'll use web.config's custom errors nodes :

          

<system.web>

<customErrors mode="On" defaultRedirect="errorpage.aspx">

<error statusCode="403" redirect="AccessIsNotAllowed.aspx"/>
<error statusCode="404" redirect="PageNotFound.aspx"/>
<error statusCode="500" redirect="InternalServerError.aspx"/>

</customErrors>

</system.web>

Now all errors are redirected to more friendly error pages, and if you prefer, why not pull in additional error information, and e-mail yourself or the appropriate party when the error occurs.

Don't forget that you may also implement global error handling not dealt with our try/catch code blocks, via your global.asax file's Application_Error method using .NET's System Error codes:

          

protected void Application_Error (Object sender, EventArgs e) {

Server.Transfer ("error.aspx?error="+Server.GetLastError().Message);

}

Now error handling in .NET, can and should at times be given over to Stored Procedures via the two good methods found in SQL:

  1. @@ERROR :

    DECLARE @RetValue int

    UPDATE database
    SET database.dbfield = 'Jimmy'
    WHERE dbfield = 'Dimitrios'

    IF @@ERROR <> 0

    BEGIN

    PRINT 'An error occurred'

    SET @RetValue = @@ERROR

    END

    ELSE

    BEGIN

    PRINT 'All OK'

    SET @RetValue = '0'

    END

    RETURN @RetValue



    With the @@Error function you declare a variable for the error, return this value at the end and read this client side or print the error through SQL itself. Note an error return of zero (0) indicates no errors at all.

  2. RAISERROR:

    Hard-coded error messages:

    RAISERROR ('Error Occurred in : %s', 16, 1)

    Or utilizing the sp_addmessage system stored procedure to produce error messages using the msg_id, as long as the msg_id is over 50000.

    RAISERROR (50001, 16, 1, @columnId)

Debugging

Debugging is the art of program flow error handling, where you track down errors that has prevented your code from fully executing. .NET has much improved the classic ASP way of Response.Writing to find and trap program errors and flow. To employ debugging you could do this:

  1. Application wide in your web.config file:

    <system.web>

    <compilation debug="true"/>

    </system.web>


  2. Or locally and easier is by setting Debug to true in your page's @ Page Directive and also your user-control's @ Control Directive

Tracing

Tracing in .NET is another means of debugging, but allows you to insert custom statements added to your page's output, whereby you can "trace" the flow of your code and determine the order of things at run time. Tracing, contrary to debug mode, presents you with a lot of information about your page. It further can help much in ascertaining any performance issues concerning your code. In any event, like debugging, tracing could be implement the same two ways:

  1. Application wide via the web.config file:

    <system.web>

    <trace enabled="true"/>

    </system.web>


  2. Or locally within each page's @ Page Directive:


    <%@ Page Trace="true" TraceMode="SortByTime | SortByCategory" %>


    And within your code you would trace by writing:

    Trace.Write("Tracing has started")

    Trace.Warn("Examine more closely here")

    Trace.Write("Tracing has ended")


    Trace. Write produces standard output, whereas using Trace.Warn would output your text in red for added notice. And bear in mind that the best place to Trace is usually before and after any major code sections, i.e. Datagrid binding, editing, etc.

With all this error handling now under our belt, what happens now when an error does occur? How do we fix it? Well, next we'll look at some common, though not basic, .NET system errors.

Common .NET Errors

  • CS0019: Operator '&' cannot be applied to operands of type 'type' and 'type'

    This happens when you are trying to use the wrong operator for the specific operation.

  • CS0029: Cannot implicitly convert type 'object' to 'string'

    This error occurs when you are typically trying to present an object as a string. The solution here is to append ".ToString()," that now represents the object as a string.

  • CS0103: The name 'whatever' does not exist in the class or namespace / CS0246: The type or namespace name 'whatever' could not be found (are you missing a using directive or an assembly reference?)

    This will usually occur when you have not declared a variable globally and or it's missing altogether. CS0246 error also occurs when you are improperly declaring a object or variable, as well as it not existing in your code.

  • CS0117: 'object' does not contain a definition for 'Length'

    Again this has to do with improperly trying to convert a value. To remedy this properly cast or box the value in question.

  • CS0118: 'System.Configuration.ConfigurationSettings.AppSettings' denotes a 'property' where a 'method' was expected

    This error occurs when your AppSettings key is accessed with parentheses in VB, instead of brackets in C# or vice-versa.

  • CS0119: 'whatever' denotes a 'class' which is not valid in the given context / Object reference not set to an instance of an object

    One cause of this error is to improperly call an object without the "new" keyword in C# and "As New" in VB

  • CS0161: 'method name': not all code paths return a value

    This will happen when you are trying to make a function method behave like a subroutine or vice-versa. Use the proper method keyword to remedy this. Void is for methods that do not return a value, although you could response write the output. Whereas, applying a keyword value like string, int, works alongside a return value at the end of the method.

  • CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement

    This error is produced by a number of sources.

    1. If you call an object that requires () after the command, you'll get this error. Ex. database.Open() not database.Open
    2. Using an assignment operator as opposes to an equality operator


  • CS1001: Identifier expected / BC30183: Keyword is not valid as an identifier.

    Both these similar errors occur when you forget to identify a variable without a keyword, or you were inadvertently forcing a keyword to behave as a variable or type.

  • BC30451: Name 'whatever' is not declared.

    This happens when you are referencing a web form control or variable that either doesn't exist or is named different. Also by chance the control in question may be missing the mandatory runat="server" option.

  • BC30512: Option Strict On disallows implicit conversions from 'type' to 'type'.

    Improper type conversions produce this error. Convert the value or method to the proper cast to fix this.

  • BC30554/BC30560: 'object' is ambiguous.

    This error occurs when you have more than one similarly named dll in your bin folder when compiling or it matched your control inherits call. Furthermore, more than one dll root namespace or class within are named the same. Your page or control are trying to access one source file where a source file and dll may simultaneously exist.

  • BC30574: Option Strict On disallows late binding

    This can happen from any number of reasons in your code. Some common ones are:

    1. Not setting the object's proper data type
    2. An expression that binds data was incorrectly parsed against the object at runtime In Datagrids, for instance, this occurs when omitting the DataBinder.Eval method on a data field
  • BC30684: 'name' is a type and cannot be used as an expression

    This can happen usually when you forget to use the new keyword when instantiating an object or invoking a constructor.

Phew, hope this all helps in fixing those pesky errors you may run into when coding. Don't think for a minute that they're all here. Yeah, right, take a look at a complete list of Compiler Errors CS0001 Through CS9999. Here I simply showed a number of the more common ones you'll probably run into, with some quick ways in figuring them out and remedying them.

Performance Testing

I really won't dwell too much on this topic, as whole books are available on this topics alone! Performance testing takes time and patience to fully realize before and after results. To monitor your site's performance, use the Admin Tools' performance monitors and take the time to add the appropriate counters to the system monitor and take it from there.

Not only that, but .NET gives you ability to programmatically do the same things, alongside accessing system logs and system processes with its System.Diagnostics namespace. Read Displaying Performance Monitor Information through an ASP.NET Web Page for more info.

At any rate, read the book in the above link, and check out the article. Subsequently, Microsoft also offers a software tool that pushes your app to its limits, its WAS Tool. This tool and other useful information can be found here - Performance Optimizing Tools.

Conclusion

Well you have alot to think about and take into consideration regarding the creation of a finely-tuned application machine, both scalable and secure, not to mention lightning fast. At least it'll give you pride in the finished result, when you present a robust, high-performance application, with little or no down time. In the world of Internet development and deployment, that speaks volumes!

Until Next Time. Happy .NETing </>

[Originally published on dotnetjunkies.com - 6/03]

".NET" 카테고리의 다른 글
  • Windows Workflow Foundation 규칙 엔진 소개 (0)2007/07/23
  • Introduction to Windows Communication Foundation (0)2007/06/15
  • High-Performance .NET Application Development &... (0)2007/04/29
  • 데이터 바인딩 어플리케이션 만들기 기초 (0)2007/01/11
  • 개체를 이용한 데이터 바인딩 어플리케이션 만들기 (0)2007/01/11
2007/04/29 11:52 2007/04/29 11:52
Posted by webdizen
Tags .NET, Application, Architecture, Development, High-Performance
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2866

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1  Next»

RSS HanRSS
Blog Image
webdizen
이곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (3009)
Webdizen (141)
Life (6)
Diary (16)
Blog (9)
IDEA (2)
Travel (10)
Book (16)
Photo (7)
Movie (8)
Music (14)
Leisure Sports (10)
Funny (6)
Hardware (121)
Software (120)
Windows (5)
Unix & Linux (120)
Installation (5)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (804)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (2)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (4)
Network (66)
Web (79)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • 타블렛
  • 웹 로그
  • Website
  • Internet
  • Audio
  • 석재복합연구소
  • 메모리
  • Security
  • Windows Presentation Foundation
  • find
  • 파일 시스템
  • 메모리 관리
  • CEdit
  • 양주 가격
  • Prado Framework
  • Programming
  • 생각의 탄생
  • TweetDeck
  • Dll Rebase
  • 악성 소프트웨어

Recent Articles

  • 트위터(Twitter)의 시작!.
  • 청년 리더의 조건.
  • 애플의 타블렛 PC - 아이패드....
  • 미래의 인터페이스 - 육감 기....
  • 기초발성법 동영상 강좌.

Recent Comments

  • 관리자만 볼 수 있는 댓글입....
    비밀방문자 03/12
  • 상대방의 이야기를 열심히 경....
    DoNuts 03/03
  • Lots of students know techn....
    Bobbi35Shannon 02/25
  • 좋은글 잘 보고 갑니다..
    Und_hacker 01/08
  • 재밌네요~ 첫번째꺼는 요즘....
    Hybrid 2009

Recent Trackbacks

  • printf,scanf를 이용한 형식....
    yundream의 프로그래밍 이야기 03/10
  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 2009
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007

Archive

  • 2010/02 (1)
  • 2010/01 (6)
  • 2009/12 (5)
  • 2009/09 (3)
  • 2009/08 (1)

Calendar

«   2010/03   »
일 월 화 수 목 금 토
  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      

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.