Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

루비 예외(Exception) 완벽 가이드: raise부터 rescue까지

루비에서 예외(Exception)는 애플리케이션에서 발생하는 비정상적인 상황에 대한 정보를 담고 있는 특별한 데이터 구조입니다. 프로그램이 특정 상황을 처리할 방법을 알지 못해 더 이상 실행을 지속할 수 없을 때 보내는 신호가 바로 예외입니다. 루비에서는 이러한 신호를 보내는 행위를 예외 발생(raise)이라고 부릅니다.

raise 'this is an exception'
 
puts "This message won't be printed."

예외를 실제로 확인해보는 가장 빠른 방법은 직접 하나를 발생시켜 보는 것입니다. raise 메서드에 메시지를 전달하면 루비는 전달받은 텍스트를 설명으로 하는 RuntimeError 객체를 생성합니다. 동시에 코드의 어느 지점에서 예외가 발생했는지 가리키는 스택 트레이스(stack trace)도 함께 추가됩니다.

$ ruby raise.rb
raise.rb:1:in `<main>': this is an exception (RuntimeError)

위 예제 스크립트를 실행해보면 예상대로, 예외가 발생한 이후에는 메시지가 출력되지 않는다는 사실을 확인할 수 있습니다. 처리되지 않은(unhandled) 예외는 프로그램을 강제로 종료시켜 실행을 중단하기 때문입니다. 이는 작업 하나를 완료하지 못한 상태로 애플리케이션이 계속 실행되는 상황을 방지하기 위한 장치입니다.

다양한 예외 타입

루비는 내부적으로 문제가 발생하면 자동으로 예외를 일으킵니다. 예를 들어 숫자를 0으로 나누려고 하면 예외가 발생합니다.

irb(main):001:0> 42/0
ZeroDivisionError: divided by 0
        from (irb):1:in `/'
        from (irb):1
        from /Users/jeff/.asdf/installs/ruby/2.3.0/bin/irb:11:in `<main>'

이 경우 예외의 타입은 앞서 살펴본 RuntimeError가 아닌 ZeroDivisionError입니다. 루비의 예외 계층 구조를 이루는 예외 타입은 수십 가지에 달하며, 각각 서로 다른 상황에서 발생합니다.

RuntimeError는 비교적 포괄적인(generic) 예외 클래스지만, 그보다 구체적인 예외들도 존재합니다. 예를 들어 NoMethodError는 존재하지 않는 메서드를 호출하려 할 때 발생하며, ArgumentError는 이미 존재하는 메서드에 잘못된 인자가 전달되었음을 알려줍니다.

예외 처리(rescue)하기

대부분의 애플리케이션은 예외로부터 복구하는 능력이 필요합니다. 웹 애플리케이션에서 사용자가 폼에 잘못된 데이터를 입력하면 오류가 발생할 수 있는데, 이럴 때 웹 서버 전체가 다운되는 대신 사용자에게 오류 메시지를 화면에 표시해 주어야 합니다.

이런 경우 예외가 발생하더라도, 호출 스택의 최상단에 도달하기 전에 웹 서버가 해당 예외를 처리(rescue)하여 애플리케이션이 비정상 종료되는 것을 막아줍니다.

begin
  raise 'this is an exception'
rescue RuntimeError => exception
  puts "Exception rescued: #{exception.inspect}"
  # => "Exception rescued: #<RuntimeError: this is an exception>"
end
 
puts "This message *will* be printed."

앞선 예제를 begin/rescue 블록으로 감싸면 예외가 그대로 전파되지 않으므로 애플리케이션이 종료되지 않습니다. 대신 rescue 블록이 실행되어 예외가 성공적으로 처리되었음을 알리는 메시지를 출력합니다. 또한 앱이 비정상 종료되지 않았기 때문에 마지막 메시지 역시 정상적으로 출력됩니다.

이 예제에서는 RuntimeError만 명시적으로 처리하고 있으므로, 그 외의 다른 오류는 잡히지 않습니다. 어떤 오류를 처리할지 구체적으로 지정하는 것이 좋은데, 이렇게 하면 발생한 오류의 종류에 따라 서로 다른 대응을 할 수 있기 때문입니다. 아울러 너무 많은 예외를 한꺼번에 처리하지 않도록 주의하는 것도 중요합니다.

예외 처리에 대해 더 깊이 알고 싶거나, 예외를 올바르게 처리하는 요령이 궁금하다면 Ruby Magic의 예외 입문 시리즈를 확인해 보시기 바랍니다.

루비에서 예외를 발생시키거나 처리하는 것과 관련해 궁금한 점이 있다면 @AppSignal로 언제든지 문의해 주세요. 물론 이 글이 유용했는지, 혹은 더 알아보고 싶은 주제가 무엇인지 알려주셔도 좋습니다.