엑셀 VBA 매크로를 실행할 때 다음 메시지가 표시될 수 있습니다.
첨자가 사용 범위를 벗어났습니다.
오류 9는 코드가 배열이나 컬렉션에서 존재하지 않는 항목을 찾으려고 할 때 발생합니다.
Excel VBA에서는 존재하지 않는 워크시트 이름, 열려 있지 않은 통합 문서, 실제 개수를 초과한 시트 번호와 배열 범위를 벗어난 인덱스가 대표적인 원인입니다.
오류 메시지만 보고 시트 이름부터 임의로 바꾸면 배열 문제를 놓칠 수 있습니다. 반대로 모든 오류 9를 배열 오류로 생각하면 실제로 열리지 않은 통합 문서를 찾지 못할 수 있습니다.
이 글은 Microsoft의 VBA 오류 9, Excel Workbooks·Worksheets 컬렉션, ThisWorkbook·ActiveWorkbook, LBound·UBound, Split, Range.Value와 Option Explicit 공식 문서를 기준으로 작성했습니다. 예제의 파일명, 시트명과 셀 주소는 실제 통합 문서 구조에 맞게 변경해야 합니다.
![]() |
| VBA 런타임 오류 9는 존재하지 않는 시트·통합 문서 구성원이나 배열의 유효 범위를 벗어난 요소를 참조할 때 발생할 수 있습니다. 위 이미지는 오류 진단 구조를 설명하기 위한 대표 개념 이미지입니다. |
디버그를 눌러 강조된 줄의 참조 종류부터 확인합니다
오류 창에서 디버그를 선택하면 VBA 편집기가 오류가 발생한 줄을 강조합니다.
강조된 줄에서 괄호 안에 들어간 값이 무엇인지 확인하면 원인을 세 가지로 나눌 수 있습니다.
| 강조된 코드 형태 | 참조 대상 | 먼저 확인할 내용 |
|---|---|---|
Worksheets("데이터")
|
워크시트 컬렉션 | 시트 이름과 대상 통합 문서 |
Workbooks("보고서.xlsx")
|
열려 있는 통합 문서 컬렉션 | 파일이 열려 있는지와 실제 파일명 |
Worksheets(4)
|
시트의 위치 번호 | Worksheets.Count와 인덱스 범위 |
values(index)
|
배열 요소 | LBound·UBound와 배열 차원 |
존재하지 않는 워크시트 이름을 참조한 경우
다음 코드는 현재 참조한 통합 문서에 이름이 정확히
데이터인 워크시트가 있다는 전제에서 실행됩니다.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("데이터")
실제 탭 이름이 “원본데이터”, “데이터 ” 또는 “Data”라면 요청한 구성원을 찾지 못할 수 있습니다.
다음 항목을 확인합니다.
- 시트 탭에 표시된 이름과 코드의 문자열이 일치하는지
- 코드가 확인하는 통합 문서가 맞는지
- 시트가 삭제되거나 이름이 변경되지 않았는지
- 셀에서 시트명을 가져온다면 불필요한 공백이 포함되지 않았는지
- 차트 시트를 Worksheet로 잘못 가정한 것은 아닌지
숨겨진 워크시트도 해당 통합 문서의 Worksheets 컬렉션에 존재한다면 이름으로 참조할 수 있습니다. 단순히 화면에 보이지 않는다는 이유만으로 오류 9가 발생하는 것은 아닙니다.
실제 시트 이름을 직접 실행 창에 출력합니다
코드에 적힌 이름을 계속 추측하지 말고 대상 통합 문서에 들어 있는 워크시트 이름을 확인할 수 있습니다.
Option Explicit
Public Sub PrintWorksheetNames()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print _
CStr(ws.Index) & _
" | " & _
ws.Name & _
" | CodeName: " & _
ws.CodeName
Next ws
End Sub
VBA 편집기에서 보기 → 직접 실행 창을 선택하거나 Ctrl+G를
누르면 출력 결과를 확인할 수 있습니다.
매크로가 PERSONAL.XLSB나 추가 기능에 들어 있다면
ThisWorkbook이 작업 대상 파일이 아닐 수 있습니다. 이 경우에는
뒤에서 설명하는 대상 통합 문서 구분이 먼저 필요합니다.
시트를 찾은 뒤 사용하도록 함수를 분리합니다
존재하지 않는 시트를 바로 참조하지 않고 워크시트 목록에서 이름을 확인한 뒤 반환하도록 만들 수 있습니다.
Option Explicit
Public Sub UpdateDataSheet()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = GetWorksheetOrFail_( _
wb, _
"데이터" _
)
ws.Range("A1").Value2 = "업데이트"
End Sub
Private Function GetWorksheetOrFail_( _
ByVal wb As Workbook, _
ByVal sheetName As String _
) As Worksheet
Dim ws As Worksheet
If wb Is Nothing Then
Err.Raise _
vbObjectError + 901, _
"GetWorksheetOrFail_", _
"통합 문서 개체가 설정되지 않았습니다."
End If
If Len(sheetName) = 0 Then
Err.Raise _
vbObjectError + 902, _
"GetWorksheetOrFail_", _
"찾을 워크시트 이름이 비어 있습니다."
End If
For Each ws In wb.Worksheets
If StrComp( _
ws.Name, _
sheetName, _
vbTextCompare _
) = 0 Then
Set GetWorksheetOrFail_ = ws
Exit Function
End If
Next ws
Err.Raise _
vbObjectError + 903, _
"GetWorksheetOrFail_", _
"워크시트를 찾을 수 없습니다. " & _
"통합 문서: " & wb.Name & _
", 시트: " & sheetName
End Function
이 함수는 시트가 없을 때 오류를 무시하거나 빈 Worksheet 변수를 반환하지 않습니다. 어떤 통합 문서에서 어떤 시트를 찾지 못했는지 설명하는 오류를 발생시킵니다.
vbTextCompare는 영문 대소문자 차이를 무시하지만 공백이나 다른
문자는 자동으로 수정하지 않습니다.
ThisWorkbook과 ActiveWorkbook을 목적에 맞게 선택합니다
오류 9는 시트 이름이 맞는데도 잘못된 통합 문서의 Worksheets 컬렉션을 확인할 때 자주 발생합니다.
| 참조 | 의미 | 사용할 수 있는 상황 |
|---|---|---|
ThisWorkbook
|
현재 VBA 코드가 저장된 통합 문서 | 매크로와 작업 시트가 같은 파일에 있음 |
ActiveWorkbook
|
현재 활성 창에 표시된 통합 문서 | 사용자가 활성화한 파일을 대상으로 실행하도록 설계함 |
Workbooks("파일명.xlsx")
|
현재 열려 있는 통합 문서 중 지정한 이름 | 대상 파일명이 정해져 있고 이미 열려 있음 |
매크로가 작업 대상 파일에 저장돼 있다면 다음처럼 사용할 수 있습니다.
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("데이터")
매크로가 PERSONAL.XLSB에 있고 현재 화면에서 선택한 업무 파일을 처리하도록 설계했다면 활성 통합 문서를 별도 변수로 받을 수 있습니다.
Dim wb As Workbook
Dim ws As Worksheet
If Application.ActiveWorkbook Is Nothing Then
Err.Raise _
vbObjectError + 904, _
"GetTargetWorkbook", _
"활성 통합 문서가 없습니다."
End If
Set wb = Application.ActiveWorkbook
Set ws = GetWorksheetOrFail_(wb, "데이터")
사용자가 다른 파일을 클릭하면 ActiveWorkbook도 바뀔 수 있습니다. 작업 대상이 고정된 매크로라면 파일명을 확인하거나 Workbook 변수를 명시적으로 설정하는 편이 적절합니다.
Workbooks 컬렉션에는 현재 열려 있는 파일만 있습니다
다음 코드는 이름이 업무보고서.xlsx인 통합 문서가 현재 Excel에
열려 있다는 전제에서 실행됩니다.
Dim wb As Workbook
Set wb = Workbooks("업무보고서.xlsx")
파일이 폴더에 존재하더라도 현재 Excel에 열려 있지 않으면 Workbooks 컬렉션에서 찾을 수 없습니다.
현재 열려 있는 통합 문서 이름과 경로는 다음 코드로 확인할 수 있습니다.
Option Explicit
Public Sub PrintOpenWorkbooks()
Dim wb As Workbook
For Each wb In Application.Workbooks
Debug.Print _
wb.Name & _
" | " & _
wb.FullName
Next wb
End Sub
출력된 wb.Name에는 일반적으로 파일 확장자가 포함됩니다. 코드에
확장자가 빠졌거나 실제 파일이 다른 형식으로 저장됐다면 이름을 다시 확인해야
합니다.
열린 통합 문서를 확인한 뒤 반환합니다
존재하지 않는 통합 문서를 Workbooks 컬렉션에서 곧바로 꺼내기보다 현재 열린 통합 문서를 순회해 이름을 확인할 수 있습니다.
Option Explicit
Public Sub ReadReportWorkbook()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = GetOpenWorkbookOrFail_( _
"업무보고서.xlsx" _
)
Set ws = GetWorksheetOrFail_( _
wb, _
"데이터" _
)
Debug.Print ws.Range("A1").Value2
End Sub
Private Function GetOpenWorkbookOrFail_( _
ByVal workbookName As String _
) As Workbook
Dim wb As Workbook
If Len(workbookName) = 0 Then
Err.Raise _
vbObjectError + 905, _
"GetOpenWorkbookOrFail_", _
"찾을 통합 문서 이름이 비어 있습니다."
End If
For Each wb In Application.Workbooks
If StrComp( _
wb.Name, _
workbookName, _
vbTextCompare _
) = 0 Then
Set GetOpenWorkbookOrFail_ = wb
Exit Function
End If
Next wb
Err.Raise _
vbObjectError + 906, _
"GetOpenWorkbookOrFail_", _
"열린 통합 문서를 찾을 수 없습니다: " & _
workbookName
End Function
이 함수는 현재 열려 있는 통합 문서만 찾습니다. 파일이 닫혀 있다면 파일
경로를 확인한 뒤 Workbooks.Open으로 여는 단계가 별도로
필요합니다.
파일을 자동으로 열 것인지, 열려 있지 않으면 실행을 중단할 것인지는 매크로의 업무 목적에 따라 정해야 합니다.
숫자로 시트를 참조할 때 Count를 넘지 않도록 확인합니다
다음 코드는 대상 통합 문서에 네 번째 워크시트가 있을 때만 실행됩니다.
Set ws = ThisWorkbook.Worksheets(4)
워크시트가 세 개뿐이라면 인덱스 4는 유효한 구성원이 아니므로 오류 9가 발생합니다.
인덱스가 변수로 정해진다면 Worksheets.Count와 비교한 뒤 사용합니다.
Dim wb As Workbook
Dim ws As Worksheet
Dim sheetIndex As Long
Set wb = ThisWorkbook
sheetIndex = 4
If _
sheetIndex < 1 Or _
sheetIndex > wb.Worksheets.Count _
Then
Err.Raise _
vbObjectError + 907, _
"GetWorksheetByIndex", _
"워크시트 인덱스가 유효하지 않습니다. " & _
"요청값: " & CStr(sheetIndex) & _
", 시트 개수: " & _
CStr(wb.Worksheets.Count)
End If
Set ws = wb.Worksheets(sheetIndex)
특정 업무 시트를 찾는 목적이라면 순서 번호보다 시트 이름을 사용하는 편이 의미를 명확하게 보여줍니다.
시트 추가·삭제·이동으로 순서가 바뀌면
Worksheets(2)가 이전과 다른 시트를 가리킬 수 있습니다.
모든 시트를 처리할 때는 고정 번호보다 For Each를 사용합니다
시트 개수만큼 반복하려고 숫자를 직접 작성하면 시트가 추가되거나 삭제됐을 때 유효 범위를 벗어날 수 있습니다.
고정된 숫자 범위에 의존하는 코드:
Dim sheetIndex As Long
For sheetIndex = 1 To 12
Debug.Print Worksheets(sheetIndex).Name
Next sheetIndex
실제 워크시트가 12개보다 적다면 존재하지 않는 인덱스를 참조하게 됩니다.
모든 워크시트를 처리하는 경우에는 다음처럼 작성할 수 있습니다.
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print ws.Name
Next ws
순서 번호가 실제 처리 기준일 때는
1 To wb.Worksheets.Count 범위를 사용합니다.
Dim sheetIndex As Long
For sheetIndex = _
1 To ThisWorkbook.Worksheets.Count
Debug.Print _
ThisWorkbook _
.Worksheets(sheetIndex) _
.Name
Next sheetIndex
시트 탭 이름이 자주 바뀌면 CodeName을 검토합니다
워크시트에는 사용자가 탭에서 보는 Name과 VBA 프로젝트의 속성
창에서 확인하는 CodeName이 있습니다.
시트 탭 이름을 바꿔도 CodeName은 자동으로 바뀌지 않습니다. 매크로와 해당 워크시트가 같은 통합 문서의 VBA 프로젝트에 있다면 CodeName으로 참조할 수 있습니다.
예를 들어 VBA 속성 창의 CodeName이 SheetData라면 다음과 같이
참조할 수 있습니다.
SheetData.Range("A1").Value2 = "업데이트"
이 방식은 사용자가 탭 이름을 “데이터”, “원본”, “7월 데이터”로 바꿔도 같은 CodeName의 워크시트를 참조합니다.
PERSONAL.XLSB나 추가 기능의 매크로가 다른 업무 파일을 처리한다면 그 대상 파일의 시트를 현재 프로젝트의 CodeName으로 직접 참조할 수 없습니다. 외부 통합 문서를 처리하는 코드는 Workbook 변수와 시트 이름을 명시적으로 확인하는 편이 적절합니다.
배열 인덱스는 LBound와 UBound 사이에서 사용합니다
오류 9는 워크시트뿐 아니라 배열의 유효 범위를 벗어났을 때도 발생합니다.
다음 배열에서 사용할 수 있는 인덱스는 1부터 3까지입니다.
Dim departmentNames(1 To 3) As String
departmentNames(1) = "영업"
departmentNames(2) = "구매"
departmentNames(3) = "관리"
다음 코드는 인덱스 4가 존재하지 않으므로 오류가 발생합니다.
Debug.Print departmentNames(4)
배열을 반복할 때는 시작값과 끝값을 임의로 정하지 말고 실제 배열의 하한과 상한을 사용합니다.
Dim departmentNames(1 To 3) As String
Dim itemIndex As Long
departmentNames(1) = "영업"
departmentNames(2) = "구매"
departmentNames(3) = "관리"
For itemIndex = _
LBound(departmentNames) _
To _
UBound(departmentNames)
Debug.Print _
departmentNames(itemIndex)
Next itemIndex
VBA 배열의 하한은 선언 방식과 Option Base 설정 등에 따라 달라질 수 있으므로 모든 배열이 항상 0 또는 1부터 시작한다고 가정하지 않습니다.
Split 함수의 결과는 0부터 시작합니다
VBA의 Split 함수는 문자열을 나눠 0부터 시작하는 1차원 배열을
반환합니다.
Dim cityNames As Variant
cityNames = Split( _
"서울|부산|대전", _
"|" _
)
Debug.Print cityNames(0)
Debug.Print cityNames(1)
Debug.Print cityNames(2)
이 배열에서 cityNames(3)을 사용하면 네 번째 요소가 존재하지
않으므로 오류 9가 발생합니다.
요소 개수가 입력 문자열에 따라 달라진다면 LBound와 UBound로 반복합니다.
Dim cityNames As Variant
Dim itemIndex As Long
cityNames = Split( _
"서울|부산|대전", _
"|" _
)
For itemIndex = _
LBound(cityNames) _
To _
UBound(cityNames)
Debug.Print cityNames(itemIndex)
Next itemIndex
동적 배열은 ReDim으로 크기를 정한 뒤 사용합니다
다음 선언은 동적 배열 변수만 만들고 사용할 수 있는 요소 개수는 아직 정하지 않은 상태입니다.
Dim itemNames() As String
크기가 정해지지 않은 상태에서 다음처럼 요소를 사용하면 오류가 발생할 수 있습니다.
itemNames(0) = "항목 A"
실제 요소 범위를 정한 뒤 값을 저장합니다.
Dim itemNames() As String
ReDim itemNames(0 To 2)
itemNames(0) = "항목 A"
itemNames(1) = "항목 B"
itemNames(2) = "항목 C"
실행 중에 배열 크기를 다시 정한다면 이후 사용하는 인덱스도 새 범위 안에 있는지 확인해야 합니다.
여러 셀의 Value는 2차원 배열로 처리합니다
여러 셀로 구성된 범위의 Value 또는 Value2를
Variant 변수에 저장하면 각 셀 값이 들어 있는 2차원 배열을 받을 수 있습니다.
Dim values As Variant
values = _
ThisWorkbook _
.Worksheets("데이터") _
.Range("A2:C10") _
.Value2
이 배열은 행과 열의 두 인덱스를 사용합니다.
Debug.Print values(1, 1)
실제 범위를 확인하지 않고 values(20, 1)처럼 존재하지 않는 행
인덱스를 사용하면 오류 9가 발생할 수 있습니다.
행과 열을 각각 LBound와 UBound로 확인합니다.
Dim values As Variant
Dim rowIndex As Long
Dim columnIndex As Long
values = _
ThisWorkbook _
.Worksheets("데이터") _
.Range("A2:C10") _
.Value2
For rowIndex = _
LBound(values, 1) _
To _
UBound(values, 1)
For columnIndex = _
LBound(values, 2) _
To _
UBound(values, 2)
Debug.Print _
values( _
rowIndex, _
columnIndex _
)
Next columnIndex
Next rowIndex
한 셀만 읽은 결과는 2차원 배열이 아니라 단일 값일 수 있습니다. 배열용 코드가 필요한 경우 원본 범위가 실제로 여러 셀인지도 확인해야 합니다.
변수에서 가져온 인덱스는 사용 전에 범위를 확인합니다
사용자 입력, 반복 계산 또는 다른 함수의 결과를 배열 인덱스로 사용한다면 그 값이 실제 배열 범위에 포함되는지 검사합니다.
Dim itemNames(1 To 3) As String
Dim requestedIndex As Long
itemNames(1) = "항목 A"
itemNames(2) = "항목 B"
itemNames(3) = "항목 C"
requestedIndex = 4
If _
requestedIndex < LBound(itemNames) Or _
requestedIndex > UBound(itemNames) _
Then
Err.Raise _
vbObjectError + 908, _
"ReadArrayItem", _
"배열 인덱스가 유효하지 않습니다. " & _
"요청값: " & CStr(requestedIndex) & _
", 허용 범위: " & _
CStr(LBound(itemNames)) & _
" ~ " & _
CStr(UBound(itemNames))
End If
Debug.Print itemNames(requestedIndex)
인덱스가 숫자로 변환될 수 있는지까지 확인해야 하는 상황이라면 형식 검사와 범위 검사를 구분합니다. 숫자로 변환할 수 없는 값은 오류 9보다 형식 불일치 오류 13의 원인이 될 수 있습니다.
On Error Resume Next로 존재하지 않는 항목을 숨기지 않습니다
다음 코드는 시트를 찾지 못해도 이후 줄로 계속 진행합니다.
On Error Resume Next
Set ws = ThisWorkbook.Worksheets("데이터")
ws.Range("A1").Value2 = "업데이트"
시트를 찾지 못하면 ws는 올바른 Worksheet 개체를 참조하지
않습니다. 오류 9는 숨겨지지만 이후 ws.Range에서 객체 변수가
설정되지 않았다는 다른 오류가 발생할 수 있습니다.
존재 여부를 확인해야 한다면 전체 프로시저의 오류를 무시하기보다 앞에서 제시한 이름 검색 함수처럼 컬렉션을 순회하거나, 제한된 한 구간에서만 오류 처리를 사용하고 즉시 원래 상태로 복원해야 합니다.
오류 9를 Resume Next로 숨긴 뒤 오류 91이나 빈 결과가 발생한다면 원래 존재하지 않았던 시트·통합 문서 참조가 해결된 것이 아닙니다. 참조 대상의 존재 여부를 확인하고 처리 중단 또는 대체 경로를 명시해야 합니다.
Option Explicit으로 인덱스 변수의 오타를 줄입니다
모듈 맨 위에 Option Explicit을 작성하면 모든 변수를 명시적으로
선언해야 합니다.
Option Explicit
Public Sub PrintArrayItems()
Dim itemNames(1 To 3) As String
Dim itemIndex As Long
itemNames(1) = "항목 A"
itemNames(2) = "항목 B"
itemNames(3) = "항목 C"
For itemIndex = _
LBound(itemNames) _
To _
UBound(itemNames)
Debug.Print itemNames(itemIndex)
Next itemIndex
End Sub
인덱스 변수 이름을 잘못 입력했을 때 의도하지 않은 Variant 변수가 새로 만들어지는 문제를 컴파일 단계에서 찾는 데 도움이 됩니다.
다만 Option Explicit이 존재하지 않는 시트 이름이나 잘못된 배열 범위를 자동으로 수정해 주는 것은 아닙니다. 변수 선언과 실제 참조 검사를 함께 적용해야 합니다.
오류가 발생한 코드 형태별 수정 방향
대상 Workbook 변수를 먼저 확인하고 실제 워크시트 이름을 출력합니다. 삭제·이름 변경·공백 포함 여부를 확인합니다.
파일이 현재 Excel에 열려 있는지 확인하고 실제 wb.Name과 확장자를 출력합니다.
번호가 1 이상 Worksheets.Count 이하인지 확인합니다. 업무상 특정 시트를 찾는다면 이름 참조를 검토합니다.
배열 선언, ReDim 실행 여부와 LBound·UBound를 확인합니다.
Split 결과가 0부터 시작한다는 점과 실제 분리된 요소 개수를 확인합니다.
여러 셀 결과가 2차원 배열인지 확인하고 행·열 차원의 LBound와 UBound를 각각 사용합니다.
원래 시트나 통합 문서를 찾지 못한 상태가 숨겨졌는지 확인하고 오류를 무시하는 코드를 제거합니다.
최종 확인 순서
- 오류 창에서 디버그를 눌러 정확한 코드 줄을 확인합니다.
- 괄호 안의 값이 시트명, 파일명, 컬렉션 번호 또는 배열 인덱스인지 구분합니다.
- ThisWorkbook·ActiveWorkbook·지정한 Workbook 중 어느 파일을 참조하는지 확인합니다.
- 현재 열린 통합 문서와 실제 워크시트 이름을 직접 실행 창에 출력합니다.
- 숫자 인덱스는 컬렉션의 Count 범위 안에 있는지 확인합니다.
- 배열은 크기가 정해져 있는지 확인하고 LBound·UBound를 사용합니다.
- 여러 셀의 값은 행과 열을 가진 2차원 배열로 처리합니다.
- On Error Resume Next로 존재하지 않는 참조를 숨기지 않습니다.
- Option Explicit으로 선언되지 않은 변수와 인덱스 변수 오타를 확인합니다.
내용 확인에 사용한 Microsoft 공식 자료
- Microsoft Learn: Subscript out of range (Error 9)
- Microsoft Learn: Application.Workbooks
- Microsoft Learn: Workbooks.Item
- Microsoft Learn: Worksheets.Item
- Microsoft Learn: Worksheets.Count
- Microsoft Learn: Application.ThisWorkbook
- Microsoft Learn: Application.ActiveWorkbook
- Microsoft Learn: Worksheet.CodeName
- Microsoft Learn: LBound 함수
- Microsoft Learn: UBound 함수
- Microsoft Learn: Split 함수
- Microsoft Learn: Range.Value 속성
- Microsoft Learn: ReDim 문
- Microsoft Learn: Option Explicit 문
공식 문서 확인일: 2026년 7월 29일

댓글 쓰기