코드 인스펙션을 통한 소프트웨어 품질 향상
코딩 규칙의 적용


근래 각종 매체를 통해 국방, 항공, 핸드폰, 자동차, 철도 등에서 발생하는 각종 기능 오동작 사례들을 쉽게 접할 수 있다. 이러한 기능 오동작의 대부분이 소프트웨어의 문제로 밝혀지면서, 이를 예방할 수 있도록 개발 단계에서부터 소프트웨어의 품질을 올릴 수 있는 방법에 대한 많은 노력들이 이루어지고 있다. 본 고에서는 이러한 노력 중에서 소프트웨어 개발 조직의 성숙도와 상관없이 비교적 쉽게 적용할 수 있고 비용 대비 효과가 뛰어난 방법인 코드 인스펙션 (Code Inspection) 에 대해 논하고자 한다.
소프트웨어공학에서는 코드 인스펙션을 수행하는 방법을 여러 가지 소개하고 있지만, 이러한 방법들을 통해 추구하는 내용은 코딩 규칙을 적용시켜 규칙의 위반을 보는 것과 의미기반의 오류를 검출하여 잠재적인 결함들을 제거시키는 것이다. 본 고에서는 첫 번째 시간으로 코드 인스펙션의 효과를 살펴보고 대표적인 코딩 규칙인 MISRA-C에 대해 소개하도록 한다. 이어 코드 인스펙션을 적용할 때의 여러 가지 고려사항에 대해 살펴본 뒤 코딩 규칙을 적용함에 있어서 가장 바람직한 방법들을 제시하고자 한다. 


소프트웨어의 문제점?
 
