모든 작업의 마무리는 코드 품질 관리입니다.
우리는 코드 품질이라는 주제로 여러 편의 기사를 게재한 바 있습니다. 이를 통해 품질, 특히 코드 품질이라는 문제는 다양한 각도에서 접근할 수 있는(그리고 그렇게 접근해야 하는), 광범위한 주제라는 점을 알아보았습니다. 시리즈 마지막 편인 이번 글에서는 가장 핵심이 되는 항목을 간추려 조명해 보도록 하겠습니다.
코드 품질, 대체 무엇이길래?
Robert M. Pirsig이 쓴 “모터사이클 정비의 道”(Zen and the Art of Motorcycle Maintenance)를 보면, 품질(또는 ‘질’)이 과연 무엇인가라는 것을 오랜 시간을 들여 고민해 보면, 여러분의 삶 자체에 크나큰 변화가 찾아올 수 있다는 것을 알 것입니다. 그러니, 여기서부터는 조심스럽게 한 걸음 씩 나아가 보도록 하겠습니다.
코드 품질이 무엇이냐 하는 질문에는 정답이 없습니다. 하지만 우수한 코드 품질을 가르는 기준에 대해서는 여러가지 다양한 주장과 측정 규준이 존재합니다. 그 중 일부를 간추려 보면 다음과 같습니다.
- 하자의 개수 어찌 보면 당연하게 들릴 수 있는 말입니다. 하지만 이 말은 진정 무슨 의미일까요? 그리고 어떻게 측정을 하면 될까요? 물론 시간이 흐르면서 발견되는 하자를 세어서 통계적으로 측정하는 방법도 있습니다. 하지만 하자의 정확한 맥락이나 상황을 특정하지 않으므로, 주관이 개입할 여지가 있습니다.
- 코드는 관리의 대상이 됩니다. 따라서 적당한 노력으로 이를 읽고 해석할 수 있습니다. 그리고 복수의 개발자가 작업을 하는 경우, 그 정의에는 반드시 차이가 있게 됩니다.
- 코드 전체 또는 일부를 다른 프로젝트에 사용할 수 있습니다. 이 역시 측정하기 어려운 문제입니다. 코드의 재사용이 얼마나 용이한지를 측정할 수 있는 객관적인 기준이 없기 때문입니다.
- 코드가 안전하고 보안성을 갖추었습니다. 짐작하시는 바와 같이, 이 역시 명확하게 정의 내리기가 어려운 개념입니다. 장애 발생 시점 간의 평균 간격, 누적 발생 보안 하자 개수 등 다양한 통계적 측정 방식을 사용할 수 있습니다. 그러나 이것은 시간이 흐름에 따라 우리가 개선을 이루는지, 아니면 악화가 되고 있는지를 알려 줄 수는 있을 뿐이며, 우리가 지금 어느 정도의 수준에 있는지에 대해서는 대략적인 추정이 가능할 뿐입니다.
- 코드의 복잡성 – 코드 베이스가 얼마나 복잡하다고 인식되는지를 측정하는 방법은 여러가지가 있으며, 복잡성이 다른 측정치에도 영향을 미치게 되므로 코드의 복잡성은 가급적 낮은 수준으로 유지하는 것이 바람직하다고 할 수 있습니다. 그러나 일부 복잡성의 측정 지표를 낮추기 위해 코드를 다시 작성하다 보면 가독성이 떨어지고, 관리도 어려워질 수 있습니다. 이것은 코드의 관리 목적에 어긋나는 것입니다.
위의 여러 예시에서 공통적으로 살펴 볼 수 있는 주제는 이러한 측정이 오직 사실에 바탕을 두어 이루어질 수 있다는 점입니다. 위와 같은 측정은 코드 개선의 측정에 있어 중요한 요소가 될 수 있습니다. 하지만 실제로 어떻게 개선을 이룰 수 있는지를 알아 내는 데에는 큰 도움이 되지 않습니다. 또 한가지 생각해 보아야 할 교훈은 우수한 품질의 기준을 정확하게 정의하기는 어렵지만, 품질이 좋지 않은 코드는 한 번 보면 확연히 티가 난다는 것입니다.
반복되는 주제
임베디드 기기를 대상으로 하는 소프트웨어 개발 과정 역시 일반 소프트웨어 개발 과정에서 겪을 수 있는 여러 문제점이 발생하게 됩니다. 개발자가 취급하는 기기 중에는 핵심 임무를 수행하는 중요한 기기들도 존재합니다. 뿐만 아니라 원격지, 가혹 환경에서 작동해야 하는 기기들도 존재하며, 일부 기기는 소형 코인셀 전지를 사용해 거의 무기한으로 작동해야 하는 것들도 있습니다. 이와 같은 전개 시나리오는 데스크톱 또는 서버 소프트웨어 시장에서는 좀처럼 발생하지 않는 것들입니다.
Shawn Prestridge가 Move fast and break things? Not so fast in embedded 라는 제목의 기사에서 지적한 바와 같이, 임베디드 소프트웨어 개발에서는 최근 소프트웨어 엔지니어링에서 유행하고 있는 트렌드가 적용되지 않는 경우도 있을 수 있습니다. 임베디드 소프트웨어 개발에서는 한 번 내린 의사 결정이 두고두고 영향을 미치기도 합니다. 따라서 프로젝트 초기에 기본적인 원칙을 확립해 두는 것이 좀 더 나은 품질의 소프트웨어를 빠르게, 안정적으로 공급할 수 있는 첩경이 될 것입니다.
이번 시리즈를 통틀어 누누이 강조하고 있는 내용이 하나 있습니다. 미리 준비를 해 두면, 두고두고 코드 품질과 안전성, 보안에 긍정적으로 작용한다는 것입니다. 코드품질, 안전성, 보안의 정의를 어떻게 내리는 가는 별개의 문제이지만 말입니다. 이쪽이 개발자의 정신 건강에도 긍정적으로 작용합니다.
보안이 최우선
만일 완전히 새로운 아이디어에 바탕을 둔 신제품을 개발하던, 아니면 기존의 설계에서 덧붙여 나가던, 한 가지는 확실합니다. 요즘 설계되는 기기는 대부분 통신 연결 기능을 갖추게 됩니다. 와이파이, 블루투스, ZigBee, 심지어 유선 연결 등 연결의 방식을 제쳐 두고, 우선 지난 수 년간 얼마나 다양한 자동차 CAN 버스 해킹 시도가 있었는지를 한 번 생각해 보시기 바랍니다. 어떤 방향으로든 개발자가 의사 결정을 내리는 시점에서, 그에 따라 향후 해킹, 크래킹, 기기 무결성 침해와 같은 취약성이 발생하게 됩니다. 여기에, 커넥티드 IoT와 관련해 전 세계 지역별로 새롭게 제정되는 규제에 노출되게 됩니다. 현재 이러한 법규의 초점은 민감한 개인 정보의 보존 및 보호에 맞추어 져 있습니다. 그러나 소프트웨어의 무결성을 보존하는 방식을 통해, 하나의 취약 기기에서 다른 기기로 옮겨가며 이루어지는 캐스케이딩 어택을 방지하는 것도 중요한 과제가 되고 있습니다.
개발자가 개발 초기에 내리는 보안 관련 의사 결정은 실제 제품의 생산과 전개는 물론, 제품의 생애주기 전반에 걸쳐 영향을 미치게 됩니다. 뿐만 아니라 프로젝트 진행 과정 중 보안 관련 결정 사항을 변경하는 경우에는 상당한 비용이 소요될 수 있으며, 변경 자체가 불가능할 수도 있습니다. 그러므로 지금 당장은 일부러 보안 문제를 무시하려고 하더라도 나중에 그로 인해 외통수에 몰리는 일이 없도록 하십시오. 나중에 후회할 일이 생길지도 모릅니다. 이와 관련해 Clive Watts는 설계를 통한 최선의 보안 기법 구현(How to cover the best security practices by design)에서 이와 관련해 고민해 봐야 하는 부분들을 하나 하나 짚어주고 있습니다.
분석하라!
이번 시리즈에서는 다양한 맥락에서의 정적 분석과 런타임 분석에 대해 집중적으로 살펴보았습니다. 임베디드 개발에서는 주로 C 및 C++을 통해 개발을 하게 됩니다. 하지만 이 언어들 그 자체로는 신뢰할 수 있는 시스템 코드를 작성하는 데에 적절하다고는 할 수 없습니다. 이들 언어에서는 그 작동 양상이 정의되어 있지 않은 부분이 많으며, 구현에 따라 정의되는 부분이 상당합니다. 따라서 하나의 툴/하드웨어 조합만을 고집하는 개발자는 조합에서는 성공하기 어렵습니다. 이와 같이 언어 자체가 지니는 모호성이 상당하므로, 아무리 베테랑 프로그래머라도 이것을 모두 기억하는 것은 불가능합니다. 뿐만 아니라 코드에 오자가 있는데 어떻게 컴파일이라도 되는 경우에는 원래의 의도와 완전히 다른 방식으로 작동하게 됩니다.
기능적 안전성 요건을 충족해야 하는 개발자라면 이런 경우 별도로 정해져 있는 코딩 표준(MISRA 등)을 준수함으로써 이런 문제를 사전에 방지하는 것이 ‘매우 권장된다’는 것은 잘 아실 것입니다. 그렇지만 여기서 드리고 싶은 말씀은 임베디드 시스템 개발이라면 명확하게 정의되어 있는 하위 언어를 사용하는 것이 언어 상에 고질적으로 존재하는 기괴한 규칙에 의해 발생하는 하자를 막는 데에 반드시 도움이 된다는 점입니다. MISRA 지침은 아주 좋은 출발점이 될 수 있습니다. 그러나 커넥티드 시스템의 경우에는 CERT C도 고려해 보시는 것이 좋습니다. CWE도 나쁘지 않습니다. MISRA 또는 CERT에서는 임베디드 관련 CWE 내용의 상당 부분을 포 함하고 있습니다. 하지만 CWE 관련 내용 전반을 한 번 살펴 보시는 것도 나쁘지 않습니다.
또 지금가지 코드 품질이라는 개념이 모호하다는 말씀을 드려 온 부분을 생각하면 자동화 코드 분석은 개발 초기부터 코드 품질 개선에 도움을 얻을 수 있는 길이 될 것입니다. 코드 구조체 중에서 문제의 소지가 있는 부분을 바로 알려주기 때문입니다. 이와 같은 분석 결과를 바탕으로 함으로써 나중에 망연자실할 일이 없이 미연에 문제 요소를 제거, 확실하게 품질의 개선을 이룰 수가 있습니다.
MISRA와 같은 하위 언어(language subset) 표준과 함께, 하위 언어에서 포함하지 않는 부분에 대해서는 자체적인 코딩 표준을 같이 적용하게 되면 일관성을 유지하는 데에 도움이 됩니다. 예를 들어 명칭 부여 방식, 모듈 내 기능 정리, 글로벌 접근 변수 및 데이터 구조체의 이용 규칙, ‘휘발성’(volatile) 키워드의 사용 시기 등이 여기에 해당됩니다. 명확한 규칙, 그리고 가능한 경우 규칙의 준수를 위한 일부 툴 지원을 통해 코드의 품질을 비약적으로 향상시킬 수 있을 것입니다.
안 쓰면 버리자
더 나은 코드 품질을 실현하는 동시에 전반적인 생산성 향상을 원하시는 경우, 적절한 툴을 사용하는 것이 무엇보다 중요합니다. 여기서 예상되는 단점은 일부 기능, 또는 프로세스 개선을 위해서는 새로운 툴이 필요하다는 점입니다. 그렇지만 이러한 툴을 이미 보유하고 있을 수도 있다는 점은 긍정적인 측면이 될 것입니다. 또, 혹시 혜안이 있는 관리자라면 회사 차원에서 이미 분석 툴 등을 구비해 놓았을 수도 있습니다.
개발 환경에서 사용할 수 있는 툴의 자료를 꼼꼼히 읽어 두는 것도 미처 알지 못했던 기능을 발견해 도움을 줄 수도 있습니다. 가장 먼저 살펴보면 좋은 부분이 바로 디버깅 환경입니다. 사용자 피드백을 바탕으로 생각해 보면 아마 기존에 알지 못했던 기능을 새로 발견하게 되실 가능성이 높습니다. “도저히 답이 없으면 매뉴얼을 읽어라”라는 격언이 있습니다. 이 말은 사실에 꽤나 가깝습니다. 대부분의 경우는 기존에 알고 자주 사용하시는 기능의 범주 내에서 별탈없이 해결이 가능합니다. 그렇지만 어려운 문제에 부닥치게 되면, 미리 매뉴얼을 읽어 두기를 잘 했다는 생각이 드실 것입니다.
마지막까지 꼼꼼하게
이번 시리즈의 이전 글을 읽어 보셨다면, 코드 품질 관리를 위해 새로운 툴을 선택하셨기를 바랍니다. 앞서 알아본 바와 같이, 코드 품질이란 과연 무엇인지를 명확하게 정의하는 것은 매우 어렵습니다. 반면, 품질이 낮은 코드 작성을 피할 수 있는 도구와 아이디어라면 그간 많이 전해 드렸습니다.
또 다른 핵심 포인트는 코드 품질에 신경을 쓰게 되면 코드의 안전성과 보안성도 자동으로 높아지며, 신뢰성은 물론이고 관리도 더 편해진다는 점입니다. 이것은 실로 산뜻한 부작용이라고 할 수 있겠습니다! :) 그러니, 하나도 코드 품질, 둘도 코드 품질입니다.
작성: Anders Holmberg, IAR 시스템스 임베디드 개발 툴 총괄 관리자