녹화한 매크로 수정하기: VBA 기초 5가지와 완료 행 추출

녹화한 코드에서 범위 주소만 바꿔 가며 완료 행을 복사하다 보면, 완료 건수가 0일 때나 잘못된 금액이 섞였을 때 결과가 애매해집니다. 이 실습은 상태가 완료인 행을 같은 파일의 Result로 옮기며 변수·조건·반복·범위 지정·오류 복구를 한 작업 안에서 읽습니다. 실제 업무에서 쓴 경험을 주장하는 사례가 아니라 재현용 가상 자료입니다.

완료 2행과 결과 영역의 경계

practice-60.xlsx의 Data!A1:E5는 상태, 담당 기호, 금액, 기준일, 메모 순서입니다. 조건은 Check!B2의 완료입니다.

상태 담당 기호 금액 가상 기준일 메모
완료 담당-A 120000 2026-09-01 확인
대기 담당-B 95000 2026-09-01 검토
완료 담당-C 180000 2026-09-02 확인
보류 담당-D 70000 2026-09-02 자료 대기

독립 기대값은 담당-A,담당-C 순서의 2행, 합계 300000입니다. Result!A2:E1001은 이 매크로의 예약 출력 영역입니다. 재실행 때 비워지므로 다른 자료를 넣으면 안 됩니다. G열의 연습 메모는 이 영역 밖이며 보존 대상입니다. 날짜는 값으로 복사할 뿐 유효한 날짜인지 검사하지 않습니다.

다섯 가지를 코드의 실제 역할로 연결하기

Option Explicit와 Dim은 어느 변수가 시트인지, 행인지, 임시 출력 배열인지 드러냅니다. If는 입력을 거부할 조건과 선택할 조건을 나눕니다. For Each는 배열이나 컬렉션의 각 요소에 대해 문장 묶음을 반복합니다. 이 코드에서는 Data의 행을 검사합니다. With와 ThisWorkbook은 다른 활성 파일의 셀에 쓰는 실수를 줄이도록 대상을 명시합니다. 마지막 On Error 경로는 이전 결과를 지우고 화면 갱신·이벤트 값을 복구하도록 구성했습니다.

Range.Value2는 셀 값을 읽고 쓰는 Variant 속성입니다. 여러 행을 고른 다음 출력 배열에 담아 Result로 쓰지만, 단지 배열을 쓴다는 이유로 처리 속도를 보장하지 않습니다.

전체 모듈을 넣고 2행·0행·3행을 비교하기

매크로를 저장할 연습 사본은 .xlsm 형식을 사용합니다. CompletedRows.bas를 사본의 VBA 프로젝트로 가져옵니다. 첫 Attribute 줄은 가져오기용이며, VBA 편집기의 파일 가져오기를 사용합니다.

  1. 새 사본에서 Check!B2와 B7:B9를 선택해 조건과 기대값을 확인하고 CopyCompletedRows를 실행합니다. Result!A2:E3에 담당-A,C가 들어가는지, Check!C7=2·C8=300000인지 확인합니다. 결과에서 담당 기호를 직접 읽어 C9에 담당-A,담당-C로 입력합니다.

  2. 별도 새 사본에서 Data!A2와 A4를 대기로 바꿉니다. 기대값 B7=0, B8=0, B9=(없음)을 입력하고 실행합니다. 예약 결과 영역은 비어 있고 G열 메모는 남아야 합니다. C9에도 직접 (없음)을 적어 비교합니다.

  3. 다시 원본 사본에서 A3만 완료로 바꿉니다. 기대값은 3행, 395000, 담당-A,담당-B,담당-C입니다. 결과 순서와 합계가 모두 맞는지 확인합니다. 각 실험은 다른 사본에서 시작해 이전 변경을 섞지 않습니다.

Attribute VB_Name = "CompletedRows"
Option Explicit

