IDL/Programming

CATCH 명령을 사용한 에러 대응 방법

이상우_idl 2015. 12. 18. 10:30
반응형

에러(Error)라는 것은 프로그래밍이란 작업을 하다보면 반드시 만날 수 밖에 없다는 것은 다들 공감하실 것으로 생각합니다. 물론 에러라는 것을 무작정 부정적으로만 볼 일은 아닙니다. 프로그래머가 저지른 실수를 친절하게 알려줌으로써 문제점을 개선할 수 있는 여지를 제공해주기 때문입니다. 너무 많은 에러는 좌절을 주기도 하지만, 그렇다고 에러가 전혀 나오지 않는 경우에는 오히려 더 불안하기도 합니다. 한 마디로 애증의 관계라고 볼 수 있을 것입니다.


에러의 종류를 나눈다는 것이 좀 웃기는 일일 수도 있지만, 제가 나름대로 특히 오늘 다룰 주제와 연관성을 위해서 굳이 두 종류로 나눠보았습니다. 하나는 프로그래머의 실수에 의하여 발생하는 에러입니다. 이런 에러들은 발견해서 충분히 수정할 수 있는 그리고 반드시 수정해야만 하는 에러들이라고 볼 수 있습니다. 우리가 통상적으로 겪게 되는 에러들은 이러한 범주에 속합니다. 그런데 또 하나의 종류가 있습니다. 에러의 발생 요인을 미리 예측하기 힘든 경우 또는 예측 자체는 가능하다 하더라도 딱히 대처할 방안이 마땅치 않은 경우들입니다. 이런 에러들은 문제의 원인을 수정하는 것 자체가 프로그래머의 의지대로 되지 않는 경우들이 많습니다. 결국은 피해가는 것이 가장 최선의 길이 되는 그런 케이스입니다.


사실 이런 두번째 범주에 속하는 에러는 통상적인 프로그래밍 작업에서는 자주 마주하게 되지는 않습니다. 그래서 이러한 예를 하나 들어본다면, IDL에서 네트워크를 통하여 http 또는 ftp 기반으로 파일을 다운로드 또는 업로드하는 기능이 있습니다. 아주 유명한 기술은 아니지만 IDLnetURL이라는 클래스의 객체를 사용하여 이러한 작업이 가능한데요. 어떤 파일을 다운로드할 경우, 내가 원하는 이름의 파일이 내가 예상했던 위치에 존재한다면, 별다른 문제없이 다운로드가 됩니다. 그런데 간혹 그 파일이 존재하지 않는 경우가 있습니다. 또는 다운로드를 받았는데 뭔가 문제가 있어서, 예상했던 크기에 비하여 너무 작다든지 하는 여러가지 변수들이 생길 수 있습니다. 이런 경우 근본 원인은 받고자 하는 파일이 제 위치에 없거나 손상이 되어 있거나 또는 네트웍 상황이 불안정하다거나 하는 여러가지 종류들이 있을텐데, 이러한 원인들의 공통점은 프로그래머인 나의 입장에서 어떻게 손을 써볼 수는 없다는 것입니다. 그 웹사이트나 FTP사이트의 관리자가 아닌 한 전혀 방법이 없죠. 사실 관리자라고 해도 방법이 딱히 없을 수도 있습니다.


이렇게 충분히 예상은 되지만 그 원인을 프로그래머가 능동적으로 제어할 수 없는 에러가 발생할 것이 뻔히 보이는, 그럼에도 불구하고 하긴 해야 하는 작업을 프로그램으로 만들 경우가 바로 오늘의 주제입니다. 흔치 않은 상황이라 서론이 쓸데없이 길어진 것에 대하여 양해를 부탁드립니다. 여하튼 이런 경우에 바로 에러 핸들링(Error Handling), 즉 에러가 발생할 경우의 대처법이라는 개념이 등장합니다. 즉, 피할 수 없는 에러가 발생할 경우 플랜B에 해당되는 작업을 수행하도록 하는 흐름을 프로그램내에 심어놓는 방법에 관한 고민이 바로 오늘의 주제입니다. 원래 IDL에서 어떤 프로그램에서 에러가 발생하면, 콘솔창에 에러 메시지가 출력되고 그 프로그램의 구동은 즉시 중단됩니다. 물론 문법적인 명백한 오류일 경우에는 컴파일 자체가 안되는 경우도 있지만, 컴파일까지는 일단 되어 있는데 실행 단계에서 에러가 나는 경우가 오늘의 주제에 부합됩니다. 예를 들어 제가 다음과 같은 프로그램을 컴파일해서 실행했더니 바로 에러를 내면서 중단되었습니다.


PRO cause_intentional_error

a = 1

b = 2

PRINT, a+b

PRNT, a*b

END


에러의 원인은 다음과 같이 PRNT라는 존재하지 않는 명령을 사용하려고 했기 때문이라고 나옵니다. 혹시라도 PRNT라는 이름의 프로그램을 따로 갖고 있지 않는 한은 이와 같은 에러가 날 수 밖에 없습니다.


