엑셀 매크로를 버튼·단축키에 연결하고 같은 결과로 검산하기
버튼과 단축키에 서로 다른 매크로 사본을 연결하면 한쪽만 고쳤을 때 결과가 달라집니다. 여기서는 가상 주문 네 행의 집계 코드를 하나만 두고, 양식 컨트롤 버튼·이름 있는 도형·단축키를 그 코드로 연결합니다. 기존의 수량×단가 예제와 합계 9800000을 기준으로 연결 상태를 검산합니다.
코드와 입력 계약은 독립 계산과 정적 검사로 확인했습니다. Windows Excel에서 버튼을 누르거나 OnKey를 실행한 기록은 없으므로 실제 작동 완료로 소개하지 않습니다. 매크로 실행이 허용된 Windows 데스크톱 Excel의 .xlsm 연습용 복사본에서 아래 확인을 마쳐야 합니다.
네 주문과 두 번의 합계 비교
Sheet1!A1:D5에 다음 가상 입력을 넣습니다. 날짜는 표의 식별용 문자열이며 이 코드가 주차를 골라내는 조건은 아닙니다. 범위 안의 네 행을 그대로 계산합니다.
| 날짜 | 품목 | 수량 | 단가 |
|---|---|---|---|
| 2026-08-20 | 노트북 | 5 | 1500000 |
| 2026-08-21 | 모니터 | 2 | 300000 |
| 2026-08-22 | 키보드 | 10 | 50000 |
| 2026-08-23 | 도킹스테이션 | 4 | 300000 |
| 출력 위치 | 기본 입력의 예상 값 | C3을 3으로 바꾼 뒤 |
|---|---|---|
| E2 | 7500000 | 7500000 |
| E3 | 600000 | 900000 |
| E4 | 500000 | 500000 |
| E5 | 1200000 | 1200000 |
| E6 | 9800000 | 10100000 |
모니터 한 대가 늘면 300000이 더해집니다. 이 변화가 버튼과 단축키에서 같아야 합니다. 출력 범위는 주간집계 결과!A1:E6이며 기존 시트가 있으면 이 범위의 내용만 다시 씁니다. 그 범위에 원래 있던 값과 수식은 바뀌므로 다른 보고서와 섞지 마세요.


같은 매크로를 연결하는 순서
-
.xlsm 복사본의
Sheet1에 네 행을 입력하고, ZIP의WeeklyReport.bas를 UTF-8 텍스트 편집기로 열어 표준 모듈에 붙여 넣습니다. -
VBA 편집기에서 컴파일한 뒤
WeeklyReport를 직접 실행해 E2:E6을 검산합니다. -
개발 도구의 양식 컨트롤 단추를 빈 곳에 추가하고 같은 파일의
WeeklyReport를 지정합니다. 한 번 눌러 결과를 다시 확인합니다. -
도형을 사용할 때는 Sheet1에 도형을 만들고 이름 상자에서
RunWeeklyReport로 정합니다.ConnectNamedShape를 실행해 연결한 뒤 도형을 눌러 봅니다. -
다른 매크로가 Ctrl+Shift+P를 사용하지 않는지 확인한 후
RegisterShortcut을 실행합니다. C3을 3으로 입력하고 단축키로 실행해 E3=900000, E6=10100000을 확인합니다. -
연습을 마치기 전에
ReleaseShortcut을 실행합니다. 자동 등록·해제 이벤트는 이 예제에 넣지 않았습니다.

