정적 분석으로 자바스크립트 코드의 오류와 코드 스멜(Code Smell) 찾기 ① - 정적 분석 도구와 자바스크립트 코드 결함

정적 분석으로 자바스크립트 코드의 오류와 코드 스멜(Code Smell) 찾기 ①: 정적 분석 도구와 자바스크립트 코드 결함

자바스크립트는 근래 전 세계적으로 가장 인기 있는 프로그래밍 언어입니다. 특히 오픈 소스 생태계에서의 사용이 활발하여 2016년부터는 깃허브(GitHub)에서 최고 인기 언어로 선정되고 있기도 하죠. 자바스크립트는 웹 사이트뿐 아니라 서버, 모바일 및 데스크톱 애플리케이션 개발을 아우르는 대표적인 크로스 플랫폼 언어로 큰 관심을 받고 있습니다.

하지만 자바스크립트로 작성된 코드 베이스가 커져 가고 다양하고 파편화된 기술들이 등장하면서 코드를 디버깅하고 관리하는 품질 비용은 급격히 증가하고 있습니다. 자바스크립트의 약한 타입 시스템이나 동적 특성 외에도 코드 문제를 즉시 확인해주는 컴파일러(compiler)의 부재는 더욱 코드 품질 관리를 어렵게 합니다. 타입 검사(type checking)를 지원하는 타입스크립트나 Flow를 사용하면 버그의 15%를 예방할 수 있다는 연구 결과도 있을 정도입니다.

전통적으로 정적 분석 도구는 이런 품질 비용을 줄이는 데 유용한 역할을 했습니다. 연구에 따르면 제품 출시 후 발견된 결함을 수정하는 비용은 코딩이나 단위 테스트 단계에서보다 6배 가량 더 많다고 합니다. 정적 분석 도구는 개발 초기 단계에서부터 코드 결함을 발견하여 C/C++이나 Java 같은 언어에 대한 품질 비용을 크게 줄여왔습니다.

그렇다면 자바스크립트에 정적 분석 도구를 적용해 보는 건 어떨까요?

자바스크립트 개발에서도 정적 분석 도구를 통해 품질 비용을 낮추고 개발의 어려움을 줄일 수 있습니다. 자바스크립트는 그 언어 특성(약한 타입 시스템과 동적인 동작)으로 인해 정적 분석을 적용하기 어려운 것으로 알려져 왔지만 최근에 개발되는 정적 분석 도구는 이러한 문제를 극복하고 있습니다.

본 아티클을 통해 정적 분석 도구가 자바스크립트 개발에 어떤 도움을 줄 수 있는지 소개하고자 합니다. 정적 분석 도구의 작동 방식과 코드 결함의 예시를 통해 정적 분석 도구로써 예방할 수 있는 문제에 대해 알아보겠습니다. 끝으로 정적 분석 도구를 선택할 때 고려해야 할 사항들로 마무리합니다.

정적 분석의 작동 방식

먼저 정적 분석 도구를 이해하는 데 필요한 개념을 알아보겠습니다.

정적 분석 도구는 컴파일러와 비슷하게 작동합니다. 컴파일러처럼 소스 코드를 분석하여 프로그램의 추상 구문 트리(Abstract Syntax Tree, AST)와 기호 테이블(symbol table)을 생성합니다. 이 추상 구문 트리로부터 중간 표현(Intermediate Representation, IR)이 생성되며, 중간 표현에서 제어 흐름 그래프(Control Flow Graph, CFG)가 구성됩니다.

 정적 분석 도구의 아키텍처 [그림 1] 정적 분석 도구의 아키텍처  중간 표현(IR)의 예 [그림 2] 중간 표현(IR)의 예

정적 분석 도구는 이렇게 소스 코드로부터 구축된 구문 트리 및 흐름 그래프를 활용하여 특정 속성이나 의심스러운 코딩 패턴을 탐지합니다. 반면 간단한 분석만 가능한 단순 코드 분석 도구(JSHint 또는 ESLint 같은 linters)는 구문 트리만 구성하고 패턴에 일치하는 구문이나 스타일 문제를 찾습니다. 예를 들어, 'with' 문의 사용을 탐지하는 경우는 구문 트리에서 'with' 노드의 존재 여부만 확인하면 찾을 수 있는 문제가 되죠.

정적 분석 도구는 단순 코드 분석 도구와 다르게 구문 트리 외에도 흐름 그래프를 구성하고 전체 프로그램의 실행 흐름을 고려해(데이터 흐름 분석) 코드를 검사합니다. 변수의 현재 값이나 조건문(conditional statement)에서의 가능 조건과 같은 프로그램의 추상화된 상태를 흐름 그래프를 통해 추적하여 널 포인터 역참조(NULL pointer dereference)나 모듈 간의 잘못된 함수 호출 같이 더 어렵고 실제적으로 유익한 문제를 찾아낼 수 있습니다.

정적 분석 도구와 자바스크립트 코드 결함

