본문 바로가기

진리는어디에/VBA

[VBA] For 루프 완벽 가이드

이 포스트는 VBA의 For 루프와 For Each 루프에 대한 내용을 다루고 있습니다. 원문은 Excel Macro Mastery
 사이트의 VBA For Loop – A Complete Guide(by Paul Kelly)입니다.

VBA For 루프 요약

형식 설명 예제
For ... Next Run 10 times For i = 1 To 10
Next
For ... Next Run 5 times. i=2,4, 6 etc. For i = 2 To 10 Step 2
Next
For ... Next Run in reverse order For i = 10 To 1 Step -1
    Debug.Print i
Next
For ... Next Go through Collection For i = 1 To coll.Count
    Debug.Print coll(i)
Next
For ... Next Go through array For i = LBound(arr) To UBound(arr)
    Debug.Print arr(i)
Next i
For ... Next Go through 2D array For i = LBound(arr) To UBound(arr)
    For j = LBound(arr,2) To UBound(arr,2)
        Debug.Print arr(i, j)
    Next j
Next i
For Each ... Next Go through Collection Dim item As Variant
For Each item In coll
    Debug.Print item
Next item
For Each ... Next Go through array Dim item As Variant
For Each item In arr
    Debug.Print item
Next item
For Each ... Next Go through 2D array Dim item As Variant
For Each item In arr
    Debug.Print item
Next item
For Each ... Next Go through Dictionary Dim key As Variant
For Each key In dict.Keys
    Debug.Print key, dict(key)
Next key
Both types Exit Loop For i = 1 To 10
    If Cells(i,1) = "found" Then
        Exit For
    End If
Next i

For 루프 소개

VBA에서 For 루프는 강력하고 유용한 요소입니다. 우리는 For 루프를 이용해 사람이 몇 시간에 걸려 처리 할 반복적인 일을 수 밀리초 만에 수행할 수 있습니다. 또한 어플리케이션에 필요한 코드의 양을 크게 줄여 줍니다. For 루프는 1957년 포트란 프로그래밍 언어에서 사용된 이후 모든 주요 프로그래밍 언어의 필수 요소였습니다. 이전에 루프를 사용한 적이 없다면 이 포스트는 아주 좋은 출발점이 될것입니다.

For 루프에 대해 간단히 소개하면 동일한 코드라인을 여러번 실행하는 방법입니다. 일반적으로 코드라인에는 루프가 수행 될때 마다 약간씩 변경 되는 변수가 포함되어 있습니다. 예를 들어 루프를 이용하면 A1 셀부터 A10 셀까지 각각 1부터 10까지 쓰는 반복적인 코드를 간단하게 쓸 수 있습니다.

For 루프 예제 1

아래 코드를 살펴 보도록 하겠습니다. 1부터 5 까지 출력하는 간단한 코드입니다.

Debug.Print 1
Debug.Print 2
Debug.Print 3
Debug.Print 4
Debug.Print 5

1 부터 5까지 출력하기 위해 거의 비슷한 코드를 다섯 줄이나 썼습니다. 만일 이런 방식으로 1부터 20까지 출력한다고 생각해 보십시오. 위의 예에 15줄을 더 추가해야 합니다. 그렇다면 100까지는? 10000까지는? 상상만해도 아찔합니다.

그러나 For 루프를 사용하면 Debug.Print를 한번만 사용하면 됩니다. 위에서 이미 말했지만 For 루프는 동일한 코드라인을 여러번 실행 시켜 줍니다.

For i = 1 To 20
    Debug.Print i
Next i

만일 1부터 1000까지의 숫자를 출력해야 하는 경우는 20을 1000으로 변경하기만 하면 됩니다. 이렇게 For 루프는 여러번 반복되어야 하는 작업을 아주 간단하게 표현할 수 있습니다.

일반적으로 코드를 작성할 때 20 또는 1000과 같은 숫자 대신 변수를 사용합니다. 이렇게 변수를 이용하면 큰 유연성을 얻을 수 있습니다. 예를 들어 루프 횟수가 이미 고정되어 있는 앞의 예제들과 달리 다음 섹션에서 나오는 예에는 실행 중일때 루프를 실행할 횟수를 결정 할 수 있습니다.

For 루프 예제 2

