본문 바로가기

진리는어디에/VBA

[VBA] 에러 처리 완벽 가이드

이 포스트는 Excel Macro Mastery 사이트의 'VBA Error Handling  - A Complete Guide(by Paul Kelly)'의 내용을 다시 정리한 것입니다. 이번 포스트에서는 VBA의 에러 처리 방법에 대해 다룹니다.

들어가며

본 포스트는 'VBA 에러 처리'에 대한 자세한 가이드를 제공합니다. VBA 에러 처리에 대해 이미 익숙하며 기억을 더듬기위한 레퍼런스가 필요하신 분은 아래 Quick Guide 테이블을 참고해 주세요.

VBA 에러 처리에 대한 특정 주제를 찾고 있다면 오른쪽의 목차에서 찾아 보시면 됩니다. 만일 여러분이 VBA에 대해 익숙하지 않으신 분이라면 본 포스트는 논리적인 순서로 배치되어 있으므로 처음 부터 끝까지 읽어 보시길 권장합니다.

Quick Guide

항목 설명
On Error Goto 0 에러가 발생하면 프로그램을 중단하고 오류를 표시합니다.
On Error Goto -1 현재 에러 셋팅을 삭제하고 기본값으로 되돌립니다.
On Error Resume Next 에러를 무시하고 프로그램을 계속 진행합니다.
On Error Goto [Label] 에러가 발생하면 특정 레이블로 이동합니다. 이를 통해 에러를 처리할 수 있습니다.
Err Object 에러가 발생하면 여기에 에러를 저장합니다.
Err.Number 에러 넘버입니다. 특정 에러가 발생했는지 확인해야 하는 경우에만 유용합니다.
Err.Description 에러 텍스트를 포함합니다.
Err.Source Err.Raise를 사용할 때 이것을 추가할 수 있습니다.
Err.Raise 커스텀 에러를 생성하기 위해 사용하는 기능입니다.
Err.Function 에러 넘버로 부터 에러 텍스트를 리턴합니다. 사용되지 않습니다.
Err.Statement 에러를 시뮬레이션 합니다. 이것 대신 Err.Raise를 사용하십시오.

VBA 에러의 종류

VBA에는 세 가지 유형의 에러가 있습니다.

  1. 구문(syntax) 오류
  2. 컴파일(compilation) 오류
  3. 런타임(runtime) 오류

위 에러 중 '에러 핸들링'을 통해 처리되는 에러는 '런타임 오류'입니다. 이번 섹션에서는 런타임 오류가 무엇인지 명확하게 이해할 수 있도록 각 에러 유형을 살펴 보도록 하겠습니다.

구문 오류

VBA 스크립트에 한 줄을 입력하고 엔터키를 누르면 VBA는 제대로 된 문법으로 작성된 것인지 확인하고 올바르지 않다면 에러 메시지를 표시합니다.

예를 들어 If를 입력하고 Then 키워드를 잊은 경우 VBA는 다음 오류 메시지를 표시 합니다.

구문 오류의 몇 가지 예는 다음과 같습니다.

' Then 키워드 누락
If a > b

' i 변수 다음 '=' 연산자 누락
For i 2 To 7