수식처럼 보이는 텍스트는 복사 전에 중단
날짜·품목의 문자열은 왼쪽 일반 공백을 제외한 첫 글자가 =, +, -, @이면 복사하지 않습니다. 탭·줄바꿈을 포함한 ASCII 제어문자(0~31, 127)가 있어도 출력 범위를 비우기 전에 중단합니다. Sheet1!B2에 문자열 =1+1을 넣는 반례에서는 합계 계산을 진행하지 않고 이전 출력이 남는 것이 입력 계약입니다. 가상 입력의 날짜 문자열 2026-08-20과 일반 품목명, C:D의 숫자 계산은 바뀌지 않습니다.
B2를 수식 예시: =1+1처럼 설명으로 시작하는 일반 라벨이나 원래 노트북으로 고친 뒤 재실행합니다. 수식처럼 보이는 값을 반드시 그대로 복사해야 하는 작업은 이 예제의 범위가 아닙니다. 원본 셀에 이미 들어 있는 Excel 수식의 실행을 막는 장치도 아닙니다. 검사는 읽은 Value2의 문자열만 대상으로 합니다.
집계·연결·해제가 함께 있는 코드
FormulaR1C1은 R1C1 표기법으로 수식을 읽거나 설정합니다. E열의 RC[-2]*RC[-1]은 같은 행의 C열과 D열을 곱합니다. 수동 계산 모드에서도 출력 확인 전에 해당 범위를 계산하도록 Calculate를 명시했습니다.
Option Explicit
Public Sub WeeklyReport()
Dim src As Worksheet, dst As Worksheet, sh As Worksheet
Dim raw As Variant, r As Long, q As Double, price As Double
Dim oldScreen As Boolean, oldEvents As Boolean, errorText As String
oldScreen = Application.ScreenUpdating
oldEvents = Application.EnableEvents
On Error GoTo Failed
Set src = ThisWorkbook.Worksheets("Sheet1")
raw = src.Range("A1:D5").Value2
If CStr(raw(1, 1)) <> "날짜" Or CStr(raw(1, 2)) <> "품목" Or CStr(raw(1, 3)) <> "수량" Or CStr(raw(1, 4)) <> "단가" Then
Err.Raise vbObjectError + 301, , "Expected headers: date, item, quantity, unit price"
End If
For r = 2 To 5
If IsError(raw(r, 1)) Or IsError(raw(r, 2)) Then Err.Raise vbObjectError + 302, , "Error in date or item"
If Len(Trim$(CStr(raw(r, 1)))) = 0 Or Len(Trim$(CStr(raw(r, 2)))) = 0 Then Err.Raise vbObjectError + 303, , "Blank date or item"
RejectFormulaLikeText raw(r, 1), "A" & r
RejectFormulaLikeText raw(r, 2), "B" & r
q = WholeAmount(raw(r, 3), "C" & r)
price = WholeAmount(raw(r, 4), "D" & r)
If q * price > 1000000000000# Then Err.Raise vbObjectError + 304, , "Row amount exceeds the example limit"
Next r
For Each sh In ThisWorkbook.Worksheets
If sh.Name = "주간집계 결과" Then Set dst = sh
Next sh
Application.ScreenUpdating = False
Application.EnableEvents = False
If dst Is Nothing Then
Set dst = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
dst.Name = "주간집계 결과"
End If
If dst.ProtectContents Then Err.Raise vbObjectError + 305, , "Output sheet is protected"
If IsNull(dst.Range("A1:E6").MergeCells) Then Err.Raise vbObjectError + 306, , "Output contains merged cells"
If dst.Range("A1:E6").MergeCells Then Err.Raise vbObjectError + 307, , "Output contains merged cells"
dst.Range("A1:E6").ClearContents
dst.Range("A1:D5").Value2 = raw
dst.Range("E1").Value2 = "총액"
dst.Range("E2:E5").FormulaR1C1 = "=RC[-2]*RC[-1]"
dst.Range("D6").Value2 = "합계"
dst.Range("E6").Formula = "=SUM(E2:E5)"
dst.Range("E2:E6").Calculate
Debug.Print "E6=" & dst.Range("E6").Value2
Application.EnableEvents = oldEvents
Application.ScreenUpdating = oldScreen
MsgBox "Compare E2:E6 on the output sheet.", vbInformation
Exit Sub
Failed:
errorText = Err.Number & " / " & Err.Description
Application.EnableEvents = oldEvents
Application.ScreenUpdating = oldScreen
MsgBox "Stopped: " & errorText & vbCrLf & "Partial output is possible; input was not changed.", vbExclamation
End Sub
Private Function QualifiedMacro() As String
QualifiedMacro = "'" & Replace(ThisWorkbook.Name, "'", "''") & "'!WeeklyReport"
End Function
Public Sub RegisterShortcut()
Application.OnKey "^+P", QualifiedMacro()
End Sub
Public Sub ReleaseShortcut()
Application.OnKey "^+P"
End Sub
Public Sub ConnectNamedShape()
ThisWorkbook.Worksheets("Sheet1").Shapes("RunWeeklyReport").OnAction = QualifiedMacro()
End Sub
Private Function WholeAmount(ByVal v As Variant, ByVal address As String) As Double
Select Case VarType(v)
Case vbByte, vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal
Case Else
Err.Raise vbObjectError + 201, , address & ": enter a numeric cell value"
End Select
If v < 0 Or v > 1000000000# Then
Err.Raise vbObjectError + 202, , address & ": allowed range is 0..1000000000"
End If
If v <> Fix(v) Then Err.Raise vbObjectError + 203, , address & ": integer required"
WholeAmount = CDbl(v)
End Function
Private Sub RejectFormulaLikeText(ByVal value As Variant, ByVal address As String)
Dim text As String, character As Long
If VarType(value) <> vbString Then Exit Sub
text = CStr(value)
For character = 0 To 31
If InStr(1, text, Chr$(character), vbBinaryCompare) > 0 Then
Err.Raise vbObjectError + 501, , address & ": control characters are not allowed"
End If
Next character
If InStr(1, text, Chr$(127), vbBinaryCompare) > 0 Then
Err.Raise vbObjectError + 502, , address & ": control characters are not allowed"
End If
text = LTrim$(text)
Select Case Left$(text, 1)
Case "=", "+", "-", "@"
Err.Raise vbObjectError + 503, , address & ": formula-like text is not copied; use a descriptive label"
End Select
End Sub
Application.OnKey는 지정한 키 조합이 눌렸을 때 프로시저를 실행합니다. Shape.OnAction은 도형을 선택했을 때 실행할 매크로 이름을 지정합니다. 이 예제는 통합문서 이름을 포함한 매크로 경로를 두 연결에 공통으로 사용해 같은 이름의 다른 파일과 혼동할 여지를 줄였습니다. 파일 이름을 바꾸면 연결을 다시 등록하세요.