엑셀의 대부분의 작업은 데이터가 있는 모든 행을 읽는 것입니다. 이 작업을 진행하는 방법은 보통 다음과 같습니다.

  1. 데이터가 있는 마지막 행 찾기
  2. 변수에 값 저장
  3. 위에서 저장된 변수를 사용하여 루프가 실행되는 횟수를 결정

과일의 종류와 판매량이 적힌 엑셀 시트를 가지고 있다고 상상해 보세요. 당신은 아래 리스트에서 오렌지의 판매 개수를 구하고 싶어하며 이 목록의 크기는 그날 그날 판매량에 따라 달라집니다.

아래 코드를 이용하여 오렌지의 판매량을 구할수 있습니다.

Sub CountFruit()

    ' 시트에서 가장 마지막 행 구하기
    Dim LastRow As Long
    LastRow = Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Row

    Dim i As Long, Total As Long
    ' 마지막 컬럼 row를 제외한 2번째 row 부터 마지막 row까지 루프 실행
    For i = 2 To LastRow
        ' 셀의 텍스트가 "Orange" 인지 체크
        If Sheet1.Cells(i, 1).Value = "Oranges" Then
            ' Add value in column B to total
            Total = Total + Sheet1.Cells(i, 2).Value
        End If
    Next i

    ' 전체 오렌지 갯수 출력
    Debug.Print "Total oranges sold was:"; Total

End Sub

우리는 시트에 몇 행의 오렌지 판매 데이터가 들어있을지 예상할 수 없습니다. 그래서 데이터의 처음 부터 마지막 까지 루프를 돌며넛 Orange라는 텍스트를 가진 셀을 검색합니다.

또한 총 몇개의 데이터가 주어질지도 알수 없기 때문에 이전의 예제처럼 루프 실행 횟수를 미리 지정할 수도 없습니다. 그래서 데이터의 가장 마지막 행을 구하여 LastRow 변수에 저장한 뒤 이것만큼 루프를 돌도록 했습니다.

만일 이것을 한 라인씩 수작업으로 코드를 써줘야 한다고 상상해 보십시오. 아마도 미쳐버릴 겁니다.

For 루프 문법

For <변수> = <시작 값> To <종료 값>
Next <변수>

시작 및 종료 값은 1, 20, 1000과 같은 상수나 LastRow 같은 변수가 올 수 있습니다. Next 뒤의 변수는 선택 사항이지만 어떤 For 루프에 속하는지 명확하게 구분하기 위해 적어 주는 것이 좋습니다.

For 루프 작동 방식

1부터 3까지 출력하는 간단한 For 루프를 살펴 보도록 하겠습니다.

    Dim i As Long
    For i = 1 To 3
        Debug.Print i
    Next i

이 코드가 동작하는 방식은 다음과 같습니다.

  1. i는 1로 설정 됩니다. i의 값(현재 1)이 출력 됩니다.
  2. 다시 위로 올라가 i는 2로 설정 됩니다. i의 값(현재 2)이 출력 됩니다.
  3. 다시 위로 올라가 i는 3으로 설정 됩니다. i의 값(현재 3)이 출력 됩니다.
  4. i가 3까지 증가 했으므로 루프는 종료 됩니다.

만일 루프를 사용하지 않는다면 아래와 같은 코드가 됩니다.

    Dim i As Long
    i = i + 1
    Debug.Print i
    i = i + 1
    Debug.Print i
    i = i + 1
    Debug.Print i

i = i + 1라인은 i에 1를 더하는데 사용되며 카운터를 업데이트 하도록 프로그래밍하는 일반적인 방법입니다.

For 루프에서 Step 사용하기

앞의 예제에서 우리는 i의 값이 1씩 증가하는 것을 보았습니다. For 루프는 이와 같이 기본적으로 주어진 변수를 1씩 증가 시키며 동작하빈다. 만일 1이 아닌 2씩 또는 3씩 증가 시키고 싶다면 Step를 사용할 수 있습니다.

다음 예제는 Step을 이용하여 1부터 20까지 2씩 증가하며 루프를 도는 예를 보여주고 있습니다.

    For i = 2 To 20 Step 2
        Debug.Print i
    Next i

반대로 음수를 사용하여 카운터를 감소 시킬 수도 있습니다.

    For i = 20 To 2 Step -2
        Debug.Print i
    Next i

