엑셀 VBA에서 Application.ScreenUpdating이 먹히지 않을 때 완벽 해결 가이드

이 글의 목적은 엑셀 VBA에서 Application.ScreenUpdating 속성이 꺼졌음에도 화면 깜빡임이나 스크롤, 시트 재그림이 발생하는 문제를 체계적으로 진단하고, 재현 가능한 원인별 해결책과 실무형 코드 패턴을 제공하여 성능과 안정성을 동시에 확보하도록 돕는 것이다.

왜 ScreenUpdating=False인데 화면이 깜빡이는가

ScreenUpdating=False는 엑셀이 “화면을 다시 그림”을 억제하도록 지시하는 옵션이다. 그러나 다음 조건에서는 강제 리프레시가 트리거되어 깜빡임이 발생하거나 옵션이 무력화되는 것처럼 보일 수 있다.

  • 강제 리렌더 API 호출 : PrintPreview, ExportAsFixedFormat(PDF), CopyPicture, CalculateFullRebuild, Find/Replace UI, 일부 Shapes 조작 등은 내부적으로 다시 그리기를 요청한다.
  • 윈도 메시지 처리 : DoEvents는 페인팅 메시지를 처리하여 일시적으로 화면이 그려질 수 있다.
  • 선택·스크롤 변경 : .Select, .Activate, Application.Goto, ActiveWindow.ScrollRow/Column 등은 뷰포트를 변경한다.
  • 오류로 인한 옵션 복구 누락 : 런타임 오류로 True로 되돌리는 코드가 실행되지 않으면 사용자가 수동으로 작업하는 동안 화면이 계속 갱신되어 “작동하지 않는 것”처럼 보인다.
  • 이벤트 루프 재진입 : Worksheet_Change, Calculate 등 이벤트에서 다시 그리기를 유발하는 코드가 중첩 호출된다.
  • 차트·도형 엔진 : 차트 축 자동 맞춤, SeriesCollection 대량 변경, 이미지 배치 등은 일부 버전에서 재도색을 요청한다.
  • 다중 창·프리즈창 : FreezePanes 토글, ArrangeWindows, Split 변경은 창 레이아웃을 다시 계산한다.
  • 자동 계산 : Application.Calculation=xlAutomatic 상태에서 대량의 수식 셀 갱신은 내부 타이밍에 따라 페인트를 동반한다.
  • COM/외부 폼 : UserForm, WebBrowser 컨트롤, 외부 COM 대화상자는 자체 리프레시를 실행한다.
주의 : ScreenUpdating=False는 “화면 그리기 억제 힌트”일 뿐 강제적 프레임 락이 아니다. 특정 메서드는 이를 우선한다.

문제 재현 체크리스트와 우선순위

증상가능 원인즉시 점검해결 1순위
깜빡임 지속 선택 이동, 스크롤 .Select/.Activate 호출 여부 셀·개체 직접 참조로 치환
PDF 저장 중 깜빡임 ExportAsFixedFormat 해당 호출 전후 옵션 상태 별도 숨김 창로 오프로딩
도중에만 깜빡임 DoEvents 사용 DoEvents 위치 파악 핵심 구간에서는 제거
옵션이 원복되지 않음 에러 핸들링 없음 On Error 유무 Finally 패턴 적용
이벤트 루프 반복 EnableEvents=True 이벤트 내부 호출 체인 EnableEvents=False로 보호
차트만 깜빡임 축 자동맞춤 축 자동 스케일 상태 수정 전 자동맞춤 잠시 끄기

권장 글로벌 보호 래퍼 패턴

핵심은 “입장-핵심 작업-정리” 3단계이다. 모든 UI·계산·이벤트 옵션을 원자적으로 관리하고, 오류 발생 시에도 반드시 복원하도록 한다.

Public Sub DoWork_Safe() Dim s As Boolean, c As XlCalculation, e As Boolean, a As Boolean, d As Boolean On Error GoTo EH
' 1) 입장: 상태 스냅샷
With Application
    s = .ScreenUpdating
    c = .Calculation
    e = .EnableEvents
    a = .DisplayAlerts
    d = .DisplayStatusBar
    .ScreenUpdating = False
    .EnableEvents = False
    .DisplayAlerts = False
    .DisplayStatusBar = False
    .Calculation = xlCalculationManual
    .Cursor = xlWait
End With

' 2) 핵심 작업: 선택 이동 금지, DoEvents 최소화
CoreWorkNoSelect

