![]() |
Korea
![]() |
| ||||||||||||
IBM 홈 | 제품 & 서비스 | 고객지원 & 다운로드 | 회원가입 |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() | |||
![]() |
Diagnosing Java Code : '스팩(specification)'이라는 줄타기를 하다. |
![]() |
![]() |
![]() | |||
![]() |
![]() |
프로그램 사양은 신뢰성 있는 소프트웨어 개발에 중요하다. 잘 정의된 사양이 없으면 소프트웨어 시스템의 잘못된 작동을 진단하기가 어렵다. 그러나 많은 소프트웨어 시스템의 프로그램 사양은 허술하게 정의되어 있다. 더 나쁜 것은 프로그램 사양이 아예 없는 시스템도 많다는 것이다. 직관적으로 말해, 프로그램 사양은 프로그램의 행동을 기술해 놓은 것이다. 사양은 여러 형태를 취할 수 있지만, 하나의 스레드는 그 형태와 상관없이 모든 인스턴스에서 수행된다: 사양은 시스템이 올바로 작동하고 있는지를 알 수 있게 해주기 때문에, 일정 형태의 시스템 사양을 가지는 것은 필수적이다. 사양은 개발 중인 시스템의 안정성과 중요성 뿐 아니라 시스템 배치 후 수정이 얼마나 쉬운지에 따라 형식에 맞추어 정의될 수도 있고 느슨하게 정의될 수도 있다. 사양이 왜 중요한지와 왜 종종 무시되는지, 그리고 어떻게 상황을 개선시킬 수 있는지를 먼저 논의해보자. 마이크로 프로세서 설계의 세계에서 시스템은 개인용 컴퓨터에서부터 필수적인 의료 및 군대 시스템에 이르기까지 다양한 애플리케이션에
광범위하게 배치된다. 다음은 이 세계에서 당연하며 깰 수 없는 규칙이다. 배치후에 칩 설계를 수정하려면 엄청나게 비용이 많이 든다.
따라서 마이크로프로세서 사양이 보통 형식에 맞추어 작성되는 것은 놀라운 일이 아니다. 형식화된 사양은 자동적으로 해석되고 분석될 수 있다는 점에서 엄청난 이점을 가지고 있다. 마이크로프로세스의 경우 설계의 많은 측면이 올바른지가 자동적으로 입증될 수 있다. 소프트웨어 세계에서 배치와 중대성 측면에서 마이크로스로세스와 가장 유사한 가공물은 프로그래밍 언어이다. 인기있는 프로그래밍 언어는 사소한 것에서부터 아주 중대한 수준의 시스템까지 모든 레벨의 시스템에서 수 많은 프로그램을 작성하는데 사용될 수 있다. 칩과 마찬가지로 사람들이 언어를 사용한 후에 그 언어의 설계를 바꾸려면 엄청난 비용이 든다. 기존 프로그램이 수정되고 재컴파일되어야 하기 때문이다. 따라서 프로그래밍 언어에 대한 사양은 다른 소프트웨어 시스템과 비교했을 때 형식에 따라 작성된 경우가 많다. 이 형식성은 구문의 경우 특히 중요하다. 실제로 모든 현대 프로그래밍 언어는 형식에 맞추어 명시된 구문을 가지고 있다. 대부분의 파서들은 그 문법을 읽고 산출물로 충분히 완성된 파서들을 만들어 내는 자동 파서 생성기를 사용하여 구축된다. 불행히도 언어의 의미론은 그렇게 엄격하게 명시되지 않는 경향이다. 그러한 엄격성이 불가능하기 때문은 아니다. ML과 같은 언어들은 형식을 갖춘 의미론을 가지고 있으며 그 결과, 그에 관한 많은 정리가 입증되어왔고 특정 측면(예를 들면 유형 시스템 완전성과 같은)에서 그들의 정확성이 확인되었다. 그러나 ML과 같은 언어는 예외이다. 왜 그런지에 대해 두 가지 이유를 들 수 있다. 우선, 프로그래밍 언어 사양의 특성을 실제로 입증하는 것이 하드웨어 설계에서보다 훨씬 어렵기 때문에 형식화된 사양에 대한 요구가 적은 것이다. 대신 많은 언어가 산문체로 명시된다. 컴파일러 작성자와 같이 실제로 이를 사용할 대부분의 사용자에게는 이 산문체의 사양이 충분하다. 실제로 컴파일러 작성자는 종종 보다 덜 형식화된 사양을 좋아한다. 프로그램을 최적화할 여지를 더 많이 주기 때문이다. 언어의 또다른 사용자는 프로그래머들인데, 이들 대부분이 쉽게 이해할 수 있는 비형식화된 사양을 훨씬 더 좋아한다. 두번째 이유는 많은 언어가 프로그래밍 언어를 전공하지 않은 개인 개발자에 의해 "취미 삼아" 개발되었다는 점이다. 불행히도 이 개발자들이 프로그래밍 언어 의미론을 명시하기 위해 개발된 형식을 항상 알고 있는 것은 아니다.
C++ 언어 사양은 심지어는 구문 레벨에서도 불명확한 점이 많다. 또한 사양의 많은 부분이 구현에 의존하고 있다. 그 결과 C++ 프로그램은 한 플랫폼 외에서는 의도한대로 행동하지 않는 경우가 종종 있다. Python 언어 사양은 구체적인 많은 부분을 구현에 맡기거나 정의하지 않은채 두었다. 결과적으로 Jython과 CPythoen과 같은 구현은 상호간에 동일한 행동을 제공해야 하는 만만챦은 과제에 직면하였다. 이 문제는 Python 언어가 상대적으로 단순하지 않았다면 (좋은 의미의) 더 악화되었을 것이다. 자버 언어에는 어떤 형식화된 사양 (ML의 사양과 유사함)도 존재하지 않지만 정확한 비형식적 사양의 개발에 많은 관심이 주어졌다. 자바 언어는 보통 JVM용 바이트코드로 컴파일되는데, 그 자체가 잘 명시되어 있다 (몇몇 형식 분석에 의해 그 사양의 불명확한 점이 일부 발견되기는 했지만). 또한 자바 API는 모두 JVM의 일부분으로 명시되어 있다. 그 결과 자바 코드의 이식성은 유례없이 높다. 여기서부터 우리가 얻을 수 있는 결론은 가능한한 정확한 사양을 가지는 것이 도움이 된다는 것이다. 그러나 사양에 문제가 있을 경우 가장 많은 비용이 드는 프로그래밍 언어의 세계에서조차 그러한 정확함은 드문데, 정확한 사양을 작성하는데 비용이 많이 들기 때문이다. 기업들은 시스템을 먼저 출범시키고 상세한 사양은 나중에 구체화하는 것이 (혹은 결코 구체화하지 않을 가능성이 더 높지만) 비용면에서 더 효과적이라는 점을 발견하였다. 의문의 여지 없이, 수명 주기가 짧고 배치 경로가 좁은 애플리케이션에 대해 정확한 전면적인 사양을 작성하는 것은 비용이 너무 많이 든다. 개발팀이 시스템에 대한 사양을 형식에 맞춰 작성하는 작업을 끝내기 전에 경쟁사들이 시스템을 출시할 수도 있다. 또한 고객 요구사항의 변경에 따라 대단위 사양이 업데이트되는 일은 매우 드물고, 따라서 무시된다. 전면적인 사양 작성에 비용이 너무 많이 든다면 개발팀은 소프트웨어를 명시할 때 어떤 방식을 취해야 할 것인가? 위 질문에 답하기 전에, 종종 선택되는 한가지 방식을 살펴보자. 그러나 이것은 가능한 것 중 실제로 최악의 방법이다. 위의 방식들과 대조적으로, 수많은 소프트웨어가 구별할 수 있는 사양 없이 구현된다. 소프트웨어가 완성되면 이의 구현 내용이 사양으로 제시된다. I 다시 말해, 소프트웨어가 어떤 행동을 보여주든간에 그것이 명시된 행동이라고 말해지는 것이다. 이 방식은 변경되기 쉬운 일정 종류의 형식화된 안에 대해 작업하느라 개발자가 시간을 소비하지 않아도 되기 때문에 좋다고 주장하는 사람도 있을 것이다. 그러나 프로젝트 사양이 종종 변경되는 것은 사실이지만, 구현 내용은 여러 면에서 조잡한 사양이 되기 마련이다. 다음에 몇가지 이유를 들어 보겠다.
구현시 행해지는 선택중 많은 것이 임의적이다. 따라서 향후 다른 플랫폼에서 구현하려면 기존 구현 외에는 의지할 것이 없다. 개발자들은 구현이 필요로 하는 행위를 결정하기 위해 수많은 구현 상세를 힘겹게 살펴보아야 할 것이다. 보다 추상화되어 명시되어 있다면 그러한 행동을 결정하기가 훨씬 더 쉬워질 것이다. 또한 구현이 글자 그대로 자체 사양이 된다면 그 구현시 나타나는 어떤 행위도 버그로 인정하기가 불가능할 것이다. 사양을 만들지 않았기 때문에 나타나는 예측 못한 결과이기 때문에, 해롭거나 짜증나는 행위를 "정상적"이라고 하는 소프트웨어 업체를 우리는 얼마나 많이 봐 왔던가? 세번째로, 최초의 개발자에게 구현이 왜 사양 역할을 효과적으로 할 수 없는지의 확실한 이유는 그러한 구현이 아직 존재하지 않기
때문이다. 이 개발자들은 그들이 개발하고 있는 시스템에 대해 일부 행동 모델에 의존해야 하기 때문에 그 모델의 소스가 소프트웨어의
사양 역할을 할 수 있다. 이 점은 개발자들이 적절한 비용으로 어떤 종류의 사양을 사용할 수 있는지에 대해 일부 해답을 제시한다. 한 기능을 구현하는 방법을 결정하기 위해 개발자는 그 기능이 어떤 것일지 마음속으로 어느 정도의 모델을 가지고 있어야 하지만, 전체 애플리케이션에 대해 마음 속의 모델을 가질 필요는 없다. 즉 사양은 한 번에 한 부분씩 개발될 수 있다. 이렇게 하면 사양 개발이 더 쉬워질 뿐 아니라 고객이 변경을 요구할 때 보다 효과적으로 수정될 수 있다. Extreme Programming (XP)에서 시스템에서 필요한 기능은 스토리 를 사용해 점진적으로 명시된다. 각 스토리는 시스템 행동의 한 측면을 간단하게 기술하고 있다. 예를 들어, Java IDE 사양에 포함될 수 있는 다음 스토리를 보자.
믿건 말건간에, 자바 언어에서는 정리하기가 실제로 어려운 스토리인데 그것은 부분적으로 블록 코멘트의 몇몇 독특한 특성 때문이다. 개발팀의 속도에 따라 그 스토리를 두 개 이상의 소규모 스토리로 나누는 것이 좋을 것이다. 그러나 이 스토리는 간단하고 명확한 언어로 작성된 짧은 스토리임에 주목하라. 따라서 필요할 때 분리하기 쉽고 사양의 부분들이 결합되는 것을 방지한다. 또한 스토리는 짧기 때문에 사양을 완전히 정비하지 않고도 업데이트할 수 있고 새로운 스토리를 추가할 수 있다. 따라서 스토리는 최종 제품에 대한 요구사항이 구현 과정 동안 종종 변경되는 산업 환경에서 특히 적합하다. 사양이라는 주제를 마치기 전에 말하고 싶은 마지막 이슈가 있는데 단위 테스트에 관한 것이다. XP에서 단위 테스트는 한 생활 방식이다. 프로그래머들은 구현을 작성하기 전에 단위 테스트를 작성하기 시작하고, 기능의 새로운 측면 각각에 대해 더 많은 단위 테스트를 계속 작성한다. 소프트웨어 프로젝트에서 엄격한 단위 테스트를 수행하면 두가지의 큰 이점이 있다.
정적인 유형과 마찬가지로 단위 테스트는 실행시킬 수 있는 형식으로 된 문서라 할 수 있다. 단위 테스트는 이상적으로 구현의 모든 측면을 다루기 때문에, 그리고 기능이 제대로 작동하는지 확인하기 위해 간단한 방식으로 그 기능을 호출하기 때문에, 프로젝트에 새로 합류하거나 몇몇 코드를 새로이 유지보수하기 시작하는 프로그래머들은 다양한 기능성 컴포넌트들이 무엇을 하는지 알아 보기 위해 단위 테스트들을 살펴보는 것이 좋다. 단위 테스트가 문서화될 수 있다는 의견을 처음 들었을 때 많은 사람들은 회의적이다. " 프로그램이 작성된 것과 동일한 언어로 프로그램에 대한 문서를 어떻게 작성할 수 있습니까?"그러나 이 질문은 코드 문서화의 핵심을 놓친 것이다. 코드는 코드가 하는 일을 설명하기 위해 문서화되어서는 안된다. 코드 자체가 이미 그것을 설명하고 있다. 대신, 문서는 한 블럭의 코드가 왜 그것을 수행하는지를 설명해야 한다. 코드를 읽는 사람이면 누구나 코드가 작성된 언어에 이미 익숙할 것이다. 그렇지 않다면 어떤 언어로 쓰여진 문서라도 도움이 되지 않을 것이다. 그러나 한 블록의 코드가 프로그램의 나머지 부분과 어떻게 상호작용하는지가 항상 명확한 것은 아니고, 그것이 바로 문서가 필요한 이유이다. 코드를 읽는 사람은 그것이 작성된 언어에 익숙하기 때문에 (익숙해야 하기 때문에), 코드의 의도를 코드와 동일한 언어로 설명하는 것은 아주 타당한 일이다. 또한 단위 테스트는 갱신 프로세스를 촉진시킨다. 어떤 기능이 망가졌는지 알아 보기 위해 일군의 단위 테스트를 언제든지 코드에 실행시킬 수 있다면 프로그래머는 그렇지 았은 경우보다 훨씬 더 자신있게 코드를 갱신할 수 있다. 소개된 버그의 대다수가 즉각적으로 추적될 수 있다. 따라서 단위 테스트는 강력한 소프트웨어를 작성하고자 할 때 큰 이점을 가져다 준다. 사실 단위 테스트는 문서 형태로 되어 있고 (구축 단계에 이들을 포함시킴으로써) 자동적으로 집행될 수 있기 때문에 , 테스트 자체를 시스템에 대한 사양으로 사용하도록 제안하고 싶은 유혹을 느낄 만하다. 모든 시스템 레벨의 테스트를 통과하기 위해 어떤 유효한 구현 내용을 필요로 할지 모른다는 점에서 테스트를 사양의 일부로 하는 것은 타당한 일이다. 그러나 단위 테스트로 전체 사양을 구성하는 것에는 몇가지 심각한 단점이 있다. 우선, 한 시스템에 대한 테스트 세트는 필연적으로 불완전하기 마련이다. 얼마나 많은 테스트를 명시하든간에 우리가 서술할 수 있는 것보다 많은 입력 사항과 시스템 상태가 항상 존재한다. 우리는 "가장 합리적인" 범위를 명시함으로써 테스트들을 해석할 수 있지만, 그러한 범위는 종종 불명확할 것이다. 또한 단위 테스트는 본질적으로 특정 구현의 특성을 적용한다. 시스템이 구현되는 방식은 한가지 이상이 있기 마련이다. 따라서 단위 테스트를 사양으로 사용하면 특정 구현을 사양으로 사용할 때와 동일한 단점이 있다. 따라서 테스트를 사양 전체로 보지 말고 사양을 확충하는 것으로 보는 것이 가장 바람직하다.
또한 구현을 사양으로 혼동할 때의 함정 뿐 아니라 단위 테스트로 여러분의 사양을 정의한다는 멋진 개념에 완전히 의존할 경우 발생하는 문제에 대해 내가 충분하게 입증했기를 바란다.
| ||||||||||