• 최초 작성일: 2008-06-13
  • 최종 수정일: 2008-06-13
  • 조회수: 48,049 회
  • 작성자: 엑셀러 권현욱
  • 강의 제목: 버그(Bug)의 유래와 디버깅(Debugging) 테크닉

엑셀러 권현욱

들어가기 전에

류시화 잠언시집 "지금 알고 있는 걸 그때도 알았더라면" 중 한 부분을 인용합니다. 감리교의 창시자 존 웨슬리의 글입니다.

"할 수 있는 한 최선을 다하라.
당신이 할 수 있는 모든 수단과
당신이 할 수 있는 모든 방법으로
당신이 할 수 있는 모든 장소에서
당신이 할 수 있는 모든 시간에
당신이 할 수 있는 모든 사람들에게
당신이 할 수 있는 한 오래오래."

정녕 최선을 다했다면 어떤 결과가 나오든 후회하지는 않을 것 같습니다. 나는 지금 "할 수 있는" 최선을 다하고 있다고 자신있게 말할 수 있는가... 정말로...?



내공이 부족한 컴퓨터 프로그래머라 할 지라도 몇십줄 짜리 코딩을 할 때에는 자신의 내공 부족이 잘 드러나지 않습니다. 하지만 입문 단계를 벗어난 프로그래머가 500행, 1000행이 넘어가는 프로그램을 만들다 보면 어김없이 마주하게 되는 것이 '버그'라는 녀석입니다(간혹 '뻑'이라고 발음하는 분도 있습니다만 이제부터는 '버~그' 혹은 '버~ㄱ'으로 발음해 주세요 ^^;).

버그는 학자(?)에 따라 분류 방법이 사뭇 다를 수 있습니다만, 크게 '프로그래밍 오류(Programming errors)'와 '런타임 오류(Run-time errors)'로 양분할 수 있습니다. 프로그래밍 오류는 각종 구문 오류(Syntax errors)나 소프트웨어의 버전 오류, 논리적 오류 등을 말하며, 런타임 오류는 논리적으로는 이상이 없으나 프로그램 실행 과정에서 발생하는 오류를 말합니다. 여기서는 편의상 '작성된 코드가 프로그래머의 의도대로 작동하지 않는 경우'를 통칭해서 버그라고 부를 예정이니 감안하시기 바랍니다.

버그의 유래

'버그'라는 말이 어떻게 해서 생겨났는지 아시나요?

프로그램 안에 존재하는 문법적 실수나 오류 등을 흔히 '버그'라고 합니다. 왜 '오작동(malfunction)'이나 '실수(mistake)' 등과 같은 용어를 놔두고 하필이면 '버그'라는 정감있는(?) 표현을 하게 되었을까요? 거기에는 다음과 같은 사연이 있습니다.

코볼(COBOL)이라는 프로그래밍 언어의 탄생에 결정적인 기여를 한 것으로 평가되는 컴퓨터 학자 그레이스 호퍼(Grace Murray Hopper)는 1945년 여름에 '마크Ⅱ'라는 컴퓨터 시스템을 붙들고 씨름을 하고 있었는데, 계속해서 틀린 결과를 얻게 되자 그 원인을 찾기 위해 고심했다.

우습게도 그가 문제의 원인을 발견한 장소는 비트의 세계가 아니라 원자의 세계였는데 컴퓨터 내부의 부속 안에 진짜 나방이 끼어서 죽어 있었던 것이다. 나방을 발견한 그레이스 호퍼는 죽어 있는 나방을 조심스럽게 꺼내서 공책의 한 페이지에 스카치 테이프를 이용해서 붙여 놓은 다음 그 밑에 이렇게 적어 놓았다고 한다.

"First actual case of bug being found." (버그가 발견된 첫번째 사례)

- 행복한 프로그래밍, 임백준

아래 사진은 미국 해군박물관에 전시되어 있는 세계 최초의 버그입니다. 그러니까 버그의 생일이 1945년 9월 9일이로군요. 사진을 보니 '버그'가 아니라 '모쓰(moth)'라고 불러야 할 것 같습니다.

로드 중...

세계 최초의 버그

그건 그렇고, VB Editor에서 모듈을 하나 삽입하고 다음 코드를 실행시켜 보세요.([삽입] - [모듈] 메뉴). 예? 프로시저를 어떻게 실행시키는지 모르신다구요? (어허...) 아래 강의 내용 중에서 "Function 프로시저" 아랫 부분의 내용을 살펴보세요.