' 3) 정리: 정상 종료 복원
GoTo Finally
EH:
' 오류 로깅 등 처리
Finally:
With Application
.Calculation = c
.EnableEvents = e
.DisplayAlerts = a
.DisplayStatusBar = d
.ScreenUpdating = s
.Cursor = xlDefault
.StatusBar = False
End With
End Sub

Private Sub CoreWorkNoSelect()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("데이터")
' 나쁜 예: ws.Range("A1").Select : Selection.Value = 1
' 좋은 예:
ws.Range("A1").Value = 1
' 필요 시 범위 쓰기 일괄 처리
Dim arr
arr = ws.Range("A1:C100").Value
' ... 배열에서 계산 ...
ws.Range("A1:C100").Value = arr
End Sub
주의 : .ScreenUpdating=False 이후에도 .Select를 사용하면 뷰포트가 이동하여 사용자는 “화면이 바뀐다”고 인식한다. 셀·도형은 객체 직접 지정으로 조작해야 한다.

선택·스크롤 유발 코드 제거 패턴

' 나쁜 예 Rows("1:1").Select Selection.Insert
' 좋은 예
Rows("1:1").Insert

' 나쁜 예
Application.Goto Worksheets("보고서").Range("B2"), True
Worksheets("보고서").Range("B2").Value = "완료"

' 좋은 예
Worksheets("보고서").Range("B2").Value = "완료"

' 나쁜 예
Charts("매출").Activate
ActiveChart.Axes(xlValue).MaximumScale = 100

' 좋은 예
With Charts("매출")
.Axes(xlValue).MaximumScale = 100
End With

DoEvents는 언제 쓰고 언제 빼야 하는가

DoEvents는 UI 응답성을 높이지만 페인팅 메시지 처리로 순간적인 그리기를 허용한다. 장시간 루프에서는 주기적으로 쓰되, 대량 쓰기·서식 변경 등 “플러딩” 구간에서는 제거하거나 호출 간격을 늘려야 한다.

Dim i As Long For i = 1 To 100000 ' 핵심 연산 If (i Mod 2000) = 0 Then DoEvents ' 지나친 호출 금지 Next i 

PDF/이미지 출력 시 깜빡임 최소화

ExportAsFixedFormatCopyPicture는 내부 렌더 파이프라인을 강제한다. 다음 전략으로 영향도를 줄인다.

  1. 작업용 임시 워크북 또는 숨김 창에 시트를 복제하여 출력한다.
  2. 출력 범위에만 최소 서식 적용 후 출력한다.
  3. 출력 직전·직후 상태 복원 코드로 감싼다.
Sub ExportPDF_Offscreen() Dim wb As Workbook, tmp As Workbook Set wb = ThisWorkbook wb.Worksheets("보고서").Copy Set tmp = ActiveWorkbook ' 새로운 임시 통합 문서 With Application .ScreenUpdating = False .DisplayAlerts = False End With tmp.Worksheets(1).ExportAsFixedFormat Type:=xlTypePDF, Filename:="C:\out.pdf" tmp.Close SaveChanges:=False Application.DisplayAlerts = True Application.ScreenUpdating = True End Sub 

이벤트 루프 차단과 안전한 재진입 방지

이벤트 프로시저에서 다시 ScreenUpdating을 켜거나 선택 이동을 수행하면 깜빡임이 심해진다. 이벤트 내부에서는 최소 작업만 수행하고, 대량 처리는 별도 프로시저로 위임한다.

Private Sub Worksheet_Change(ByVal Target As Range) On Error GoTo ExitHere Application.EnableEvents = False ' 최소 검증만 수행 If Not Intersect(Target, Range("A:A")) Is Nothing Then ValidateInput Target End If ExitHere: Application.EnableEvents = True End Sub 

자동 계산과 화면 갱신의 상호작용

자동 계산이 켜진 상태에서 대량의 셀 값을 갱신하면 내부 배치 계산 시점에 일부 화면 반응이 나타날 수 있다. 다음 규칙을 권장한다.

  • 대량 쓰기 전후에 Calculation=Manual로 전환하고 종료 시 원복한다.
  • 가능하면 배열 쓰기(한 번에 Range.Value=Variant(2D))로 작업하여 페인트 포인트를 줄인다.
  • 계산 강제 호출은 Calculate 대신 필요한 범위에 국한한다.
With Application .Calculation = xlCalculationManual .ScreenUpdating = False End With
' ... 대량 작업 ...

Range("B2:B10000").Calculate ' 필요한 범위만

With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With

차트와 도형 편집 시 플리커 억제 트릭

차트 엔진은 축 자동 맞춤과 레이아웃 계산을 수행한다. 대량 변경 시에는 일시적으로 자동을 꺼두고, 변경 후 한 번만 다시 계산하도록 한다.

