프로그래밍/C#

[C#] 예외 필터 (exception filter)

갓똥 2020. 4. 20. 18:38
728x90
반응형

1. 핵심 정리

using System;
using System.Net;

class Program {
    static void Main() {
        WebClient wc = new WebClient();
        try {
            stirng s = wc.DownloadString("http://wwww.nnaver.ccom");
        }
        catch(WebException e) {
            if(e.Status == WebExceptionStatus.NameResolutionFailure)
                Console.WriteLine("URL 입력 에러");
            else if(e.Status == WebExceptionStatus.Timeout)
                Console.WriteLine("시간 초과");
            else
                Console.WriteLine("다른 문제");
        }
        catch(Exception e) {
        }
        wc.Dispose();
    }
}

 

웹 클라이언트 객체를 만들어 특정 웹사이트의 문자를 꺼내오려했는데
오타가 나서 예외가 발생하는 상황이다.
앞서 보았던것은 입력값 오류, null, 스레드간 동시 호출의 예외가 있었는데
각 항목별로 조금 더 자세하게 받아올 수 있다.
Exception 클래스의 멤버로 Status가 있는데 이를 이용한 방법이다.
각 예외에 대한 Status도 존재한다.
정의로 이동해보면 enum타입으로 있다.

각 예외에 대해 다르게 작성할 수 있어서 위와 같이 URL오류, 시간 초과의 문제로 나누었다.
하지만 접속 실패, 기타 필요한 예외 처리를 하면 할수록 코드가 길어질텐데 
이럴 때 예외 필터란 것을 쓸 수 있다.

 

using System;
using System.Net;

class Program {
    static void Main() {
        WebClient wc = new WebClient();
        try {
            stirng s = wc.DownloadString("http://wwww.nnaver.ccom");
        }
        catch(WebException e) when(e.Status == WebExceptionStatus.NameResolutionFailuer) {
            Console.WriteLine("URL 입력 에러");
        }
        catch(WebException e) when(e.Status == WebExceptionStatus.Timeout) {
            Console.WriteLine("시간 초과");
        }
        
        catch(Exception e) {
        }
        wc.Dispose();
    }
}

 

필요한 예외를 골라 참일 경우 해당 catch문을 실행한다.
C# 6.0부터 지원하는 문법이다.

 


2. 활용

using System;

class Server {
    public void Connect() {
        throw new TimeoutException();
    }
}

class Program {
    static void Foo() {
        Server wc = new Server();
        
        try {
            wc.Connect();
        }
        catch(Exception e) when(Logging(e)) {
            Console.WriteLine("Foo catch");
        }
    }
    
    static bool Logging(Exception e) {
        Console.WriteLine($"LOG : {e.Message}");
        return false;
    }
    
    static void Main() {
        try {
            Foo();
        }
        catch (Exception e) {
            Console.WriteLine("처리");
        }
    }
}

 

위 코드에서 예외 필터의 활용 사례를 볼 수 있다.
상황 자체는 서버에 접속하려 하지만 무조건 예외를 뱉게 되어있다.
예외에 대한 처리가 Foo함수와 Main함수에 있는데
Foo에서 먼저 처리를 하고 Main에서 처리를 할 것이다.
근데 자세히 보면 Foo에서는 예외가 발생해도 catch문을 실행하지 않는다.
왜냐하면 예외 필터는 해당 예외가 참일 때 실행되는데 Logging함수는 항상 false를 뱉는다.
따라서 catch문을 실행하지 않는다.
그럼 왜 이렇게 썼을까?
답은 함수이름과 같이 로깅을 위해 쓴 것이다.
catch문을 실행하게 되면 약간의 성능저하가 발생되는데 로그만 찍어주고 catch문에 들어가지 않으므로
성능저하를 줄일 수 있다.

 ① 성능 저하를 줄이기 위해

 

using System;

class Server {
    public void Connect() {
        throw new TimeoutException();
    }
}

class Program {
    static void Foo() {
        int retryCount = 0;
        bool bSuccess = false;
        
        Server wc = new Server();
        
        while(bSuccess == false) {
            try {
                wc.Connect();
                bSuccess = true;
            }
            catch(TimeoutException e) {
                if(++retryCount < 4)
                    Console.WriteLine("다시 시도");
                else
                    throw;
            }
        }
    }
    
    static void Main() {
        try {
            Foo();
        }
        catch (Exception e) {
            Console.WriteLine("예외 처리");
        }
    }
}

 

다시 이전 코드와 비슷하지만 조금 다른 코드이다.
bSuccess로 접속 여부를 판단하여 접속이 안된다면 로그를 찍고 다시 접속을 시도한다.
3번의 실패가 일어나게되면 Main으로 예외를 던져 처리하게 되는 코드이다.
충분히 있을 수 있는 코드인데 이 코드도 문제는 catch를 총 4번 처리한다는 것이다.
앞서 말했듯이 catch는 약간의 성능 저하가 생기므로 되도록 횟수를 줄이는 것이 좋다.
이것도 예외 필터를 사용해 해결 가능하다.

 

using System;

class Server {
    public void Connect() {
        throw new TimeoutException();
    }
}

class Program {
    static void Foo() {
        int retryCount = 0;
        bool bSuccess = false;
        
        Server wc = new Server();
        
        while(bSuccess == false) {
            try {
                wc.Connect();
                bSuccess = true;
            }
            catch(TimeoutException e) when(++retryCount < 4) {
                Console.WriteLine("다시 시도");
            }
        }
    }
    
    static void Main() {
        try {
            Foo();
        }
        catch (Exception e) {
            Console.WriteLine("예외 처리");
        }
    }
}

 

예외 필터가 참일 경우만 실행되는 것을 이용하여 위와 같이 할 수 있다.
728x90
반응형