Either: 핵심 개념, 활용 및 작동 원리

저자: IT Sectr 게시일: 2026-05-26 읽는 시간: 8 분

Either는 두 가지 가능한 변형 중 하나의 값을 나타내는 함수형 타입입니다. Left는 오류, Right는 성공을 나타냅니다. 예외와 달리 Either는 타입 수준에서 오류 처리를 명시적으로 만들고 try-catch 블록이 필요하지 않습니다. Arrow, 2026에 따르면, Either는 Kotlin 프로젝트에서 부수 효과 없이 실패할 수 있는 작업을 구성하는 데 널리 사용됩니다.

핵심 사항

  • Either는 상호 배타적인 두 가지 변형(Left(오류) 및 Right(성공))을 나타내는 대수적 데이터 타입입니다.
  • Left는 전통적으로 오류 정보를 포함하고, Right는 올바른 값을 포함합니다.
  • 명시적 타입 지정 — Either는 컴파일 시점에 두 변형을 모두 처리하도록 강제하여 예기치 않은 오류를 제거합니다.
  • 합성 — Either는 중첩 검사 없이 변환 체인을 위해 map, flatMap 및 fold를 지원합니다.
  • Result와의 차이점 — Either는 Error나 Throwable뿐만 아니라 임의의 오류 타입을 저장할 수 있습니다.

Either란?

Either는 함수형 프로그래밍에서 차용한 대수적 데이터 타입으로, 두 가지 가능한 타입 중 정확히 하나의 값을 나타냅니다. 오류 처리의 맥락에서 관례는 다음과 같습니다: Left 타입(Left)은 문제 설명을 포함하고, Right 타입(Right)은 성공적인 결과를 포함합니다.

Either의 개념은 Haskell 언어와 Either a b 타입 범주에서 유래했으며, 여기서 a는 왼쪽 값의 타입이고 b는 오른쪽 값의 타입입니다. 모바일 개발에서 Either는 Kotlin용 Arrow 라이브러리와 Swift의 함수형 접근 방식 덕분에 인기를 얻었습니다.

예외에 비해 Either의 주요 장점은 숨겨진 실행 경로가 없다는 것입니다. Either를 반환하는 함수는 시그니처에서 실패할 수 있음을 명시적으로 선언합니다. 컴파일러는 두 변형이 모두 처리되었는지 확인합니다.

예외를 던지는 것과 달리 Either는 데이터 흐름 투명성을 유지합니다. Either를 반환하는 함수를 호출할 때 호출하는 쪽에서 try-catch가 필요하지 않습니다 — pattern-matching이나 fold로 충분합니다. 이는 각 데이터 소스가 Either를 반환하고 map과 flatMap을 통해 변환 체인이 구축되는 반응형 접근 방식의 아키텍처에서 특히 중요합니다.

Either 작동 방식: Left 및 Right 구조

Either는 LeftRight라는 두 가지 하위 타입으로 구성됩니다. Either 인스턴스는 주어진 시간에 둘 중 하나만 될 수 있습니다. Kotlin의 일반적인 시그니처는 Either<E, A>이며, 여기서 E는 오류 타입이고 A는 성공 값의 타입입니다.

Left 타입: 오류 컨테이너

Left는 실패 사례를 나타냅니다. 예외와 달리 Left는 실행 흐름을 중단하지 않습니다 — 처리해야 하는 값을 반환할 뿐입니다. 오류 타입은 무엇이든 될 수 있습니다: String, Int, 사용자 정의 sealed class 또는 도메인 모델입니다.

Right 타입: 성공 컨테이너

Right는 작업의 올바른 결과를 포함합니다. 이름이 의미하듯이, 관례에 따라 Right는 성공을 의미합니다. map 및 flatMap과 같은 변환은 Right에 적용되어 각 단계를 확인하지 않고도 계산 체인을 가능하게 합니다.

flatMap을 통한 Either 합성

flatMap은 Either 합성의 기본 메커니즘입니다. 현재 값이 Right인 경우 flatMap은 전달된 함수를 적용하고 새 Either를 반환합니다. Left인 경우 flatMap은 변환을 건너뛰고 오류를 전파합니다. 이 동작을 단락 평가라고 합니다.

flatMap 외에도 Either는 mapLeft(오류 변환용), fold(한 곳에서 두 변형을 모두 처리하기 위한), getOrElse(기본값으로 값을 추출하기 위한)를 지원합니다. 이러한 함수는 단순한 추출부터 Kotlin 코루틴이나 Swift Combine의 비동기 호출을 포함한 복잡한 합성까지 모든 시나리오를 다룹니다.

