엑셀 VBA 런타임 오류 91 해결: 객체 변수 또는 With 블록 변수가 설정되지 않았습니다

엑셀 매크로를 실행할 때 다음 메시지가 나타날 수 있습니다.

런타임 오류 '91':
개체 변수 또는 With 블록 변수가 설정되지 않았습니다.

이 오류는 VBA가 워크시트, 셀 범위, 통합 문서 같은 객체를 사용하려 했지만 해당 변수가 현재 유효한 객체를 가리키지 않을 때 발생합니다.

가장 흔한 원인은 객체 변수를 선언만 하고 Set으로 할당하지 않은 경우와, Range.Find가 검색 결과를 찾지 못해 Nothing을 반환한 경우입니다.

이 글은 Microsoft의 VBA 오류 91, Set 문과 Excel Range.Find 공식 문서를 기준으로 작성했습니다. 예제 코드는 실제 파일 구조에 맞게 시트 이름과 검색 범위를 변경해야 합니다.

엑셀 VBA 런타임 오류 91의 객체 변수와 Find 결과 확인
디버그에서 멈춘 코드의 객체 변수가 Nothing인지 확인하고 Set 할당과 Find 결과 검사를 추가합니다.

오류 창이 나타나면 먼저 ‘디버그’를 누르세요

오류 창에서 종료를 바로 누르면 어느 코드에서 문제가 발생했는지 확인하기 어렵습니다. 먼저 디버그를 선택하세요.

VBA 편집기가 열리면서 오류가 발생한 코드가 노란색으로 표시됩니다. 노란색 줄에서 마침표 왼쪽에 있는 객체 변수를 확인하면 원인을 좁힐 수 있습니다.

노란색 줄에서 확인할 부분

ws.Cells(2, 1).Value에서 오류가 발생했다면 ws를 확인합니다.

foundCell.Row에서 오류가 발생했다면 foundCell을 확인합니다.

wb.Worksheets(1).Name에서 오류가 발생했다면 wb를 확인합니다.

오류가 발생한 줄 전체를 무작정 바꾸기보다, 속성이나 메서드를 호출하기 직전의 객체가 올바르게 설정됐는지 확인해야 합니다.

원인 1: 객체 변수를 선언만 하고 Set으로 할당하지 않음

아래 코드는 ws를 Worksheet 객체 변수로 선언했지만 실제 워크시트를 할당하지 않았습니다.

오류가 발생하는 코드

Sub CheckLastRow()

    Dim ws As Worksheet
    Dim lastRow As Long

    lastRow = ws.Cells( _
        ws.Rows.Count, "A" _
    ).End(xlUp).Row

    MsgBox lastRow

End Sub

ws는 선언됐지만 어떤 워크시트도 가리키지 않습니다. 따라서 ws.Cells를 사용하는 줄에서 오류 91이 발생합니다.

Set으로 워크시트를 할당한 코드

Sub CheckLastRow()

    Dim ws As Worksheet
    Dim lastRow As Long

    Set ws = ThisWorkbook.Worksheets("Data")

    lastRow = ws.Cells( _
        ws.Rows.Count, "A" _
    ).End(xlUp).Row

    MsgBox lastRow

End Sub

실제 시트 이름이 Data가 아니라면 코드의 "Data" 부분을 Excel 아래쪽 시트 탭에 표시되는 이름으로 변경해야 합니다.

시트 이름이 잘못되면 오류 91이 아니라 런타임 오류 9가 나타날 수 있습니다. 띄어쓰기와 특수문자까지 실제 시트 이름과 정확히 일치하는지 확인하세요.

원인 2: Find가 검색 결과를 찾지 못함

Range.Find는 검색 결과가 있으면 해당 셀을 Range 객체로 반환하지만, 결과가 없으면 Nothing을 반환합니다.

다음처럼 검색 결과에 바로 .Row를 연결하면 찾는 값이 없을 때 오류 91이 발생할 수 있습니다.

검색 결과를 바로 사용하는 위험한 코드

totalRow = ws.Columns("A").Find( _
    What:="총계" _
).Row

A열에 정확히 총계라는 값이 없다면 Find 결과는 Nothing입니다. 이 상태에서는 .Row를 사용할 수 없습니다.

검색 결과를 먼저 확인하는 코드

Dim foundCell As Range