Sub MyCalc()
    Dim x As Integer
    Dim y As Integer

    x = 100
    y = InputBox("숫자를 입력하세요")

    MsgBox "결과값은 " & x / y & "입니다"
End Sub

CODE

이 프로시저를 실행시키면 InputBox가 나타나고, 여기에 어떤 값을 입력하면 두 값을 나눈 결과가 MsgBox에 나타납니다. 언뜻 보면 별 이상 없이 잘 작동하는 것처럼 보입니다. 과연 그럴까요?

InputBox에 숫자 0을 입력하면 다음과 같은 반갑지 않은 오류 메시지가 나타납니다.

로드 중...

디버깅 1

이 오류 메시지가 나타나지 않도록 하기 위해 코드를 다음과 같이 수정합니다.

Sub MyCalc2()
    Dim x As Integer
    Dim y As Integer

    x = 100
    y = InputBox("숫자를 입력하세요")

    If y = 0 Then
        MsgBox "0으로는 나눌 수 없습니다"
        Exit Sub
    End If

    MsgBox "결과값은 " & x / y & "입니다"
End Sub

CODE

If-Then 구문을 통해 사용자가 0을 입력할 경우 MsgBox를 통해 잘못된 값이 입력되었음을 알려주고 프로시저를 빠져나갑니다(Exit Sub). 이제 다 된 것일까요? InputBox에 숫자 대신 문자열을 입력하거나 '취소' 버튼을 클릭해 보세요. '13' 런타임 오류, 즉 데이터의 형식이 일치하지 않는다는 오류 메시지가 나타납니다(오류 코드에 대한 설명은 아래에 다시 나오니까 그냥 읽고 넘어가세요).

로드 중...

디버깅 2

디버깅 과정을 또 추가해 보겠습니다.

Sub MyCalc3()
    Dim x As Integer
    Dim y As Variant

    x = 100
    y = InputBox("숫자를 입력하세요")

    If Not IsNumeric(y) Or y = "" Then
        MsgBox "제대로 된 숫자값을 입력하세요!"
        Exit Sub
    End If

    If y = 0 Then
        MsgBox "0으로는 나눌 수 없습니다"
        Exit Sub
    End If

    MsgBox "결과값은 " & x / y & "입니다"
End Sub

CODE

If-Then 구문을 하나 더 들어갔군요. 문자열을 입력하거나 아무 것도 입력하지 않을 경우에 대한 처리 로직을 추가하였습니다. 이제 다 되었을까요? 완벽하게 처리되지는 않았지만 그런 대로 사용할 수는 있을 것 같습니다.

디버깅 3

레이블 Label을 이용하여 쬐~금 더 수정해 보겠습니다.

Sub MyCalc4()
    Dim x As Integer
    Dim y As Variant
    Dim strMsg As String
    On Error GoTo ErrorTrap

    x = 100
    y = InputBox("숫자를 입력하세요")

    MsgBox "결과값은 " & x / y & "입니다"

ErrorTrap:
    If Err.Number <> 0 Then
        strMsg = "오류가 발생하였습니다. " & vbCrLf
        strMsg = strMsg & "반드시 0이 아닌 숫자값을 입력하세요!"
        MsgBox strMsg
    End If
End Sub

CODE

"On Error GoTo XXX"라는 못보던 구문이 하나 나왔습니다(사실은 이것도 그 동안 VBA 강의 시간에 수도 없이 다루었던 내용입니다). 이 구문은 "에러가 발생하면 무조건 XXX라는 레이블로 가서 실행하라"는 뜻입니다. MyCalc3 프로시저에서와 같이 오류의 종류에 따라 별도의 조건분기 처리를 하지 않아도 되므로 편리합니다. On Error 문은 보통 다음과 같은 3가지 형태로 사용됩니다.

  • On Error GoTo 레이블: 오류가 발생하면 "지정한 레이블로 이동"해서 프로시저를 계속 실행하라는 의미입니다. 레이블 뒤에는 반드시 콜론(:)을 적어주어야 합니다.
  • On Error Resume Next: 프로그램을 실행하는 도중 "오류가 발생하더라도 잔소리 하지 말고 그 다음 라인을 진행하라"는 명령입니다. 예전에 컬렉션 오브젝트를 통해 중복되지 않는 데이터를 추출할 때 설명드린 바 있습니다. 기억이 도무지 안 나는 분은 VB0030 강좌를 참고하세요.
  • On Error GoTo 0: 이 구문을 실행하면 원래의 오류 점검 상태로 되돌아 갑니다. 즉, 오류가 발생하면 시끄럽게 하도록 둔다는 얘깁니다.