국내에는 IT산업이 발달되어 있고 수많은 개발자들이 프로그래밍 언어에 대하여 학습을 하고 있다. 하지만 대부분의 교육은 프로그램의 구현 위주로 이루어 지기 때문에 구현 단계에서 많은 문제가 발생하곤 한다. 이러한 문제점에 대하여 fault, failure, flaw, error, mistake 등 수많은 용어들이 있다. 통칭하면 우리가 흔히 사용하는 용어인 '버그'라고 할 수 있다. 버그의 종류는 무기체계의 종류와 비교할 만큼 매우 다양하다.
대표적인 버그는 산술연산과 관련된 버그로 "0으로 나누기 연산", "산술 넘침(Arithmetic overflow or underflow)"이 있고, 논리연산과 관련된 버그로 "무한 루프", "재귀호출" 등이 있다. 리소스 관련 버그로는 "Null pointer 참조", "초기화 하지 않은 변수 사용", "메모리 오버 플로우" 등이 있다. 이외에 멀티 쓰레딩과 관련된 버그, 퍼포먼스와 관련된 버그 등 다양한 버그들이 있다.
좀더 상세한 내용에 대해서는 CWE(Common Weakness Enumeration, http://cwe.mitre.org/)를 참조하기 바란다.
다음은 내장형 소프트웨어의 버그로 발생한 대표적인 사고이다.

AECL사의 방사선 치료기 테락(Therac)-25 (1985년)
8비트형 변수에 대하여 최대값을 넘는 연산을 수행하며 "산술 넘침(Arithmetic overflow)"의 버그가 발생하였는데, 안전확인기능을 수행하지 않았고 결국 X-Ray 촬영 과정에서 과도한 방사선이 방출이 되었다. 이로 인하여 여섯 건의 사고가 발생하여 세 명이 사망하고, 다른 세 명은 심각한 방사능 후유 장애를 겪어야 했다.

아리안(Ariane) 5호 폭발사고 (1996년)
Ariane 5호를 개발하는 과정에서 이전에 성공적으로 발사가 되었던 Ariane 4호의 코드들을 재 사용하였다. 그 중 Ariane 5호의 IRS (Inertial Reference System)에 Ariane 4호에서 사용했던 요구사항을 재사용하면서 소스코드 역시 그대로 재사용하였다. IRS에서 "horizontal_veloc_bias"라고 하는 방향 벡터값을 설정하는 변수를 Integer로 변환하는 과정에서 16비트 기반으로 개발되었던 Ariane 4호의 소스코드에 64비트 기반인 Ariane 5호의 명령이 전달 되었다. 결국, 64비트 플로팅 포인트 값을 16비트 정수형 값으로 변환하는 과정에서 16비트 정수형으로 표현할 수 없는 값이 입력되어 문제가 되어 오버 플로우 (Overflow)가 발생이 되었다. 결국 방향 벡터 값을 잘못 인식한 Ariane 5호는 오버 플로우 결함으로 의도하지 않은 잘못된 소스코드를 수행하였고, 그 결과로 5억달러에 상당하는 대규모 프로젝트가 단 37초만에 실패하고 말았다.


[그림 1] Ariane 5호의 폭발 장면과 오류에 해당되는 소스코드 내용

위와 같은 문제들은 언제든지 발생할 수 있다. 하지만 내장형 소프트웨어에서는 이러한 사소한 문제들도 큰 사고로 직결될 수 있다는 것을 항상 명심해야 한다.


코드 인스펙션의 효과

인스펙션은 작업 산출물의 오류를 발견하는 공식적, 절차적 프로세스라고 할 수 있다. 인스펙션은 요구사항, 설계, 코딩 등 다양한 형태의 문서에 대하여 적용할 수 있다. 인스펙션의 절차를 간단히 설명하면 다음과 같다.

단계 / 작업 내용
계획 / 인스펙션 대상과 검토 항목을 정하고, 일정을 수립한다.
작업 소개 / 주최자가 모든 인스펙션 참가자에게, 계획 단계에서 정리한 내용을 설명한다.
준비 / 검토자가 사전 문서의 내용을 개별적으로 검사한다.
인스펙션 회의 / 모든 인스펙션 참가자가 모여 문서 내에 문제점을 제안하고 토론하여 개선 방안을 제시한다.
결과 적용 / 게시자 또는 작성자가 문서를 회의 과정에서 제안된 개선 방안대로 보안하거나, 발견된 문제점을 수정한다.
후속 조치 / 결과 적용 후, 수정 보완이 정확하게 이루어졌는지 점검한다.
[표 1] 인스펙션의 절차

통계적으로 이러한 인스펙션 활동이 각각의 단계에서 수행되면 동적시험에서 검출되는 오류의 약 60~80%를 제거할 수 있다는 사실이 알려져 있다.
이러한 인스펙션을 모든 개발 단계에 적용을 하면 가장 바람직하겠지만, 현실적으로 한번에 모든 과정에 적용하기 어려우므로 조직의 성숙도에 따라 단계별로 진행하는 것이 바람직하다. 그렇다면 어떠한 단계가 가장 효과가 좋은지 알아볼 필요가 있다. 다음의 표를 살펴보자.

SW life cycle / Defect Ratio Defect Potential(Per Function Point) / Removal Efficiency / Defect Remaining
Requirement / 20% / 1.00 / 77% / 0.23
Design / 30% / 1.25 / 85% / 0.19
Coding / 35% / 1.75 / 95% / 0.09
Documentation / 5% / 0.60 / 80% / 0.12
Bad Fixes / 10% / 0.40 / 70% / 0.12
Total / 100% / 5.00 / 85% / 0.75
[표 2] Average for Defect Removal Efficiency (U.S, 2007)

<표 2>를 보면 소프트웨어의 결함은 코딩 단계에서 많이 발생하며 수정하는 효율도 가장 높은 것으로 나타난다. 그래서 코드 인스펙션 부분은 전 세계에서 가장 많이 활성화 되어 있고, 국내에서도 코드 인스펙션은 소프트웨어 품질향상을 위한 초기 활동에 도입시키는 방법이다.
코드 인스펙션을 적용하는 대표적인 방법으로는 코딩 규칙을 적용하는 방법과 의미 기반의 Run-time 에러를 찾아내는 방법이 있으며, 본고에서는 코딩 규칙의 적용에 대해 먼저 알아보겠다.


코딩규칙?

일반적으로 요구사항이나 설계로부터 발생하는 오류는 동적 시험 단계에서 쉽게 눈으로 확인할 수 있다. 하지만 언어적 결함에 의하여 발생하는 오류는 개발 당시에는 정확하게 동작되고 나중에 문제가 발생하는 경우가 많기 때문에 동적 시험에서도 발견이 어렵다.
언어적 결함의 원인이 되는 대표적인 항목으로 "Undefined Behavior"와 "Implementation-defined Behavior"가 있다.
"Undefined Behavior"는 ISO-C 표준에 그 동작이 정의되어 있지 않은 항목들이다. 예를 들어 0으로 나누기 연산을 하는 것, 메모리 영역을 초과하여 사용하는 것, 지역변수의 주소를 반환하는 것 등이 있다. 그 증상으로는 시스템 리셋 등 심각한 증상을 보이는 문제들이 다수 존재한다.
"Implementation-defined Behavior"은 컴파일러의 구현에 따라서 달라질 수 있는 동작들을 가리킨다. "Implementation-defined Behavior"항목이 많을수록 소프트웨어는 호환성이 떨어지게 된다. 내장형 소프트웨어의 개발환경에서는 기존에 개발된 소스코드를 재사용하는 경향이 많기 때문에 어떠한 부분이 "Implementation-define Behavior" 항목인지 정확히 확인하지 않고 사용할 경우 많은 문제가 발생하게 된다. 이전에 잘 동작하던 소스코드를 다시 사용했을 경우 정상동작을 하지 않는 상황이 발생하면, 대부분 이러한 항목을 상당수 많이 포함된 소스코드라고 할 수 있다.
그렇다면 소프트웨어의 안전성을 확보하기 위하여, "모든 개발자가 언어의 표준을 보고 작성을 해야 하는가?" "만약 표준을 공부한다면 얼마나 봐야 하는 것인가?"라는 의문이 떠오른다. 이러한 수많은 고민의 결과 만들어진 것이 바로 코딩규칙이다.


MISRA-C는 무엇인가?

다음은 2011년 4월에 VDC에서 약 600여개의 개발 조직을 대상으로 표준 코딩 규칙을 사용하는 현황에 대하여 설문조사 한 자료이다.
코딩 규칙 / 사용률(%)
Cert C / 10.8%
Cert C++ / 26.14%
HIC++ / 17.61%
MISRA C / 36.36%
MISRA C++ / 6.25%
JSF AV++ / 1.14%
Other Standard / 1.7%
[표 3] VDC Market research for Coding Standard (2011, VDC)

<표 3>에서 보듯이 신뢰성 있는 표준 코딩 규칙으로는 MISRA에서 작성된 코딩 규칙이 가장 많이 사용되며, MISRA 기반의 JSF ++(JSF++ 는 MISRA-C를 기반으로 개발 되었다.)가 포함 될 경우 실제로 코딩 규칙을 적용하고 있는 조직의 약 44% 가 MISRA를 표준 코딩 규칙으로 선정하고 있다. (In-house, None, Don't know or N/A 항목 제외)
MISRA-C는 MISRA(Motor Industry Software Reliability Association)에서 개발된 C 프로그래밍에 대한 표준 가이드이다. MISRA-C의 목적은 ISO C언어로 작성된 내장형 소프트웨어의 안전성, 호환성, 신뢰성을 확보하는 것이다. MISRA-C의 초판은 1998년에 출간되었으며, 공식적으로 MISRA-C:1998로 알려져 있다. MISRA-C의 두 번째 버전은 2004년에 출간되었으며 MISRA-C:2004로 잘 알려져 있다.
MISRA-C는 자동차 산업계에서 만든 모델이지만, 자동차 산업 이외의 산업인 국방, 우주/항공, 의료장비, 철도 등 다양한 산업에서 Best Practice로서 광범위하게 적용되는 가이드라인이다.

 
[그림 2] MISRA-C:2004


코딩 규칙의 적용

앞에서 우리는 프로그래밍 언어로 개발하는 과정에서 여러 문제점이 발생할 수 있다는 것을 알았고, 내장형 소프트웨어에서는 사소한 버그가 대형 사고로 이어질 수 있기 때문에 높은 관심을 가지고 구현해야 한다는 것을 알았다. 또한 전 산업에서 Best Practice라고 언급되는 MISRA-C에 대하여 알아보았다. 그렇다면, 이러한 코딩 규칙을 무기체계에 적용시키기 위한 방법은 어떠한 것이 있을까? 누구나 떠올리기 쉬운 방법은 다음과 같다.
첫 번째, MISRA-C의 위반사항을 검출할 수 있는 적절한 코드 인스펙션 도구를 선정한다.
두 번째, 도구를 적용하여 MISRA-C 위반사항을 검출하고 수정을 한다.
그렇다면 이 두 단계 과정만으로 성공적인 품질향상을 이룰 수 있을까? 결론은 "어렵다"이다. 일반적으로 앞의 두 단계만 가지고 코딩 규칙을 적용하면 대부분 실패하게 된다. 실패에 대한 그 이유를 하나씩 살펴보자.
첫 번째, 도구들마다 다양한 기능들은 가지고 있겠지만, 실제 적용하면 상당한 시간과 노력을 기울이고도 좋은 결과를 얻지 못할 수 있다. 이유가 무엇일까? 어떠한 규제를 단순히 통과하기 위한 "시늉"이 아니라, 진정한 소프트웨어 품질 향상과 성공적인 프로젝트 수행을 위해서는 다음의 사항들을 고려해야 한다. 도구가 정확하게 위반사항들을 작은 오 검출 비율로 검출하는 것이 중요하다. 더불어, 도구를 개발 프로세스에 적용하기 쉬워야 하고, 더 나아가 개발 프로세스와 융합하여 자동화를 추구하는 방안까지 염두해야 할 것이다. 이를 위해 다양한 도구 업체들의 달콤한 마케팅 자료에 속지 말고 성공사례, Reference, 그리고 사용을 위한 도구 공급사의 기술지원 역량을 세심하게 확인해야 한다.
두 번째, 기본적으로 코딩 규칙의 교육이 반드시 필요하다. 각각의 코딩 규칙은 고유의 의미를 갖고 있다. 그 의미를 정확하게 파악해야 명확한 품질 향상을 꾀할 수 있다. 예를 들어 작년 11월에 개정된 방위사업청 지침에 수록된 포함된 코딩 규칙을 보면 1) 개발자의 실수를 방지, 2) 데이터 변형의 엄격함 3) 가독성과 관련된 규칙 등 다양한 의도를 가지고 작성 되어졌다.
규칙의 정확한 의미를 파악하지 못 할 경우, 코딩 규칙 위반사항을 "결함"으로 오해를 하게 된다. 코딩 규칙의 순기능 중 하나인 "개발자의 실수를 방지"하는 특징만을 부각하여 자칫 규칙 위반 사항에 대해 "결함"이라고 인식을 하게 되면, 검출된 결과가 개발자 자신이 생각하는 "결함"과 차이가 있기 때문에 코딩 규칙의 실효성에 대해 의문을 표하게 되고 결국 다시 "시늉"으로 전락될 수 있다. 이를 방지하기 위해서는 소프트웨어 개발자들은 각 코딩 규칙에 대한 교육을 반드시 수료할 필요가 있다. 또한, 반드시 예외처리에 대한 논의와 절차 방안이 필요하다. 무작정 예외처리 적용을 하게 된다면 모든 기준들이 유명무실하게 되기 때문이다. 명심하자. 소프트웨어 코딩 규칙은 관리의 목적보다 궁극적으로 소프트웨어 품질을 향상시켜 프로젝트를 성공으로 이끌기 위함이다.


