엑셀 매크로와 VBA 차이: 행 추가·필터 재실행까지 비교하기

매크로를 녹화했는데 새로 추가한 행만 모양이 다르다면, 먼저 저장된 범위를 읽어야 합니다. 이 글에서는 같은 가상 매출표에 고정 범위 서식과 마지막 행을 계산하는 서식을 적용하고, 겉모양 외에 합계와 필터 주소까지 비교합니다. 매크로는 실행할 작업이고 VBA는 그 작업을 표현하는 언어입니다. 매크로 기록기는 사용자의 작업 단계를 VBA 코드로 기록합니다. 둘을 서로 다른 자동화 제품으로 고를 필요는 없습니다.

6행에서 시작해 7행으로 늘리는 실험

practice-56.xlsx의 Source에는 날짜·부서·담당자·매출이 A:D에 있습니다. 금액은 순서대로 125000, 98000, 143000, 87000, 116000, 131000이며 모두 가상 입력입니다. 6행 합계는 700000입니다. Expected는 원래 6행의 서식 견본이고, 실행 로그가 아닙니다. Verification의 B열이 독립 기대값이며 C열과 D열은 매크로가 실제로 쓸 칸입니다. 배포 파일의 D7은 NOT_RUN입니다.

검사 위치 초기 기대값 무엇을 잡는가
Verification!B2 6 데이터 행 누락
B3 700000 값 변경 또는 범위 오류
B4 31,78,120 머리글 전체의 배경색
B5 #,##0"원" 매출 셀 전체의 표시 형식
B6 TRUE 데이터 전체를 포함하는 필터

날짜는 비어 있지 않은 값인지 확인하지만 실제 달력 날짜인지 판정하지 않습니다. D열은 숫자 형식의 정수만 받습니다. 음수 정수까지 허용하므로 환불 정책을 구현한 코드라고 해석하지 마세요.

같은 입력에 두 매크로를 차례로 적용하기

매크로를 보관할 실습 사본은 매크로 사용 통합 문서인 .xlsm 형식으로 저장합니다. ZIP의 BAS를 해당 사본의 VBA 프로젝트로 가져오고, 아래 절차를 복사본에서 수행합니다. BAS 첫 줄의 Attribute는 가져오기용 메타데이터입니다. 코드 창에 전체를 붙여 넣는 대신 파일 가져오기를 사용합니다.

  1. Source를 선택한 뒤 ResetSource, RecordedStyleExample, VerifyReport를 차례로 실행합니다. C2=6, C3=700000, C6=A1:D7과 D2:D7의 PASS를 직접 확인합니다.

  2. Source!A8:D8에 2026-08-30 / 온라인 / 지우 / 50000을 입력합니다. Verification!B2=7, B3=750000으로 기대값을 먼저 바꿉니다. ResetSource 후 고정 예제를 실행하고 검증하면 D8의 서식과 전체 필터 검사가 FAIL이어야 합니다. 합계만 맞는다고 성공으로 보지 않습니다.

  3. 다시 ResetSource 후 RefactoredFormatReport와 VerifyReport를 실행합니다. 기대 필터는 A1:D8, 행 수는 7, 합계는 750000입니다. 정상 실행 시 모든 검사 PASS가 예상되지만, 실제 Excel에서 확인한 뒤 결과로 기록해야 합니다.

ResetSource는 값이나 기대값을 원복하지 않습니다. 6행 실험으로 돌아가려면 새 사본을 열거나 추가한 A8:D8을 지우고 B2:B3도 되돌립니다. 단순히 Reset을 실행하고 합계가 복구됐다고 생각하면 다음 실험의 전제가 달라집니다.

완전한 참조 모듈

고정 예제의 Select/Selection은 녹화 코드의 특징을 비교하기 위해 남겨 두었습니다. 동적 예제는 A:D 전체에서 마지막 사용 행을 찾아 A열이 비었는데 D열만 있는 행도 놓치지 않습니다. Microsoft 문서는 Mac에서 Range.AutoFilter를 지원하지 않으며 Selection과 ListObject의 유사 메서드는 지원한다고 설명합니다. 그래서 필터 helper는 Mac에서 Source를 활성화하고 Selection을 사용합니다. 이 코드를 '선택 동작이 전혀 없는 코드'라고 부를 수는 없습니다.

Attribute VB_Name = "MacroVsVBA"
Option Explicit

