본문 바로가기

C#

[예외처리]UnhandledException와 UnhandledExceptionFilter의 차이

목차 

1. UnhandledException와 UnhandledExceptionFilter란 

1.1 정의

1.2 동작흐름 

2. 각 예외처리 핸들러 상세 정의

2.1.UnhnadledExceptionFilter

2.2 UnhandledException

3. 주요 차이점 정리

4. 해당 핸들러의 필요성 

4.1 사용자 경험의 차이 

4.2 예외 핸들러의 올바른 사용법 

5. 두 핸들러의 역할 분담 

5.1 UnhandledExceptionFilter 

5.2 UnhandledExceptionFilter 

6. 핸들러 구현 시 체크리스트

 

 

1. UnhandledException와 UnhandledExceptionFilter란 

 

1.1 정의

WPF애플리케이션에서 예상치 못한 예외로 프로그램이 종료되는것을 방지하기 위해 제공하는 이벤트 핸들러. 

 

1.2 동작흐름 

 

예외발생 → UnhandledExceptionFilter → UnhandledException → 애플리케이션 종료

 

 

2. 각 예외처리 핸들러 상세 정의

2.1.UnhnadledExceptionFilter

  • 예외처리의 첫번 째 관문
  • 필터 기능을 수행 
  • 특정 예외를 처리하거나 무시할지 결정할 수 있음 (e.RequestCatch의 값에 따라)
  • 디버깅 제어 가능
  • 다음과 같은 코드로 작성하면 디버그 빌드에서만 컴파일
#if DEBUG
    // 이 코드는 Debug 빌드에서만 컴파일됨
    // Release 빌드에서는 완전히 제거됨
#endif

 

  •  예외를 잡지 않는 경우 
    • 복구 불가능한 예외: OutOfMemoryException, StackOverflowException 등
    • 데이터 무결성 위험: 예외를 무시하고 계속 실행하면 데이터가 손상될 수 있는 경우
    • 보안상 이유: 예외 정보가 민감한 정보를 포함하는 경우

 

 

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        
        this.Dispatcher.UnhandledExceptionFilter += 
            new DispatcherUnhandledExceptionFilterEventHandler(Dispatcher_UnhandledExceptionFilter);
    }
    
    private void Dispatcher_UnhandledExceptionFilter(object sender, 
        DispatcherUnhandledExceptionFilterEventArgs e)
    {
        // 특정 예외 타입은 무시하고 싶을 때
        if (e.Exception is InvalidOperationException)
        {
            Console.WriteLine($"InvalidOperationException 무시: {e.Exception.Message}");
            e.RequestCatch = false; // 이 예외는 잡지 않겠다
            return;
        }
        
        // 개발 모드에서만 예외를 처리하지 않고 디버거로 넘기고 싶을 때
        #if DEBUG
        if (System.Diagnostics.Debugger.IsAttached)
        {
            e.RequestCatch = false; // 디버거가 처리하도록 함
            return;
        }
        #endif
        
        // 나머지 예외는 처리하겠다
        e.RequestCatch = true;
    }
}

 

 

2.2 UnhandledException

  • UnhandledExceptionFilter를 통과한 예외를 처리하는 핸들러
  • 주로 정리작업과 로깅을 함
  • e.Handled = true로 종료방지 설정

 

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        
        this.Dispatcher.UnhandledException += 
            new DispatcherUnhandledExceptionEventHandler(Dispatcher_UnhandledException);
    }
    
    private void Dispatcher_UnhandledException(object sender, 
        DispatcherUnhandledExceptionEventArgs e)
    {
        // 예외 정보 로깅
        LogException(e.Exception);
        
        // 사용자에게 친숙한 에러 메시지 표시
        MessageBox.Show(
            "예상치 못한 오류가 발생했습니다. 애플리케이션을 다시 시작해주세요.",
            "오류 발생",
            MessageBoxButton.OK,
            MessageBoxImage.Error);
        
        // 크리티컬하지 않은 예외라면 계속 실행 시도
        if (e.Exception is not OutOfMemoryException && 
            e.Exception is not StackOverflowException)
        {
            e.Handled = true; // 종료 방지 시도
        }
        
        // 그렇지 않으면 e.Handled = false (기본값)로 두어 애플리케이션 종료
    }
    
    private void LogException(Exception ex)
    {
        var logEntry = $"[{DateTime.Now}] {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}\n";
        File.AppendAllText("error.log", logEntry);
    }
}

 

 

 

3. 주요 차이점 정리

 

구분 UnhandledExceptionFilter UnhandledException
실행 시점 예외 발생 직후, 가장 먼저 Filter 이후, 두 번째
주요 목적 예외 선별 및 필터링 예외 처리 및 정리 작업
제어 능력 완전한 무시 가능 종료 방지 시도만 가능
사용 사례 특정 예외 무시, 디버깅 제어 로깅, 사용자 알림, 데이터 저장
핵심 프로퍼티 RequestCatch (bool) Handled (bool)

 

 

 