% Attempt to call undefined procedure: 'PRNT'.

% Execution halted at: CAUSE_INTENTIONAL_ERROR    6 /Users/sang-ui/Documents/Education/blog/cause_intentional_error.pro

%                      $MAIN$          


물론 엄밀히 말하면, 이러한 에러는 발견이 얼마든지 가능하고 수정도 즉시 가능한 아주 평이한 에러의 범주에 속합니다. 하지만 에러 대처법에 대한 설명을 위하여, 이 에러가 앞서 언급했던 두번째 범주에 속하는 에러라고 가정을 합시다. 그래서 이 에러는 발생 가능성이 분명 존재하지만 근본적인 원인 제거가 불가능하기 때문에, 일단 에러가 발생하면 일단 피하고 플랜B를 발동시키는 방법을 소개하고자 합니다. IDL의 도움말에서 Routines (by topic)의 Error Handling이라는 섹션을 보면 몇가지 항목들이 있는데요. 이 중에서 가장 위에 있는 CATCH라는 명령을 사용한 에러 대처법을 알아보기로 하겠습니다. 도움말에서 이 명령에 대한 내용을 보면 사실 예제 코드가 있긴 합니다. 지금 제가 드리고자 하는 설명도 이 내용과 유사하다고 보면 됩니다.


일단 이 CATCH 명령을 사용하여 에러를 피하는 방법부터 봅시다. 다음 프로그램은 이러한 목적으로 CATCH가 사용된 예제입니다.


PRO test_error_handling

CATCH, err

IF err NE 0 THEN BEGIN

  PRINT, 'Error Occurred!'

  PRINT, 'Error Index :', err

  CATCH, /CANCEL

  RETURN

ENDIF

a = 1

b = 2

PRINT, a+b

PRNT, a*b

CATCH, /CANCEL

PRINT, a/b

END


여기서 먼저 CATCH 명령의 인자로 사용된 err이라는 변수는 미리 값을 지정해놓는 것이 아니라, CATCH가 실행될 경우 이 변수를 통해서 어떤 값을 돌려주게 되는 역할입니다. CATCH 명령의 역할은 한 마디로 에러 감시 초소라고 보면 됩니다. 이 초소는 에러가 발생하지 않는 경우에는 별다른 일도 하지 않습니다. 그런데 에러가 발생할 경우에는 그 에러에 관한 가장 기본적인 정보가 이 초소로 전달됩니다. 그 정보가 바로 err 변수에 전달이 됩니다. 따라서 프로그래머는 이 err의 값을 확인해서 에러에 상응하는 정보인가를 확인하면 됩니다. 이 err의 값은 에러가 없는 조용한 상태에서는 0입니다. 하지만 에러가 일단 발생하면 0이 아닌 다른 값을 갖게 됩니다. 이 값은 결국 에러 인덱스(Error Index)라는 것인데, 에러의 종류별로 약속되어 있는 고유한 코드번호같은 것입니다. 여기서는 그냥 0이냐 아니냐에만 신경을 쓰기로 하겠습니다.


결국 프로그램안에서 CATCH를 사용한다는 것은 에러 감시 초소를 가동시켜서 수시로 에러 발생 여부를 감시하는 체제로 들어가겠다는 얘기입니다. 그리고 에러가 발생하기만 하면 바로 뭔가 조치를 취해야 할텐데, 그 구체적인 내용들을 바로 뒤에 이어지는 IF문 블록에 넣어둔 것입니다. 여기서 명시된 내용들을 보면, 에러가 발생했다는 문구를 하나 출력하라는 명령이 먼저 나온 후 CATCH라는 명령이 또 사용되었습니다. 그런데 앞서와는 달리 여기서는 CATCH 뒤에 /CANCEL이라는 키워드가 붙어 있습니다. 이 키워드의 의미는 말 그대로 감시 체제를 해제하라는 것입니다. 일단 에러가 발생했을 경우에 처리될 작업들이 명시되어 있으므로 이 부분에서는 이와 같이 감시 체제를 해제하는 것이 좋은데, 의무사항은 아니지만 이렇게 하는 것이 필요할 때가 있습니다. 어쨌든 감시체제 해제 후 RETURN을 하라고 되어 있는 것이 보입니다. 이것은 프로그램 레벨에서 다시 메인 레벨로 돌아가라는, 즉 간단히 얘기하면 프로그램의 작업을 더 이상 진행하지 않고 되돌아와서 종료하라는 의미입니다. 사실 이것은 프로그램이 에러 때문에 강제로 중단되는 상황(Stop)이 아니라, 에러 발생시 더 이상 진행하지 않고 후퇴하여(Return) 정상적으로 종료시키겠다는(End) 프로그래머의 의도에 의한 능동적인 조치라고 보면 됩니다. 여기까지가 IF문 블록내에 명시된 "플랜B" 작업의 내용입니다.


