안녕하세요!
Kio
입니다 👻
지난 번 에러처리(Error Handling) 를 다룬 적이 있었는데요.
오늘은 그 이후에 도입된Result Type
에 대해 알아보고자 합니다.
Let's get started 🥰
그렇다면 왜
Result Type
이 나온 걸까요?
라떼는... 그런 거 없었는데.... 도대체 왜...?😱
- 열거형을 선언하고
enum McDonaldOrderError: Error {
case invalidSelection
case LackOfMoney
case outOfStock
}
- 필요한
function
을 만들고
struct Hamburger {
var name: String
var price: Int
var count: Int
}
let bigMac = Hamburger(name: "BigMac", price: 4600, count: 3)
let myMoney = 4000
func OrderMcDonaldMenu(orderedMenu: Hamburger) throws {
if orderedMenu.name != "BigMac" {
throw McDonaldOrderError.invalidSelection
}
if orderedMenu.price > myMoney {
throw McDonaldOrderError.LackOfMoney
}
if orderedMenu.count == 0 {
throw McDonaldOrderError.outOfStock
}
}
do
에서try
를 던지고, 오류 발생 시catch
에서 해줄 역할을 정의
do {
try OrderMcDonaldMenu(orderedMeun: bigMac)
} catch McDonaldOrderError.invalidSelection {
print("저희 매장에 주문한 메뉴가 없습니다. 메뉴이름을 다시 확인해주세요.")
} catch McDonaldOrderError.LackOfMoney {
print("메뉴를 주문하기에 고객님의 잔액이 부족합니다.")
} catch McDonaldOrderError.outOfStock {
print("현재 재고가 없어 주문이 불가능합니다.")
}
위 1-3 번의 과정을 거쳐야 에러처리가 가능했습니다.
이러한 에러처리의 단점은 무엇이 있을까요?
function
에서Error
를 던지기만 하고 어떤 과정으로 이어지는지 알길이 없습니다.do-catch
과정을 또 봐야합니다...throws
키워드는 에러를 던진다는 뜻이지만, 어떤 에러를 던지는지 특정하기 어렵습니다.→
그래서 이를 개선하고자 나온 게 Result Type
입니다.
그럼 이게 얼마나 더 괜찮은지 알아볼까요?!
Generic Enumeration
A value that represents either a success or a failure, including an associated value in each case.
Result
타입은Generic Enumeration
로 선언되어 있고,
경우에 따른 연관값을 포함하여, 성공과 실패를 나타내는 값입니다.
Apple
에서는Result
가 아래처럼 정의되어 있네요.
@frozen enum Result<Success, Failure> where Failure : Error
정의와 선언만으로는 이해가 가지 않습니다 😱
저에겐 항상 예시가 필요해요...😭
enum McDonaldOrderError: Error {
case invalidSelection
case LackOfMoney
case outOfStock
}
struct Hamburger {
var name: String
var price: Int
var count: Int
}
let bigMac = Hamburger(name: "BigMac", price: 4600, count: 3)
let myMoney = 4000
func OrderMcDonaldMenu(orderedMeun: Hamburger) throws {
if orderedMeun.name != "BigMac" {
throw McDonaldOrderError.invalidSelection
}
if orderedMeun.price > myMoney {
throw McDonaldOrderError.LackOfMoney
}
if orderedMeun.count == 0 {
throw McDonaldOrderError.outOfStock
}
}
do {
try OrderMcDonaldMenu(orderedMeun: bigMac)
} catch McDonaldOrderError.invalidSelection {
print("저희 매장에 주문한 메뉴가 없습니다. 메뉴이름을 다시 확인해주세요.")
} catch McDonaldOrderError.LackOfMoney {
print("메뉴를 주문하기에 고객님의 잔액이 부족합니다.")
} catch McDonaldOrderError.outOfStock {
print("현재 재고가 없어 주문이 불가능합니다.")
}
enum McDonaldOrderError: Error {
case invalidSelection
case LackOfMoney
case outOfStock
}
struct Hamburger {
var name: String
var price: Int
var count: Int
}
let bigMac = Hamburger(name: "BigMac", price: 4600, count: 3)
let myMoney = 4000
// 🛑🛑🛑🛑🛑 여기서부터 코드가 달라집니다
func orderMcDonaldMenu(orderedMenu: Hamburger) -> Result<Bool, McDonaldOrderError> {
if orderedMenu.name != "BigMac" {
return .failure(.invalidSelection)
}
if orderedMenu.price > myMoney {
return .failure(.LackOfMoney)
}
if orderedMenu.count == 0 {
return .failure(.outOfStock)
}
return .success(true)
}
let isOrderable = orderMcDonaldMenu(orderedMenu: bigMac)
switch isOrderable {
case .success(let data):
print(data)
case .failure(let error):
print(error)
}
차이가 보이시나요?!
기존에 orderMcDonaldMenu
에서 throws
키워드를 선언하고 Error를 던졌었죠.
func OrderMcDonaldMenu(orderedMenu: Hamburger) throws {
// code
}
Result Type
을 적용한 곳에서는 Result
라는 걸 return 하고 있는 걸 볼 수 있습니다.
func orderMcDonaldMenu(orderedMenu: Hamburger) -> Result<Bool, McDonaldOrderError> {
// code
}
Result Type을 사용하고자 한다면
성공했을 경우와, 실패했을 경우의 값을 넣어줘야합니다.Result<Bool, McDonaldOrderError>
이렇게 넣어줬다면 성공하면 Bool타입인 true or false를 받고
실패하게 된다면 제가 미리 선언한 Error 열거형으로 반환합니다.
let isOrderable = orderMcDonaldMenu(orderedMenu: bigMac)
switch isOrderable {
case .success(let data):
print(data)
case .failure(let error):
print(error)
}
그리고 Result Type은
Result<Success, Failure>
으로 이루어졌기 때문에
switch문을 통해 success, failure 의 경우에 따라 처리를 해주면 됩니다!
(이 부분이 이해가 안 간다면 위에 선언부분을 다시 한번 확인해주세요)
switch문은 성공과 실패의 경우를 모두 다룬 건데요
성공했을 때만 다루는 방법도 있답니다!
if let result = try? isOrderable.get() {
print(result)
}
위처럼 작성한다면 성공했을 때만 실행이 가능합니다.
....? 🤔 😱 👀
무슨 말인지는 지금 다시 설명할게요!
Returns the success value as a throwing expression.
성공했을 경우만 throw 하는 표현식으로 리턴합니다.
Apple
에서는 Result Methodget()
이 아래처럼 정의되어 있네요.
func get() throws -> Success
if let result = try? isOrderable.get() {
print(result)
}
다시 이 코드로 돌아왔네요!
switch문에서는 성공과 실패일 경우를 모두 다뤘었는데
get()
은 성공일 경우만 다루는 거에요.
실패의 경우는 저 구문을 타지 않으니 버리는거죠!
어느 쪽이 더 좋다라기보다는Result Type
에서
처리하는 방법이 다양하다 라고 알아두시면 좋겠네요👻
이렇게
Result Type
에 대해 알아보았는데요!
기존Error Handling
과 어떤 점이 다른 지 기억하면 좋을 것 같네요 👻
throwing
형식의 함수는 어떤Error
를 던지는 지 알기 어렵습니다.do-catch
문만 본다면 더 어떤 상황에 이런Error
를 던지는지도 알기 어렵죠 😱
반면Result
는Error
형식이 선언되고, 결과를 성공과 실패로 나누어 처리한다는 점에서 가독성이 더 좋지 않을까 생각이 되네요 🥳
또 하나의 적용할 방법을 알게 되어 기분이 좋습니다 🥰
Result - developer.apple
Result get() - developer.apple
잘못된 정보가 있으면 언제든 코멘트 부탁드립니다 👻
좋은 글 잘 봤습니다 Kio :)