Set foundCell = ws.Columns("A").Find( _
    What:="총계", _
    After:=ws.Cells(ws.Rows.Count, "A"), _
    LookIn:=xlValues, _
    LookAt:=xlWhole, _
    SearchOrder:=xlByRows, _
    SearchDirection:=xlNext, _
    MatchCase:=False, _
    SearchFormat:=False _
)

If foundCell Is Nothing Then

    MsgBox "'총계'를 찾지 못했습니다.", _
           vbInformation
    Exit Sub

End If

MsgBox "총계가 있는 행: " & foundCell.Row

핵심은 Find의 결과를 Range 변수에 먼저 저장하고, 다음 조건으로 결과가 존재하는지 확인하는 것입니다.

If foundCell Is Nothing Then

결과가 없을 때는 메시지를 표시하거나 해당 작업을 건너뛰도록 처리합니다. 결과가 있다는 사실을 확인한 뒤에만 .Row, .Value 같은 속성을 사용하세요.

Find의 검색 조건을 생략하지 않는 이유

Excel의 찾기 창에서 선택한 검색 범위나 검색 대상 설정은 다음 Find 실행에도 영향을 줄 수 있습니다. 코드가 실행될 때마다 같은 조건으로 검색하려면 다음 인수를 가능한 한 명시하는 것이 좋습니다.

  • LookIn:=xlValues: 셀에 표시되는 값을 기준으로 검색
  • LookAt:=xlWhole: 셀 전체 내용이 검색어와 같을 때만 일치
  • SearchOrder:=xlByRows: 행을 기준으로 검색
  • SearchDirection:=xlNext: 다음 방향으로 검색
  • MatchCase:=False: 영문 대소문자를 구분하지 않음
  • SearchFormat:=False: 특정 셀 서식을 검색 조건으로 사용하지 않음

검색어가 포함된 셀도 찾으려면 LookAt:=xlWholeLookAt:=xlPart로 변경할 수 있습니다.

xlWhole과 xlPart의 차이
셀 내용이 총계일 때 두 설정 모두 찾을 수 있습니다.
셀 내용이 상반기 총계일 때 xlWhole은 찾지 못하고 xlPart는 찾을 수 있습니다.

원인 3: With 블록에서 사용하는 객체가 Nothing 상태임

오류 메시지에 표시되는 ‘With 블록 변수가 설정되지 않았습니다’는 With 뒤에 사용한 객체가 유효하지 않은 경우에도 나타날 수 있습니다.

오류가 발생하는 With 코드

Sub WriteResult()

    Dim targetCell As Range

    With targetCell
        .Value2 = "완료"
        .Font.Bold = True
    End With

End Sub

targetCell을 선언했지만 셀을 할당하지 않았으므로 .Value2에서 오류 91이 발생합니다.

객체를 할당한 뒤 사용하는 코드

Sub WriteResult()

    Dim ws As Worksheet
    Dim targetCell As Range

    Set ws = ThisWorkbook.Worksheets("Data")
    Set targetCell = ws.Range("B2")

    With targetCell
        .Value2 = "완료"
        .Font.Bold = True
    End With

End Sub

With 블록 자체를 삭제하는 것이 해결책은 아닙니다. With가 사용하는 객체가 어느 줄에서 할당돼야 하는지 확인해야 합니다.

원인 4: 함수가 객체를 찾지 못하고 Nothing을 반환함

별도의 함수에서 Range, Worksheet 또는 Workbook 객체를 반환하도록 만들었더라도 조건에 맞는 객체를 찾지 못하면 반환값이 Nothing일 수 있습니다.

호출한 쪽에서는 함수가 항상 객체를 돌려줄 것이라고 가정하지 말고 결과를 확인해야 합니다.

Dim targetCell As Range

Set targetCell = FindTotalCell( _
    ThisWorkbook.Worksheets("Data") _
)

If targetCell Is Nothing Then

    MsgBox "총계 셀을 찾지 못했습니다.", _
           vbInformation
    Exit Sub

End If

MsgBox targetCell.Address

객체를 반환하는 함수의 결과에도 동일하게 Is Nothing 검사를 적용할 수 있습니다.

총계 셀을 안전하게 찾는 전체 예제

아래 코드는 이름이 Data인 워크시트의 A열에서 총계라는 값을 찾습니다. 검색 결과가 없으면 오류를 발생시키지 않고 안내 메시지를 표시합니다.

이 코드는 셀의 내용을 수정하지 않고 검색된 행 번호만 메시지로 보여줍니다.