참고: Step이 양수이면 시작 번호는 종료 번호보다 작아야 합니다. 다음 루프는 시작 번호 20이 10보다 크기 때문에 실행되지 않습니다. VBA는 이미 목표 값 10에 도달했다고 생각합니다.

    For i = 20 To 10 Step 1
        Debug.Print i
    Next i

Exit를 이용해 For 루프 빠져 나가기

때때로 모든 루프를 완료하기 전에 For 루프를 빠져나가야 할 필요가 있습니다. 이때 Exit 키워드를 이용해 For 루프를 종료하고 빠져 나갈 수 있습니다. 아래 예는 잘못된 데이터를 만나는 경우 For 루프를 종료하고 있습니다.

For i = 1 To 1000

    ' 만일 셀이 비어 있다면 Exit를 이용해 For 루프를 종료 한다.
    If Cells(i, 1) = "" Then
        MsgBox "Blank Cell found - Data error"
        Exit For
    End If

Next i

Collection 자료 구조에 For 루프 사용하기

Collection 자료 구조에 For 루프를 사용 할 수 있습니다. 아래 예제는 모든 열려 있는 Workbook의 이름을 출력하고 있습니다.

    Dim i As Long
    For i = 1 To Workbooks.Count
        Debug.Print Workbooks(i).FullName
    Next i

중첩 For 루프 사용하기

때때로 For 루프 안에 다른 For 루프를 사용할 수도 있습니다. 아래의 예는 열려 있는 Workbook을 순회하며 그 안에 속해 있는 Worksheet들의 이름을 출력합니다.

첫 번째 루프는 Workbook을 순회하며, 첫 번째 루프 내부에서 Workbook 안의 Worksheet들을 순회하는 두 번째 루프가 실행 됩니다. 설명 보다는 아래 코드를 보면 이해가 더 쉬울 것입니다.

Sub ListWorksheets()

    Dim i As Long, j As Long
    ' 전체 Workbook을 순회하는 첫번째 루프
    For i = 1 To Workbooks.Count

        ' workbook(i)의 전체 worksheets를 순회하는 두번째 루프
        For j = 1 To Workbooks(i).Worksheets.Count
            Debug.Print Workbooks(i).Name + ":" + Worksheets(j).Name
        Next j

    Next i

End Sub

위 코드는 다음과 같이 동작 합니다 :

  1. 첫 번째 루프의 i는 1로 셋팅 됩니다.
  2. 두번째 루프의 j는 1부터 Workbooks(1)에 속한 worksheet 카운트 만큼 순회하며 worksheet의 이름을 출력 합니다.
  3. 다시 첫 번째 루프로 돌아가 i는 2로 셋팅 됩니다.
  4. 두번째 루프의 j는 1부터 Workbooks(2)에 속한 worksheet 카운트 만큼 순회하며 worksheet의 이름을 출력 합니다.
  5. 계속 이런식으로 i가 Workbooks.Count 만큼 증가할 때까지 루프를 실행 합니다.

For Each 루프

For Each 루프는 For 루프와 비슷하지만 Collection 이나 Array 자료구조를 순회하며 각 요소들을 순회하는데 사용됩니다. 예를 들어 우리는 Application.Workbooks는 Collection 자료구조이므로 우리는 모든 열려 있는 Workbook들을 순회하는데 For Each를 사용할 수 있습니다.

    Dim wk As Workbook
    For Each wk In Workbooks
        Debug.Print wk.FullName
    Next wk

For Each 루프의 문법

For Each <변수> in <Collection 또는 Array>
Next <변수>

For Each 루프에 사용 되는 변수는 Collection 또는 Array이 가지고 있는 요소의 타입과 동일한 타입이어야 합니다. 아래 예제에서는 Workbook 타입의 변수를 사용하고 있습니다.

만일 Collection에서 정의된 데이터 타입이 다른 경우 우리는 Variant 타입의 변수를 선언해서 사용할 수 있습니다.

예를 들어 VBA는 Sheets라고 불리는 Collection 자료 구조를 가지고 있습니다. Sheets는 Worksheet 타입과 Chart 타입의 요소들을 가지고 있습니다. 이렇게 다양한 타입을 요소로 가지고 있는 Collection을 순회하기 위해 우리는 Variant 타입의 변수를 선언하여 사용할 수 있습니다.

    Dim sh As Variant
    For Each sh In ThisWorkbook.Sheets
        Debug.Print sh.Name
    Next sh