그리고 IF문 밖으로 나와서 주목해야 할 부분이 또 있습니다. 후반부에서 CATCH, /CANCEL이 또 사용되었다는 것인데요. 이 명령의 목적 자체는 앞서 IF문 안에 있던 놈과 마찬가지로 에러 감시 체제를 해제하라는 의미입니다. 근데 여기서 왜 또 사용이 되었을까요? 사실 같은 명령이 중복 사용된 것처럼 보이지만 엄밀히 따지면 중복이 아닙니다. IF문 안에 있는 것은 에러가 발생한 비상 상황에서의 작업 목록에 들어가 있는 것이고, IF문 밖에 있는 것은 에러가 없는 평상시에 해당되는 내용입니다. 평상시의 경우에는 에러 감시의 범위를 프로그램상의 어느 부분까지로 한정지을 것인가를 결정하는 역할을 합니다. 사실 IF문 밖의 이 명령이 없어도 됩니다. 다만 이러한 경우에는 감시의 대상을 프로그램 전체로 간주하게 됩니다. 그런데 위 프로그램에서와 같이 IF문 밖에도 이 명령이 따로 존재한다면, CATCH, err과 CATCH, /CANCEL 사이에 해당되는 부분만을 감시 범위로 제한하라는 의미가 됩니다. 감시 범위의 제한이라는 것이 프로그램이 짧을 경우에는 별로 신경쓰지 않아도 되는 이슈이지만, 프로그램이 길어질 경우에는 에러의 발생이 예상되는 구간을 따로 확인해서 정해놓고 이와 같은 방식으로 특정 구간에 대한 집중 감시를 하는 것이 가능하다는 점을 반드시 염두에 두는 것이 좋습니다. 위의 프로그램을 실행하면 다음과 같은 결과가 콘솔창에 출력됩니다. 강제적으로 중단된 상황이 아니라 프로그래머의 의지에 의하여 종료된 상황이라는 것을 쉽게 알 수 있습니다.


       3

Error Occurred!

Error Index :         -95


앞서 소개한 예제코드에서는 에러 발생시 필요한 조치를 취한 다음 더 이상 작업을 진행하지 않고 그냥 되돌아와서 종료되도록 했었는데요. 이렇게 되돌아오지 않고, 프로그램내 다른 부분으로 건너뛰게 하는 방법도 있습니다. 이 경우에는 GOTO 명령을 사용하면 됩니다. 다음은 GOTO를 사용하여 위의 프로그램을 약간 수정해본 것입니다.


PRO test_error_handling

CATCH, err

IF err NE 0 THEN BEGIN

  PRINT, 'Error Occurred!'

  PRINT, 'Error Index :', err

  CATCH, /CANCEL

  GOTO, skip

ENDIF

a = 1

b = 2

PRINT, a+b

PRNT, a*b

CATCH, /CANCEL

skip : aaa = 0

PRINT, a^2+b^2

END


이 프로그램을 실행하면 다음과 같은 결과가 출력됩니다. 즉 에러가 발생한 상황에서 IF문내의 작업들을 수행한 후 GOTO에 의하여 skip이라는 이름의 라벨로 건너뛰게 됩니다. 그리고 그 뒤에 명시된 연산을 수행한 후 프로그램이 정상적으로 종료되는 방식입니다.


       3

Error Occurred!

Error Index :         -95

       5


CATCH를 사용한 에러 대처법에 관해서는 이 정도면 충분히 설명이 되었으리라 생각됩니다. 사실 에러의 대처법과 관련해서는 이 외에도 on_ERROR, on_IOERROR 등의 명령들도 있습니다. 하지만 위에서 본 것과 같은 특정 구간에 대한 집중 감시 기능이 제가 알기로는 이러한 명령들에서는 자체적으로 지원이 되지는 않습니다. 따라서 저도 실전에서 주로 CATCH를 많이 사용하며, 비슷한 작업을 해야 하는 다른 IDL 프로그래머들에게도 주로 이 방법을 권하고 있습니다.


한가지 반드시 염두에 두어야 할 것은, 에러 대처와 관련된 내용은 프로그램을 만드는데 있어서 가장 나중에 해야 할 작업입니다. 프로그램의 구동에 있어서 발생 가능한 에러에 대한 충분한 수정과 파악이 이루어진 후에나 이와 같은 에러 대처 부분을 넣어야 합니다.그렇지 않고 무턱대로 에러 대처 기능을 넣었을 경우, 그 프로그램의 기본 기능에서 발생할 수 있는 에러를 놓치게 되는 경우가 발생할 수 있습니다. 그러면 혹시라도 발견되지 않은 치명적인 문제가 방치된 채로 그 프로그램이 실전에 투입될 수도 있습니다. 따라서 에러 대처를 위한 기능을 사용할 때에는 이러한 점을 반드시 주의하시기 바랍니다.

반응형