들어가며
엑셀로 여러 지점의 매출 데이터를 관리하다 보면, 각 시트에 흩어진 정보를 일일이 찾아보는 것이 매우 번거롭습니다. 이번 글에서는 VBA를 활용하여 여러 시트에 분산된 데이터를 한 번에 검색하고, 제품별·지역별·지점별 통계를 자동으로 생성하는 강력한 통합 검색 시스템을 소개합니다. 특히 VBA에서 자주 발생하는 런타임 오류 13번(형식 불일치) 문제를 해결한 안정적인 코드입니다.
시스템 개요
이 시스템은 총 6개의 주요 기능으로 구성되어 있습니다:
- 통합 검색: 모든 지점 시트에서 조건에 맞는 데이터를 한 번에 검색
- 제품별 통계: 검색 결과를 기반으로 제품별 판매량과 매출 집계
- 지역별 통계: 지역별 판매 현황 분석
- 지점별 통계: 각 지점의 실적 비교
- 검색 조건 초기화: 입력된 검색 조건을 한 번에 삭제
- 통계 대시보드: 전체 매출 현황을 한눈에 보여주는 요약 화면
1. 통합 검색 기능
기능 설명
통합 검색 기능은 워크북 내 모든 시트를 순회하며 사용자가 입력한 조건(제품명, 지역, 지점, 기간)에 맞는 데이터를 찾아 '검색결과' 시트에 자동으로 정리합니다. 검색 속도를 높이기 위해 화면 업데이트를 중지하고, 자동 계산 기능을 일시적으로 비활성화하는 최적화 기법을 적용했습니다.
주요 코드
Sub 통합검색()
Dim wsMenu As Worksheet
Dim wsResult As Worksheet
Dim ws As Worksheet
Dim lastRow As Long
Dim resultRow As Long
Dim i As Long
' 검색 조건 변수
Dim searchProduct As String
Dim searchRegion As String
Dim searchBranch As String
Dim searchStartDate As String
Dim searchEndDate As String
Dim startDate As Date
Dim endDate As Date
' 검색 조건 읽기
Set wsMenu = ThisWorkbook.Sheets("검색메뉴")
searchProduct = Trim(wsMenu.Range("C5").Value)
searchRegion = Trim(wsMenu.Range("C6").Value)
searchBranch = Trim(wsMenu.Range("C7").Value)
searchStartDate = Trim(wsMenu.Range("C8").Value)
searchEndDate = Trim(wsMenu.Range("C9").Value)
' 날짜 변환
Dim useStartDate As Boolean, useEndDate As Boolean
useStartDate = False
useEndDate = False
If searchStartDate <> "" Then
If IsDate(searchStartDate) Then
startDate = CDate(searchStartDate)
useStartDate = True
End If
End If
If searchEndDate <> "" Then
If IsDate(searchEndDate) Then
endDate = CDate(searchEndDate)
useEndDate = True
End If
End If
' 검색결과 시트 초기화
Set wsResult = ThisWorkbook.Sheets("검색결과")
wsResult.Rows("2:" & wsResult.Rows.Count).Delete
resultRow = 2
' 화면 업데이트 중지 (속도 향상)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' 모든 지점 시트 검색
For Each ws In ThisWorkbook.Worksheets
' 검색메뉴와 검색결과 시트는 제외
If ws.Name <> "검색메뉴" And ws.Name <> "검색결과" Then
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
' 데이터가 있는 경우만 처리 (헤더 제외)
If lastRow > 1 Then
For i = 2 To lastRow
Dim matchProduct As Boolean
Dim matchRegion As Boolean
Dim matchBranch As Boolean
Dim matchDate As Boolean
' 각 조건 확인
matchProduct = (searchProduct = "" Or InStr(1, ws.Cells(i, 3).Value, searchProduct, vbTextCompare) > 0)
matchRegion = (searchRegion = "" Or InStr(1, ws.Cells(i, 4).Value, searchRegion, vbTextCompare) > 0)
matchBranch = (searchBranch = "" Or InStr(1, ws.Cells(i, 2).Value, searchBranch, vbTextCompare) > 0)
' 날짜 조건 확인
matchDate = True
If useStartDate Or useEndDate Then
If IsDate(ws.Cells(i, 1).Value) Then
Dim cellDate As Date
cellDate = CDate(ws.Cells(i, 1).Value)
If useStartDate And cellDate < startDate Then
matchDate = False
End If
If useEndDate And cellDate > endDate Then
matchDate = False
End If
End If
End If
' 모든 조건이 맞으면 결과에 추가
If matchProduct And matchRegion And matchBranch And matchDate Then
' 데이터 복사
ws.Rows(i).Copy Destination:=wsResult.Rows(resultRow)
resultRow = resultRow + 1
End If
Next i
End If
End If
Next ws
' 화면 업데이트 재개
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
' 결과 표시
Dim resultCount As Long
resultCount = resultRow - 2
If resultCount > 0 Then
MsgBox resultCount & "건의 데이터를 찾았습니다!" & vbCrLf & _
"검색결과 시트를 확인하세요.", vbInformation, "검색 완료"
wsResult.Activate
Else
MsgBox "검색 조건에 맞는 데이터가 없습니다.", vbExclamation, "검색 결과 없음"
End If
End Sub
코드 설명
코드의 핵심은 크게 세 단계로 나뉩니다. 첫 번째로 '검색메뉴' 시트에서 사용자가 입력한 검색 조건을 읽어옵니다. 이때 날짜 데이터는 IsDate 함수로 유효성을 검증한 후 CDate 함수로 변환합니다. 두 번째로 워크북의 모든 시트를 순회하면서 각 행의 데이터가 검색 조건과 일치하는지 확인합니다. InStr 함수를 사용하여 부분 일치 검색을 지원하며, vbTextCompare 옵션으로 대소문자를 구분하지 않습니다. 세 번째로 조건에 맞는 행을 '검색결과' 시트에 복사하고, 최종적으로 몇 건의 데이터를 찾았는지 메시지로 알려줍니다.
2. 제품별 통계 기능
기능 설명
제품별 통계 기능은 검색 결과를 바탕으로 각 제품의 총 판매량, 매출액, 평균 단가를 계산합니다. 이전 버전에서는 Dictionary 배열 요소를 직접 수정하려 할 때 런타임 오류 13번(형식 불일치)이 발생했는데, 이를 임시 변수에 값을 저장한 후 새로운 배열로 교체하는 방식으로 완벽히 해결했습니다.
주요 코드
Sub 제품별통계()
Dim wsResult As Worksheet
Dim wsStats As Worksheet
Dim lastRow As Long
Dim i As Long
Dim dict As Object
Dim product As String
Dim amount As Double
Dim quantity As Long
' 검색결과가 있는지 확인
Set wsResult = ThisWorkbook.Sheets("검색결과")
lastRow = wsResult.Cells(wsResult.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox "검색결과가 없습니다. 먼저 검색을 실행하세요.", vbExclamation
Exit Sub
End If
' Dictionary 객체 생성
Set dict = CreateObject("Scripting.Dictionary")
' 제품별 집계 - 수정된 방식
For i = 2 To lastRow
product = wsResult.Cells(i, 3).Value ' 제품명
quantity = wsResult.Cells(i, 5).Value ' 수량
amount = wsResult.Cells(i, 7).Value ' 총액
If dict.Exists(product) Then
' 기존 값을 임시 변수에 저장
Dim tempQty As Long
Dim tempAmt As Double
tempQty = dict(product)(0)
tempAmt = dict(product)(1)
' 새로운 배열로 교체
dict(product) = Array(tempQty + quantity, tempAmt + amount)
Else
dict.Add product, Array(quantity, amount)
End If
Next i
' 통계 시트 생성 또는 초기화
On Error Resume Next
Set wsStats = ThisWorkbook.Sheets("제품별통계")
If wsStats Is Nothing Then
Set wsStats = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsStats.Name = "제품별통계"
Else
wsStats.Cells.Clear
End If
On Error GoTo 0
' 헤더 작성
wsStats.Range("A1").Value = "제품명"
wsStats.Range("B1").Value = "판매수량"
wsStats.Range("C1").Value = "매출액"
wsStats.Range("D1").Value = "평균단가"
' 헤더 서식
With wsStats.Range("A1:D1")
.Font.Bold = True
.Interior.Color = RGB(68, 114, 196)
.Font.Color = RGB(255, 255, 255)
.HorizontalAlignment = xlCenter
End With
' 데이터 작성
Dim Row As Long
Dim key As Variant
Row = 2
For Each key In dict.Keys
wsStats.Cells(Row, 1).Value = key
wsStats.Cells(Row, 2).Value = dict(key)(0)
wsStats.Cells(Row, 3).Value = dict(key)(1)
wsStats.Cells(Row, 4).Value = dict(key)(1) / dict(key)(0)
Row = Row + 1
Next key
' 열 너비 자동 조정
wsStats.Columns("A:D").AutoFit
' 숫자 서식
wsStats.Range("B2:B" & Row - 1).NumberFormat = "#,##0"
wsStats.Range("C2:D" & Row - 1).NumberFormat = "#,##0"
wsStats.Activate
MsgBox "제품별 통계가 생성되었습니다!", vbInformation
End Sub
3. 지역별 통계 기능
기능 설명
지역별 통계는 제품별 통계와 유사한 방식으로 작동하지만, 집계 기준이 지역(4번 열)이 됩니다. 각 지역의 총 판매량과 매출액을 계산하여 지역별 실적을 비교할 수 있습니다.
주요 코드
Sub 지역별통계()
Dim wsResult As Worksheet
Dim wsStats As Worksheet
Dim lastRow As Long
Dim i As Long
Dim dict As Object
Dim region As String
Dim amount As Double
Dim quantity As Long
' 검색결과가 있는지 확인
Set wsResult = ThisWorkbook.Sheets("검색결과")
lastRow = wsResult.Cells(wsResult.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox "검색결과가 없습니다. 먼저 검색을 실행하세요.", vbExclamation
Exit Sub
End If
' Dictionary 객체 생성
Set dict = CreateObject("Scripting.Dictionary")
' 지역별 집계 - 수정된 방식 (오류 해결)
For i = 2 To lastRow
region = wsResult.Cells(i, 4).Value ' 지역
quantity = wsResult.Cells(i, 5).Value ' 수량
amount = wsResult.Cells(i, 7).Value ' 총액
If dict.Exists(region) Then
' ★ 수정: 기존 값을 임시 변수에 저장 후 새로운 배열로 교체
Dim tempQty As Long
Dim tempAmt As Double
tempQty = dict(region)(0)
tempAmt = dict(region)(1)
' 새로운 배열로 교체 (기존 배열을 직접 수정하지 않음)
dict(region) = Array(tempQty + quantity, tempAmt + amount)
Else
dict.Add region, Array(quantity, amount)
End If
Next i
' 통계 시트 생성 또는 초기화
On Error Resume Next
Set wsStats = ThisWorkbook.Sheets("지역별통계")
If wsStats Is Nothing Then
Set wsStats = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsStats.Name = "지역별통계"
Else
wsStats.Cells.Clear
End If
On Error GoTo 0
' 헤더 작성
wsStats.Range("A1").Value = "지역"
wsStats.Range("B1").Value = "판매수량"
wsStats.Range("C1").Value = "매출액"
' 헤더 서식
With wsStats.Range("A1:C1")
.Font.Bold = True
.Interior.Color = RGB(112, 173, 71)
.Font.Color = RGB(255, 255, 255)
.HorizontalAlignment = xlCenter
End With
' 데이터 작성
Dim Row As Long
Dim key As Variant
Row = 2
For Each key In dict.Keys
wsStats.Cells(Row, 1).Value = key
wsStats.Cells(Row, 2).Value = dict(key)(0)
wsStats.Cells(Row, 3).Value = dict(key)(1)
Row = Row + 1
Next key
' 열 너비 자동 조정
wsStats.Columns("A:C").AutoFit
' 숫자 서식
wsStats.Range("B2:B" & Row - 1).NumberFormat = "#,##0"
wsStats.Range("C2:C" & Row - 1).NumberFormat = "#,##0"
wsStats.Activate
MsgBox "지역별 통계가 생성되었습니다!", vbInformation
End Sub
4. 지점별 통계 기능
기능 설명
지점별 통계는 각 지점(2번 열)의 판매 실적을 집계합니다. 어떤 지점이 가장 많이 팔았는지, 어느 지점의 매출이 높은지를 한눈에 파악할 수 있어 실적 관리에 유용합니다.
주요 코드
Sub 지점별통계()
Dim wsResult As Worksheet
Dim wsStats As Worksheet
Dim lastRow As Long
Dim i As Long
Dim dict As Object
Dim branch As String
Dim amount As Double
Dim quantity As Long
' 검색결과가 있는지 확인
Set wsResult = ThisWorkbook.Sheets("검색결과")
lastRow = wsResult.Cells(wsResult.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox "검색결과가 없습니다. 먼저 검색을 실행하세요.", vbExclamation
Exit Sub
End If
' Dictionary 객체 생성
Set dict = CreateObject("Scripting.Dictionary")
' 지점별 집계
For i = 2 To lastRow
branch = wsResult.Cells(i, 2).Value ' 지점
quantity = wsResult.Cells(i, 5).Value ' 수량
amount = wsResult.Cells(i, 7).Value ' 총액
If dict.Exists(branch) Then
Dim tempQty As Long
Dim tempAmt As Double
tempQty = dict(branch)(0)
tempAmt = dict(branch)(1)
dict(branch) = Array(tempQty + quantity, tempAmt + amount)
Else
dict.Add branch, Array(quantity, amount)
End If
Next i
' 통계 시트 생성 또는 초기화
On Error Resume Next
Set wsStats = ThisWorkbook.Sheets("지점별통계")
If wsStats Is Nothing Then
Set wsStats = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsStats.Name = "지점별통계"
Else
wsStats.Cells.Clear
End If
On Error GoTo 0
' 헤더 작성
wsStats.Range("A1").Value = "지점"
wsStats.Range("B1").Value = "판매수량"
wsStats.Range("C1").Value = "매출액"
' 헤더 서식
With wsStats.Range("A1:C1")
.Font.Bold = True
.Interior.Color = RGB(237, 125, 49)
.Font.Color = RGB(255, 255, 255)
.HorizontalAlignment = xlCenter
End With
' 데이터 작성
Dim Row As Long
Dim key As Variant
Row = 2
For Each key In dict.Keys
wsStats.Cells(Row, 1).Value = key
wsStats.Cells(Row, 2).Value = dict(key)(0)
wsStats.Cells(Row, 3).Value = dict(key)(1)
Row = Row + 1
Next key
' 열 너비 자동 조정
wsStats.Columns("A:C").AutoFit
' 숫자 서식
wsStats.Range("B2:B" & Row - 1).NumberFormat = "#,##0"
wsStats.Range("C2:C" & Row - 1).NumberFormat = "#,##0"
wsStats.Activate
MsgBox "지점별 통계가 생성되었습니다!", vbInformation
End Sub
5. 검색 조건 초기화
기능 설명
검색 조건 초기화는 간단하지만 매우 유용한 기능입니다. '검색메뉴' 시트의 C5:C9 범위(제품명, 지역, 지점, 시작일, 종료일)에 입력된 모든 값을 한 번에 지워줍니다.
주요 코드
Sub 검색조건초기화()
Dim wsMenu As Worksheet
Set wsMenu = ThisWorkbook.Sheets("검색메뉴")
wsMenu.Range("C5:C9").ClearContents
MsgBox "검색 조건이 초기화되었습니다.", vbInformation
End Sub
6. 전체 통계 대시보드
기능 설명
전체 통계 대시보드는 검색 결과의 전체적인 현황을 요약해서 보여줍니다. 총 거래 건수, 총 판매 수량, 총 매출액, 평균 거래 금액을 자동으로 계산하여 깔끔한 형태로 표시합니다.
주요 코드
Sub 전체통계대시보드()
Dim wsResult As Worksheet
Dim wsDash As Worksheet
Dim lastRow As Long
Dim totalQty As Long
Dim totalAmt As Double
Dim avgAmt As Double
' 검색결과가 있는지 확인
Set wsResult = ThisWorkbook.Sheets("검색결과")
lastRow = wsResult.Cells(wsResult.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox "검색결과가 없습니다. 먼저 검색을 실행하세요.", vbExclamation
Exit Sub
End If
' 합계 계산
totalQty = Application.WorksheetFunction.Sum(wsResult.Range("E2:E" & lastRow))
totalAmt = Application.WorksheetFunction.Sum(wsResult.Range("G2:G" & lastRow))
avgAmt = totalAmt / (lastRow - 1)
' 대시보드 시트 생성
On Error Resume Next
Set wsDash = ThisWorkbook.Sheets("통계대시보드")
If wsDash Is Nothing Then
Set wsDash = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsDash.Name = "통계대시보드"
Else
wsDash.Cells.Clear
End If
On Error GoTo 0
' 제목
wsDash.Range("B2").Value = "📊 매출 통계 대시보드"
wsDash.Range("B2").Font.Size = 16
wsDash.Range("B2").Font.Bold = True
' 요약 정보
wsDash.Range("B4").Value = "총 거래 건수:"
wsDash.Range("C4").Value = lastRow - 1
wsDash.Range("C4").NumberFormat = "#,##0"
wsDash.Range("B5").Value = "총 판매 수량:"
wsDash.Range("C5").Value = totalQty
wsDash.Range("C5").NumberFormat = "#,##0"
wsDash.Range("B6").Value = "총 매출액:"
wsDash.Range("C6").Value = totalAmt
wsDash.Range("C6").NumberFormat = "#,##0"
wsDash.Range("B7").Value = "평균 거래금액:"
wsDash.Range("C7").Value = avgAmt
wsDash.Range("C7").NumberFormat = "#,##0"
' 서식
wsDash.Range("B4:B7").Font.Bold = True
wsDash.Range("C4:C7").Interior.Color = RGB(217, 225, 242)
wsDash.Columns("B:C").AutoFit
wsDash.Activate
MsgBox "통계 대시보드가 생성되었습니다!", vbInformation
End Sub
시스템 사용 방법
1단계: 워크북 구조 준비
이 시스템이 제대로 작동하려면 다음과 같은 시트 구조가 필요합니다:
- 검색메뉴 시트: C5(제품명), C6(지역), C7(지점), C8(시작일), C9(종료일) 셀에 검색 조건을 입력합니다
- 검색결과 시트: 검색된 데이터가 자동으로 저장됩니다
- 각 지점 시트: 1열(날짜), 2열(지점), 3열(제품명), 4열(지역), 5열(수량), 7열(총액) 형식으로 데이터가 정리되어 있어야 합니다
2단계: VBA 코드 입력
엑셀에서 Alt + F11을 눌러 VBA 편집기를 열고, 삽입 메뉴에서 '모듈'을 추가한 후 위의 코드를 모두 복사해서 붙여넣습니다.
3단계: 매크로 실행
개발 도구 탭의 매크로 버튼을 클릭하거나, Alt + F8을 눌러 원하는 기능을 선택하여 실행합니다. 권장 실행 순서는 다음과 같습니다:
- 먼저 '통합검색'을 실행하여 원하는 데이터를 찾습니다
- 그 다음 '제품별통계', '지역별통계', '지점별통계' 중 필요한 것을 실행합니다
- 전체 현황이 궁금하면 '전체통계대시보드'를 실행합니다
- 새로운 검색을 하려면 '검색조건초기화'로 조건을 지운 후 다시 시작합니다
핵심 기술 설명
Dictionary 객체 활용
이 시스템의 핵심은 Scripting.Dictionary 객체입니다. Dictionary는 키(Key)와 값(Value)의 쌍으로 데이터를 저장하는 자료구조로, 배열보다 훨씬 빠르고 효율적으로 데이터를 검색하고 집계할 수 있습니다. 제품명이나 지역명을 키로 사용하고, 수량과 금액을 배열로 저장하여 중복 없이 데이터를 누적합니다.
성능 최적화
대량의 데이터를 처리할 때는 Application.ScreenUpdating = False로 화면 갱신을 중지하고, Application.Calculation = xlCalculationManual로 자동 계산을 비활성화하는 것이 매우 중요합니다. 이 두 가지만으로도 실행 속도가 수십 배 향상될 수 있습니다. 작업이 끝나면 반드시 다시 활성화해야 합니다.
오류 처리
On Error Resume Next를 활용하여 시트가 이미 존재하는지 확인하고, 없으면 새로 만들고 있으면 내용을 지우는 방식으로 처리합니다. 또한 검색 결과가 없을 때는 통계 기능을 실행하지 못하도록 사전에 검증하여 오류를 방지합니다.
응용 및 확장 아이디어
이 시스템을 기반으로 다음과 같은 기능을 추가할 수 있습니다:
- 차트 자동 생성: 통계 결과를 바탕으로 막대 그래프나 원형 차트를 자동으로 생성하면 시각적인 분석이 가능합니다
- 엑셀 파일로 내보내기: 검색 결과나 통계를 별도의 엑셀 파일로 저장하는 기능을 추가하면 보고서 작성이 편리합니다
- 이메일 자동 발송: Outlook과 연동하여 통계 결과를 자동으로 이메일로 발송하는 기능을 구현할 수 있습니다
- 고급 필터링: 금액 범위, 판매량 상위 N개 등 더 복잡한 조건을 추가할 수 있습니다
- 월별/분기별 집계: 날짜를 기준으로 월별, 분기별 통계를 자동으로 생성하는 기능도 유용합니다
마무리
이번 글에서 소개한 통합 검색 시스템은 여러 시트에 분산된 데이터를 효율적으로 관리하고 분석할 수 있는 강력한 도구입니다. 특히 VBA에서 자주 발생하는 런타임 오류 13번 문제를 해결한 안정적인 코드이므로, 실무에서 바로 활용할 수 있습니다. Dictionary 객체와 성능 최적화 기법을 이해하면 다양한 데이터 처리 작업에 응용할 수 있습니다. 여러분의 업무 환경에 맞게 수정하여 사용해보시기 바랍니다.