키 해제와 복구 범위
OnKey에서 Procedure를 생략하면 해당 키는 Excel의 기본 동작으로 돌아갑니다. 다른 코드가 미리 등록한 사용자 지정 동작을 기억했다가 복구하는 기능은 아닙니다. 이미 사용 중인 키를 덮어쓴 뒤 원래대로 돌아갈 것이라고 기대하지 마세요. 키 사용권을 따로 확인할 수 없다면 양식 버튼만 사용하는 편이 이 예제의 범위에 맞습니다.
이 코드는 파일을 닫는 이벤트에 자동으로 매달리지 않습니다. 파일을 닫거나 다른 작업으로 옮기기 전에 명시적으로 해제합니다. 통합문서 이름 변경·매크로 차단·도형 이름 오타·키 충돌은 Windows Excel에서 각각 확인할 항목이며 정적 검사의 통과만으로 해결됐다고 볼 수 없습니다.
오류 입력과 이전 결과 확인
수량이나 단가에 2개, 텍스트 숫자, 빈칸, 음수, 소수를 넣으면 출력 범위를 비우기 전에 중단합니다. C3을 숫자 2로 다시 입력하고 같은 네 행으로 재실행하세요. 각 숫자는 0 이상 1000000000 이하 정수이며 한 행의 곱은 1000000000000을 넘지 않도록 제한했습니다. 이것은 예제의 계산 범위이지 업무 데이터의 일반 규칙이 아닙니다.
출력 시트 보호나 병합 셀이 발견되면 중단합니다. 보호를 임의로 해제하지 말고 별도 연습 시트를 준비하세요. 쓰기를 시작한 뒤 예상치 못한 오류가 나면 일부 출력만 바뀔 수 있습니다. 복사본을 저장하지 않고 닫아 원래 상태로 돌아가고, 입력 네 행과 E2:E6을 다시 대조합니다. 입력 시트의 데이터를 지우는 코드는 없습니다.
검증 결과와 적용 한계
첨부 python verify_contract.py는 8개 정적·독립 계산 검사를 실행합니다. 원래 합계와 수량 변경 후 합계, 수식 문자열, 명시적 통합문서 연결, 입력의 숫자 계약을 확인했습니다. 버튼 클릭, 단축키 등록, 실제 Excel 수식 계산을 수행한 시험은 아닙니다.
이 코드는 A1:D5만 읽고 A1:E6만 출력하므로 행이 늘면 자동 포함되지 않습니다. 날짜로 주간 범위를 선택하지 않으며, 빠른 실행 도구 모음 등록이나 Mac의 키 조합 차이를 검증하지 않았습니다. 실제 업무 파일로 옮기기 전에는 버튼·도형·단축키 각각의 실행 결과, 키 해제, 파일 재열기 후 상태와 실패 시 출력 보존을 별도로 기록하세요.
공식 자료
Range.FormulaR1C1 property (Excel)
Application.OnKey method (Excel)
Shape.OnAction property (Excel)
출처 원문 수집일: S1 2026-09-24; S2 2026-09-24; S3 2026-09-24.