Elvis Operator 는 왼쪽 피피랜드가 null이 아닐 경우 왼쪽을, 그리고 null일 경우 오른쪽을 반환하는 이중 연산자입니다. Kotlin, Groovy 그리고 다른 언어들에서 ?:로 작성됩니다. Kotlin 언어 문서, 2024에 따르면, 이 연산자는 언어의 null 안전 시스템의 일부로, 가독성을 일지 않으면서 조건문을 한 줄로 줄일 수 있게 합니다. 완전한 if-else 표현식과 달리, Elvis Operator는 왼쪽이 null일 경우에만 오른쪽을 평가하므로, 비용이 많이 드는 기본 표현식을 사용할 때 성능 이점을 제공합니다.
주요 포인트
Elvis Operator 는 엘비스 프레슬리의 헤어스타일에서 이름을 따온 이중 연산자입니다. ?: 기호는 옆쪽에서 본 가수의 얼굴을 연상시킵니다: 물음표는 눈과 구레나르다드, 콜론은 입입니다. 연산자는 두 개의 피피랜드를 받아 첨번째가 null이 아니면 그것을 반환하고, 그렇지 않으면 둘째를 반환합니다. 이를 통해 완전한 if-else 구조를 한 개의 빠른 표현식으로 대체할 수 있습니다.
“Elvis Operator”라는 이름은 Kotlin 그리고 Groovy 커뮤니티에서 자리잡았으며, 그 후 Xtend 및 다른 JVM 언어로 확산되었습니다. Swift와 Dart에서는 유사한 구조를 nil-coalescing operator라고 부르지만, 기능적으로는 동일합니다. 엘비스 프레슬리와의 시각적 연관성은 이 연산자가 현대 프로그래밍 언어 구문에서 가장 인식된 기호 중 하나가 되는 데 도움을 주었습니다.
Elvis Operator는 NullPointerException (NPE)을 방지하는 null safety 시스템의 일부입니다. JetBrains 2023 연구에 따르면, NPE는 프로덕션 JVM 코드에서 발생하는 모든 예외 중 28%를 차지합니다. Elvis Operator를 사용하면 할당 시점에서 NPE를 완전히 제거하고, 기본값을 보장합니다. 안전 호출 연산자 ?.와 결합하면, 단 하나의 if 블록 없이 신뢰할 수 있는 체인이 만들어집니다.
Kotlin에서 Elvis Operator는 두 표현식 사이에 ?:로 작성됩니다. 왼쪽 표현식이 null이 아니면 결과는 왼쪽과 같고, 그렇지 않으면 오른쪽이 평가됩니다. 오른쪽 표현식은 느게 평가됩니다 — 왼쪽 피피랜드가 null일 경우에만 그렇으며, 비용이 많이 드는 계산이나 API 호출을 사용할 때 성능에 중요합니다.
val name: String? = getUserName()
val displayName = name ?: "Guest"
fun getLength(str: String?): Int {
return str?.length ?: 0
}
val config: Config? = loadConfig()
val timeout = config?.timeout ?: 30
println("Timeout: $timeout")
// Elvis with throw 필수 매개변수용
val requiredId = nullableId ?: throw IllegalArgumentException("ID is required")
// Elvis with return 조기 종료용
fun process(data: String?): Result {
val safe = data ?: return Result.failure("No data")
return processInternal(safe)
}
let과의 결합은 Kotlin에서 일반적인 패턴으로, Elvis Operator가 기본값을 제공하고 let이 비-null 결과에 대한 작업을 수행합니다. 이 접근 방식은 가독성을 일지 않으면서 전체 if-else 블록을 한 개의 호출 체인으로 대체합니다. Elvis Operator는 throw와 함께 사용하여 필수 매개변수가 없는 경우 강제로 중단할 수도 있습니다 — 이것은 입력 데이터를 검증하는 관용적인 방법입니다.
Kotlin에서 일반적인 패턴은 Elvis + return입니다. 값이 null인 경우, 함수는 즐각 결과를 반환하거나 예외를 발생시킵니다. Elvis with return 관용표현은 데이터 변환 함수에서 자주 사용됩니다: val processed = rawData ?: return defaultValue. 이것은 Swift의 guard let보다 간결하고 코드 중첩을 늘리지 않습니다.
Elvis Operator는 if-else보다 길이가 짧고 변수를 반복할 필요가 없습니다. 비교해 보세요: val x = a ?: b 대 val x = if (a != null) a else b. 복잡한 표현식에서는 코드 감소가 60%까지 달하며, 의미는 완전히 동일합니다 — 컴파일러는 두 변족 모두에 대해 동일한 바이트코드를 생성할 수 있습니다.
| 기준 | Elvis Operator | if-else |
|---|---|---|
| 길이 | 1 줄, 5–10 문자 | 3 줄, 15–30 문자 |
| 표현식 반복 | 아니오 | 네 (a != null) ? a : b |
| 느게 평가되는 오른쪽 | 네 | 네 (elvis가 더 짧음) |
| 부작용 | 오른쪽에서만 | 두 개의 분기에서 |
| 가독성 | 단순한 경우 높음 | 복잡한 로직에 더 좋음 |
Elvis Operator는 기본값 할당, 안전 액세스를 통한 함수 호출 체인, 그리고 누락된 값 로깅에 최적적입니다. Kotlin의 관용 패턴은 표현식에서 단순한 null 확인을 위한 if-else의 대체로 권장합니다. ?.와 결합하면, 임시 변수 없이 간결한 체인을 생성합니다.
if-else는 여러 조건이 있거나, 부작용을 실행해야 하거나, 두 피피랜드 모두가 여러 작업이 있는 복잡한 코드 블록일 경우 여전히 좋습니다. Elvis Operator는 중첩된 블록을 지원하지 않습니다 — 각 피피랜드에 대해 단일 표현식만 가능합니다. 오른쪽이 부작용이 있는 함수를 호출하는 경우, if-else의 가독성이 더 높습니다.
Groovy는 Kotlin과 똑같은 ?: 구문을 사용합니다. 두 언어 모두 JVM에서 실행되고 null 안전 철학을 공유하기 때문입니다. 이 연산자는 Groovy에서 먼저 나타났습니다 — 2011년에 출시된 버전 1.8부터 언어에 포함되었습니다. Groovy는 Elvis 할당도 지원합니다: x ?= value (x가 null인 경우에만 할당).
def name = getUserName()
def display = name ?: "Guest"
def map = ["key": "value"]
def result = map?.get("missing") ?: "default"
println(result)
// Groovy에서 Elvis 할당
def config = null
config ?= "defaultConfig"
assert config == "defaultConfig"
Swift에서는 동등 기능이 nil-coalescing operator라고 불리며 ??로 작성됩니다. Dart에서도 동일한 ?? 구문이 사용됩니다. 차이점은 Swift와 Dart가 피피랜드 타입의 일치를 요구하는 반면, Kotlin은 공통 슈퍼타입으로 자동 변환을 통해 다른 타입을 허용한다는 점입니다. TypeScript에서는 ?? 연산자(nullish coalescing)가 버전 3.7에서 나타났으며, 모든 falsy 값을 필터링하는 ||와 달리 null과 undefined에만 작동합니다. PHP에서는 ?? 연산자가 버전 7.0에서, C#에서는 ??가 버전 6.0부터 나타났습니다.
가장 흔한 실수는 Elvis Operator ?:를 안전 호출 연산자 ?.와 혼동하는 것입니다. Kotlin에서 ?.는 객체가 null이 아닌 경우에만 메소드나 프로퍼티를 호출하며, ?:는 null일 경우 기본값을 반환합니다. 이들은 순차적으로 결합되지만, 각각 자체의 기능을 수행합니다. 혼동은 ?. 없이 ?:를 사용하려는 초보자들 사이에서 발생합니다.
Elvis의 오른쪽은 느게 평가되지만, 그 안에 부작용이 있는 함수 호출이 포함된 경우 예기치 못한 동작을 초래할 수 있습니다. 개발자는 때때로 비용이 많이 드는 계산이나 로깅을 오른쪽에 놓지만, 값이 비-null인 경우 실행되지 않습니다. 오른쪽은 필요할 경우에만 평가되는 표현식이라는 사실을 기억하는 것이 중요합니다.
여러 Elvis를 연속적으로 사용하면 코드 가독성이 떨어질 수 있습니다. 예를 들어, a ?: b ?: c ?: d는 왼쪽에서 오른쪽으로 평가되며 첫 번째 비-null을 반환합니다. 꼬호를 통한 그룹핑이나 로직을 별도 함수로 추출하면 가독성이 향상되고 디버깅이 쉬워집니다. 체인이 3개 요소를 초과하면 when 표현식이나 도우미 함수를 사용하는 것이 좋습니다.
초보 개발자는 때때로 부우값 확인을 위해 Elvis Operator를 사용하려고 시도합니다: val x = flag ?: false. 이것은 flag가 null일 수 있는 경우에만 작동합니다. null이 없는 부우 조건에는 일반적인 if-else거나 삼항 연산자가 필요합니다 — Kotlin에는 삼항 연산자가 없지만, if-else 표현식으로 대체됩니다: val x = if (flag) a else b.
자주 묻는 질문
Elvis Operator는 null만 확인하는 반면, 삼항 연산자는 임의의 부우 조건을 받을 수 있습니다. Kotlin에는 삼항 연산자가 없으며 if-else 표현식으로 대체됩니다. Elvis는 null일 때 기본값을 제공하는 하나의 특정 작업을 해결하는 반면, 삼항 연산자는 모든 조건표현식을 다룹니다.
네, 오른쪽 피피랜드는 함수 호출이 될 수 있습니다: val x = a ?: calculateDefault(). 느게 평가된 덕분에 함수는 a가 null일 경우에만 평가됩니다. 이를 통해 값이 있는 경우 성능 손실 없이 비용이 많이 드는 계산, API 호출 또는 데이터베이스 쿼리를 사용할 수 있습니다.
Java에는 직접적인 Elvis Operator가 없습니다. Java 8부터는 Optional.ofNullable(value).orElse(default)를 사용할 수 있습니다. Java 14+에서는 switch 표현식이 도입되었지만, 완전한 ?:는 없습니다. Kotlin과 Groovy는 이를 언어의 내장 구문 구조로 제공합니다.
일반적인 패턴은 안전 호출 ?. 다음 Elvis를 사용하는 것입니다. 예: user?.address?.city ?: “Unknown”. 체인에 어떤 요소가 null이면 기본값이 반환됩니다. 이것은 중첩된 if 확인을 대체하는 간결한 방법으로, 전통적인 접근법에 비해 코드를 3–5배 줄입니다.
Elvis Operator는 언어 레벨의 구문 구조인 반면, orElse는 Java에서 Optional의 메소드입니다. Elvis는 느게 평가되지만 (오른쪽은 null일 경우에만 평가), orElse는 항상 인수를 평가합니다. Java에서 느게 평가되는 변리안을 위해 orElseGet이 있으며, Supplier를 받습니다. Elvis는 더 깨끔하고 Optional을 만들 필요가 없습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.