오류가 발생하면 엑셀은 그와 관련된 정보를 Err이라는 오브젝트에 저장합니다. 따라서 이 오브젝트의 여러 가지 프로퍼티를 이용하면 오류 정보에 접근할 수 있습니다. 예를 들어 다음과 같이 하면 발생한 오류의 번호와 설명을 볼 수 있습니다.

MsgBox Err.Number & " : " & Err.Description

사실 엑셀이 제공하는 오류 설명만으로는 무엇을 어떻게 고쳐야 할 지 막막한 경우가 많습니다만 오류의 종류에 대해서는 알 수 있으므로 조금은 수월하게 대처할 수 있으리라 생각합니다. 다음은 코딩할 때 비교적 자주 접하게 되는 오류에 대해 정리한 것입니다.

오류 코드 정리

오류 번호 설명
3 GoSub가 없는 Return입니다
5 프로시저 호출이 잘못되었습니다
6 숫자가 너무 큽니다
7 메모리가 부족합니다
9 아래 첨자 사용이 잘못되었습니다
11 0으로 나누었습니다
13 형식이 일치하지 않습니다
91 개체 변수 또는 With 문의 변수가 설정되어 있지 않습니다
321 파일 형식이 잘못되었습니다
337 구성 요소를 찾을 수 없습니다
423 속성이나 메서드를 찾을 수 없습니다
424 개체가 필요합니다
438 개체가 이 속성 또는 메서드를 지원하지 않습니다
449 선택적 인수가 아니거나 속성 지정이 잘못되었습니다
31036 파일에 저장하는 중에 오류가 발생하였습니다

오류 코드는 이것 말고도 더 있습니다. 보다 자세한 사항은 도움말을 참고하시기 바랍니다.

디버깅 4

MyCalc4 프로시저는 아직도 문제가 있습니다. 예를 들어 입력 오류가 발생한 경우, '똑바로 입력하라'고 안내만 하고는 덜렁 프로시저를 빠져 나와 버립니다. 사용자가 계속해서 제대로 된 숫자값을 입력할 수 있도록 바꾸어 봅니다.

Sub MyCalc5()
    Dim x As Integer
    Dim y As Variant
    Dim intResult As Integer
    Dim strMsg As String

OneMoreTime:
    On Error GoTo ErrorTrap

    x = 100
    y = InputBox("숫자를 입력하세요")

    MsgBox "결과값은 " & x / y & "입니다"

ErrorTrap:
    intResult = MsgBox("오류가 발생했습니다. 다시 입력하시렵니까?", vbYesNo)
    If intResult = vbYes Then Resume OneMoreTime
End Sub

CODE

OneMoreTime이라는 레이블이 하나 더 사용되었습니다. 또한 사용자가 다시 입력한다고 했을 경우 Resume 문을 사용하여 지정한 레이블로 이동하도록 해주었습니다.

레이블은 꼭 필요한 경우에만 사용하는 것이 좋습니다. 레이블이 많아질수록 코드는 점점 난해해져서 나중에는 자신도, 며느리도 알 수 없는 복잡한 코드(일명 '스파게티' 코드)가 되기 십상입니다. '꼭 필요한 경우'란 앞의 예제와 같은 오류 처리를 위한 구문 정도라고 할 수 있습니다.

만약 여기서 Resume 대신 GoTo를 쓰면 어떻게 될까요? 원하는 대로 작동할까요? 직접 테스트 해 보세요(왠지 묻는 폼이 잘 안될 것 같죠? ^^).

디버깅하는 과정을 단계별로 설명드렸습니다만, 아무리 디버깅 작업을 완벽하게 한다고 해도 모든 오류를 잡기는 어렵습니다. 현존하는 최고의 스프레드시트 프로그램의 하나인 엑셀에도 수백 개의 버그가 있으니 말입니다. 그래서 혹자는 "버그가 없는 소프트웨어는 사용할 만한 가치도 없다." 라고 말하기도 했습니다. 세상에 완벽한 것이 있을까요? 불완전한 인간이 만들어 낸 산물인 만큼, 완벽을 지향할 따름이지요.

다른 강의도 그렇습니다만 이번 강의만큼은 한번 읽고 던져버리지 마시고 사골 우려 드시듯이 반복해서 참고하시기 바랍니다.

다음 시간에는 엑셀이 제공하는 막강한 디버깅 도구들에 대해 알아보겠습니다.

오늘은 여기까지...