정적 분석 도구로 예방할 수 있는 여러 유형의 코드 결함을 예시와 함께 다뤄보겠습니다.

최근 Rollbar(웹 애플리케이션의 실시간 오류 모니터링 서비스)에서 상위 10개의 자바스크립트 오류를 발표했습니다. 이 실행 오류는 실제 운영 중인 웹 사이트에서 수집된 것으로 만약 정적 분석 도구가 개발 단계에서 미리 이 오류를 찾고 예방할 수 있었다면 품질 비용을 낮추고 사용자 경험을 향상시킬 수 있었을 것입니다.

이 실행 오류 중 [그림 3]은 범위를 벗어난 값이 함수에 전달되었을 때 발생하는 RangeError의 예를 보여 주고 있습니다. 예를 들어 'Number.toFixed()'는 0에서 20까지의 인자만 허용하기 때문에 아래의 'Number.toFixed(25)'는 RangeError가 발생합니다.

RangeError 예시 (Rollbar) [그림 3] RangeError 예시 (Rollbar)

이러한 오류는 정적 분석 도구와 단순 코드 분석 도구 모두가 구문 트리를 통해 호출할 함수가 'toFixed'인지와 해당 인자를 탐지할 수 있습니다. 하지만 함수에 전달된 인자가 상수가 아닌 변수라면 변수 상태를 추적 가능한 정적 분석 도구만 이 RangeError를 탐지할 수 있습니다.

또 다른 실행 오류의 예는 널(null) 객체를 참조하여 발생하는 TypeError입니다.

TypeError 예시 (Rollbar) [그림 4] TypeError 예시 (Rollbar)

[그림 4]의 9행을 보면 'testFunction()'이 인자 없이 호출되고 있습니다. 그 결과 'testArray' 인자가 정의되지 않은 값(undefined)을 갖게 되므로 루프(loop)에서 'length' 속성에 접근할 때 TypeError가 발생하게 됩니다.
정적 분석 도구를 사용하면 위와 같은 오류도 해결할 수 있습니다. 정적 분석 도구가 함수의 의미와 그 호출 정보를 알고 있기 때문이죠. 따라서 위 예제에서는 원인(9행에서 인자가 누락된 채로 함수가 호출됨)과 오류 지점(4행에서 널 객체의 속성이 참조됨)을 미리 알려 줄 수 있습니다.

[그림 4]의 오른쪽 예는 이 널 포인터 문제의 간략화된 패턴을 보여줍니다. 정적 분석 도구는 변수 'test'가 1행에서의 할당 때문에 정의되지 않은 값을 갖게 된다는 것을 인식하고 2행에서 널 포인터가 참조되는 문제를 탐지할 수 있습니다.

마지막으로 [그림 5]를 통해 널 포인터 문제의 또 다른 패턴을 볼 수 있습니다.

불충분한 널 체크의 예 (Apache Ambari) [그림 5] 불충분한 널 체크의 예 (Apache Ambari)

2행에서 'data' 인자가 널인지 체크하고 있습니다. 하지만 널로 체크된 경우에도 실행이 멈추지 않기 때문에 널 값이 그대로 저장되고 결국 6행에서 'data' 객체에 접근할 때 TypeError가 발생하게 됩니다.
정적 분석 도구는 이렇게 변수의 널 체크가 일관되게 적용되지 않는 상황도 실행 흐름 상의 변수 사용을 추적하여 미리 찾아낼 수 있습니다.

지금까지 정적 분석의 작동 방식과 Rollbar에서 발표한 실제 오류 사례 중 일부를 살펴보았습니다. 다음 아티클에서는 이제 깃허브에 있는 수천 개의 공용 자바스크립트 및 타입스크립트 프로젝트를 분석하여 정적 분석 도구로 수집한 코드 결함 사례와 정적 분석 도구를 선택할 때 고려해야 할 사항들을 살펴보겠습니다.


에스코어는 시스템 소프트웨어 기술력과 컨설팅 역량을 바탕으로, 고객의 성공적인 Digital Transformation을 위한 IT 전략 수립, 신기술 기반 소프트웨어 R&D 및 플랫폼 개발/운영 서비스를 One-Stop으로 통합 제공합니다.

▶   해당 콘텐츠는 저작권법에 의하여 보호받는 저작물로 기고자에 저작권이 있습니다.
▶   해당 콘텐츠는 사전 동의없이 2차 가공 및 영리적인 이용을 금하고 있습니다.


김강호 프로
김강호 프로 IT테크놀로지 전문가
에스코어

김강호 프로는 타이젠 웹 SDK의 개발 리더를 역임했고 현재 자바스크립트 정적 분석 솔루션인 DeepScanTM의 제품 관리자를 맡고 있습니다. DeepScan 서비스(https://deepscan.io)를 통해 개발자와 관계를 맺고 싶어 합니다.

구독하기

인사이트 리포트 소식을 메일로 받아보세요