Sub QuietChartEdit() Dim ch As ChartObject Set ch = Worksheets("대시보드").ChartObjects("매출차트") With Application .ScreenUpdating = False End With With ch.Chart .Axes(xlCategory).HasTitle = False .Axes(xlValue).MaximumScaleIsAuto = False .Axes(xlValue).MaximumScale = 100 ' Series 대량 추가·삭제 수행 End With ' 필요 시 마지막에만 Refresh ch.Chart.Refresh Application.ScreenUpdating = True End Sub 

다중 창, 분할, 프리즈창이 억제되는 조건

다중 창(New Window)이 열려 있거나 프리즈창이 활성화된 워크시트에서 셀 삽입·삭제·필터 변경 등 뷰 변형을 수행하면 화면 갱신이 가시화될 수 있다. 다음 절차를 참고한다.

  1. 가능하면 작업 대상 시트의 FreezePanes를 임시 해제한다.
  2. 작업 후 원복한다.
Sub WithFreezePanes() Dim wasFrozen As Boolean With ActiveWindow wasFrozen = .FreezePanes If wasFrozen Then .FreezePanes = False End With
' ... 구조 변경 작업 ...

If wasFrozen Then ActiveWindow.FreezePanes = True
End Sub

사용자 폼과 외부 컨트롤

UserForm이 표시된 상태에서 워크시트 조작을 수행하면 폼 페인트와 워크시트 페인트가 상호 작용한다. 모달 폼을 쓰는 경우 폼 표시 전후로 핵심 작업을 분리하는 것이 안전하다. 모델리스 폼은 빈도 낮은 타이머 기반 리프레시만 허용한다.

' 폼 열기 전 데이터 준비를 끝낸다 PrepareDataFast UserForm1.Show vbModal 

성능 우선 순서: “덜 그리기”보다 “덜 바꾸기”

화면 그리기를 막는 것보다 변경 횟수를 줄이는 편이 더 효과적이다. 다음 설계를 권장한다.

  • 셀 단위 루프 금지 : For Each Cell 대신 Variant 2차원 배열 사용.
  • 서식 일괄 적용 : .Interior.Color, .Font.Bold 등은 영역 단위로 적용.
  • 필터·정렬 최소화 : 조건이 같은 작업은 묶어서 한 번만 실행.
  • 이벤트 기반 설계 : 값 변경→계산→서식 적용의 파이프라인을 명확히 분리.
' 나쁜 예: 행마다 색칠 Dim r As Range For Each r In Range("A2:A100000") If r.Value >= 0 Then r.Interior.Color = RGB(220,255,220) Next
' 좋은 예: 사전 계산 후 일괄 서식
With Range("A2:A100000")
.FormatConditions.Delete
.FormatConditions.Add Type:=xlCellValue, Operator:=xlGreaterEqual, Formula1:="=0"
.FormatConditions(1).Interior.Color = RGB(220,255,220)
End With

안전 복원 “Finally” 패턴 템플릿

모든 모듈의 공통 유틸리티로 두고 재사용하면 휴먼 에러를 크게 줄일 수 있다.

Public Type AppState ScreenUpdating As Boolean EnableEvents As Boolean DisplayAlerts As Boolean DisplayStatusBar As Boolean Calculation As XlCalculation End Type
Public Sub WithAppQuiet(ByVal work As String)
Dim st As AppState
On Error GoTo EH
Snapshot st
QuietOn
Application.StatusBar = work

' << 핵심 작업 호출 지점 >>

GoTo Finally
EH:
' 로깅 가능
Finally:
Restore st
End Sub

Private Sub Snapshot(ByRef st As AppState)
With Application
st.ScreenUpdating = .ScreenUpdating
st.EnableEvents = .EnableEvents
st.DisplayAlerts = .DisplayAlerts
st.DisplayStatusBar = .DisplayStatusBar
st.Calculation = .Calculation
End With
End Sub

Private Sub QuietOn()
With Application
.ScreenUpdating = False
.EnableEvents = False
.DisplayAlerts = False
.DisplayStatusBar = False
.Calculation = xlCalculationManual
.Cursor = xlWait
End With
End Sub

Private Sub Restore(ByRef st As AppState)
With Application
.Calculation = st.Calculation
.EnableEvents = st.EnableEvents
.DisplayAlerts = st.DisplayAlerts
.DisplayStatusBar = st.DisplayStatusBar
.ScreenUpdating = st.ScreenUpdating
.Cursor = xlDefault
.StatusBar = False
End With
End Sub

원인별 빠른 처방표

