Either
1. 개요
1. 개요
Either는 함수형 프로그래밍과 타입 이론에서 중요한 개념으로, 두 가지 가능한 값 중 하나만을 보유할 수 있는 합 타입이다. 이는 주로 성공적인 결과 또는 실패와 같은 대체 가능한 결과를 표현하는 데 사용된다. 예를 들어, 어떤 연산의 결과가 정상적인 값이거나 오류 메시지일 수 있을 때, 이 두 가지 가능성을 하나의 타입으로 통합하여 다룰 수 있게 해준다.
대표적인 구현으로는 하스켈의 Either 타입과 스칼라의 Either 타입이 있으며, 코틀린에서는 유사한 목적으로 Result 타입이나 서드파티 라이브러리의 Either 타입이 활용된다. 이러한 구현들은 오류 처리를 위한 기존의 예외 처리 메커니즘에 대한 대체 수단을 제공한다. 이를 통해 프로그래머는 모든 가능한 결과를 타입 시스템을 통해 명시적으로 처리하도록 강제받아 더욱 안정적인 코드를 작성할 수 있다.
2. 수학적 정의
2. 수학적 정의
수학에서, 합 타입은 여러 가지 가능한 타입 중 하나의 값을 가질 수 있는 타입을 의미한다. Either는 두 가지 가능한 타입 중 하나를 보유하는 특별한 합 타입이다. 이는 주로 성공 또는 실패와 같은 두 가지 대체적인 결과를 명시적으로 나타내는 데 사용된다.
타입 이론에서, Either는 보통 Either<L, R>과 같이 표기되며, 여기서 L은 "왼쪽" 타입, R은 "오른쪽" 타입을 나타낸다. 이 타입의 값은 Left<L> 또는 Right<R> 중 하나의 인스턴스로만 존재할 수 있다. 관례적으로, Left는 오류나 예외적인 경우를, Right는 정상적인 결과 값을 담는 데 사용된다.
이러한 수학적 정의는 함수형 프로그래밍 언어에서 직접 구현의 기초가 된다. 예를 들어, 하스켈의 Either 타입과 스칼라의 Either 타입이 대표적이다. 이 개념은 코틀린의 Result 타입이나 Either 타입 라이브러리 구현에서도 확인할 수 있다. Either 타입은 단순한 오류 코드 반환보다 더 풍부하고 안전한 오류 처리의 대체 수단을 제공한다.
3. 프로그래밍에서의 구현
3. 프로그래밍에서의 구현
3.1. 함수형 언어에서의 Either
3.1. 함수형 언어에서의 Either
함수형 프로그래밍 언어에서 Either 타입은 두 가지 가능한 경우 중 하나를 정확히 하나만 담을 수 있는 합 타입으로 정의된다. 이는 주로 연산의 결과가 성공적인 값 또는 실패를 나타내는 값 중 하나로 귀결되는 상황을 모델링하는 데 사용된다. 하스켈과 스칼라 같은 대표적인 함수형 언어에서는 이를 위한 Either 타입이 표준 라이브러리에 포함되어 있으며, 일반적으로 Left와 Right라는 두 개의 생성자를 통해 값을 생성한다. 관례적으로 Right는 성공적인 결과를, Left는 오류나 예외적인 상황을 담는 데 사용된다.
이 타입의 핵심 가치는 오류 처리를 명시적으로 만든다는 점에 있다. 예외를 던지는 방식과 달리, 함수의 시그니처에 Either를 사용하면 호출자가 반드시 성공과 실패 두 가지 경우를 모두 처리해야 한다는 사실을 타입 시스템을 통해 인지하게 된다. 이는 타입 안전성을 높이고 런타임 오류를 줄이는 데 기여한다. 또한, Either는 모나드의 일종으로 구현되는 경우가 많아, map, flatMap과 같은 연산을 통해 실패 가능성이 있는 연산들을 깔끔하게 체이닝할 수 있다.
코틀린은 함수형 패러다임을 지원하는 언어로, 초기에는 표준 라이브러리에 Either 타입이 포함되지 않았으나, Arrow 같은 서드파티 라이브러리를 통해 제공되었다. 이후 코틀린의 표준 라이브러리는 실패를 나타내는 데 더 특화된 Result 타입을 도입하여 비슷한 역할을 수행하게 되었다. 이러한 구현들은 모두 함수형 프로그래밍에서 순수 함수를 유지하면서 부수 효과를 제어하는 패턴의 일환으로 볼 수 있다.
3.2. 객체지향 언어에서의 유사 패턴
3.2. 객체지향 언어에서의 유사 패턴
객체지향 프로그래밍 언어에는 하스켈이나 스칼라의 Either와 정확히 일치하는 내장 타입이 드물지만, 동일한 의도를 구현하는 여러 디자인 패턴과 라이브러리가 존재한다. 이러한 패턴들은 주로 예외 처리를 명시적이고 제어 가능한 방식으로 대체하거나, 메서드가 두 가지 이상의 가능한 결과 타입을 반환해야 할 때 사용된다.
가장 일반적인 접근법은 성공과 실패 케이스를 각각 나타내는 전용 클래스를 정의하는 것이다. 예를 들어, 자바에서는 Optional이 단일 값의 부재를 처리하지만, 두 가지 가능한 타입을 다루기 위해 커스텀 Result나 Either 클래스를 직접 구현하거나, Vavr 같은 서드파티 함수형 프로그래밍 라이브러리를 도입한다. 코틀린은 표준 라이브러리에 Result<T> 클래스를 제공하여 성공 시 T 타입의 값을, 실패 시 Throwable을 캡슐화하는 방식으로 Either와 유사한 역할을 수행한다.
또 다른 객체지향적 패턴으로는 전략 패턴이나 상태 패턴을 활용하는 방법이 있다. 이는 런타임에 서로 다른 동작을 캡슐화한 객체를 교체하는 방식으로, 타입 시스템을 통한 정적 보장보다는 유연성을 중시한다. 그러나 이러한 패턴들은 Either가 제공하는 타입 안전성과 패턴 매칭의 편의성을 완전히 대체하기는 어렵다. 따라서 최근 많은 객체지향 언어들은 함수형 프로그래밍 패러다임을 수용하며, 제네릭과 대수적 데이터 타입을 지원하여 라이브러리 수준에서 Either와 같은 합 타입을 더 쉽게 구현할 수 있는 환경을 조성하고 있다.
4. 사용 사례
4. 사용 사례
4.1. 오류 처리
4.1. 오류 처리
Either는 전통적인 예외 처리 방식의 대안으로서 함수형 프로그래밍에서 널리 사용되는 오류 처리 패턴이다. 이 접근법의 핵심은 오류를 함수의 반환 값으로 명시적으로 표현하는 데 있다. Either는 일반적으로 Left와 Right라는 두 가지 서브타입을 가지며, 관례적으로 Left는 실패(오류) 값을, Right는 성공 값을 담는 데 사용된다. 이는 모든 가능한 오류 경로가 타입 시스템에 의해 강제되고, 호출자가 반드시 이를 처리하도록 유도함으로써 런타임에서의 예기치 않은 예외 발생 가능성을 줄인다.
이 패턴을 사용하면 오류 처리가 함수의 시그니처에 직접 반영된다. 예를 들어, 파일을 읽는 함수가 Either<IOException, String> 타입을 반환한다면, 이 함수는 IOException(왼쪽) 또는 성공적인 문자열 내용(오른쪽) 중 하나만을 반환할 수 있음을 명확히 알린다. 호출자는 map, flatMap, fold와 같은 고차 함수를 통해 성공 케이스와 실패 케이스를 안전하게 변환하거나 처리할 수 있다. 이는 오류를 부수 효과가 아닌 일반 값으로 취급하여 프로그램의 흐름을 더 예측 가능하고 조합 가능하게 만든다.
Either를 이용한 오류 처리는 특히 비동기 프로그래밍이나 파이프라인 형태의 데이터 처리에서 강점을 발휘한다. 여러 단계를 거치는 연산에서 중간 단계에서 발생한 오류를 Left로 전파하면, 이후의 연산은 자동으로 건너뛰고 최종적으로 호출 체인의 시작 부분에서 한 번에 모든 오류를 처리할 수 있다. 이는 중첩된 try-catch 블록이나 반복적인 오류 검사 코드를 제거하여 로직을 단순화한다. 하스켈, 스칼라, 코틀린 등의 언어에서는 이 개념을 표준 라이브러리에서 제공하며, 자바와 같은 언어에서는 Vavr 같은 서드파티 라이브러리를 통해 유사한 기능을 사용할 수 있다.
4.2. 유효성 검사
4.2. 유효성 검사
Either 타입은 유효성 검사와 같은 복잡한 검증 로직을 명확하고 안전하게 표현하는 데 적합하다. 단순한 성공/실패 이상으로, 검증 실패 시 어떤 규칙이 위반되었는지에 대한 구체적인 정보를 실패 케이스에 담아 반환할 수 있다. 예를 들어, 사용자 입력 폼을 검증할 때, 모든 필드가 올바르면 성공 값을, 하나라도 잘못되었다면 해당 필드와 오류 메시지의 목록을 실패 값으로 반환할 수 있다. 이는 단일 예외를 던지는 방식보다 더 풍부한 정보를 제공한다.
여러 독립적인 검증 단계를 함수형 프로그래밍의 합성 원리를 통해 연결할 때 Either의 장점이 두드러진다. 각 검증 함수는 Either<오류타입, 성공타입>을 반환하도록 작성되며, flatMap이나 map과 같은 컴비네이터를 사용해 순차적으로 연결한다. 이때 한 단계라도 실패(Left)가 발생하면 이후의 검증 로직은 실행되지 않고 최초의 오류가 최종 결과로 전달된다. 이는 조기 반환 패턴을 타입 시스템을 통해 안전하게 구현한 것으로, 코드의 가독성과 유지보수성을 높인다.
접근 방식 |
| 전통적인 예외 처리 |
|---|---|---|
오류 정보 | 타입에 안전하게 캡슐화 | 예외 객체에 담김 |
제어 흐름 | 명시적 타입 반환을 통한 합성 | 암시적인 점프(throw/catch) |
선언성 | 높음. 검증 규칙이 함수 합성으로 표현됨 | 낮음. 명령형 분기문에 의존 |
복수 오류 수집 | 특별한 설계( | 어려움 |
따라서 Either는 유효성 검사 도메인을 본질적으로 나타내는 타입으로, 검증 성공의 결과와 검증 실패의 원인을 모두 타입 시스템 안으로 끌어들인다. 이는 런타임 오류 가능성을 줄이고, 오류 처리 로직을 일반적인 비즈니스 로직과 동일한 방식으로 다룰 수 있게 하여 더 견고한 소프트웨어를 만드는 데 기여한다.
5. 장단점
5. 장단점
Either 타입을 사용하는 주요 장점은 명시적이고 안전한 오류 처리 흐름을 가능하게 한다는 점이다. 기존의 예외 처리는 제어 흐름이 암시적이며, 함수 시그니처에서 어떤 예외가 발생할 수 있는지 명시하지 않아 예상치 못한 런타임 오류로 이어질 수 있다. 반면 Either는 실패 가능성을 타입 시스템에 포함시켜, 함수가 Left(일반적으로 실패) 또는 Right(일반적으로 성공)를 반환한다고 명시적으로 선언한다. 이로 인해 컴파일러가 오류 처리를 강제하거나 경고할 수 있어, 실수로 오류를 무시하는 상황을 방지하고 코드의 신뢰성을 높인다.
또 다른 장점은 합성 가능성이다. Either는 함수형 프로그래밍의 모나드 패턴을 따르는 경우가 많아, map, flatMap과 같은 연산을 통해 성공 경로(Right 값)를 따라 깔끔하게 연산을 체이닝할 수 있다. 이는 중첩된 조건문이나 일찍 반환하는 패턴을 사용하지 않고도 오류 처리를 통합할 수 있게 해준다. 예를 들어, 여러 단계의 유효성 검사나 데이터 변환 과정에서 한 단계라도 실패(Left)하면 전체 체인이 실패로 단락 평가되고, 모든 단계가 성공하면 최종 결과(Right)가 도출된다.
그러나 Either 타입에도 단점은 존재한다. 가장 큰 단점은 기존의 예외 처리 방식에 비해 코드가 다소 장황해질 수 있다는 점이다. 모든 가능한 실패 경로를 타입으로 다루려면 패턴 매칭이나 변환 함수를 사용해야 하며, 이는 간단한 오류를 처리할 때는 과도한 보일러플레이트 코드를 유발할 수 있다. 또한, 객체지향 프로그래밍 환경이나 기존 예외에 크게 의존하는 라이브러리와의 통합 시 어색함이 발생할 수 있어, 변환 계층이 필요할 수 있다.
마지막으로, Either는 단순히 "성공 또는 실패" 이상의 일반적인 두 가지 경우의 수를 표현하는 데 사용될 수 있지만, 실무에서는 주로 오류 처리에 활용된다. 이때 Left에 담길 오류 정보의 타입을 어떻게 설계할지(단순 문자열, 열거형, 커스텀 오류 클래스 등)에 대한 결정이 필요하며, 이는 프로젝트 전반에 걸쳐 일관된 규약을 필요로 한다.
6. 관련 개념
6. 관련 개념
6.1. Option/Maybe
6.1. Option/Maybe
Option 또는 Maybe는 함수형 프로그래밍에서 널리 사용되는 또 다른 합 타입이다. 이 타입은 값이 존재할 수도 있고 존재하지 않을 수도 있는 상황, 즉 널 가능성을 명시적으로 다루기 위해 설계되었다. Option(스칼라, 러스트) 또는 Maybe(하스켈) 타입은 일반적으로 Some(value)(또는 Just(value))와 None(또는 Nothing)이라는 두 가지 생성자로 구성된다. 이는 값의 부재를 예외나 널 포인터를 사용하지 않고도 안전하게 표현할 수 있는 방법을 제공한다.
Either 타입과 Option/Maybe 타입은 모두 선택적 값을 모델링한다는 점에서 유사하지만, 주요 차이점은 실패의 원인에 대한 정보를 담을 수 있는지 여부에 있다. Option은 값의 유무만을 나타내는 반면, Either는 실패 시에도 의미 있는 정보(예: 오류 메시지)를 담을 수 있다. 따라서 Either는 Left에 오류 정보를, Right에 성공 값을 담아 오류 처리에 더 풍부하게 사용될 수 있다. 반면 Option은 단순히 값이 없다는 사실만을 전달할 때 더 간결하다.
많은 현대 프로그래밍 언어와 라이브러리는 이 개념들을 채택하고 있다. 예를 들어, 자바 8 이상에서는 Optional 클래스가, 스위프트에서는 Optional이 이와 동일한 역할을 수행한다. 이러한 타입들을 사용함으로써 개발자는 널 안전성을 높이고, 런타임 오류를 줄이며, 의도를 더 명확하게 코드에 반영할 수 있다.
6.2. Try
6.2. Try
Try는 함수형 프로그래밍에서 예외 처리를 다루기 위한 타입으로, Either와 유사하게 성공 또는 실패를 나타내는 합 타입이다. 주로 연산이 예외를 던질 수 있는 상황에서, 예외를 값으로 감싸서 명시적으로 처리할 수 있도록 설계되었다. Either가 일반적인 두 가지 대안을 표현하는 데 사용된다면, Try는 성공한 값 또는 발생한 예외라는 특정한 두 가지 경우에 특화되어 있다.
대표적인 구현으로는 스칼라의 scala.util.Try가 있다. 스칼라의 Try는 Success와 Failure라는 두 서브타입으로 구성되며, Failure는 Throwable을 담는다. 이를 통해 개발자는 예외를 일급 객체로 다루며, 함수 합성과 고차 함수를 사용해 안전하게 오류 흐름을 제어할 수 있다. 다른 언어에서는 코틀린의 Result 타입이나, 자바의 CompletableFuture나 Vavr 라이브러리의 Try가 유사한 역할을 수행한다.
Try의 주요 장점은 선언적 프로그래밍 스타일을 가능하게 하여, 전통적인 try-catch 블록에 의존하는 명령형 오류 처리 코드보다 가독성과 유지보수성이 뛰어나다는 점이다. 또한 체이닝을 통해 여러 연산을 순차적으로 연결할 때, 중간에 예외가 발생하면 이후 연산을 실행하지 않고 조기에 실패 경로로 전환할 수 있다. 그러나 모든 예외를 래핑하기 때문에, 검사 예외와 비검사 예외를 구분하지 않고 모두 처리하게 되어 상황에 따라 과도한 부담이 될 수 있다는 단점도 있다.
Try는 Option이나 Either와 함께 함수형 오류 처리 패턴을 구성하는 중요한 도구이다. Option은 값의 유무를, Either는 일반적인 두 가지 가능성을, Try는 성공/예외라는 구체적인 가능성을 다룬다는 점에서 차이가 있다. 이러한 타입들은 모나드로서의 공통적인 특성을 공유하며, 이를 통해 부수 효과를 최소화하고 예측 가능한 프로그램 흐름을 구성하는 데 기여한다.
6.3. Result
6.3. Result
Result는 함수형 프로그래밍에서 오류 처리를 위한 핵심적인 합 타입이다. 이 타입은 일반적으로 성공적인 연산의 결과값과 실패를 나타내는 오류 값 중 하나만을 보유한다. 이는 예외 처리를 명시적인 값으로 다루는 패러다임을 제공하며, 함수의 반환 타입만으로도 성공 또는 실패 가능성을 명확히 표현할 수 있게 한다. 스칼라의 Try나 코틀린의 Result가 이 개념의 대표적인 구현체에 해당한다.
Result 타입은 성공과 실패 케이스를 구분하는 방식으로 작동한다. 예를 들어, 성공 케이스는 계산된 값을 감싸고, 실패 케이스는 발생한 예외나 오류 메시지를 담는다. 이를 통해 프로그래머는 제어 흐름을 중단시키지 않고도 오류 상태를 체계적으로 전파하고 처리할 수 있다. 함수 합성이나 체이닝 과정에서도 각 단계의 결과를 안전하게 변환하거나 다음 단계로 전달하는 것이 가능해진다.
Result의 주요 장점은 부수 효과를 최소화하고 프로그램의 결정론을 높이는 데 있다. 모든 가능한 경로(성공과 실패)가 타입 시스템에 의해 강제되므로, 런타임에 예상치 못한 예외가 발생할 위험이 줄어든다. 이는 Option이나 Maybe 타입이 값의 부재를 다루는 것과 유사하지만, 실패의 원인에 대한 추가 정보를 제공한다는 점에서 더 풍부한 표현력을 가진다.
7. 여담
7. 여담
Either는 단순한 타입 이상으로, 함수형 프로그래밍의 핵심 철학을 반영하는 도구이다. 이는 제어 흐름을 명시적인 값으로 표현함으로써, 프로그램의 부작용을 최소화하고 예측 가능성을 높이는 데 기여한다. 특히 오류 처리를 예외 처리라는 특별한 메커니즘에 의존하기보다는 일반적인 타입 시스템의 일부로 통합하려는 접근의 대표적인 사례이다.
이러한 개념은 하스켈이나 스칼라 같은 언어를 넘어, 최근 자바나 C# 같은 객체지향 프로그래밍 언어에서도 패턴 매칭이나 레코드 클래스와 같은 기능의 도입으로 더욱 자연스럽게 사용될 수 있는 환경이 조성되고 있다. 이는 소프트웨어 개발 패러다임이 함수형 프로그래밍의 아이디어를 점차 흡수하고 있음을 보여준다.
Either의 설계는 종종 "실패를 값으로" 다룬다는 점에서 옵션 타입과 유사하지만, 실패의 원인에 대한 추가 정보를 담을 수 있다는 점에서 더 풍부한 표현력을 가진다. 이는 단순히 '값 없음'을 나타내는 Maybe나 Option과 구별되는 중요한 특징이다. 따라서 유효성 검사나 파싱 같이 실패 시 구체적인 이유를 전달해야 하는 복잡한 비즈니스 로직을 모델링하는 데 매우 적합하다.
결국 Either는 프로그램의 가능한 모든 상태를 타입 안전성을 보장하며 명시적으로 서술하도록 강제하는, 일종의 설계 도구 역할을 한다. 이를 통해 개발자는 컴파일 타임에 더 많은 오류를 발견하고, 가독성이 높으며 유지보수가 쉬운 코드를 작성할 수 있게 된다.