어떠한 규칙이 좋은 규칙인가?

<표3>을 보면 언어의 구분을 제외하고 MISRA-C가 가장 많이 사용되고 있다. 그렇다면 MISRA-C는 전부 적용이 용이한가?
--------------------------------------------------------------------------------------
Rule 1.1 (required): All code shall conform to ISO 9899:1990 "Programming languages ? C", amended and corrected by ISO/IEC 9899/COR1:1995, ISO/IEC 9899/AMD1:1995, and ISO/IEC 9899/COR2:1996.
-------------------------------------------------------------------------------------
위의 규칙 1.1 예제는 MISRA-C의 첫 번째 규칙이다. MISRA-C 원문에는 이 규칙에 대하여 어떠한 예제 코드도 제공하지 않는다. 매뉴얼로 이 규칙을 적용하기 위해서는 개발자가 ISO-C 표준에 대하여 모두 숙지하고 있어야 한다. 위의 예제는 사실상 매뉴얼로 적용이 불가능 하다고 할 수 있다. 하나의 코딩 규칙이 너무 광범위한 내용을 다루고 있기 때문이다.
----------------------------------------------------------------------
Rule 10.5 (required): If the bitwise operators ~ and << are applied to an operand of underlying type unsigned char or unsigned short, the result shall be immediately cast to the underlying type of the operand.
--------------------------------------------------------------
위의 규칙 10.5 예제는 적용하기 쉽고 교육의 효과도 높은 규칙이라고 할 수 있다. 정확한 의미를 갖고 있으며 "underlying type"과 규칙이 설명하고자 하는 핵심 부분인  "integral promotion"에 대하여 학습할 수 있다. (용어참조: ISO 9899:1990, MISRA-C:2004)
이와 같이 MISRA-C 내에서도 광의적인 내용이 포함된 규칙부터 세부적인 규칙까지 다양하게 포함되어 있다. 좋은 규칙에 대한 기준은 다양할 수 있지만, 하나의 규칙에 대한 다양한 해석이 이뤄지는 동상이몽과 같은 규칙들은 위반여부를 판단하는 기준 수립이 어려울 것이다. 금번에 방위사업청에서 개정한 코딩 규칙 가이드는 개발자들로 하여금 오해의 폭을 줄이고, 실무적으로 적용시킬 수 있도록 예시에 대한 내용을 포함하였다.
하지만, 제정된 코딩 규칙만으로 충분하다고 할 수는 없다. 이는 최소한으로 지켜야 할 코딩 규칙을 제정한 것이며, 각 프로젝트/기업별 적용할 코딩 규칙의 마지노선으로 활용이 되어야 할 것이다. 각 프로젝트들 마다 개발자의 성향들을 파악하여 프로젝트에 맞는 코딩 규칙들을 선별적으로 적용하고, 더 나아가 점차적으로 규칙의 수를 확장하는 방법이 가장 효과적인 방법이 될 것이다.