원인해결책코드 스니펫
선택 이동 객체 직접 참조 Range("A1").Value=1
DoEvents 과다 호출 간격 증가/핵심 구간 제거 If i Mod 2000 = 0 Then DoEvents
PDF 출력 임시 워크북에서 출력 Worksheets(1).ExportAsFixedFormat ...
이벤트 재진입 이벤트 보호 Application.EnableEvents=False
자동 계산 수동 계산 전환 Application.Calculation=xlCalculationManual
차트 플리커 자동맞춤 잠정 해제 후 일괄 반영 Axes(...).MaximumScaleIsAuto=False
프리즈창 작업 전 해제 후 원복 ActiveWindow.FreezePanes=False
오류 후 상태 불일치 Finally 복원 패턴 On Error GoTo EH ... Finally:

실무 트러블슈팅 절차(10단계)

  1. 코드 첫 줄에 전역 보호 래퍼를 도입한다.
  2. .Select/.Activate/Application.Goto 호출을 전수 조사하여 제거한다.
  3. 반복 루프의 DoEvents 호출 빈도를 낮춘다.
  4. 배열 읽기/쓰기로 전환하여 I/O 횟수를 줄인다.
  5. 자동 계산을 수동으로 전환하고 범위 계산으로 한정한다.
  6. 이벤트 핸들러 내부에서 대량 작업을 금지한다.
  7. 차트·도형 변경은 일괄로 모아 한 번에 반영한다.
  8. PDF·이미지 출력은 숨김(또는 임시) 통합 문서에서 수행한다.
  9. 프리즈창·분할·다중 창을 작업 전에 정리한다.
  10. 오류·종료 경로 모두에서 상태 복원이 수행되는지 테스트한다.

진단용 로깅과 최소 재현 예제 만들기

간헐적 깜빡임은 특정 호출 조합에서만 발생한다. 문제가 되는 라인을 찾기 위해 이분 탐색 방식으로 로그를 삽입한다.

Debug.Print "T1: Before data write", Timer ' ... 코드 블록 A ... Debug.Print "T2: After data write", Timer ' ... 코드 블록 B ... Debug.Print "T3: Before chart edit", Timer 

최소 재현 예제는 “하나의 프로시저, 하나의 시트, 데이터 10~100행” 수준으로 줄여서 공개 검토가 가능하도록 만든다.

안전한 종료와 사용자 경험

작업 완료 후 ScreenUpdating=True로 복원하는 것만으로 충분하지 않다. 상태 표시줄 메시지, 커서, 선택 영역 등을 합리적으로 정리해야 사용자는 깔끔한 경험을 한다.

Application.StatusBar = "완료: " & Format(Now, "yyyy-mm-dd hh:nn:ss") Application.Cursor = xlDefault ' 선택 복원 필요 시 ThisWorkbook.Worksheets("대시보드").Range("A1").Select ' 선택 복원도 최소화 권장 
주의 : 선택 복원은 꼭 필요한 경우에 한해 수행해야 한다. 대부분의 매크로는 사용자의 현재 뷰포트를 보존하는 편이 UX에 유리하다.

FAQ

ScreenUpdating=False인데도 스크롤이 움직이는 이유는 무엇인가?

코드에 .Select, .Activate, Application.Goto 혹은 ScrollRow/ScrollColumn을 직접 호출했을 가능성이 높다. 해당 구문을 제거하고 객체 직접 참조로 치환해야 한다.

DoEvents를 완전히 제거해야 하나?

아니다. 장시간 연산에서 무응답 방지를 위해 제한적으로 사용한다. 다만 대량 쓰기·서식 적용 구간처럼 페인트가 발생하기 쉬운 부분에서는 호출 빈도를 낮추거나 제거한다.

PDF 저장 시 깜빡임을 완전히 없앨 수 있는가?

내부 렌더 과정은 완전 차단이 어렵다. 임시 통합 문서에서 출력하거나, 출력 범위를 최소화하여 체감 깜빡임을 크게 줄일 수 있다.

이벤트 핸들러 안에서 ScreenUpdating을 꺼도 효과가 적은 이유는?

이벤트가 연쇄적으로 발생하면 다른 핸들러에서 선택 이동·계산·폼 갱신 등을 수행할 수 있다. 진입 시 EnableEvents=False로 보호하고 대량 작업은 이벤트 밖에서 실행한다.

차트 편집 시 가장 효과적인 최적화는 무엇인가?

축 자동맞춤을 일시 해제하고 시리즈 변경을 한 번에 적용한 뒤 마지막에 한 번만 .Refresh를 호출하는 것이다.