kotlin
fun parseInt(input: String): Either<String, Int> =
    input.toIntOrNull()?.let { Right(it) }
    ?: Left("변환 실패: $input")

fun divide(a: Int, b: Int): Either<String, Int> =
    if (b == 0) Left("0으로 나누기")
    else Right(a / b)

val result = parseInt("10")
    .flatMap { divide(it, 2) }

Kotlin에서의 Either: 실제 적용

Kotlin 생태계에서 Either는 Arrow 라이브러리에 구현되어 있습니다. Kotlin 표준 라이브러리는 Result를 제공하지만 Either는 더 많은 유연성을 제공합니다: 임의의 오류 타입, flatMap을 통한 합성, 함수형 패턴 지원.

Arrow 라이브러리와 Either

Arrow는 Either, Option, Validated 및 기타 타입을 추가하는 Kotlin용 함수형 라이브러리입니다. Arrow.Either는 두 개의 하위 클래스가 있는 sealed class입니다: ArrowCore.LeftArrowCore.Right. 라이브러리는 편리한 확장도 제공합니다: .getOrElse(), .fold(), .mapLeft().

예제: 네트워크 요청과 Either

실제 시나리오를 고려해 보세요 — 네트워크 없음, 서버 오류, 잘못된 응답 등의 가능한 오류가 있는 Android 앱의 네트워크 요청입니다. Either를 사용하면 모든 변형을 단일 반환 타입으로 결합할 수 있습니다.

kotlin
sealed class NetworkError {
    data class NoConnection(val message: String): NetworkError()
    data class ServerError(val code: Int): NetworkError()
    data class ParseError(val detail: String): NetworkError()
}

suspend fun fetchUser(id: String): Either<NetworkError, User> {
    return try {
        val response = api.getUser(id)
        if (response.isSuccessful) {
            Right(response.body()!!)
        } else {
            Left(NetworkError.ServerError(response.code()))
        }
    } catch (e: IOException) {
        Left(NetworkError.NoConnection(e.message ?: ""))
    }
}

Swift에서의 Either: 표준 Result와의 차이점

Swift에서는 버전 5.0부터 기본 제공 Result 타입을 사용할 수 있으며, 개념적으로는 Either와 유사하지만 제한 사항이 있습니다: 오류는 Error 프로토콜을 준수해야 하며, 성공 값은 단일 타입이어야 합니다. Swift의 Either는 두 개의 제네릭 매개변수가 있는 enum을 사용하여 구현됩니다.

표준 Result vs. Either

Result<Success, Failure>는 Swift의 기본 제공 타입이며, 여기서 Failure: Error입니다. Either는 오류 타입에 제약을 가하지 않으므로 String, 사용자 정의 구조체 또는 중첩된 enum을 통한 여러 오류 타입을 저장할 수 있습니다.

swift
enum Either<E, A> {
    case left(E)
    case right(A)

    func map<B>(_ transform: (A) -> B) -> Either<E, B> {
        switch self {
        case .left(let e): return .left(e)
        case .right(let a): return .right(transform(a))
        }
    }
}

let result: Either<String, Int> = .right(42)
let mapped = result.map { $0 * 2 }

모바일 앱에서 Either를 사용해야 하는 경우

Either는 예외 없이 명시적이고 타입 안전한 오류 처리가 필요한 시나리오에 최적입니다. Kotlin과 Swift를 사용한 모바일 개발의 주요 사용 사례를 살펴보겠습니다.

  • 네트워크 요청 — 각 요청은 연결 오류, 서버 오류 또는 구문 분석 오류를 반환할 수 있습니다. Either는 모든 변형을 하나의 타입으로 수집합니다.
  • 양식 유효성 검사 — 사용자 정의 오류 타입이 있는 Either는 입력 필드 유효성 검사 결과(유효하지 않은 이메일 또는 비밀번호)를 편리하게 나타냅니다.
  • 데이터 리포지토리 — Either는 통합된 오류 타입으로 다양한 소스(캐시, DB, 네트워크)의 결과를 결합할 수 있습니다.
  • 명령 해석기 — 앱이 사용자 입력이나 명령을 구문 분석하는 경우 Either는 오류를 보고하는 타입 안전한 방법을 제공합니다.