결론
 
코딩 규칙은 소프트웨어의 품질을 향상시키는 수많은 방법중의 하나이다. 코딩 규칙을 선정함에 따라 가독성, 유지보수성, 신뢰성 등 다양한 소프트웨어의 품질의 향상을 얻을 수 있지만, 초기부터 너무 많은 코딩 규칙들이 적용하면 품질 향상을 꾀할 수 있으나 개발의 병목현상이 발생되어 프로젝트의 기간에 지장을 초래하게 될 것이다.
지금까지 말한 내용을 정리해 보면 다음과 같다.
- 소프트웨어에는 개발 과정에서 다양한 버그들이 만들어진다.
- 코드 인스펙션을 통해 개발 과정에서의 버그들을 검출할 수 있으며, 코드 인스펙션은 코딩 규칙과 의미기반 오류를 검출하는 과정이 있다.
- 코딩 규칙에는 MISRA가 각 산업 군 (국방/항공, 자동차, 의료, 중공업 등)에서 널리 사용되며, 코딩 규칙의 대표성을 지니고 있다.
- 좋은 코딩 규칙을 선정하고, 개발 언어에 부합되는 서브 셋을 구성한다.
- 선정된 코딩 규칙들을 소프트웨어 개발자들은 숙지한다.
- 프로젝트별, 개발자별 특성에 맞춰 선정된 코딩 규칙을 점차 확대 적용시킨다.
- 더 나아가 내부 프로세스에 맞도록 자동화시켜 소프트웨어 개발 전순기에 모두 적용시키도록 한다.
국내에서는 좋은 코딩 규칙을 선정하고 교육을 수행하는 개발 조직이 일부에 한하여 수행되고 있다. 코딩 규칙은 모든 조직원이 그 필요성을 인식하고 적극적으로 추진해야 큰 효과를 얻을 수 있으므로, 이러한 내용들을 방산 업체 전반적으로 확대하여 소프트웨어 품질, 더 나아가 방산 프로젝트의 성공을 위해 방위사업청에서 적극 장려할 계획이다.


참고문헌

위키백과 - C programming language
http://en.wikipedia.org/wiki/C_(programming_language)
CWE(Common Weakness Enumeration)
http://cwe.mitre.org/
ISO 9899:1990
http://www.iso.org/iso/catalogue_detail.htm?csnumber=17782
MISRA공식 홈페이지
http://www.misra.org.uk/
VDC Market Research for Coding Style
MISRA-C:2004 - Guidelines for the use of the C language in critical systems
위키백과 - Fagan Inspection
http://en.wikipedia.org/wiki/Fagan_inspection
MEASURING DEFECT POTENTIALS AND DEFECT REMOVAL EFFICIENCY
http://www.rbcs-us.com/images/documents/Measuring-Defect-Potentials-and-Defect-Removal-Efficiency.pdf


이 기사를 공유합니다
저작권자 © 테크월드뉴스 무단전재 및 재배포 금지