Public Sub RecordedStyleExample()
    Dim ws As Worksheet
    On Error GoTo Fail
    InvalidateVerification
    Set ws = ThisWorkbook.Worksheets("Source")
    ThisWorkbook.Activate
    ws.Activate
    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    ' Deliberately fixed: compare this with the dynamic procedure.
    Range("A1:D7").Select
    Selection.Borders.LineStyle = xlContinuous
    Range("A1:D1").Select
    With Selection
        .Interior.Color = RGB(31, 78, 120)
        .Font.Color = RGB(255, 255, 255)
        .Font.Bold = True
    End With
    Range("D2:D7").Select
    Selection.NumberFormat = AmountFormat()
    Range("A1:D7").Select
    Selection.AutoFilter
    ws.Columns("A:D").AutoFit
    Exit Sub
Fail:
    ReportFailure "Recorded format", Err.Description
End Sub

Public Sub RefactoredFormatReport()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim previousUpdating As Boolean
    Dim detail As String
    previousUpdating = Application.ScreenUpdating
    On Error GoTo Fail
    InvalidateVerification
    Set ws = ThisWorkbook.Worksheets("Source")
    lastRow = ValidatedDataLastRow(ws)
    Application.ScreenUpdating = False
    ws.Range("A1:D" & lastRow).Borders.LineStyle = xlContinuous
    With ws.Range("A1:D1")
        .Interior.Color = RGB(31, 78, 120)
        .Font.Color = RGB(255, 255, 255)
        .Font.Bold = True
    End With
    ws.Range("D2:D" & lastRow).NumberFormat = AmountFormat()
    EnableReportFilter ws, lastRow
    ws.Columns("A:D").AutoFit
    Application.ScreenUpdating = previousUpdating
    Exit Sub
Fail:
    detail = Err.Description
    On Error Resume Next
    Application.ScreenUpdating = previousUpdating
    On Error GoTo 0
    ReportFailure "Dynamic format", detail
End Sub

Public Sub ResetSource()
    Dim ws As Worksheet
    Dim lastRow As Long
    On Error GoTo Fail
    InvalidateVerification
    Set ws = ThisWorkbook.Worksheets("Source")
    lastRow = LastOccupiedRow(ws)
    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    ws.Range("A1:D" & lastRow).ClearFormats
    ws.Columns("A:D").ColumnWidth = 14
    ' No Select: this also works while another sheet is active.
    Exit Sub
Fail:
    ReportFailure "Reset", Err.Description
End Sub

Public Sub VerifyReport()
    Dim ws As Worksheet, checkWs As Worksheet
    Dim lastRow As Long, actualRows As Long
    Dim actualTotal As Double, expectedRows As Variant, expectedTotal As Variant
    Dim expectedColor As String, expectedFormat As String
    Dim headerOK As Boolean, formatOK As Boolean, filterOK As Boolean
    Dim filterAddress As String, overallOK As Boolean
    Dim cell As Range

    On Error GoTo Fail
    InvalidateVerification
    Set checkWs = ThisWorkbook.Worksheets("Verification")
    Set ws = ThisWorkbook.Worksheets("Source")
    checkWs.Range("D7").Value2 = "ERROR"
    lastRow = ValidatedDataLastRow(ws)
    actualRows = lastRow - 1
    actualTotal = WorksheetFunction.Sum(ws.Range("D2:D" & lastRow))
    expectedRows = checkWs.Range("B2").Value2
    expectedTotal = checkWs.Range("B3").Value2
    If Not NativeNumber(expectedRows) Then Err.Raise vbObjectError + 5601, , "B2 must be a numeric row count."
    If expectedRows < 1 Or expectedRows > ws.Rows.Count - 1 Or expectedRows <> Fix(expectedRows) Then
        Err.Raise vbObjectError + 5602, , "B2 is outside the data-row range."
    End If
    If Not NativeNumber(expectedTotal) Then Err.Raise vbObjectError + 5603, , "B3 must be a numeric total."
    If expectedTotal <> Fix(expectedTotal) Then Err.Raise vbObjectError + 5604, , "This example uses integer KRW amounts."
    expectedColor = CStr(checkWs.Range("B4").Value2)
    expectedFormat = AmountFormat()
    If VarType(checkWs.Range("B6").Value2) <> vbBoolean Then
        Err.Raise vbObjectError + 5605, , "Keep B6 as the Boolean TRUE for this exercise."
    End If
    If Not CBool(checkWs.Range("B6").Value2) Then
        Err.Raise vbObjectError + 5606, , "This exercise requires the full-range filter."
    End If

    headerOK = True
    For Each cell In ws.Range("A1:D1")
        If RGBLabel(CLng(cell.Interior.Color)) <> expectedColor Then headerOK = False
    Next cell
    formatOK = True
    For Each cell In ws.Range("D2:D" & lastRow)
        If CStr(cell.NumberFormat) <> expectedFormat Then formatOK = False
    Next cell
    filterAddress = "(none)"
    If ws.AutoFilterMode Then filterAddress = ws.AutoFilter.Range.Address(False, False)
    filterOK = (filterAddress = ws.Range("A1:D" & lastRow).Address(False, False))

    checkWs.Range("C2").Value2 = actualRows
    checkWs.Range("C3").Value2 = actualTotal
    checkWs.Range("C4").Value2 = IIf(headerOK, expectedColor, "MIXED_OR_WRONG")
    checkWs.Range("C5").Value2 = IIf(formatOK, expectedFormat, "MIXED_OR_WRONG")
    checkWs.Range("C6").Value2 = filterAddress
    checkWs.Range("D2").Value2 = PassFail(actualRows = CLng(expectedRows))
    checkWs.Range("D3").Value2 = PassFail(actualTotal = CDbl(expectedTotal))
    checkWs.Range("D4").Value2 = PassFail(headerOK)
    checkWs.Range("D5").Value2 = PassFail(formatOK)
    checkWs.Range("D6").Value2 = PassFail(filterOK)
    overallOK = WorksheetFunction.CountIf(checkWs.Range("D2:D6"), "PASS") = 5
    checkWs.Range("D7").Value2 = PassFail(overallOK)
    MsgBox "Verification: " & checkWs.Range("D7").Value2, IIf(overallOK, vbInformation, vbExclamation)
    Exit Sub
