2011년 6월 2일 목요일

Exceptions, Catch, and Throw

Exceptions, Catch, and Throw

The Exception Class

예외에 대한 정보를 가진 것은 Exception 클래스 혹은 그 자식 클래스의 객체이다. 예외를 발생시키려면 내장된 Exception 클래스를 쓸 수도 있고, 새로 클래스를 만들 수도 있다. 직접 만든다면, StandardError를 상속하는 자식 클래스로 만드는 것이 좋다. 모든 예외는 그 예외와 연관된 메시지, 그리고 스택 역추적 정보를 가지고 있다.

Handing Exceptions

예외를 처리하기 위해서는 우선 예외가 발생할 만한 코드를 begin/end 블록으로 감싼뒤, rescue 구문을 하나 이상 사용해서 루비에게 우리가 처리할 에러들을 알린다.

op_file = File.open(opfile_name, "w")
begin
   while data = socket.read(512)
   op_file.writer(data)
   end

rescue SystemCallError
   $stderr.print "IO failed: "+$!
   op_file.close
   File.delete(opfile_name)
   raise
end

SystemCallError 예외(자동으로 SystemCallError 를 상속 한 자식들도 포함) rescue 줄에 선언한다.
예외가 발생하면, 루비는 이후의 예외 처리 과정과 별도로, 관련된 Exception 객체 참조를 전역 변수 $!에 담는다. 파일을 닫고, 지운 뒤, raise를 아무 매개변수 없이 호출해서 $!에 담긴 예외를 다시 발생시킨다. 이것은 예외를 거르거나, 다룰 수 없는 예외를 더 높은 계층으로넘기는 코드를 작성하게 해주는 유용한 기법이다.
begin 블록 안에 여러 개의 rescue 절을 적어줘도 되고, 각 rescue 절에는 처리하려는 예외를 여러개 쓸 수도 있다. 각 rescue 절의 끝에는 예외 객체를 받을 지역 변수 이름을 쓸 수 있다.

begin
   eval string
rescue SyntaxError, NameError => boom
   print "String doesn't compile: "+boom
rescue StandardError => bang
   print "Error running script: "+bang
end

예외가 발생했을 경우 rescue에서 해당 예외를 찾을 수 없거나, 예외가 begin/end 블록 바깥에서 발생한 경우에는 스택을 거슬러 올라가서 호출자에서 예외 처리 구문을 찾는다. 호출자에도 없으면 호출자의 호출자를 찾는 식으로 계속 이어나간다.

System Error

시스템 에러는 운영체제가 에러 코드를 반환했을 때 일어난다. 루비는 이 에러들을 잡아서 특정 예외 객체로 감싼다. 예외 객체들 각각은 SystemCallError의 하우 클래스이며, Errno 모듈에서 정의하고 있다.

Tidying Up

예 외가 발생했건 하지 않았건, 코드 블록의 끝부분에 특정 작업을 반드시 실행해야 할 경우가 있다. 루비에서는 ensure 절이 이 일을 한다. ensure는 rescue 절 뒤에 오는데, 여기에 블록이 끝날때 반드시 실행되어야 하는 코드를 적어준다. 블록이 정상적으로 종료되건, 예외가 발생하고 rescue 되건, 처리되지 않은 예외에 의해 종료되건 간에 상관없이 ensure 블록은 항상 실행될 것이다.

f = File.open("testfile")
begin
 # .. process
rescue
 # .. handle error
ensure
 f.close unless f.nil?
end

else 절도 이와 비슷한 일을 한다. else가 있을 경우 else는 rescue 뒤에, 그리고 ensure 절 앞에 온다.
else 절의 코드는 실제 실행 코드에서 아무런 예외도 발생하지 않았을 때만 실행된다.

f = File.open("testfile")
begin
 # .. process
rescue
 # .. handle error
else
 puts "Congratulations-- no errors!"
ensure
 f.close unless f.nil?
end

Play It Again

때때로 예외의 원인을 고칠 수 있는 경우도 있다. rescue 절에 retry 구문을 사용하여 전체 begin/end 블록을 다시 실행할 수 있다. 하지만 무한 루프에 빠질 가능성이 다분하므로 주의해야 한다.

@esmtp = true

begin
 # First try an extended login. If it fails because the
 # server doesn't support it, fall back to a normal login

 if @esmtp then
@command.ehlo(helodom)
 else
@command.helo(helodom)
 end

rescue ProtocolError
 if @esmtp then
@esmtp = false
retry
 else
raise
 end
end

Raising Exceptions

raise
단순히 현재의 예외를 다시 발생시킨다. 현재 예외가 없을 경우에는 RuntimeError를 발생시킨다.

raise "bad mp3 encoding"
RuntimeError 예외를 새로 만드는데, 주어진 문자열을 예외 메시지로 설정한다. 이 예외는 호출 스택을 따라 올라간다.

raise InterfaceException, "Keyboard failure", caller
첫 번째 매개변수로 예외 클래스를 만들고, 두 번째 매개변수를 관련된 메시지로 설정한다. 세 번재 매개변수를 스택 추적으로 정한다.

raise

raise "Missing name" if name.nil?
if i >= myNames.size
 raise IndexError, "#{i} >= size (#{myNames.size})"
end

raise ArgumentError, "Name too big", caller

Adding Information to Exception

새 로운 예외를 정의함으로써 에러가 난 시점에서 알 수 있는 정보 중 전달할 필요가 있는 것을 추가로 담을 수 있다. 만약 에러가 발생했는데 상황이 모두 정상이면 예외에 플래그를 설정하여 처리자에게 재시도해 볼 가치가 있음을 알릴 수 있다.

class RetryException < RuntimeError
 attr :okToRetry
 def initialize(okToRetry)
@okToRetry = okToRetry
 end
end

def readData(socket)
 data = socket.read(512)
 if data.nil?
raise RetryException.new(true), "transient read error"
 end
 # .. normal processing
end

begin
 stuff = readData(socket)
 # .. process stuff
rescue RetryException => detail
 retry if detail.okToRetry
 raise
end

Catch and Throw

raise와 rescue 메거니즘은 무언가 잘못되었을 때 실행을 엄추는 데는 효과적이다. 하지만, 일반적인 실행 중에도 여러 겹으로 둘러싸인 코드 밖으로 탈출할 수 있다면 유용할 것이다.

catch (:done)  do
 while gets
throw :done unless fields = split(/\t/)
songList.add(Song.new(*fields))
 end
 songList.play
end

catch 는 이름이 붙여진 블록을 정의한다. 이 블록은 throw를 만날 때까지 실행된다. throw를 만나게 되면 루비는 해당 심벌을 가진 catch를 찾아 코드를 거슬러 올라간다. 그리고 찾게 되면, 루비는 스택을 해당 위치로 되돌린 뒤 블록을 종료한다. 위 예에서 문자열이 정상적인 형식을 가지지 않았을 경우 해당 while을 종료하는 것에 그치지 않고, 노래를 연주하는 것조차 생략한다. throw에 선택적으로 두 번째 형식인수를 주어 실행한 경우, 그 값이 catch 블록의 결과값으로 반환된다.

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...