MFC 개발 중 가장 자주 쓰는 함수 중 하나인 Invalidate(). 하지만 단순히 화면을 새로 그릴 때 무분별하게 호출하고 있지는 않으신가요?
보내주신 자료를 바탕으로, 불필요한 갱신을 줄이고 성능을 극대화하는 'Dirty Flag 패턴'의 핵심을 정리했습니다.
1. Invalidate()의 진짜 의미: "화면이 훼손되었음을 알리는 신호"
Invalidate()는 "지금 당장 화면을 그려라!"라는 명령이 아닙니다. "이 영역의 데이터가 변경되어 유효하지 않으니, OS가 한가할 때 다시 그려달라(WM_PAINT)"고 요청하는 마킹(Marking) 행위입니다.
Invalidate()가 호출된 영역을 'Dirty(더러워진)' 상태로 간주하고, 나중에 메시지 큐가 여유로울 때 WM_PAINT 메시지를 발생시켜 갱신을 수행합니다.
2. 성능의 핵심: Dirty Flag 패턴
데이터는 변경되지 않았는데 Invalidate()를 남발하면, CPU는 불필요한 OnPaint 연산을 반복하게 됩니다. 이때 Dirty Flag(갱신 필요 여부 플래그)를 사용하면 성능을 획기적으로 개선할 수 있습니다.
- 데이터 변경 감지 →
bIsDirty = true로 설정. Invalidate()는 데이터가 변경되었을 때만 호출.OnPaint()처리 완료 후 →bIsDirty = false로 초기화.
예시 코드: 플래그를 활용한 효율적 갱신
void CMyView::UpdateData(int nNewValue) {
if (m_nData != nNewValue) { // 값이 실제로 변했을 때만
m_nData = nNewValue;
m_bIsDirty = true; // Dirty Flag 설정
Invalidate(); // 비로소 갱신 요청
}
}
void CMyView::OnPaint() {
CPaintDC dc(this);
if (m_bIsDirty) {
// 실제 그리기 로직 수행
m_bIsDirty = false; // 갱신 완료 후 플래그 해제
}
}
3. 주의사항: Invalidate vs UpdateWindow
개발자들이 자주 헷갈리는 부분입니다. 상황에 맞춰 정확히 사용해야 합니다.
Invalidate(): "언젠가 그려줘." (비동기, 큐에 메시지 적재)UpdateWindow(): "당장 그려!" (동기, WM_PAINT를 즉시 강제 발송)
반복적인 데이터 갱신이 일어날 때는 Invalidate()만 사용하여 OS가 갱신을 모아서 처리하게 두는 것이 훨씬 효율적입니다.
마무리
MFC UI 개발의 고수와 하수를 나누는 기준은 "얼마나 Invalidate()를 아껴 쓰는가"에 달려있습니다.
데이터 변경을 추적하는 Dirty Flag 패턴을 도입하여, 화면 깜빡임(Flickering) 없는 매끄러운 윈도우 프로그래밍을 경험해 보세요!