Fail:
    ReportFailure "Verification", Err.Description
End Sub

Private Sub EnableReportFilter(ByVal ws As Worksheet, ByVal lastRow As Long)
    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    If InStr(1, Application.OperatingSystem, "Mac", vbTextCompare) > 0 Then
        ' Microsoft documents Selection.AutoFilter as supported on Mac.
        ThisWorkbook.Activate
        ws.Activate
        ws.Range("A1:D" & lastRow).Select
        Selection.AutoFilter
    Else
        ws.Range("A1:D" & lastRow).AutoFilter
    End If
End Sub

Private Function LastOccupiedRow(ByVal ws As Worksheet) As Long
    Dim lastCell As Range
    Set lastCell = ws.Range("A:D").Find(What:="*", After:=ws.Range("A1"), _
        LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, _
        SearchDirection:=xlPrevious, MatchCase:=False, SearchFormat:=False)
    If lastCell Is Nothing Then
        LastOccupiedRow = 1
    Else
        LastOccupiedRow = lastCell.Row
    End If
End Function

Private Function ValidatedDataLastRow(ByVal ws As Worksheet) As Long
    Dim lastRow As Long, r As Long, value As Variant
    lastRow = LastOccupiedRow(ws)
    If lastRow < 2 Then Err.Raise vbObjectError + 5610, , "Source has no data rows."
    For r = 2 To lastRow
        value = ws.Cells(r, "A").Value2
        If IsError(value) Then Err.Raise vbObjectError + 5611, , "Date cell error in row " & r
        If Len(Trim$(CStr(value))) = 0 Then Err.Raise vbObjectError + 5612, , "Missing date in row " & r
        value = ws.Cells(r, "D").Value2
        If Not NativeNumber(value) Then Err.Raise vbObjectError + 5613, , "Amount must be numeric, not text, in row " & r
        If value <> Fix(value) Then Err.Raise vbObjectError + 5614, , "Use integer KRW in row " & r
    Next r
    ValidatedDataLastRow = lastRow
End Function

Private Function NativeNumber(ByVal value As Variant) As Boolean
    Select Case VarType(value)
        Case vbByte, vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal
            NativeNumber = True
        Case Else
            NativeNumber = False
    End Select
End Function

Private Function AmountFormat() As String
    AmountFormat = CStr(ThisWorkbook.Worksheets("Verification").Range("B5").Value2)
    If Len(AmountFormat) = 0 Then Err.Raise vbObjectError + 5615, , "B5 must contain the amount number format."
