예외 처리: try...with 식

예외 처리: try...with 식

F#에서 예외를 처리하는 데 쓰는 try...with 식을 설명하는 주제예요. 코드가 실행되는 도중 오류가 나도 프로그램이 멈추지 않고, 그 오류를 잡아 적절히 대응하도록 해 주는 장치죠.

출처: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/exception-handling/the-try-with-expression

본문

구문

try
    expression1
with
| pattern1 -> expression2
| pattern2 -> expression3
...

설명

try...with 식은 F#에서 예외를 처리하는 데 사용해요. C#의 try...catch 문과 비슷한 개념이죠. 위 구문에서 expression1의 코드가 예외를 발생시킬 수 있어요. try...with 식은 값을 반환한다는 점이 핵심이에요.

예외가 발생하지 않으면 전체 식은 expression1의 값을 반환해요. 예외가 발생하면 각 패턴이 예외와 차례로 비교되고, 첫 번째로 일치하는 패턴에 해당하는 식(이걸 예외 처리기라고 해요)이 실행되어 전체 식은 그 예외 처리기에 있는 식의 값을 반환해요. 만약 어떤 패턴도 일치하지 않으면, 예외는 일치하는 처리기를 찾을 때까지 호출 스택을 타고 위로 전파되어요.

예외 처리기에 있는 각 식이 반환하는 값의 타입은 try 블록 안의 식이 반환하는 타입과 반드시 일치해야 해요.

오류가 발생했다는 사실은 종종 예외 처리기의 각 식에서 돌려줄 유효한 값이 없다는 뜻이기도 해요. 그래서 자주 쓰는 패턴은 식의 타입을 옵션 타입(option type)으로 두는 거예요. 다음 코드가 그 패턴을 보여 줘요.

let divide1 x y =
    try
        Some (x / y)
    with
    | :? System.DivideByZeroException -> printfn "Division by zero!"; None

let result1 = divide1 100 0

예외는 .NET 예외일 수도 있고, F# 예외일 수도 있어요. F# 예외는 exception 키워드로 직접 정의할 수 있어요.

예외 타입이나 기타 조건으로 필터링할 때 다양한 패턴을 쓸 수 있는데, 그 옵션을 다음 표로 정리했어요.

패턴 설명
:? exception-type 지정한 .NET 예외 타입과 일치시켜요.
:? exception-type as identifier 지정한 .NET 예외 타입과 일치시키되, 예외에 이름을 붙인 값으로 만들어 줘요.
exception-name(arguments) F# 예외 타입과 일치시키고 인자를 바인딩해요.
identifier 어떤 예외든 일치시키고 이름을 예외 객체에 바인딩해요. :? System.Exception as identifier와 동등해요.
identifier when condition 조건이 참이면 어떤 예외든 일치시켜요.

예제

다음 코드 예제들이 각각의 예외 처리기 패턴을 어떻게 쓰는지 보여 줘요.

// 이 예제는 .NET 예외에 이름을 붙이기 위해 as 키워드를 사용하는 모습을 보여 줘요.
let divide2 x y =
    try
        Some( x / y )
    with
    | :? System.DivideByZeroException as ex -> printfn "Exception! %s " (ex.Message); None

// 이 버전은 조건을 사용해 같은 예외를 여러 경로로 분기하는 모습을 보여 줘요.
let divide3 x y flag =
    try
        x / y
    with
    | ex when flag -> printfn "TRUE: %s" (ex.ToString()); 0
    | ex when not flag -> printfn "FALSE: %s" (ex.ToString()); 1

let result2 = divide3 100 0 true

// 이 버전은 F# 예외를 사용하는 모습을 보여 줘요.
exception Error1 of string
exception Error2 of string * int

let function1 x y =
    try
        if x = y then raise (Error1("x"))
        else raise (Error2("x", 10))
    with
    | Error1(str) -> printfn "Error1 %s" str
    | Error2(str, i) -> printfn "Error2 %s %d" str i

function1 10 10
function1 9 2

참고 try...with 구조는 try...finally 식과 별개의 식이에요. 그래서 코드에 with 블록과 finally 블록이 둘 다 필요하다면, 두 식을 중첩해서 써야 해요.

참고 try...with는 async 식, task 식, 그리고 기타 계산 식(computation expression) 안에서도 쓸 수 있어요. 이 경우 try...with 식의 맞춤형 버전이 사용돼요. 자세한 내용은 Async Expressions, Task Expressions, Computation Expressions를 참고하세요.

더 알아보기 (Learn more)

  • 예외 처리 (Exception Handling)
  • 예외 타입 (Exception Types)
  • 예외 처리: try...finally 식 (Exceptions: The try...finally Expression)