For Each 루프의 실행 순서

앞에서 우리는 For 루프의 변수가 증가 하거나 감소하는 함으로써 양방향으로 실행할 수 있는 것을 보았습니다. 하지만 For Each는 한쪽 방향으로만 순차적으로 진행 가능합니다. For 루프의 Step 처럼 음수를 사용할 수도 없고, 한번에 2씩 증가 할 수도 없다는 뜻입니다.

예를 들어 여러분이 전체 worksheet를 순회한다면 언제나 작은 것에서 부터 큰 것으로, 왼쪽에서 부터 오른쪽으로 진행할 수 밖에 없습니다. 만일 여러분이 Range를 순회한다면 낮은 순서의 셀부터 큰 순서의 셀쪽으로만 진행할 수 있습니다.

이것은 여러분이 만일 역순으로 진행해야 한다거나, 스텝을 건너 뛰면서 진행해야 한다면 For Each는 사용할 수 없다는 뜻입니다.

아래에 For 와 For Each 루프를 비교 해보도록 하겠습니다.

    Dim wk As Worksheet
    For Each wk In ThisWorkbook.Worksheets
        Debug.Print wk.Name
    Next

    Dim i As Long
    For i = 1 To ThisWorkbook.Worksheets.Count
        Debug.Print ThisWorkbook.Worksheets(i).Name
    Next

보시다시피 For Each 루프가 코드 상으로 더 간단합니다. 그러나 오른쪽에서 왼쪽과 같이 다른 순서로 worksheet를 읽기 위해서는 For 루프를 사용해야만 합니다.

' 오른쪽에서 왼쪽으로 워크시트 읽기 
Dim i As  Long 
For i = ThisWorkbook.Worksheets.Count To 1 Step -1
    Debug.Print ThisWorkbook.Worksheets(i).Name
Next

Array에 For Each 루프 사용하기

Collection 데이터 외에도 Array에도 For Each를 사용할 수 있습니다. 하지만 Array에 사용할 때는 읽기 전용으로만 사용할 수있습니다.

Sub UseForEach()

    ' 배열을 생성하고 세 개의 값을 추가한다
    Dim arr() As Variant
    arr = Array("A", "B", "C")

    Dim s As Variant
    For Each s In arr
        ' s가 참조하고 있는 값을 변경한다. 배열 요소의 값을 바꾸는 것이 아닌 s의 참조를 바꾸는 것이다
        s = "Z"
    Next

    ' 배열의 값은 변경 되지 않았음을 보여주기 위해 출력한다.
    For Each s In arr
        Debug.Print s
    Next

End Sub

첫 번째 For Each 루프에서 "Z"를 할당하려고 시도하고 있습니다. 하지만 이 결과는 s가 가리키고 있는 배열의 값을 바꾸는 것이 아닌 s의 참조를 "Z"로 변경할 뿐입니다. 두 번째 루프에서 배열의 값을 인쇄해보면 여전히 배열의 값이 변경 되지 않았음을 알 수 있습니다.

배열의 값을 변경하기 위해서 우리는 For 루프를 사용해야 합니다. 위의 예제 코드를 For Each에서 For 루프로 변경하면 모든 배열의 값이 "Z"로 변경되는 것을 확인할 수 있습니다.

Sub UsingForWithArray()

    ' 배열을 생성하고 세 개의 값을 추가한다
    Dim arr() As Variant
    arr = Array("A", "B", "C")

    Dim i As Long
    For i = LBound(arr) To UBound(arr)
        ' 배열을 인덱스로 접근해 값을 Z로 변경한다.
        arr(i) = "Z"
    Next

    ' 배열의 값이 변경 되었음을 확인하기 위해 출력
    For i = LBound(arr) To UBound(arr)
        Debug.Print arr(i)
    Next

End Sub

만일 Collection이 Object를 저장하고 있다면 Object는 참조를 이용해 접근할 수 있으므로 For Each 루프라고 하더라도 값을 변경 할 수 있습니다. Object에 대한 자세한 사항은 [여기]를 참고하도록 합니다.

중첩 For Each 루프 사용하기

우리는 이미 중첩 For 루프에 대해서 알아 보았습니다. 먼저 앞에서 보았던 For 루프 예제를 살펴 보도록 하겠습니다.