' 오른쪽 괄호 누락
b = left("ABCD",1

구문 오류는 라인 단위로 발생합니다. 한 줄의 구문이 올바르지 않으면 구문 오류가 발생합니다.

※ 만일 에러 메시지 박스가 너무 자주 생성되어 여러분의 코딩을 방해 한다면 도구 -> 옵션으로 이동하여 "자동 구문 검사"를 선택하여 구문 오류 대화 상자를 끌 수 있습니다. 오류가 있는 경우 해당 라인은 여전히 빨간색으로 표시되지만 에러 메시지 박스는 생성되지 않습니다.

컴파일 오류

구문 오류와 달리 컴파일 오류는 여러 라인에 걸쳐 발생합니다. 예를 들어 구문 한 줄에서는 정확하지만 전체 코드를 고려했을때 올바르지 않은 경우입니다. 컴파일 오류의 예는 아래와 같습니다.

  • End If 문이 없는 If 문
  • Next가 없는 For
  • End Select 가 없는 Select
  • 존재하지 않는 Sub나 Function을 호출하는 경우
  • 잘못된 파라메터로 Sub나 Function을 호출하는 경우
  • 모듈과 같은 이름의 Sub나 Function을 정의하는 경우
  • Option Explict이 선언 되어 있지만 변수를 선언하지 않고 사용하는 경우

다음 스크린 샷은 For 루프에 매칭되는 Next문이 없을 때 발생하는 컴파일 오류를 보여주고 있습니다.

'디버그 -> 컴파일' 사용

컴파일 오류를 찾기 위해서 메뉴에서 '디버그 -> VBAProject 컴파일'을 선택 합니다. 

컴파일을 시작하면 VBA는 가장 먼저 발생하는 오류를 표시 합니다. 이 오류가 수정 되고난 후 다시 프로그램을 실행할 수 있으며 VBA는 다음 오류를 찾습니다. 

컴파일은 컴파일 오류 탐지 뿐만 아니라 구문 오류 탐지도 같이 수행합니다. 모든 오류가 해결 된 후 컴파일이 성공적 마치면 아무 일도 일어나지 않은것 처럼 보일 수 있습니다. 그러나 여러분이 다시 VBAProject 컴파일 메뉴를 다시 확인해 보시면 회색으로 표시되고 있음을 확인할 수 있습니다. 이는 현제 어플리케이션에서 아무런 컴파일 오류가 없음을 의미합니다.

  • '디버그 -> VBAProject 컴파일'은 프로젝트의 전체 오류를 찾습니다.
  • 구문 오류 역시 탐지합니다.
  • 컴파일을 한번 실행할 때 마다 하나의 오류를 출력합니다.
  • 컴파일 오류가 없는 경우 컴파일 메뉴에서 회색으로 표시 됩니다.
  • 코드를 실행하기 전에 항상 '디버그 -> VBAProject 컴파일'을 사용해야 합니다.

런타임 오류

애플리케이션에서 실행 중일 때 발생하는 오류를 '런타임 오류'라고 합니다. 런타임 오류는 일반적으로 사용자가 제어할 수 없는 범위에서 발생합니다. 예를 들어 애플리케이션이 외부 통합 문서를 읽는다고 상상해 보십시오. 이 파일이 삭제 된 후 애플리케이션이 실행 중에 이 파일을 열려고하면 오류를 표시합니다.

런타임 오류의 예는 다음과 같습니다.

  • 사용할 수 없는 데이터베이스
  • 잘못된 유저 입력
  • 숫자 대신 텍스트를 포함하는 셀

오늘 다루는 '에러 처리'는 런타임 오류를 다루는 방법들에 대한 이야기 입니다.

예상된 오류와 예상치 못한 오류

만일 런타임 오류가 발생할 수 있다고 예상되는 부분이 있다면 여러분들은 이를 위해 오류 처리 코드를 배치할 수 있습니다. 아래 코드는 파일을 열려고 시도하기 전에 파일이 존재하는지 먼저 확인합니다. 만일 파일이 존재하지 않는다면 사용자에게 에러 메시지를 표시하고 Sub를 종료 합니다.

Sub OpenFile()
    
    Dim sFile As String
    sFile = "C:\Somewhere\docs\data.xlsx"
    
    ' 파일이 존재하는지 검사하기 위해 Dir 함수 사용
    If Dir(sFile) = "" Then
        ' 만일 파일이 존재하지 않는다면 메시지 박스 출력
        MsgBox "Could not find the file " & sFile
        Exit Sub
    End If
    
    ' 파일이 존재하는 경우에만 이 코드 실행
    Workbooks.Open sFile
    
End Sub

어느 시점에 에러가 발생할 가능성이 있다고 생각되면 상황을 핸들링할 수 있는 코드를 추가하는 것이 좋습니다. 일반적으로 이런 에러를 '예상 가능한 에러'라고 합니다. 반면에 에러가 발생하는 부분에 에러를 처리하는 코드가 없는 경우 '예기치 않은 오류'로 간주 됩니다. 예기치 않은 오류는 VBA 에러 핸들링 구문을 사용하여 처리 해주어야만 합니다.

VBA 가 에러로 취급하지 않는 런타임 에러

VBA 에러 핸들링을 살펴보기 전에 우리는 한 가지 유형의 에러를 먼저 살펴 보아야만 합니다. 일부 런타임 에러는 VBA는 오류로 간주하지 않지만 사용자 측면에서는 오류로 생각되는 것들이 있습니다.

예를 들어 변수 a 와 b의 값을 더하는 어플리케이션이 있다고 상상해 보십시오

result = a + b

그런데 코드 작성자의 실수로 + 기호 대신 *를 사용했다고 가정해 보도록 하겠습니다.

result = a * b

VBA 입장에서는 이것은 분명히 오류가 아닙니다. 여러분의 코드는 완벽하게 구문 규칙에 들어 맞습니다. 그러나 프로그램의 원래 의도를 생각했을때 이는 오류 입니다. 

위와 같은 오류는 VBA에서 아무런 에러 메시지를 생성하지 않으므로 여러분은 VBA 오류 구문을 이용해 위 오류를 처리할 수는 없습니다. 이런 부분은 단위 테스트와 assertion을 이용해서 처리하도록 해야 합니다.

On Error 문

지금까지 살펴본 것처럼 런타임 오류를 처리하는 방법에는 두 가지가 있습니다.

  • 예상 된 오류 - 오류를 처리할 특정 코드를 오류가 발생할 만한 위치에 작성합니다.
  • 예기치 않은 오류 - VBA 오류 처리문을 사용하여 처리합니다.

VBA On Error 문은 VBA에서 예기치 않은 오류가 발생하는 경우 이를 처리하기 위해 사용 됩니다. 다음은 On Error문을 사용하는 네 가지 방법입니다.

  • On Error GoTo 0 : 애플리케이션은 오류가 있는 줄에서 멈추고 메시지를 표시 합니다.
  • On Error Resume Next : 애플리케이션은 에러를 무시하고 다음 라인으로 이동합니다. 아무런 메시지도 표시 되지 않습니다.
  • On Error GoTo [레이블]  : 코드가 특정 줄이나 레이블로이동합니다.
  • On Error GoTo -1 : 현재 오류를 비웁니다.

아래 부터 각 구문들을 차례로 살펴 보도록 하겠습니다.

On Error GoTo 0

이것은 VBA의 기본적인 에러 핸들링 방식입니다. 즉, 여러분이 On Error 구문을 사용하지 않았을 때 VBA는 이 동작을 수행합니다.

애플리케이션 실행 중 오류가 발생하면 VBA는 오류가 발생한 줄에서 멈추고 오류 메시지를 보여 줍니다. 애플리케이션을 계속 진행 시키기 위해서는 오류를 수정하거나 프로그램을 다시 실행 시키는 등의 사용자의 개입이 필요 합니다.

아래 코드는 아무런 On Error 구문도 사용하지 않고 있으므로 런타임 에러가 발생했을 때 VBA는 On Error GoTo 0과 같은 기본동작을 하게될 것입니다.

Sub DividedByZero()

    MsgBox 1/0

End Sub

위 예제의 3 라인에서 1을 0으로 나누고 있습니다. 하지만 0으로 나누는 것은 불가능하므로 VBA는 divide by zero 에러를 발생 시킵니다. 하지만 위에서 이미 언급했듯이 우리는 아무런 별도 에러 처리를 하지않고 VBA 기본 에러 처리를 사용하고 있으므로 여러분은 아래와 같은 메시지를 보실수 있을 겁니다.

비록 VBA가 에러의 원인을 사용자에게 알려주긴 하지만 위와 같은 방식의 오류는 애플리케이션을 불안정하게 만들고 사용자에게 불편함을 전가합니다. 좀 더 정확히 말하자면 위와 같은 오류 처리는 처리라기 보다는 단순한 어플리케이션 충돌입니다. 사용자는 프로그램을 제대로 수정하기 전까지는 종료하는것 외에는 다른 선택지가 없습니다. 

On Error Resume Next

On Error Resume Next를 사용하면 VBA가 에러가 발생하더라도 무시하고 계속 프로그램을 진행하도록할 수 있습니다.

Sub DividedByZero()
    On Error Resume Next
    MsgBox 1 / 0
End Sub

이전 섹션에서 0으로 나누어 오류를 발생 시키던 코드에 Resume Next를 추가 했습니다. 위 프로그램을 실행하면 아무런 오류도 보여주지 않고 종료 됩니다. 마치 정상 동작하는것 처럼 보이지만 실상은 오류가 발생 했지만 무시하고 있을뿐 입니다.

대부분의 경우 오류를 무시하고 프로그램을 계속 진행 시키는 방식은 지양해야 합니다. 문제가 발생하면 해당 문제에 대해 인지하고 어떠한 방식으로든 처리해야 해야지 그냥 무시하고 지나갔다간 나중에 어플리케이션이 어떻게 동작할지 예측할 수 없는 상태가 됩니다.

어지간하면 Resume Next는 사용하지 마십시오.

On Error GoTo [Lable]

이번 섹션에서는 VBA에서 실질적인 에러 처리에 대해 살펴 보도록 하겠습니다. 이는 C# 및 Java와 같은 언어에서 볼 수 있는 try ~ catch 구문과 비슷합니다.

On Error GoTo [Lable]은 오류 발생시 코드 진행을 특정 레이블로 보냅니다. 레이블은 일반적으로 프로시져(Sub나 Function)의 맨 아래에 적습니다.

Sub UsingGotoLine()

    On Error GoTo eh ' 에러 발생 시 eh 레이블로 이동
    
    Dim x As Long, y As Long
    
    x = 6
    y = 6 / 0        ' 여기에서 에러 발생
    x = 7
    
Done:
    Exit Sub
eh:                  ' 에러 발생 시 여기로 이동
    MsgBox "The following error occurred: " & Err.Description
End Sub

위 예제의 8라인에서 에러가 발생하면 13라인의 eh 레이블로 이동하여 코드를 계속 실행하게 됩니다.

NOTE 1 : On Error GoTo [Label]에서 사용되는 레이블은 현재 Sub/Function 내에 있어야 합니다. 그렇지 않은 경우 컴파일 오류가 발생합니다.

NOTE 2 : On Error GoTo [Label]을 사용할 때 오류가 발생하면 오류 처리는 기본 동작으로 돌아갑니다..즉, 코드는 오류가 있는 줄에서  멈추고 오류 메시지를 표시합니다. 이에 대한 자세한 내용은 다음 섹션을 참고해 주세요.

On Error GoTo -1

이 구문은 앞의 세 가지 구문과는 다릅니다. 특정 동작을 설정하는 대신 현재 오류를 지우는데 사용됩니다.

On Error GoTo [Label]을 사용하여 오류가 발생하면 오류 처리 동작이 기본 동작, 'On Error GoTo 0'으로 돌아갑니다. 그래서 다른 오류가 발생하는 경우 코드가 현재 줄에서 중지되고 에러 메시지를 출력하게 됩니다.

이 동작은 현재 Sub에만 적용 됩니다. Sub를 종료하면 오류가 자동적으로 지워집니다.

아래 코드를 살펴 보면 첫 번째 오류가 발생했을때 코드가 eh 레이블로 이동하고, 두 번째 오류가 발생한 1034에서 멈추가 됩니다.

Sub TwoErrors()

    On Error Goto eh
        
    Error (13) ' Type mismatch 에러 강제 발생

Done:
    Exit Sub
eh: ' Error(13) 에러가 발생하면 여기로 점프
    ' 여기서 디폴트 에러 처리로 돌아옴
    ' 아래의 에러 시점에서 코드가 멈추고 에러 메시지를 출력함
    Error (1034)
End Sub

만일 여러분이 저기에 추가적인 에러 처리를 추가한다고 하더라도 동작하지 않습니다. 에러 트랩이 아직 클리어 되지 않았기 때문입니다.

아래 코드에서 다음과 같은 내용을 추가해 보겠습니다.

On Error Goto eh_other

첫 번째 오류를 처리한 후, 오류 트랩이 클리어 되지 않았으므로 아무런 효과가 없습니다. 즉, 코드는 오류가 있는 줄에서 멈추고 메시지를 출력합니다.

Sub TwoErrors()

    On Error Goto eh
        
    ' generate "Type mismatch" error
    Error (13)

Done:
    Exit Sub
eh:
    On Error Goto eh_other
    ' generate "Application-defined" error
    Error (1034)
Exit Sub
eh_other:
    Debug.Print "eh_other " & Err.Description
End Sub

오류를 지우려면 On Error Goto -1을 사용합니다. 쥐덫을 설치하는 것과 같다고 생각하십시오. 트랩이 꺼지면 다시 설정해야 합니다.

아래 코드에서 이 줄을 추가하고 두 번째 오류로 인해 코드가 eh_other 레이블로 이동합니다.

Sub TwoErrors()

    On Error Goto eh
        
    ' generate "Type mismatch" error
    Error (13)

Done:
    Exit Sub
eh:
    ' clear error
    On Error Goto -1
    
    On Error Goto eh_other
    ' generate "Application-defined" error
    Error (1034)
Exit Sub
eh_other:
    Debug.Print "eh_other " & Err.Description
End Sub

NOTE 1 : 대부분의 경우 On Error Goto -1 보다는 Resume Next를 사용하는 편이 더 낫습니다. Resume Next는 에러 트랙을 비울뿐 아니라 코드를 다음 줄에서 부터 바로 시작 되게 합니다.

NOTE 2 : Err Object는 Clear 멤버가 있습니다. Clear를 사용하면 Err 객체의 텍스트와 숫자가 지워지지만 오류가 재설정 되지는 않습니다.

'On Error' 사용하기

위의 섹션에서 살펴 보았던것 처럼 VBA는 오류가 발생할 때 아래 세 가지 작업중 하나를 합니다.

  • 코드를 중지하고 오류를 표시합니다.
  • 오류를 무시하고 계속 진행 합니다.
  • 특정 레이블로 이동합니다.

VBA는 항상 이러한 동작 중 하나로 설정됩니다. On Error를 사용하면 VBA는 지정된 동작으로 변경되고 이전 동작은 무시 됩니다. 다음 Sub에서 VBA는 On Error문을 사용할 때 마다 오류 동작을 변경하게 됩니다.

Sub ErrorStates()

    Dim x As Long
    
    ' Go to eh label if error
    On Error Goto eh
    
    ' this will ignore the error on the following line
    On Error Resume Next
    x = 1 / 0
    
    ' this will display an error message on the following line
    On Error Goto 0
    x = 1 / 0
  
Done:  
   Exit Sub
eh:
    Debug.Print Err.Description
End Sub

Resume Next

Resume Next문은 오류를 지운 다음 오류가 발생한 줄에서 코드를 다시 시작하는데 사용 됩니다.

만일 여러분의 코드에 여러 에러가 발생하고 있고 코드를 실행하면서 계속 오류를 찾기 원한다면 이 구문이 매우 유용할 것입니다.

아래 코드는 에러가 발생하면 eh 레이블로 점프한 후 메시지를 출력하고 어플리케이션이 종료 됩니다. 

Private Sub Main()

    On Error Goto eh
    
    Dim i As Long
    For i = 1 To 3
        ' Generate type mismatch error
         Error 13
    Next i

done:
    Exit Sub
eh:
    Debug.Print i, Err.Description
End Sub

위 코드를 오류 리포팅 후 종료과 아니라 다시 코드를 계속 진행하길 원한다고 가정해 봅시다. 우리는 On Error Goto -1을 사용하여 에러를 지운 후 Goto 문을 사용하여 Next로 돌아갈 수 있습니다.

Private Sub Main()

    On Error Goto eh
    
    Dim i As Long
    For i = 1 To 3
        ' Generate type mismatch error
         Error 13
continue:
    Next i

done:
    Exit Sub
eh:
    Debug.Print i, Err.Description
    On Error Goto -1 ' clear the error
    Goto continue ' return
End Sub

Resume Next를 이용하면 같은 결과를 만들지만 더 깔끔한 코드를 만들 수 있습니다.

Private Sub Main()

    On Error Goto eh
    
    Dim i As Long
    For i = 1 To 3
        ' Generate type mismatch error
         Error 13
continue:
    Next i

done:
    Exit Sub
eh:
    Debug.Print i, Err.Description
    ' clear the error and return to the code
    Resume Next  
End Sub

Err Object

에러가 발생했을 때 Err 객체를 사용하여 에러의 세부 정보를 볼 수 있습니다.

런타임 오류가 발생하면 VBA는 자동으로 Err 객체의 세부 정보를 채웁니다. 아래 코드는 "Error number: 13 형식이 일치하지 않습니다"라는 오류 메시지를 출력합니다. 이 오류는 문자열 값을 long 타입의 total에 할당 하려고 할 때 발생 합니다.

Sub UsingErr()

    On Error Goto eh
    
    Dim total As Long
    total = "aa"

Done:
    Exit Sub
eh:
    Debug.Print "Error number: " & Err.Number _
            & " " & Err.Description
End Sub

Err.Description은 발생한 오류에 대한 세부 정보를 제공합니다. 타입 미스매치와 같은 오류가 발생할 때 일반적으로 표시되는 텍스트입니다.

Err.Number는 오류의 ID 번호 입니다. 예를 들어 타입 불일치에 대한 오류 번호는 13입니다. 특정 오류가 발생했는지 여부를 확인하는 경우에 사용 됩니다.

Err.Source 속성은 얼핏 보면 좋은 생각 처럼 보이지만 실제 VBA 에러에 대해 동작하지 않습니다. 소스는 프로젝트 이름을 리턴하므로 오류가 발생한 위치를 추정하는데 큰 도움이 되지 않습니다. 그러나 Err.Raise를 사용하여 오류를 생성하는 경우 소스를 직접 설정할 수 있으며 이때 매우 유용하게 사용될 수 있습니다.

라인 번호 얻기

Erl 함수는 에러가 발생한 라인의 번호를 얻는데 사용됩니다. C++과 같은 다른 프로그래밍 언어에 대한 경험이 있는 분들은 아래 코드에서 많은 혼란을 겪곤 합니다. 아래 코드는 0을 리턴합니다.

Sub UsingErr()

    On Error Goto eh
    
    Dim val As Long
    val = "aa"

Done:
    Exit Sub
eh:
    Debug.Print Erl
End Sub

Erl 함수가 0을 리턴하는 이유는 라인에 번호가 명시되어 있지 않기 때문입니다. 대부분의 사람들이 모르는 사실이긴 하지만 VBA는 코드의 라인 넘버를 명시할 수 있도록 허용하고 있습니다.

만일 여러분이 아래와 같이 코드를 변경하면 이제는 0 대신 20을 출력하게 될 것입니다.

Sub UsingErr()

10        On Error Goto eh
          
          Dim val As Long
20        val = "aa"

Done:
30        Exit Sub
eh:
40        Debug.Print Erl
End Sub

하지만 손으로 일일이 라인 번호를 적어 준다는것은 상당히 귀찮고 고된 일입니다. VBA에서는 자동으로 라인 넘버를 명시해주는 툴을 제공하고 있습니다.

Err.Raise 사용하기

Err.Raise를 사용하면 여러분이 원하는 곳에서 임의로 에러를 발생 시킬 수 있습니다. 이를 이용하여 Java나 C#, C++의 throw exception문 처럼 사용자 정의 오류를 생성할 수 있습니다.

Err.Raise의 포멧은 아래와 같습니다.

Err.Raise [error number], [error source], [error description]

간단한 예를 살펴 보겠습니다. 셀에 길이가 5자인 항목이 있는지 확인하고 싶다고 가정해 보겠습니다. 우리는 이것에 대한 구체적인 에러 메시지를 생성할 수 있습니다.

Public Const ERROR_INVALID_DATA As Long = vbObjectError + 513

Sub ReadWorksheet()

    On Error Goto eh
    
    If Len(Sheet1.Range("A1")) <> 5 Then
        Err.Raise ERROR_INVALID_DATA, "ReadWorksheet" _
            , "셀 A1의 값은 정확히 5글자여야 합니다"
    End If
    
    ' continue on if cell has valid data
    Dim id As String
    id = Sheet1.Range("A1")
    

Done:
    Exit Sub
eh:
    ' Err.Raise will send code to here
    MsgBox "Error found: " & Err.Description
End Sub

위 코드는 셀 A1의 내용이 5글자가 아닌 경우 8라인에서 에러를 발생 시키도록 되어 있습니다.

Err.Raise를 사용하는 부분을 좀 더 자세히 살펴 보도록 하겠습니다. 첫 번째 인자로 어떤 에러인지 에러 번호를 지정하기 위해 ERROR_INVALID_DATA라는 long 타입 변수를 넘겨주고 있습니다. 에러 번호는 번호는 반드시 vbObjectError와 함께 정의 되어야 하며 숫자는 513 부터 65535까지 사용 가능합니다. 두 번째 인자로 넘겨진 텍스트는 Err.Description을 통해 접근 가능합니다. 에러 발생시 자세한 내용을 여기에 담아 전달할 수 있습니다.

Err.Clear 사용하기

Err.Clear는 Err.Object에서 텍스트와 에러 넘버를 지우는데 사용 됩니다. 이것은 단지 Err.Object 에러 넘버와 디스크립션만을 지웁니다. 실제 에러를 지우기 위해서는 On Error -1 이나 Resume Next를 사용해야 합니다.

Err.Clear를 사용해야 하는 경우는 드물긴 하지만 꼭 필요한 경우가 있습니다.

아래 코드는 발생한 에러를 카운팅 합니다. 코드를 단순히 하기위해 각 홀수에 대해 오류를 생성하도록 해보겠습니다. 루프를 통과할 때마다 에러 넘버를 확인합니다. 번호가 0이 아니면 오류가 발생한 것입니다. 일단 우리가 에러를 카운팅하고 나면 다음 에러 체크를 위해 에러 번호를 다시 0으로 변경해주어야 합니다. 

Sub UsingErrClear()

    Dim count As Long, i As Long

    ' Continue if error as we will check the error number
    On Error Resume Next
    
    For i = 0 To 9
        ' generate error for every second one
        If i Mod 2 = 0 Then Error (13)
        
        ' Check for error
        If Err.Number <> 0 Then
            count = count + 1
            Err.Clear    ' Clear Err once it is counted
        End If
    Next

    Debug.Print "The number of errors was: " & count
End Sub

만일 위 코드 15라인의 Err.Clear 부분이 빠진다면 Err.Number는 항상 13으로 유지되어 홀수 짝수에 상관 없이 모든 결과를 에러로 카운팅하게 됩니다.

NOTE : Err.Clear는 Err.Object의 디스크립션과 에러 번호를 리셋하지만 VBA에 적재 되어 있는 에러를 삭제하지는 않습니다. 실제 에러를 지우기 위해서는 Resume Next 또는 On Error GoTo -1을 사용해야 합니다.

로깅(Logging)

로깅이란 어플리케이션이 실행 중일때 정보들을 기록하는 것을 말합니다. 오류가 발생하면 세부 정보를 파일에 기록하여 추후 어떤 이유로 오류가 발생했는지 분석하는데 도움을 줄수 있습니다.

아래 코드는 매우 간단한 로깅 방법을 보여 줍니다.

Sub Logger(sType As String, sSource As String, sDetails As String)
    
    Dim sFilename As String
    sFilename = "C:\temp\logging.txt"
    
    ' 파을을 특정 사이즈 단위로 묶는다
    If FileLen(sFilename) > 20000 Then
        FileCopy sFilename _
            , Replace(sFilename, ".txt", Format(Now, "ddmmyyyy hhmmss.txt"))
        Kill sFilename
    End If
    
    ' write를 위해 파일을 연다
    Dim filenumber As Variant
    filenumber = FreeFile 
    Open sFilename For Append As #filenumber
    
    Print #filenumber, CStr(Now) & "," & sType & "," & sSource _
                                & "," & sDetails & "," & Application.UserName
    
    Close #filenumber
    
End Sub

위 서브 프로시져는 다음과 같이 사용할 수 있습니다.

Public Const ERROR_DATA_MISSING As Long = vbObjectError + 514

Sub CreateReport()

    On Error Goto eh
    
    If Sheet1.Range("A1") = "" Then
       Err.Raise ERROR_DATA_MISSING, "CreateReport", "Data is missing from Cell A1"
    End If

    ' other code here
Done:
    Exit Sub
eh:
    Logger "Error", Err.Source, Err.Description
End Sub

로그는 오류 기록만이 아니라 다양한 다른 정보들도 기록할 수 있습니다. 오류가 발생하면 오류가 발생하기 전에 이벤트 순서를 확인할 수 있습니다.

아래는 로깅의 예입니다. 실제 로깅을 구현하는 방법은 애플리케이션의 특성에 따라 다양하게 달라집니다. 아래 예에서는 에러 뿐만 아니라 Information, Warning을 위해서도 로그를 남기고 있습니다.

Sub ReadingData()
    
    Logger "Information", "ReadingData()", "Starting to read data."
       
    Dim coll As New Collection
    ' add data to the collection
    coll.Add "Apple"
    coll.Add "Pear"
    
    If coll.Count < 3 Then
        Logger "Warning", "ReadingData()", "Number of data items is low."
    End If
    Logger "Information", "ReadingData()", "Number of data items is " & coll.Count
    
    Logger "Information", "ReadingData()", "Finished reading data."

End Sub

그외 오류 처리 방법들

이 섹션에서는 VBA에 있는 다른 오류 처리 도구들을 다루어 봅니다. 이제는 이런 방식이 거의 사용되지 않지만 레거시 코드에 존재할 수 있으므로 참고용으로 다루어 봅니다. 이런것이 있다는 정도로만 읽고 넘어가시면 됩니다.

Error 함수

Error 함수는 에러 번호에서 에러에 대한 설명을 출력하는데 사용됩니다. 이전 버전과 호환성을 위해 VBA에 포함되어 있으며 지금은 이 함수 대신 Err.Description을 사용할 수 있습니다.

다음은 몇 가지 예제 입니다.

' "0으로 나누기" 출력
Debug.Print Error(11)
 ' "유형 불일치" 출력
Debug.Print Error(13)
 ' "파일을 찾을 수 없음" 출력
Debug.Print Error(53)

Error 문(Statement)

Error 문을 사용하면 오류를 임의 발생 시킬수 있습니다. 이전 버전과 호환성을 위해 VBA에 포함되어 있으며 지금은 Err.Raise를 사용하도록 합니다.

아래 예에서는 'Divide by zero' 오류를 발생 시킵니다.

Sub SimDivError()

    On Error Goto eh
        
    ' This will create a division by zero error
    Error 11
    
    Exit Sub
eh:
    Debug.Print Err.Number, Err.Description
End Sub

 

간단한 에러 처리 전략

다양한 옵션을 사용하면 VBA에서 오류 처리하는 방법에 대해 혼란 스러울수 있습니다. 이번 섹션에서는 모든 어플리케이션에서 사용할 수 있는 간단한 오류 처리 전략을 구현하는 방법에 대해 살펴 보도록 하겠습니다.

아래는 에러 처리 전략에 대한 간단한 개요 입니다.

  1. On Error GoTo [label] 을 최상위 프로시져의 시작 부분에 배치합니다.
  2. 최상위(topmost) 프로시져의 맨 마지막에는 에러를 처리하는 레이블을 배치합니다.
  3. 예상되는 오류가 발생하면 이를 처리하고 계속 진쟁합니다.
  4. 만일 어플리케이션이 계속 진행할 수 없는 경우라면 Err.Raise를 이용해 에러 처리 레이블로 점프합니다.
  5. 예상하지 못한 오류가 발생하면 자동으로 오류 처리 레이블로 이동 됩니다.

아래 이미지는 위 개요의 전체적인 구조를 보여 줍니다.

다음 코드는 이 전략의 간단한 구현을 보여줍니다.

Public Const ERROR_NO_ACCOUNTS As Long = vbObjectError + 514

Sub BuildReport()

    On Error Goto eh
    
    ' ReadAccounts에 오류가 있으면 오류로 이동합니다.
    ReadAccounts
    
    ' 기타 등등 작업 ...
    
Done:
    Exit Sub
eh:
    ' 모든 오류는 여기로 이동합니다. 
    MsgBox Err.Source & ": The following error occured  " & Err.Description
End Sub

Sub ReadAccounts()
    
    ' 예상 되는 오류 - 코드로 처리할 수 있음
    ' 어플리케이션은 셀 A1이 0 인 경우 처리
    If Sheet1.Range("A1") = 0 Then
        Sheet1.Range("A1") = 1
    End If
    
    ' 예상 되는 오류 - 코드로 처리 불가
    ' 어플리케이션은 Account 파일을 찾지 못하면 계속 진행하지 못한다.
    ' 최상위 프로시져의 에러 레이블로 점프.
    If Dir("C:\Docs\Account.xlsx") = "" Then
        Err.Raise ERROR_NO_ACCOUNTS, "UsingErr" _
                , "There are no accounts present for this month."
    End If

    ' 예상 되지 않은 오류 - 코드로 처리 불가
    ' 만일 셀 B3에 문자열이 있는 경우 타입 불일치 오류가 발생한다.
    ' 최상위 프로시져의 에러 레이블로 점프.
    Dim total As Long
    total = Sheet1.Range("B3")
    
    ' 기타 등등 코드 진행 ...
    
End Sub

모든 서브 프로시저에 오류 처리 코드를 추가할 필요는 없습니다. 최상위 프로시져에만 오류 처리를 추가하더라도 정상적인 오류 처리가 가능합니다.

완벽한 에러 처리 전략

위의 간단한 전략에는 한 가지 큰 단점이 있습니다. 오류에 대한 정보를 제공하지 않습니다. 어플리케이션이 크래쉬가 나서 종료 되는것 보다는 나은 전략이지만, 딱 거기까지 입니다.

이번 섹션에서는 오류가 있을 때 콜스택 및 줄 번호를 제공하는 것입니다.

다음은 완벽한 에러 처리 전략의 개요입니다.

  1. 모든 서브 프로시져에 오류 처리를 배치합니다.
  2. 오류가 발생하면 오류 처리기가 오류에 세부 정보를 추가하고 다시 오류를 발생 시킵니다.
  3. 최상위 서브 프로시저에서는 지금 까지의 오류 정보를 출력합니다.

이와 같이 하위 서브 프로시져에서 부터 상위 서브 프로시져로 에러를 전파하는 것을 물방울이 물위로 올라가는것 처럼 생겼다고해서 "버블링"이라고 합니다. 아래 다이어그램은 Sub3에서 오류가 발생할때의 상황을 시각적으로 보여 줍니다.

 

다음 코드는 '완벽한 에러 처리 전략'을 구현한 예입니다.

Sub Topmost()

    On Error Goto EH
    
    Level_1

Done:
    Exit Sub
EH:
    DisplayError Err.source, Err.Description, "Module1.Topmost", Erl
End Sub

Sub Level_1()

    On Error Goto EH
    
    Level_2

Done:
    Exit Sub
EH:
    RaiseError Err.Number, Err.source, "Module1.Level1", Err.Description, Erl
End Sub

Sub Level_2()

    On Error Goto EH
    
    ' Error here
    Dim a As Long
    a = "7 / 0"

Done:
    Exit Sub
EH:
    RaiseError Err.Number, Err.source, "Module1.Level2", Err.Description, Erl
End Sub

마치며

  • 오류 처리는 애플리케이션이 실행중일 때 발생하는 오류를 처리하는데 사용됩니다.
  • 예상되는 오류를 처리하기 위해 특정 코드를 작성합니다. 예기치 않은 오류가 발생할 때 VBA 오류 처리문 On Error Goto [레이블]을 사용하여 VBA를 특정 레이블로 보냅니다.
  • Err.Description에서 오류에 대한 세부 정보를 얻을수 있습니다.
  • Err.Raise를 사용하여 사용자 정의 오류를 발생 시킬수 있습니다.
  • 최상위 프로시져에서 하나의 On Error 문을 사용하면 호출하는 하위 서브 프로시져에서 발생한 모든 에러를 잡을 수 있습니다.
  • 만일 여러분이 에러가 발생한 서브 프로시져의 이름을 기록하고 싶다면, 에러를 업데이트하고 업데이트 된 정보로 다시 에러를 발생 시킬수 있습니다.
  • 로그를 사용하여 실행중인 어플리케이션에 대한 정보를 기록할 수 있습니다.

부록 1. 같이 읽으면 좋은 글

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!