부수 효과가 없는 단순한 작업에는 Either를 사용하지 마세요 — 일반 반환 값이 더 안정적이고 읽기 쉽습니다. 또한 실패가 예상 시나리오가 아니라 예외적인 상황인 경우 Either는 과도합니다.

Either는 Kotlin의 코루틴 작업에도 효과적입니다. Either를 반환하는 함수는 스레드를 차단하지 않고 fold 또는 mapLeft를 통한 오류 처리와 함께 코루틴 내에서 호출할 수 있습니다. 이는 각 리포지토리가 Either를 반환하고 ViewModel이 결과를 UiState로 변환하는 MVVM 아키텍처의 Android 앱에서 특히 유용합니다.

Either 사용 시 일반적인 실수

Either를 처음 접하는 개발자는 종종 비슷한 실수를 합니다. 가장 일반적인 실수와 이를 방지하는 방법을 살펴보겠습니다.

  • Left 무시 — 오류 원인을 분석하지 않고 기본값으로 .getOrElse()를 호출하면 Either 사용의 의미가 사라집니다. 오류를 명시적으로 처리하세요.
  • 전역 오류 타입 — 프로젝트의 모든 Either에 공통 타입으로 String이나 Exception을 사용하면 타입 안전성이 저하됩니다. 도메인 sealed class를 만드세요.
  • 중첩된 Either — Right 내부의 Either(Either<E, Either<E, A>>)는 가독성을 떨어뜨립니다. 평탄화에는 flatMap을 사용하세요.
  • 예외와의 혼용 — 함수가 Either를 반환하지만 내부적으로 예외를 던지는 경우, 이는 명시적 오류 처리의 개념과 모순됩니다.

자주 묻는 질문

Either와 Optional의 차이점은 무엇인가요?

Optional은 값의 존재 또는 부재(Some/None)를 나타내지만 부재의 이유를 알려주지 않습니다. Either는 오류용 왼쪽과 성공용 오른쪽이라는 두 가지 구체적인 타입을 제공하여 실패 컨텍스트를 전달할 수 있습니다.

Java에서 Either를 사용할 수 있나요?

Java에는 기본 제공 Either가 없지만 Vavr 및 functionaljava 라이브러리에서 구현을 제공합니다. Android 개발에서 Vavr의 Either는 람다를 사용한 함수형 스타일의 인기 있는 대안입니다.

Either 대신 sealed class를 사용해야 하는 경우는 언제인가요?

Kotlin의 Sealed class는 변형이 두 개 이상이거나 구조가 다른 경우 더 편리합니다. 이진 결과(오류/성공)의 경우 Either가 더 간결하고 즉시 사용 가능한 함수형 결합자를 제공합니다.

Either는 멀티스레딩을 지원하나요?

Either는 불변이며 기본적으로 스레드 안전합니다. Kotlin의 코루틴에서 Either는 완벽하게 결합됩니다: flatMap은 코루틴 범위 내에서 작동하고 오류 처리에 잠금이 필요하지 않습니다.

프로젝트의 모든 함수에 Either를 사용해야 하나요?

아니요. Either는 예상되는 실패(네트워크, 유효성 검사, 비즈니스 로직)가 있는 작업에 적합합니다. 부수 효과가 없는 단순한 getter 및 계산의 경우 일반 타입이 더 읽기 쉽고 불필요한 복잡성을 추가하지 않습니다.

요약

  • Either는 Left(오류)와 Right(성공)의 두 가지 변형을 나타내는 함수형 타입으로, 타입 수준에서 오류 처리를 명시적으로 만듭니다.
  • Left는 표준 Result와 달리 String부터 도메인 sealed class까지 임의의 오류 타입을 보유할 수 있습니다.
  • flatMap은 중첩 검사 없이 Either 합성을 가능하게 합니다: 오류는 자동으로 전체 체인을 통해 전파됩니다.
  • Arrow는 기본 제공 코루틴 지원 및 확장 기능을 갖춘 Kotlin용 주요 Either 라이브러리입니다.
  • Swift는 기본 제공 Result를 사용하지만 Either는 최대 유연성을 위해 두 개의 제네릭 매개변수가 있는 enum으로 구현할 수 있습니다.
  • 네트워크 요청, 양식 유효성 검사 및 리포지토리(예상되는 오류가 있는 시나리오)에 Either를 사용하세요.
  • 전역 오류 타입, Left 무시 및 중첩된 Either를 피하세요 — 이는 타입 안전성의 이점을 감소시킵니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기