4. 해당 핸들러의 필요성 

 

4.1 사용자 경험의 차이 

4.1.1 핸들러가 없는 경우 

[사용자가 버튼 클릭] → [예외 발생] → [프로그램 강제 종료] → [작업 내용 모두 손실]
사용자: "뭐지? 갑자기 왜 꺼져?"

 

사용자의 잘못된 행동으로 인해 예외가 발생했을 경우

아무런 안내도 없이 프로그램이 종료된다면

작업 내용의 손실 및 재발 방지를 위한 후속조치가 존재하지 않게 된다.

즉, 재발이 가능하며 사용자의 경험이 떨어지게 된다.

 

4.1.2 핸들러가 있는 경우 

[사용자가 버튼 클릭] → [예외 발생] → [핸들러가 처리] → [사용자에게 안내 메시지] → [프로그램 계속 실행]
사용자: "문제가 있었지만 프로그램이 계속 돌아가네. 친절하게 알려주기도 하고."

 

사용자의 잘못된 행동을 핸들러가 처리하여 프로그램의 강제종료를 방지하고

더불어 안내메시지를 제공하여 줌으로써 재발 방지 조치를 가능하게 한다.

 

4.2 예외 핸들러의 올바른 사용법 

 

4.2.1 잘못된 접근법 

 

private void Dispatcher_UnhandledException(object sender, 
    DispatcherUnhandledExceptionEventArgs e)
{
    // "일단 로그만 남기고 계속 돌리자"
    File.AppendAllText("error.log", e.Exception.ToString());
    e.Handled = true; // 문제 해결 없이 무조건 처리됨으로 표시
}

 

 

4.2.2 올바른 접근법

 

private void Dispatcher_UnhandledException(object sender, 
    DispatcherUnhandledExceptionEventArgs e)
{
    switch (e.Exception)
    {
        case FileNotFoundException fileEx:
            // 문제: 필요한 파일이 없음
            // 해결: 기본 파일 생성
            CreateDefaultFile(fileEx.FileName);
            ShowMessage("누락된 파일을 복원했습니다.");
            e.Handled = true; // 실제로 해결했으므로 처리됨
            break;
            
        case UnauthorizedAccessException:
            // 문제: 권한 부족
            // 해결: 임시 폴더로 우회
            SwitchToTempFolder();
            ShowMessage("임시 위치에 저장합니다.");
            e.Handled = true; // 우회 방법을 제시했으므로 처리됨
            break;
            
        case OutOfMemoryException:
            // 문제: 메모리 부족
            // 해결: 불가능 → 안전한 종료
            SaveDataSafely();
            ShowMessage("메모리 부족으로 프로그램을 종료합니다.");
            e.Handled = false; // 해결할 수 없으므로 처리 안됨
            break;
    }
}

 

5. 두 핸들러의 역할 분담 

 

5.1 UnhandledExceptionFilter 

- 선별의 역할 

- 개발자가 코드 내에서 처리할 수 있는 종류인지 판단 

- 처리할 수 없다면 시스템에 맡기고 처리할 수 있다면 예외를 넘김 처리하여 해결하게 함

 

private void Dispatcher_UnhandledExceptionFilter(object sender, 
    DispatcherUnhandledExceptionFilterEventArgs e)
{
    // 처리 불가능한 크리티컬 예외는 시스템에 맡김
    if (e.Exception is OutOfMemoryException || 
        e.Exception is StackOverflowException)
    {
        e.RequestCatch = false; // "이건 우리가 해결할 수 없으니 시스템이 처리해"
        return;
    }
    
    // 처리 가능한 예외만 넘김
    e.RequestCatch = true; // "이건 우리가 해결할 수 있어"
}

 

 

5.2 UnhandledExceptionFilter 

- Filter를 통과한 예외들을 실제로 해결하는 역할 

- 처리한 이후 e.Handled = true로 설정함으로써 문제 해결했다는 표

 

private void Dispatcher_UnhandledException(object sender, 
    DispatcherUnhandledExceptionEventArgs e)
{
    bool resolved = TryResolveException(e.Exception);
    
    if (resolved)
    {
        LogResolution(e.Exception);
        e.Handled = true; // "문제를 해결했습니다"
    }
    else
    {
        LogFailure(e.Exception);
        e.Handled = false; // "해결하려 했지만 실패했습니다"
    }
}

 

 

6. 핸들러 구현 시 체크리스트

예외 핸들러를 구현할 때 다음 질문들을 스스로 해보기 

  1. 이 예외를 실제로 해결할 수 있는가?
  2. 해결 후에도 사용자가 정상적으로 작업을 계속할 수 있는가?
  3. 근본 원인을 제거했는가, 아니면 단순히 증상만 숨겼는가?
  4. 같은 예외가 반복 발생할 가능성은 없는가?
  5. 크리티컬한 시스템 예외를 무시하고 있지는 않는가?