엑셀 VBA 매크로를 실행할 때 다음 오류가 표시될 수 있습니다.
응용 프로그램 정의 또는 개체 정의 오류입니다.
오류 1004는 특정 원인 하나를 뜻하지 않습니다. Excel이 시트·셀·범위· 보호된 개체 또는 실행하려는 메서드를 현재 조건에서 처리할 수 없을 때 여러 형태의 1004 오류가 발생할 수 있습니다.
따라서 인터넷에서 찾은 한 줄짜리 수정 코드를 무조건 적용하기보다, 오류 창에서 디버그를 눌러 실제로 강조되는 줄을 먼저 확인해야 합니다.
강조된 줄이 Range 또는 Cells인지,
Select인지, 보호된 시트를 수정하는 코드인지에 따라 확인할
위치가 달라집니다.
이 글은 Microsoft의 VBA 응용 프로그램 정의 오류, Worksheet.Range, Worksheet.Cells, Range.Select, Worksheet 보호, Intersect와 Err 개체 공식 문서를 기준으로 작성했습니다. 예제의 통합 문서명, 시트명과 셀 주소는 실제 파일 구조에 맞게 변경해야 합니다.
![]() |
| VBA 런타임 오류 1004는 잘못된 범위 주소, 서로 다른 워크시트에 속한 Range와 Cells, 시트 보호 또는 Excel 개체의 실행 조건 때문에 발생할 수 있습니다. 위 이미지는 오류 원인을 구분하는 과정을 설명하기 위한 대표 개념 이미지입니다. |
오류 메시지보다 디버그에서 강조된 줄이 중요합니다
같은 오류 번호 1004라도 다음처럼 서로 다른 코드에서 발생할 수 있습니다.
| 오류가 발생한 코드 | 먼저 확인할 내용 |
|---|---|
ws.Range(...)
|
범위 주소와 Range·Cells가 같은 시트에 속하는지 |
ws.Cells(row, column)
|
행과 열 번호가 유효한 범위인지 |
targetRange.Select
|
해당 범위가 있는 워크시트가 활성 상태인지 |
| 셀 값 쓰기·행 삭제·정렬 | 워크시트 내용이 보호돼 있는지 |
Application.Intersect(...)
|
모든 범위가 같은 워크시트에 속하는지 |
오류 1004가 발생한 줄과 실제 오류 설명을 확인하지 않고
Select만 제거하거나 시트 이름만 바꾸는 방식으로는 원인을 정확히
판단할 수 없습니다.
가장 흔한 원인: Range와 Cells가 서로 다른 시트를 참조함
다음 코드는 겉보기에는 “데이터” 시트의 A2:D마지막행 범위를 만드는 것처럼 보입니다.
Dim ws As Worksheet
Dim targetRange As Range
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("데이터")
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set targetRange = _
ws.Range( _
Cells(2, 1), _
Cells(lastRow, 4) _
)
하지만 두 개의 Cells에는 워크시트 개체가 붙어 있지 않습니다.
개체가 지정되지 않은 Cells는 활성 워크시트를 참조합니다.
“데이터” 시트가 아닌 다른 시트가 활성 상태라면 다음처럼 서로 다른 워크시트의 범위를 한 문장에서 사용하게 됩니다.
ws.Range: “데이터” 시트Cells(2, 1): 현재 활성 시트Cells(lastRow, 4): 현재 활성 시트
Range의 시작 셀과 끝 셀을 같은 Worksheet 개체로 한정해야 합니다.
Dim ws As Worksheet
Dim targetRange As Range
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("데이터")
With ws
lastRow = _
.Cells(.Rows.Count, 1) _
.End(xlUp) _
.Row
Set targetRange = _
.Range( _
.Cells(2, 1), _
.Cells(lastRow, 4) _
)
End With
With ws 안에서 Range, Cells와 Rows 앞에 점을 붙이면 모두 같은
“데이터” 워크시트에 속합니다.
.Range, .Cells, .Rows의 앞에 있는
점은 현재 With 대상인 ws를 사용한다는 뜻입니다.점을 빼면 활성 워크시트에 의존하는 코드가 될 수 있습니다.
Rows.Count도 대상 워크시트에 연결합니다
마지막 데이터 행을 찾을 때 다음 코드가 자주 사용됩니다.
lastRow = _
ws.Cells(Rows.Count, 1) _
.End(xlUp) _
.Row
여기서 ws.Cells는 ws를 참조하지만 Rows.Count에는
워크시트 한정자가 없습니다.
다음처럼 같은 워크시트에서 행 개수를 가져옵니다.
lastRow = _
ws.Cells(ws.Rows.Count, 1) _
.End(xlUp) _
.Row
또는 With 블록 안에서 작성합니다.
With ws
lastRow = _
.Cells(.Rows.Count, 1) _
.End(xlUp) _
.Row
End With
행과 열 번호가 유효한지 먼저 검사합니다
Cells의 첫 번째 인수는 행 번호이고 두 번째 인수는 열 번호입니다. 값이 0이거나 음수이거나 워크시트의 최대 행·열 개수를 넘으면 유효한 셀을 반환할 수 없습니다.
다음 코드는 변수값을 검사하지 않고 바로 셀을 참조합니다.
Dim rowNumber As Long
Dim columnNumber As Long
rowNumber = 0
columnNumber = 3
ws.Cells(rowNumber, columnNumber).Value2 = "완료"
행 번호 0은 워크시트의 유효한 행이 아닙니다.
외부 입력이나 계산 결과를 사용할 때는 범위를 먼저 확인합니다.
Private Sub ValidateCellPosition_( _
ByVal ws As Worksheet, _
ByVal rowNumber As Long, _
ByVal columnNumber As Long _
)
If ws Is Nothing Then
Err.Raise _
vbObjectError + 1001, _
"ValidateCellPosition_", _
"워크시트 개체가 설정되지 않았습니다."
End If
If _
rowNumber < 1 Or _
rowNumber > ws.Rows.Count _
Then
Err.Raise _
vbObjectError + 1002, _
"ValidateCellPosition_", _
"행 번호가 유효하지 않습니다: " & _
CStr(rowNumber)
End If
If _
columnNumber < 1 Or _
columnNumber > ws.Columns.Count _
Then
Err.Raise _
vbObjectError + 1003, _
"ValidateCellPosition_", _
"열 번호가 유효하지 않습니다: " & _
CStr(columnNumber)
End If
End Sub
셀을 쓰기 전에 이 검사를 호출합니다.
Dim ws As Worksheet
Dim rowNumber As Long
Dim columnNumber As Long
Set ws = ThisWorkbook.Worksheets("데이터")
rowNumber = 10
columnNumber = 3
ValidateCellPosition_ _
ws, _
rowNumber, _
columnNumber
ws.Cells( _
rowNumber, _
columnNumber _
).Value2 = "완료"
시작 행이 마지막 행보다 큰 범위를 만들지 않습니다
머리글이 1행에 있고 데이터가 2행부터 시작한다고 가정한 코드가 있습니다.
Set targetRange = _
ws.Range( _
ws.Cells(2, 1), _
ws.Cells(lastRow, 4) _
)
실제 데이터가 없어서 lastRow가 1이라면 시작 셀은 2행, 끝 셀은
1행으로 계산됩니다.
범위를 만들기 전에 데이터 행이 존재하는지 확인합니다.
Dim firstDataRow As Long
Dim lastRow As Long
Dim targetRange As Range
firstDataRow = 2
lastRow = _
ws.Cells(ws.Rows.Count, 1) _
.End(xlUp) _
.Row
If lastRow < firstDataRow Then
Err.Raise _
vbObjectError + 1004, _
"BuildDataRange", _
"처리할 데이터 행이 없습니다."
End If
Set targetRange = _
ws.Range( _
ws.Cells(firstDataRow, 1), _
ws.Cells(lastRow, 4) _
)
데이터가 없을 때 빈 범위를 억지로 만들기보다 작업을 중단하거나 “처리할 데이터 없음” 상태를 별도로 반환하도록 설계합니다.
Select와 Activate 없이 직접 작업합니다
매크로 기록기로 만든 코드는 워크시트와 셀을 선택한 다음 선택된 범위에 명령을 실행하는 경우가 많습니다.
Worksheets("데이터").Select
Range("A2:D20").Select
Selection.ClearContents
이 구조는 현재 활성 통합 문서와 활성 워크시트 상태에 의존합니다.
단순히 범위의 값을 지우는 작업이라면 시트와 범위를 직접 지정할 수 있습니다.
Dim ws As Worksheet
Set ws = _
ThisWorkbook.Worksheets("데이터")
ws.Range("A2:D20").ClearContents
이 코드는 다른 시트가 화면에 보이더라도 지정한 통합 문서의 “데이터” 시트를 처리합니다.
Select 자체가 모든 오류 1004의 원인은 아닙니다. 선택이 실제로
필요한 작업도 있습니다. 다만 선택이 필요하지 않은 데이터 읽기·쓰기·삭제
작업에서 Select와 Selection을 제거하면 활성 상태에 의존하는 부분을 줄일 수
있습니다.
Select 메서드 오류는 대상 시트의 활성 상태를 확인합니다
다음 코드는 “데이터” 시트의 범위를 Select하려고 합니다.
Dim ws As Worksheet
Set ws = _
ThisWorkbook.Worksheets("데이터")
ws.Range("A2:D20").Select
선택 작업은 화면의 활성 상태와 관련됩니다. 실제 목적이 값을 변경하는 것이라면 Select를 제거하고 직접 작업합니다.
ws.Range("A2:D20").Value2 = 0
사용자의 화면에서 해당 범위를 실제로 선택해야 하는 매크로라면 먼저 통합 문서와 워크시트를 활성화해야 합니다.
ThisWorkbook.Activate
ws.Activate
ws.Range("A2:D20").Select
화면 선택이 필요 없는 업무 자동화 코드에서는 앞의 직접 참조 방식을 우선합니다.
시트가 보호돼 있으면 수정 작업이 제한될 수 있습니다
워크시트 내용이 보호된 상태에서는 잠긴 셀 수정, 행·열 삭제, 정렬과 같은 작업이 제한될 수 있습니다.
보호 여부는 ProtectContents 속성으로 확인할 수 있습니다.
Dim ws As Worksheet
Set ws = _
ThisWorkbook.Worksheets("데이터")
If ws.ProtectContents Then
Err.Raise _
vbObjectError + 1005, _
"UpdateData", _
"워크시트 내용이 보호돼 있어 수정할 수 없습니다: " & _
ws.Name
End If
ws.Range("A2").Value2 = "완료"
시트가 보호돼 있다는 이유로 코드에 암호를 임의로 넣거나 보호를 우회해서는 안 됩니다.
파일 소유자가 승인한 작업이라면 올바른 암호와 보호 정책에 따라
Unprotect로 보호를 해제하고 작업 후 다시 보호할 수 있습니다.
Dim ws As Worksheet
Dim wasProtected As Boolean
Set ws = _
ThisWorkbook.Worksheets("데이터")
wasProtected = ws.ProtectContents
If wasProtected Then
ws.Unprotect _
Password:="승인된_암호"
End If
On Error GoTo ErrorHandler
ws.Range("A2").Value2 = "완료"
If wasProtected Then
ws.Protect _
Password:="승인된_암호"
End If
Exit Sub
ErrorHandler:
If wasProtected Then
ws.Protect _
Password:="승인된_암호"
End If
Err.Raise _
Err.Number, _
"UpdateProtectedSheet | " & _
Err.Source, _
Err.Description
실제 업무 코드에서는 암호를 공개 글이나 저장소에 그대로 남기지 말고 조직의 보안 정책에 맞는 저장 방식을 사용해야 합니다.
워크시트 보호의 UserInterfaceOnly 설정을 사용하면 사용자
인터페이스는 보호하면서 매크로 작업을 허용할 수 있지만, 이 설정은 통합
문서를 다시 열 때 다시 적용해야 할 수 있습니다.
Intersect에 다른 워크시트의 범위를 넣지 않습니다
Application.Intersect는 둘 이상의 범위가 겹치는 부분을
반환합니다.
하지만 비교하는 범위가 서로 다른 워크시트에 속하면 오류가 발생합니다.
Dim firstRange As Range
Dim secondRange As Range
Dim resultRange As Range
Set firstRange = _
ThisWorkbook _
.Worksheets("데이터") _
.Range("A1:A10")
Set secondRange = _
ThisWorkbook _
.Worksheets("결과") _
.Range("A1:A10")
Set resultRange = _
Application.Intersect( _
firstRange, _
secondRange _
)
두 범위는 셀 주소가 같아도 서로 다른 워크시트에 있으므로 교차 범위를 계산할 수 없습니다.
같은 워크시트에 속하는지 먼저 검사합니다.
If Not firstRange.Worksheet Is secondRange.Worksheet Then
Err.Raise _
vbObjectError + 1006, _
"IntersectRanges", _
"서로 다른 워크시트의 범위는 교차 계산할 수 없습니다."
End If
Set resultRange = _
Application.Intersect( _
firstRange, _
secondRange _
)
같은 워크시트에 있지만 서로 겹치지 않는 경우에는 Intersect 결과가
Nothing일 수 있으므로 별도로 확인합니다.
If resultRange Is Nothing Then
Debug.Print "두 범위가 겹치지 않습니다."
End If
ActiveSheet 대신 작업 대상을 변수에 저장합니다
다음 코드는 실행 시점에 화면에 활성화된 시트를 처리합니다.
Range("A1").Value2 = "완료"
개체가 지정되지 않은 Range는 활성 워크시트의 Range를 의미합니다. 사용자가 다른 시트를 선택하거나 코드가 다른 통합 문서를 활성화하면 작업 대상이 바뀔 수 있습니다.
매크로가 저장된 파일의 “데이터” 시트를 처리하려는 목적이라면 다음처럼 명시합니다.
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("데이터")
ws.Range("A1").Value2 = "완료"
ThisWorkbook은 현재 VBA 코드가 저장된 통합 문서입니다. 현재
화면에 활성화된 통합 문서를 처리하도록 설계한 매크로라면
ActiveWorkbook을 사용할 수 있지만, 다른 파일이 활성화될
가능성을 고려해 이름과 상태를 확인해야 합니다.
오류 정보를 지우지 말고 번호·발생 위치·설명을 남깁니다
오류 1004라는 숫자만 기록하면 어떤 Excel 개체와 메서드에서 실패했는지 알기 어렵습니다.
오류 처리 구역에서 Err.Number, Err.Source와
Err.Description을 먼저 변수에 저장합니다.
Option Explicit
Public Sub UpdateReport()
Dim wb As Workbook
Dim ws As Worksheet
Dim lastRow As Long
Dim targetRange As Range
On Error GoTo ErrorHandler
Set wb = ThisWorkbook
Set ws = wb.Worksheets("데이터")
If ws.ProtectContents Then
Err.Raise _
vbObjectError + 1007, _
"UpdateReport", _
"데이터 시트의 내용이 보호돼 있습니다."
End If
With ws
lastRow = _
.Cells(.Rows.Count, 1) _
.End(xlUp) _
.Row
If lastRow < 2 Then
Err.Raise _
vbObjectError + 1008, _
"UpdateReport", _
"처리할 데이터 행이 없습니다."
End If
Set targetRange = _
.Range( _
.Cells(2, 1), _
.Cells(lastRow, 4) _
)
End With
targetRange.ClearContents
Exit Sub
ErrorHandler:
Dim errorNumber As Long
Dim errorSource As String
Dim errorDescription As String
errorNumber = Err.Number
errorSource = Err.Source
errorDescription = Err.Description
Err.Raise _
errorNumber, _
"UpdateReport | " & _
errorSource, _
errorDescription
End Sub
이 예제는 다음 항목을 한 번에 확인합니다.
- 작업 대상 통합 문서와 워크시트를 명시적으로 설정
- 시트 내용 보호 여부 확인
- Rows.Count를 대상 워크시트에 연결
- 데이터 행 존재 여부 확인
- Range와 두 Cells를 같은 워크시트로 한정
- 오류 번호·발생 위치·설명을 다시 전달
On Error Resume Next로 오류 1004를 숨기지 않습니다
다음 코드는 모든 오류를 무시하고 이후 코드로 진행합니다.
On Error Resume Next
Set targetRange = _
ws.Range( _
ws.Cells(2, 1), _
ws.Cells(lastRow, 4) _
)
targetRange.ClearContents
범위를 만들지 못해도 오류가 숨겨지므로
targetRange가 설정되지 않은 상태에서 다음 줄이 실행될 수
있습니다.
그 결과 원래의 오류 1004 대신 객체 변수 오류 91이 발생하거나, 아무 작업도 하지 않은 채 매크로가 끝난 것처럼 보일 수 있습니다.
특정 개체가 존재하는지 확인하기 위해 제한된 한 줄에서 오류 처리를 사용하는 경우가 아니라면, 전체 작업의 오류를 Resume Next로 무시하지 않습니다.
오류 문구별 수정 위치
오류 번호만으로 원인을 판단하지 말고 디버그에서 강조된 Excel 개체와 메서드를 확인합니다.
Range 주소가 유효한지, Range 안의 Cells가 모두 같은 Worksheet에 속하는지 확인합니다.
화면 선택이 실제로 필요한지 먼저 판단합니다. 필요하지 않으면 직접 범위를 처리하고, 필요하다면 통합 문서와 시트의 활성 상태를 확인합니다.
시트 내용 보호와 해당 작업의 보호 옵션을 확인합니다.
시작 행·끝 행, 열 번호와 데이터가 없는 경우의 값을 직접 실행 창에서 확인합니다.
비교하는 모든 범위가 같은 워크시트에 속하는지 확인합니다.
최종 점검 순서
- 오류 창에서 디버그를 눌러 강조된 줄을 확인합니다.
- Range·Cells·Rows에 대상 Worksheet가 모두 지정돼 있는지 확인합니다.
- 행 번호와 열 번호가 1 이상이고 시트 최대 범위 안인지 확인합니다.
- 시작 행이 마지막 행보다 큰 범위를 만들지 않았는지 확인합니다.
- Select가 실제로 필요한 작업인지 판단합니다.
- 워크시트의 ProtectContents 상태를 확인합니다.
- Intersect에 다른 시트의 범위를 함께 전달하지 않았는지 확인합니다.
- ActiveSheet 의존성을 Workbook·Worksheet 변수로 바꿉니다.
- 오류 번호뿐 아니라 Err.Source와 Err.Description을 기록합니다.
- 전체 프로시저의 On Error Resume Next를 제거합니다.
내용 확인에 사용한 Microsoft 공식 자료
- Microsoft Learn: Application-defined or object-defined error
- Microsoft Learn: Worksheet.Range 속성
- Microsoft Learn: Worksheet.Cells 속성
- Microsoft Learn: Range.Select 메서드
- Microsoft Learn: Worksheet.ProtectContents 속성
- Microsoft Learn: Worksheet.Protect 메서드
- Microsoft Learn: Worksheet.Unprotect 메서드
- Microsoft Learn: Application.Intersect 메서드
- Microsoft Learn: Range.Worksheet 속성
- Microsoft Learn: VBA Err 개체
- Microsoft Learn: On Error 문
공식 문서 확인일: 2026년 7월 29일

댓글 쓰기