End Function

Private Sub InvalidateVerification()
    With ThisWorkbook.Worksheets("Verification")
        .Range("C2:D7").ClearContents
        .Range("D7").Value2 = "NOT_RUN"
    End With
End Sub

Private Sub ReportFailure(ByVal stageName As String, ByVal detail As String)
    On Error Resume Next
    ThisWorkbook.Worksheets("Verification").Range("D7").Value2 = "ERROR"
    On Error GoTo 0
    MsgBox stageName & ": " & detail, vbExclamation
End Sub

Private Function PassFail(ByVal condition As Boolean) As String
    PassFail = IIf(condition, "PASS", "FAIL")
End Function

Private Function RGBLabel(ByVal colorValue As Long) As String
    RGBLabel = CStr(colorValue Mod 256) & "," & _
        CStr((colorValue \ 256) Mod 256) & "," & _
        CStr((colorValue \ 65536) Mod 256)
End Function

실패를 만들고 복구하는 순서

A8은 지운 채 D8=50000만 남기면 검증은 날짜 누락 오류여야 합니다. 숫자 125000 대신 문자 '125000을 D2에 넣어도 숫자로 자동 변환하지 않고 거부해야 합니다. 잘못된 셀을 고친 뒤 동적 서식과 VerifyReport를 다시 실행합니다. ERROR 뒤에는 이전 PASS나 이전 합계를 근거로 보고서를 쓰지 않습니다.

예상 결과와 실제 결과를 같은 칸에 넣지 않는 것이 이 실험의 핵심입니다. Verification!C열에 기대값을 손으로 복사하면 검증이 아니라 결과 꾸미기가 됩니다. 오류 경로는 표시를 ERROR로 바꾸도록 작성됐지만, 시트가 없어지거나 보호돼 쓰지 못하면 그 표시도 남기지 못할 수 있습니다. 메시지를 먼저 읽고 새 사본에서 복구하세요.

확인한 범위와 남은 확인

첨부 Python 검산은 6행·7행의 합계, 잘못된 값의 입력 계약, 고정/확장 범위의 차이를 독립적으로 확인합니다. XLSX 구조와 VBA 소스의 참조도 검사했습니다. Microsoft Excel에서 VBA를 컴파일하거나 실행하지 않았습니다. 색·테두리·필터 UI와 운영체제별 실행 결과는 위 절차로 직접 확인해야 합니다. 원본 파일 보호, 자동 백업, 대량 자료의 속도 측정은 포함하지 않습니다.

입력과 결과 칸을 찾는 기존 그림

그림은 실습 위치를 찾는 보조 자료입니다. 실제 실행 결과는 위 절차로 만든 사본의 값과 상태를 대조합니다.

가상 매출 6행 700000에서 7행 750000으로 늘려 고정·동적 범위를 비교한 설명 도해
가상 매출 6행 700000에서 7행 750000으로 늘려 고정·동적 범위를 비교한 설명 도해. 가상 입력과 기대값을 설명한 그림이며 실제 Excel 실행 화면이나 실행 성공 증거가 아닙니다.
같은 7행과 750000에서도 필터·서식 범위에 따라 FAIL·PASS 기대값을 나눈 설명 도해
같은 7행과 750000에서도 필터·서식 범위에 따라 FAIL·PASS 기대값을 나눈 설명 도해. 가상 입력과 기대값을 설명한 그림이며 실제 Excel 실행 화면이나 실행 성공 증거가 아닙니다.
NOT_RUN·PASS·FAIL·ERROR와 날짜 누락·문자 금액의 복구 순서를 구분한 설명 도해
NOT_RUN·PASS·FAIL·ERROR와 날짜 누락·문자 금액의 복구 순서를 구분한 설명 도해. 가상 입력과 기대값을 설명한 그림이며 실제 Excel 실행 화면이나 실행 성공 증거가 아닙니다.

공식 자료

Automate tasks with the Macro Recorder | Microsoft Support

Save a macro | Microsoft Support

Range.AutoFilter method (Excel)

출처 원문 수집일: S1 2026-09-24; S2 2026-09-24; S3 2026-09-24.

이어서 연습할 글

녹화 단계부터 다시 확인하려면 매크로 기록기로 영업 필터 범위 검산하기를, 실행할 코드를 단추에 연결하려면 집계 결과와 오류를 확인하는 매크로 버튼 예제를 이어서 보세요.

작성자 소개