Option Explicit

Private Const SHEET_NAME As String = "Data"
Private Const SEARCH_COLUMN As String = "A"
Private Const SEARCH_TEXT As String = "총계"

Public Sub FindTotalRowSafely()

    Dim ws As Worksheet
    Dim foundCell As Range

    On Error GoTo ErrorHandler

    Set ws = ThisWorkbook.Worksheets(SHEET_NAME)

    Set foundCell = ws.Columns(SEARCH_COLUMN).Find( _
        What:=SEARCH_TEXT, _
        After:=ws.Cells(ws.Rows.Count, SEARCH_COLUMN), _
        LookIn:=xlValues, _
        LookAt:=xlWhole, _
        SearchOrder:=xlByRows, _
        SearchDirection:=xlNext, _
        MatchCase:=False, _
        SearchFormat:=False _
    )

    If foundCell Is Nothing Then

        MsgBox _
            "'" & SEARCH_TEXT & _
            "'을(를) " & SEARCH_COLUMN & _
            "열에서 찾지 못했습니다.", _
            vbInformation

        Exit Sub

    End If

    MsgBox _
        "'" & SEARCH_TEXT & _
        "'이(가) 있는 행: " & foundCell.Row, _
        vbInformation

    Exit Sub

ErrorHandler:

    Select Case Err.Number

        Case 9

            MsgBox _
                "'" & SHEET_NAME & _
                "' 워크시트를 찾지 못했습니다." & vbCrLf & _
                "코드의 SHEET_NAME을 확인하세요.", _
                vbExclamation

        Case 91

            MsgBox _
                "사용하려는 객체가 설정되지 않았습니다." & vbCrLf & _
                "Set 할당과 Find 결과를 확인하세요.", _
                vbExclamation

        Case Else

            MsgBox _
                "오류 번호: " & Err.Number & vbCrLf & _
                "오류 내용: " & Err.Description, _
                vbExclamation

    End Select

End Sub

전체 코드에서 직접 바꿀 부분

워크시트 이름

Private Const SHEET_NAME As String = "Data"

실제 시트 탭 이름이 매출이라면 "Data""매출"로 변경합니다.

검색할 열

Private Const SEARCH_COLUMN As String = "A"

검색할 값이 C열에 있다면 "A""C"로 변경합니다.

검색할 글자

Private Const SEARCH_TEXT As String = "총계"

실제로 찾을 값이 합계라면 "총계""합계"로 변경합니다.

On Error Resume Next로 오류를 숨기지 마세요

오류 91이 나타난다는 이유로 코드 전체에 다음 문장을 추가하면 화면에는 오류가 표시되지 않을 수 있습니다.

On Error Resume Next

하지만 객체가 설정되지 않은 원인은 그대로 남습니다. 이후 코드가 실행되지 않거나 잘못된 결과가 저장돼도 원인을 찾기 어려워질 수 있습니다.

Find 결과는 Is Nothing으로 확인하고, 예상할 수 없는 오류는 On Error GoTo ErrorHandler처럼 별도의 오류 처리 구간에서 표시하는 편이 낫습니다.

Option Explicit로 변수 이름 실수 줄이기

코드 모듈의 맨 위에 다음 문장을 넣으면 선언하지 않은 변수 이름을 사용할 때 컴파일 단계에서 확인할 수 있습니다.

Option Explicit

VBA 편집기에서 디버그 → VBAProject 컴파일을 실행하면 변수 이름, 구문과 선언 문제를 매크로 실행 전에 확인하는 데 도움이 됩니다.

오류가 발생한 줄에서 이 순서로 확인하세요

1. 오류 창에서 디버그를 눌렀는지
2. 노란색 줄에서 마침표 왼쪽의 객체를 찾았는지
3. 객체 변수가 Dim으로 선언만 되고 Set되지 않았는지
4. Find 또는 객체 반환 함수가 Nothing을 반환할 수 있는지
5. If 객체 Is Nothing Then 검사가 있는지
6. With 뒤에 사용한 객체가 먼저 할당됐는지
7. On Error Resume Next가 실제 오류를 숨기고 있지 않은지

런타임 오류 91은 오류가 표시된 속성이나 메서드 자체보다, 그 앞에 있는 객체 변수가 유효한 객체를 가리키고 있는지를 확인하는 것이 핵심입니다.

코드 확인에 사용한 Microsoft 공식 자료

Post a Comment

다음 이전