트랙
/
Elixir
Elixir
/
연습 문제
/
RPN 계산기
RPN 계산기

RPN 계산기

학습 연습 문제

소개

오류

오류는 언제든 생길 수 있어요. Elixir에서는 흔히 "그냥 크래시 나게 두자"라고 말하지만, 소프트웨어 계약을 지키기 위해 함수 호출을 복구해서 알려진 정상 상태로 되돌려야 할 때도 있어요. 어떤 언어에서는 오류를 제어 흐름의 수단으로 사용하기도 하지만, Elixir에서는 이런 방식을 권장하지 않아요. 오류를 발생시킬 수 있는 함수는 이름만 봐도 알아볼 수 있는 경우가 많아요. 오류를 발생시키는 함수는 이름 끝에 !가 붙어요. 이는 {:ok, value}나 :error를 반환하는 함수와 대비돼요. 라이브러리 예제를 살펴봐요:

Map.fetch(%{a: 1}, :b)
# => :error
Map.fetch!(%{a: 1}, :b)
# => raises KeyError

Try/Rescue

Elixir는 try .. rescue를 이용해 오류를 복구하는 구문을 제공해요.

try do
  raise RuntimeError, "error"
rescue
  e in RuntimeError -> :error
end

이 구문을 하나씩 살펴봐요:

  • 1번 줄에서는 try로 블록을 선언해요.
  • 2번 줄에는 오류가 발생할 수 있는 함수 호출이 들어가요. 여기서는 raise/2를 호출하고 있어요.
  • 4번 줄에서는 rescue 절에서 발생한 오류의 모듈 이름을 패턴 매칭해요.
    • ->의 왼쪽에서는:
      • e가 오류 구조체와 매칭돼요.
      • in은 키워드예요.
      • RuntimeError는 복구하려는 오류예요.
        • 모든 오류를 복구하고 싶다면 모듈 이름 대신 _를 쓰거나 in 키워드를 아예 생략할 수 있어요.
    • 오른쪽에서는:
      • 오류가 매칭될 때 실행할 명령이에요.

오류 구조체

이렇게 복구한 오류(때로는 "예외"라고도 불러요)는 구조체예요. Elixir에서 오류를 복구하는 일은 아주 드물게 일어나요. 보통은 복구한 오류를 로그로 남기거나 외부 모니터링 서비스로 보낸 뒤, 다시 발생시켜요. 그래서 특정 오류 구조체의 내부 구조에는 보통 관심을 두지 않아요.

예외 개념에서 오류 구조체에 대해 더 자세히 배우게 돼요. 직접 오류를 정의하는 방법도 함께 다뤄요.

지침

_Instruments of Texas_에서 일하던 중, Elixir로 작성된 실험적인 역폴란드 표기법 [RPN] 계산기 작업을 맡게 됐어요. 팀에는 몇몇 연산이 오류를 일으켜 프로세스를 중단시키는 문제가 있어요. 그래서 관용적인 Elixir 코드로 오류를 더 깔끔하게 처리할 수 있도록 연산 함수를 감싸는 함수를 작성하는 일을 맡았어요.

1. 팀에 경고하기

스택만 인자로 넘겨 연산 함수를 호출하도록 calculate!/2 함수를 구현해요. 연산 함수는 다른 곳에 정의되어 있지만, 성공적으로 끝나거나 오류를 발생시킬 수 있다는 건 알고 있어요.

stack = []
operation = fn _ -> :ok end
RPNCalculator.calculate!(stack, operation)
# => :ok

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate!(stack, operation)
# => ** (ArgumentError) An error occurred

함수 이름이 !로 끝나는 것은 그 함수가 오류를 발생시킬 수 있다는 걸 프로그래머에게 알려주는 신호예요

2. 오류 감싸기

좀 더 조사해 보니 많은 함수가 성공과 실패를 나타내기 위해 아톰과 튜플을 사용한다는 걸 알게 됐어요. 이 전략을 사용해 calculate/2를 구현해요.

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate(stack, operation)
# => :error

3. 메시지 전달하기

일부 오류에는 시스템이 올바르게 동작하는 데 동료들이 꼭 알아야 하는 중요한 정보가 담겨 있어요. 오류 메시지를 전달하도록 calculate_verbose/2를 구현해요. 오류는 :message 필드를 가진 구조체예요.

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:error, "An error occurred"}
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Elixir Exercism

RPN 계산기 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Elixir 트랙을 개념 58개연습 문제 168개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.