Public Sub CopyCompletedRows()
    Const LAST_ALLOWED As Long = 1001
    Dim wsData As Worksheet, wsResult As Worksheet, wsCheck As Worksheet
    Dim rowRange As Range, picked As Collection, item As Variant
    Dim lastRow As Long, candidate As Long, colNo As Long, outRow As Long
    Dim criterion As Variant, state As Variant, amount As Variant
    Dim values() As Variant
    Dim savedScreen As Boolean, savedEvents As Boolean
    Dim resultReady As Boolean, checkReady As Boolean, problem As String

    savedScreen = Application.ScreenUpdating
    savedEvents = Application.EnableEvents
    On Error GoTo Failed
    Set wsCheck = ThisWorkbook.Worksheets("Check")
    If wsCheck.ProtectContents Then
        Err.Raise vbObjectError + 601, , "Use an unprotected practice Check sheet."
    End If
    checkReady = True
    Set wsResult = ThisWorkbook.Worksheets("Result")
    If wsResult.ProtectContents Then
        Err.Raise vbObjectError + 602, , "Use an unprotected practice Result sheet."
    End If
    resultReady = True
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    wsCheck.Range("B3").Value2 = "RUNNING"
    wsCheck.Range("B4").ClearContents
    wsCheck.Range("C9").ClearContents
    wsResult.Range("A2:E" & LAST_ALLOWED).ClearContents
    Set wsData = ThisWorkbook.Worksheets("Data")
    wsResult.Range("A1:E1").Value2 = wsData.Range("A1:E1").Value2

    criterion = wsCheck.Range("B2").Value2
    If VarType(criterion) <> vbString Then
        Err.Raise vbObjectError + 603, , "Check!B2 must contain a text condition."
    End If
    If Len(criterion) = 0 Or criterion <> Trim$(criterion) Then
        Err.Raise vbObjectError + 604, , "Check!B2 must be nonempty without outer spaces."
    End If
    lastRow = 1
    With wsData
        For colNo = 1 To 5
            candidate = .Cells(.Rows.Count, colNo).End(xlUp).Row
            If candidate > lastRow Then lastRow = candidate
        Next colNo
    End With
    If lastRow > LAST_ALLOWED Then
        Err.Raise vbObjectError + 605, , "Practice input is limited to rows 2 through 1001."
    End If
    If lastRow < 2 Then GoTo Complete
    Set picked = New Collection

    For Each rowRange In wsData.Range("A2:E" & lastRow).Rows
        With rowRange
            If Application.WorksheetFunction.CountA(rowRange) > 0 Then
                For colNo = 1 To 5
                    If IsError(.Cells(1, colNo).Value2) Then
                        Err.Raise vbObjectError + 606, , "Cell error at row " & .Row
                    End If
                Next colNo
                state = .Cells(1, 1).Value2
                If VarType(state) <> vbString Then
                    Err.Raise vbObjectError + 607, , "Text status required at row " & .Row
                End If
                If Len(state) = 0 Or state <> Trim$(state) Then
                    Err.Raise vbObjectError + 608, , "Empty status or outer spaces at row " & .Row
                End If
                If Len(Trim$(CStr(.Cells(1, 2).Value2))) = 0 Then
                    Err.Raise vbObjectError + 609, , "Missing owner label at row " & .Row
                End If
                amount = .Cells(1, 3).Value2
                Select Case VarType(amount)
                    Case vbByte, vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal
                    Case Else
                        Err.Raise vbObjectError + 610, , "Numeric amount required at row " & .Row
                End Select
                If amount < 0 Or amount > 1000000000# Or amount <> Fix(amount) Then
                    Err.Raise vbObjectError + 611, , "Invalid whole amount at row " & .Row
                End If
                If StrComp(state, criterion, vbBinaryCompare) = 0 Then picked.Add .Row
            End If
        End With
    Next rowRange

    If picked.Count = 0 Then GoTo Complete
    ReDim values(1 To picked.Count, 1 To 5)
    For Each item In picked
        outRow = outRow + 1
        For colNo = 1 To 5
            values(outRow, colNo) = wsData.Cells(CLng(item), colNo).Value2
        Next colNo
    Next item
    wsResult.Range("A2").Resize(picked.Count, 5).Value2 = values

Complete:
    wsCheck.Range("B3").Value2 = "OK"
    wsCheck.Range("B4").Value = Now
    wsCheck.Calculate
CleanExit:
    Application.EnableEvents = savedEvents
    Application.ScreenUpdating = savedScreen
    Exit Sub