Sub ListWorksheets()

    Dim i As Long, j As  Long 
    ' 첫 번째 루프는 모든 workbook을 순회합니다
    For i = 1 To Workbooks.Count

        ' 두 번째 루프는 workbook(i) 안의 모든 worksheet를 순회합니다.
        For j = 1 To Workbooks(i).Worksheets.Count
            Debug.Print Workbooks(i).Name + ":" + Worksheets(j).Name
        Next j

    Next i

End Sub

글머 이제는 위와 동일한 작업을 수행하는 For Each 버전을 살펴 보도록하죠.

Sub ReadAllWorksheets()

    Dim wk As Workbook, sh As Worksheet
    ' Workbook 순회하기
    For Each wk In Workbooks

        ' wk 내의 모든 worksheet 순회하기
        For Each sh In wk.Worksheets
            ' Print workbook name and worksheet name
            Debug.Print wk.Name + ": " + sh.Name
        Next sh

    Next wk

End Sub

보시다시피 For 루프를 사용하는 것보다 훨씬 깔끔한 코드를 만들 수 있습니다. 이 코드는 다음과 같이 실행 됩니다.

  1. Workbooks 컬렉션에서 첫번째 Workbook, 'wk' 가져오기
  2. wk의 모든  worksheet 순회하기
  3. workbook과 worksheet의 이름 출력
  4. 다시 맨 위로 올라가 다음 Workbook 가져오기
  5. 2번과 3번 과정 반복
  6. 컬렉션의 마지막 Workbook을 처리하고 나면 루프 종료

For 루프를 이용해 Range 순회하기

엑셀 VBA에서 For 루프의 가장 일반적인 용도 중에 하나는 Range를 순회하며 읽는 것입니다. 아래 이미지와 같은 데이터들이 있다고 상상해 보십시오. 우리는 데이터를 읽고 Amount 컬럼에 있는 값중 200000 보다 큰 값만을 J 컬럼에 복사할 것입니다.

 아래 코드는 수행 방법을 보여줍니다.

' VBA For 루프를 사용하여 Excel Range 읽기
Sub ForLoopThroughRange()

    ' Worksheet를 가져옵니다
    Dim sh As Worksheet
    Set sh = ThisWorkbook.Worksheets("Sheet1")
    
    ' Range를 가져옵니다
    Dim rg As Range
    Set rg = sh.Range("A1").CurrentRegion
    
    ' 기존에 존재하던 값을 삭제합니다
    sh.Range("J1").CurrentRegion.ClearContents
    
    ' 첫번째 출력행을 설정합니다.
    Dim row As Long
    row = 1
    
    ' For 루프를 이용해 모든 행들을 읽습니다
    Dim i As Long
    For i = 2 To rg.Rows.Count
    
        ' 값이 200000 보다 큰지 검사합니다
        If rg.Cells(i, 4).Value > 200000 Then
        
            ' J열에 값을 복사합니다
            sh.Cells(row, "J").Value = rg.Cells(i, 4).Value
            
            ' 다음 행으로 이동합니다.
            row = row + 1
            
        End If
        
    Next i
    
End Sub

위 예제는 엑셀 VBA를 이용하여 데이터를 복사하는 아주 기본적인 예입니다. 

VBA For 루프 요약

For 루프

  • For 루프는 For Each 루프보다 느립니다 .
  • For 루프는 5에서 10까지 항목을 선택할 수 있습니다.
  • For 루프는 항목을 반대로 읽을 수 있습니다( 예: 10에서 1로).
  • For 루프는 특히 중첩 루프의 경우 For Each 루프만큼 작성하기에 깔끔하지 않습니다 .
  • For 루프 를 종료 하려면 Exit For를 사용하십시오.

For Each 루프

  • For Each 루프는 For 루프보다 빠릅니다 .
  • For Each 루프는 컬렉션 또는 배열의 모든 항목을 순회 합니다.
  • For Each 루프는 한 방향으로만 순회할 수 있습니다.
  • For Each 루프는 특히 중첩 루프의 경우 For 루프보다 작성하기에 더 깔끔합니다 .
  • For Each 루프 를 종료 하려면 Exit For 를 사용하십시오.

부록 1. 같이 읽으면 좋은 글

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!