F.R.I.D.A.Y.

C# - 예외 처리 본문

DEV/.Net

C# - 예외 처리

F.R.I.D.A.Y. 2021. 5. 27. 21:25
반응형

 오류 발생에 대비하기 위한 예외 처리 구문


예외 처리

 임의의 수를 0으로 나누면 devided by zero라는 오류를 내면서 프로그램이 터져버린다. 가장 간단한 오류 타입이기 때문에 if 구문으로 처리하는 편이 낫지만 통제할 수 없는 경우, 혹은 외부 요인에 의한 오류라면 try-catch-finally 구문을 이용해 처리한다.

 

try-catch

 대개 분기문 등을 이용해 오류가 나지 않도록 프로그램 내에서 처리를 하지만, 경우에 따라선 분기문 처리를 하지 못하기도, 처리되지 않은 문제가 있을 수도 있다. 때문에 예외 처리 문법이 존재하는데, 대표적으로 try-catch 구문이 있다. 이 구문은 크게 세 구역으로 나뉜다.

try{
	// commands
}
catch(Exception ex){

}
finally{

}

 try-catch 구문에서 catch 구문은 try, finally 구문과 달리 그 사이에 얼마든지 집어 넣을 수 있다. if-else에서 else if에 대응한다고 보면 된다.[# 정확히 대응한다고 할 수는 없지만, 비유하자면 그렇다고. try-catch는 그 위치상 각각 if와 else에 대응한다고 보면 된다.]

 

try

 실제 명령을 수행하는 블럭으로, 오류가 예상되는 명령 집합을 try 블럭 안으로 넣어서 예외에 대응한다.

 

catch

 발생한 예외를 처리하는 블럭이다. catch 바로 뒤에 들어오는 괄호는 선택사항으로, 만일 catch 구문에 (Exception ex)등과 같이 구성하지 않으면 모든 오류를 한꺼번에 처리한다. 특정 예외를 처리하려면 괄호 안에 처리하고자 하는 예외 클래스로 변수를 만들면 된다.

 

 전체 캐치

try{}
catch{}  // 소괄호 작성하지 않음
finally{}

try{}
catch(Exception ex){} // 모든 오류의 Base Class인 Exception을 자료형으로 하는 객체 선언
finally{}

 일부 캐치

try{}
catch(ArgumentException ex){} // 인자와 관련한 오류
catch(ArithmeticException ex){} // 산술 및 캐스팅 변환 작업 관련 오류
finally{}

 

finally

 오류가 발생하던 하지않던 무조건 실행해야하는 명령을 두는 블럭이다. 대표적으로 파일입출력에서 파일 닫기를 해야할때, 모든 종류의 연결과 관련해 해당 연결을 끊을때 finally 블럭 안에서 처리한다.

 

 

throw

 해당 구문을 이용하면 즉각적인 예외 호출을 할 수 있다. 즉, 정상적인 코드라 하더라도 throw를 이용해 바로 catch 구문으로 이동할 수 있는 것.

 

사용 방식

 throw는 크게 세 가지 방법으로 구별한다고 한다.

catch에서 받은 Exception 객체를 다시 throw로 전달할 때

 코드로 설명하면

try{
	try{ throw new Exception();}   // 1
    catch(Exception ex){throw ex;} // 2
}catch(Exception ex){
	Console.WriteLine($"{ex}");    // 3
}finally{}

 이런식으로 다음 catch에 ex 객체를 넘기는 식인데, 이 방식은 예외 정보를 전달하지만 그간의 콜스택[# 호출 순서라고 보면 될 듯]을 리셋한다고. 즉, try 블럭의 특정 구문에서 오류가 났다면 호출 정보를 보전한 상태로 첫 번째 catch 구문으로 이동한다. 그러나 첫번째 catch 구문에서 throw를 날리게 되면 두 번째 catch 구문에서 잡을지언정 try의 어느 부분에서 오류가 났는지는 잊어버리는 식인듯.

 콜스택 정보를 날리기 때문에 이러한 방식의 사용은 지양해야 한다고.

 당장에 해당 코드를 기반으로 실행하면 1번에서 강제로 throw를 던지기 때문에 2번에서 throw를 받는다. 그러나 2번에서 다시 throw를 해서 3에서 그 정보를 출력해보면, 실제 오류가 난 1번의 위치가 아니라 2번 위치를 주기 때문에 그간의 콜스택을 다 날렸다고 보는 듯.

 throw 구문은 생각보다 고급 기술이기 때문에 이해한게 맞는지는 좀 더 알아봐야 할 것 같다.

 

throw 구문 다음 새 Exception 객체를 생성해 전달
try{
	try{ throw new Exception();}   // 1
    catch(Exception ex){throw new Exception(ex.Message, ex);} // 2
}catch(Exception ex){
	Console.WriteLine($"{ex}");    // 3
}finally{}

 위 코드처럼 new Exception과 같이 구성해서 현재 ex 객체를 새로 만들어지는 Exception 객체 안의 innerException으로 설정해 전달하면 throw ex;에서는 사라진 콜스택이 보전되어 출력된다.

어느 위치에서 오류가 발생했는지 보여준다.

 다만 주의할 것은 이런식의 방식에선 InnerException을 꼭 포함해야한다. 그렇지 않으면 콜스택이 날아감은 물론 이전 에러 정보 또한 날아가버린다. 때문에 꼭 InnerException에 전달해 디버깅 정보를 유지하자

 

throw 구문 다음 아무것도 작성하지 않고 전달
try{
	try{ throw new Exception();}   // 1
    catch(Exception ex){throw;}    // 2
}catch(Exception ex){
	Console.WriteLine($"{ex}");    // 3
}finally{}
throw;

 해당 명령은 현재 가지고 있는 예외 정보를 바로 상위 catch 구문에 넘길 때 사용한다. 즉, 2번의 ex 객체가 가지고 있는 정보와 3번 ex 객체에서 가지고 있는 정보가 동일하다는 소리다.

 

throw 사용 목적

 생각해보건데, try-catch 구문은 특정 블럭에 대한 예외를 처리한다. 이 때 호출로 인해 더 깊이 들어갔을 때 콜스택 정보를 try-catch에 전달함으로써 어떤 상황의 어느 부분에서 문제가 발생하고 있는지를 알리는데 사용한다고 생각해볼 수 있었다. 전적으로 효과적인 디버깅을 위함이란 소리.

 만일 throw가 이러한 목적이 아니라 다른 목적으로 만들어진 것이라면 다시 공부를 해야겠지...

 

# index

728x90
반응형

'DEV > .Net' 카테고리의 다른 글

C# - 구조체와 클래스  (0) 2021.05.28
C# - namespace(네임스페이스)  (0) 2021.05.28
C# - 반복문  (0) 2021.05.27
C# - 조건문  (0) 2021.05.27
C# - Nullable Type  (0) 2021.05.27
Comments