성공과 실패라는 두 갈래로 흐름을 나누는 Railway Oriented Programming을 소개합니다. 코틀린과 Rust의 Result 타입이 어떤 문제를 풀어주는지, 모나드 개념부터 출발해 자바스크립트에서 직접 구현하고 쓰는 방법까지 다룹니다. ||
안녕하세요. 비브로스 백엔드 개발자 고윤호입니다.
이번에는 이름은 낯설지만 막상 들어보면 친숙한 개념인 ROP Pattern을 소개하고 어떻게 사용하며 어떤 장단점이 있는지 함께 얘기해보고자 합니다.
ROP?
Railways Oriented Programming
쉽게 설명하자면 성공과 실패라는 두 갈래의 분기 흐름을 가지는 프로그래밍 방식입니다. 코틀린이나 Rust 같은 현대적인 언어를 접해보신 분들이라면 Result class에 대해서 알고 계신 분들이 있을 겁니다. Javascript에서 흔히 사용되는 Promise<Ok>는 성공 타입 밖에 알 수 없습니다.
class Result<Error, Ok> {
/* snip */
}
많은 프로그래밍 언어에서 에러 상태가 되면 진행 중인 프로세스를 중단하고 함수 혹은 메소드를 호출한 caller에게 에러를 전달합니다. 그런데 함수를 호출한 caller에서는 어떤 에러가 발생했는지 알 수 없기 때문에 항상 unknown을 받고 unknown이 어떤 에러 타입인지에 따라 분기하려면 반드시 개발자가 해당 에러에 대해서 발생할 수 있음을 알고 있어야 합니다.
function mustThrowError() {
throw new Error('This is a Error');
}
function main() {
try {
mustThrowError();
console.log('no reached');
} catch (error) {
error // is unknown
}
}
하지만 ROP Pattern에서는 에러를 던지지 않고 에러 상태를 가지고 다음 프로세스로 진행합니다. 다만, 에러 상태를 가지고 있을 경우 다음 프로세스가 실행되지 않고 넘어갑니다.
function mustThrowError() {
return Result.failure(new Error('This is a Error'));
}
function main() {
mustThrowError(); // Result<Error, Ok>;
console.log('reached'); // reached
}
기본 지식부터 알아보자
ROP는 함수형 프로그래밍 개념에서 차용한 Functor에 기반하고 있습니다. 그럼 함수형 프로그래밍을 모르면 쓸 수 없는 걸까요? 그렇지 않습니다. 왜냐하면 우리는 알게 모르게 함수형 프로그래밍을 이미 쓰고 있기 때문이죠.
우리가 흔히 쓰는 함수들을 통해 개념을 익혀봅시다.
Functor
"함자"라고도 부르는 Functor는 이미 우리 가까이서 사용하고 있습니다. Array, Promise, String 등 너무 많네요.
Functor는 값을 그 자체로 사용하지 않고 한꺼풀 감싼 것을 의미합니다. 근데 함자라고 부르는 것은 너무 와닿지 않으니 직관적이게 "상자"라고 부르겠습니다. 함수라는 단어에도 함(函)이라는 단어가 상자라는 뜻입니다.
왜 이런 형태가 필요할까요? 그건 값을 처리할지 말지에 대한 분기 여부에 따라 값의 처리 여부를 달리 할 수 있기 때문입니다. 이 다음 메소드들을 보면 좀 더 이해가 쉽습니다.
.map
map? 배열에서나 보던 메소드 아닌가요? 그럼 쉽게 설명하기 위해 배열을 기준으로 설명해보겠습니다.
const arr = [value];
arr.map(value => `value is ${value}`); // [`value is ${value}`]
.map은 Functor로 감싸여 있는 value를 꺼내 함수를 적용하고 다시 Functor로 감싸는 함수입니다. 이것을 통해서 값이 있을 경우 적절하게 처리할 수 있죠. Promise를 예시로 다시 확인해볼까요?
const promise: Promise<string> = Promi