Failed:
    problem = Err.Description
    On Error Resume Next
    If resultReady Then wsResult.Range("A2:E" & LAST_ALLOWED).ClearContents
    If checkReady Then
        wsCheck.Range("B3").Value2 = "ERROR"
        wsCheck.Range("B4").ClearContents
        wsCheck.Range("C9").ClearContents
        wsCheck.Calculate
    End If
    Application.EnableEvents = savedEvents
    Application.ScreenUpdating = savedScreen
    On Error GoTo 0
    MsgBox "Copy failed: " & problem, vbExclamation
End Sub

선택되지 않을 행도 검증해야 하는 이유

이 모듈은 비어 있지 않은 모든 입력 행을 먼저 검증합니다. 따라서 대기 행의 금액이 문자여도 전체 처리가 ERROR가 됩니다. 잘못된 자료가 이번 조건에서 우연히 제외됐다고 정상 원장으로 인정하지 않기 위한 연습 계약입니다. 상태는 문자열이며 앞뒤 공백을 허용하지 않고, 담당 기호가 비거나 셀 오류가 있어도 거부합니다. 금액은 숫자 형식의 0~1000000000 정수입니다.

Data!A2에 완료 처럼 끝 공백을 넣거나 C2에 문자 '120000을 입력해 실패를 확인합니다. 출력 영역이 비고 Check!B3가 ERROR로 바뀌는 것이 기대 동작입니다. 값을 고쳐 재실행한 뒤 2행·300000과 대조하세요. 매크로는 C9의 수동 관찰을 매번 지우므로 이전 목록을 그대로 재사용하지 않습니다.

Data 값과 필터 상태, Result의 G열은 변경 대상이 아닙니다. 반면 오류가 났다고 이전 A:E 결과를 복구하는 기능은 없습니다. 오래된 결과를 남기지 않는 방식이며 백업·트랜잭션 롤백과 다릅니다. 쓰기 불가능한 시트에서는 지우기나 ERROR 표시조차 실패할 수 있으므로 경고 메시지를 무시하지 않습니다.

독립 검산과 한계

첨부 Python 검산은 기본·0건·3건 결과와 선택 밖의 오류 행 거부를 확인합니다. XLSX의 예약 결과가 처음에는 비어 있고 G열 메모가 따로 있다는 점도 검사했습니다. Excel/VBA를 실행하지 않았으므로 실제 복사, 설정 복구, 컴파일 결과는 확인되지 않았습니다. 실습 상한은 데이터 1000행이고 숨김 행도 검사합니다. 이 코드는 여러 파일 병합이나 취소 가능한 편집기를 대신하지 않습니다.

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

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

가상 입력 네 행 중 완료인 담당 A·C를 원래 순서로 골라 두 행 300000을 기대하는 설명 도해
가상 입력 네 행 중 완료인 담당 A·C를 원래 순서로 골라 두 행 300000을 기대하는 설명 도해. 가상 예제를 설명하는 도해이며 실제 Microsoft Excel 또는 VBA 실행 화면이 아닙니다.
완료 행 추출 코드의 변수·대상·반복·입력 검사·오류 처리 다섯 기초 설명 도해
완료 행 추출 코드의 변수·대상·반복·입력 검사·오류 처리 다섯 기초 설명 도해. 가상 예제를 설명하는 도해이며 실제 Microsoft Excel 또는 VBA 실행 화면이 아닙니다.
각 새 사본에서 완료 2행 300000, 0행 0, 3행 395000과 담당 순서를 확인하는 설명 도해
각 새 사본에서 완료 2행 300000, 0행 0, 3행 395000과 담당 순서를 확인하는 설명 도해. 가상 예제를 설명하는 도해이며 실제 Microsoft Excel 또는 VBA 실행 화면이 아닙니다.

공식 자료

For Each…Next statement (VBA)

Range.Value2 property (Excel)

Save a macro | Microsoft Support

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

이어서 연습할 글

반복 범위를 더 크게 확인하려면 For 루프로 1,000행 분류하기를, 만든 매크로를 단추에 연결하려면 집계 결과를 확인하는 매크로 버튼 예제를 이어서 보세요.

작성자 소개