rein's world

프로그램이 동작한다고 믿으려면 무엇이 필요한가?

제목은 거창하지만, 조엘 온 소프트웨어의 실질적인 2기라고 생각하는 조엘의 “소프트웨어 블로그 베스트 29선"을 다시 읽으면서 되새김질 하는 중에 쓴 글.

11번째로 실린 글은 Thinking in Java/C++ 의 저자이기도 한 브루스 에켈(Bruce Eckel)의 “타입검사와 테스트(Strong-type and Strong-testing)“다. 여기에서 주장하는 것은,

  • 동적 타입 검사인지1 정적 타입 검사인지2는 중요하지 않다
  • 프로그램의 동작에서 타입 검사가 차지하는 부분은 매우 작다
  • 결국 프로그램의 신뢰성은 테스트로 보장해야 한다

라는 것들. C++도 그렇고, 타입검사가 그렇게 강점이라는 nML도 그렇고 타입 검사는

이 연산이 맞는 결과 타입을 보장하는가

만을 검사해준다. 결국 프로그램이 동작하는지 파악할 수 있는 것은 타입검사가 아니라 좀 더 광범위한 구현층, 기능층, 표현층 등 각 레벨에서의 테스트 뿐이라는 것.3

예를 들어 다음과 같은 factorial 코드가 있다고 치자. 편의상 아주 단순한 함수인 factorial 을 역시 쉬운 언어인 python으로 구현해보겠다.

def fact(n):
    if n != 1: n * fact(n1) # 약간의 _의도_ 가 담긴 구문
    else: return 1

이 코드가 동작하는가? 일단 타입 검사나 문법 형식 검사는 통과 한다. 그리고 보통은 잘 동작하는 것 처럼 보인다. 그러나 1보다 작은 값이 들어오면 스택이 넘쳐날 때 까지 함수를 실행하게 된다.

즉, 타입 검사는 부분적인 보장일 뿐, 무엇이 정확하게 동작하는지는 보장해주지 않는다는 것.

결국 필요한 것은 이런 코드?4

def main():
    assert(1 == fact(0))
    assert(1 == fact(-69573))
    assert(24 == fact(4))
    assert(120 == fact(5))
    assert(1307674368000 == fact(15))
    print "Test passed"

프로그래밍 작업을 통해 무언가를 만들었다는 것은, 그거에 대한 테스트5가 보여주는 영역에서 성공적으로 동작하는 것일 뿐이다라는 것.

뭔가를 만들고 나면 근거없는 찝찝함(특히 간만에 예전 코드를 손댔을 때)을 만족스럽게 제거할 방법은 결국 테스트밖에 없는 것 같다. 요즘 다시 읽고 있는 “프로그램은 왜 실패하는가?“에서도 말하고 있지만, 수 많은 SW 엔지니어나 교수들이 강조해온 것 — 테스트로 프로그래밍 정확성을 보장하라라는 것 — 을 실제로 따라하기 시작한지 얼마나 되는걸까 -_-;; 좀 더 반성하고 내 코드의 질을 보장해줄 수 있는 테스트를 작성할 수 있게 노력해야; 그런 의미에서 Jolt Productivity Award인 xUnit test-pattern이 무지 땡기는데…

ps. 오늘따라 함수형 언어가 검사가 잘되서 좋다! 라고 강조하는 사람이 있어서 -_-. 함수형 언어가 정적 검사들에 좀 더 적합한 것은 사실이다. 그렇지만 프로그램의 정확성은 결국 실행해보는 테스트들만이 보장해준다. 그것도 모든 것을 포함하지는 못하고…


  1. Python이나 ruby 같은 요즘의 프로그래밍 언어가 그렇듯이, 컴파일 타임에는 타입을 확인하지 않고, 실행 시간에 타입이 맞지 않으면(특정 함수가 없다거나) 런타임 예외를 던지거나 하는 언어들. ↩︎

  2. C++이나 Java, nML 처럼 컴파일 시간에 타입(형)을 검사해주는 언어. ↩︎

  3. 필연적으로 각종 테스트들과 테스트 프레임웍, 자동화된 테스트가 필요하게된다. ↩︎

  4. Java나 C# 사용자라면 익숙한 모습이다. Python 각 파일들은 main을 가질 수 있고, 해당 python 파일을 실행하면 main() 이 호출된다. 코드는 factorial 값들을 체크하는 문장들. 그러나 비슷한 논리로 이 역시도 테스트해준 영역에서나 코드의 정확성을 보장해준다… ↩︎

  5. 물론 axiomatic proof를 통한 연역적인 보장도 있겠지만, 실제로 사용될 수준의 코드들에서는 크게 의미있는 것 같지 않다. ↩︎