널 병합 연산자

**널 병합 연산자**(null coalescing operator)는 여러 프로그래밍 언어의 기본 조건식 구문의 일부인 이항 연산자이다. 버전 2.0 이후의 C 샤프, 버전 1.12.0 이후의 다트, 버전 7.0.0 이후의 PHP, 펄은 버전 5.10부터 논리 정의 또는, 파워셸은 7.0.0부터, 스위프트는 nil-coalescing 연산자라는 이름으로 지원된다.

구현마다 동작이 다르지만 널 병합 연산자는 일반적으로 가장 왼쪽 피연산자가 존재하고 널이 아닌 경우 결과를 반환하고, 그렇지 않으면 가장 오른쪽 피연산자를 반환한다. 이 동작을 통해 더 구체적인 값을 사용할 수 없는 경우 기본값을 정의할 수 있다.

삼항 조건부 if 연산자가 ` x ? x : y`. 그러나 `x ?: y`로 사용되는 이진 엘비스 연산자와 마찬가지로 널 병합 연산자는 이진 연산자이므로 피연산자를 최대 한 번 평가한다. 이는 `x` 평가에 부작용이 있는 경우 중요하다.

## 언어별 예시

### 본 셸 계열

본 셸(및 그 파생 셸)에서 "매개변수가 설정되지 않았거나 null인 경우, 단어의 확장 결과가 대체된다. 그렇지 않으면 매개변수의 값이 대체된다":

```bash
#supplied_title='supplied title' # 제공된 제목을 사용하려면 이 줄의 주석을 해제하십시오.
title=${supplied_title:-'Default title'}
echo "$title" # 출력: Default title
```


### C#

C#에서 널 병합 연산자는 `??`이다.

이 연산자는 다음과 같이 식을 단순화하는 데 가장 자주 사용된다:


```csharp
possiblyNullValue ?? valueIfNull
```


예를 들어, 페이지 제목이 없을 때 기본 제목을 부여하는 C# 코드를 구현하려는 경우, 다음과 같은 문장을 사용할 수 있다:


```csharp
string pageTitle = suppliedTitle ?? "Default Title";
```


이는 다음과 같은 더 장황한 코드들을 대체한다:

```csharp
string pageTitle = (suppliedTitle != null) ? suppliedTitle : "Default Title";
```

또는

```csharp
string pageTitle;

if (suppliedTitle != null)
{
    pageTitle = suppliedTitle;
}
else
{
    pageTitle = "Default Title";
}
```


세 가지 형태 모두 `pageTitle`이라는 변수에 동일한 값을 저장하게 된다.

`??` 연산자를 사용하면 `suppliedTitle`은 한 번만 참조되지만, 다른 두 코드 예시에서는 두 번 참조된다.

이 연산자는 동일한 식 내에서 여러 번 사용될 수도 있다:

```csharp
return some_Value ?? some_Value2 ?? some_Value3;
```

어떤 변수에 null이 아닌 값이 할당되거나 최종 값(null일 수도 있고 아닐 수도 있음)에 도달하면 식이 완료된다.

예를 들어, 변수의 값이 null로 평가될 때 해당 변수를 다른 값으로 변경해야 하는 경우, C# 8.0부터는 `??=` 널 병합 할당 연산자를 사용할 수 있다:


```csharp
some_Value ??= some_Value2;
```


이는 다음 코드의 더 간결한 버전이다:


```csharp
some_Value = some_Value ?? some_Value2;
```


널 조건 연산자 `?.` 또는 널 조건 요소 액세스 연산자 `?[]`와 결합하여, 객체나 객체의 멤버가 null인 경우 기본값을 제공하도록 널 병합 연산자를 사용할 수 있다. 예를 들어, 다음 코드는 `page` 객체가 null이거나, `page`는 null이 아니지만 그 `Title` 속성이 null인 경우 기본 제목을 반환한다:

```csharp
string pageTitle = page?.Title ?? "Default Title";
```


### CFML

ColdFusion 11, 라일로 4.1부터 CFML은 삼항 연산자의 변형인 `?:`로 널 병합 연산자를 지원한다. 이는 기능적, 구문적으로 위의 C# 대응물과 동일하다. 예시:


```cfm
possiblyNullValue ?: valueIfNull
```


### Freemarker

Apache FreeMarker에서 누락된 값은 일반적으로 예외를 발생시킨다. 그러나 누락된 값과 null 값 모두 선택적 기본값과 함께 처리될 수 있다:


```sh
${missingVariable!"defaultValue"}
```

또는 출력을 빈칸으로 남기려면 다음과 같이 한다:

```sh
${missingVariable!}
```


### 자바스크립트

자바스크립트의 가장 유사한 연산자는 ECMA스크립트 11번째 에디션 표준에 추가된 "널 병합 연산자" `??`이다. 이전 버전에서는 Babel 플러그인을 통해 사용하거나 타입스크립트에서 사용할 수 있었다. 이 연산자는 왼쪽 피연산자를 평가하고 그 결과값이 "nullish"(`null` 또는 `undefined`)가 아니면 그 값을 결과로 취하며, 그렇지 않으면 오른쪽 피연산자를 평가하여 그 결과값을 취한다.

다음 예시에서 `b`의 값이 `null`이나 `undefined`가 아니면 `a`에 `b`의 값이 할당되고, 그렇지 않으면 3이 할당된다.


```javascript
const a = b ?? 3;
```


널 병합 연산자가 도입되기 전에는 프로그래머들이 논리 OR 연산자(`||`)를 사용하곤 했다. 그러나 `??`가 구체적으로 `null`이나 `undefined`를 찾는 반면, `||` 연산자는 `null`, `undefined`, `""`, `0`, `NaN`, 그리고 당연히 `false`를 포함한 모든 거짓 같은(falsy) 값을 찾는다.

다음 예시에서 `b`의 값이 참 같은(truthy) 값이면 `a`에 `b`의 값이 할당되고, 그렇지 않으면 3이 할당된다.


```javascript
const a = b || 3;
```


### Kotlin

Kotlin은 `?:` 연산자를 사용한다. `?:`가 일반적으로 널 병합이 아닌 엘비스 연산자로 사용된다는 점을 고려하면 이례적인 기호 선택이지만, 이는 null을 거짓(false)으로 간주하는 Groovy (프로그래밍 언어)의 영향을 받은 것이다.

```scala
val title = suppliedTitle ?: "Default title"
```


### Objective-C

Obj-C에서 nil 병합 연산자는 `?:`이다. 이는 nil 참조에 대한 기본값을 제공하는 데 사용될 수 있다:


```objc
id value = valueThatMightBeNil ?: valueIfNil;
```


이는 다음과 같이 쓰는 것과 같다:

```objc
id value = valueThatMightBeNil ? valueThatMightBeNil : valueIfNil;
```


### 펄

펄(버전 5.10부터)에서 이 연산자는 `//`이며, 이에 해당하는 펄 코드는 다음과 같다:


```perl
$possibly_null_value // $value_if_null
```


possibly_null_value는 null인지 아닌지(펄 용어로는 정의되지 않음(undefined) 또는 정의됨(defined)) 평가된다. 평가에 기초하여, 이 식은 possibly_null_value가 null일 때 value_if_null을 반환하고, 그렇지 않으면 possibly_null_value를 반환한다. 부작용이 없는 경우, 이는 이를 지원하는 언어들의 삼항 연산자(**<code>[[?:]]</code>** 문)가 작동하는 방식과 유사하다. 위의 펄 코드는 아래의 삼항 연산자 사용과 동일하다:

```perl
defined($possibly_null_value) ? $possibly_null_value : $value_if_null
```

이 연산자의 가장 일반적인 용도는 단순한 null 체크를 위한 코드 양을 최소화하는 것이다.

펄은 추가적으로 `//=` 할당 연산자를 가지고 있으며, 
```perl
$a //= $b
```
는 대체로 
```perl
$a = $a // $b
```
와 동일하다.

이 연산자는 펄의 오래된 `||` 및 `||=` 연산자와는 달리 '참'이 아닌 '정의 여부'를 따진다는 점에서 다르다. 따라서 0이나 ""(길이가 0인 문자열)와 같이 거짓이지만 정의는 되어 있는 값들에 대해서는 다르게 동작한다:

```perl
$a = 0;
$b = 1;
$c = $a // $b;  # $c = 0
$c = $a || $b;  # $c = 1
```


### PHP

PHP 7.0은 `??` 구문을 사용하는 널 병합 연산자를 도입했다. 이는 NULL 또는 존재하지 않는 변수/배열 인덱스/속성을 엄격하게 확인한다. 이 점에 있어서 PHP의 `isset()` 의사 함수와 유사하게 작동한다:


```php
$name = $request->input['name'] ?? $request->query['name'] ?? 'default name';

/* 다음과 동일함 */

if (isset($request->input['name'])) {
    $name = $request->input['name'];
} elseif (isset($request->query['name'])) {
    $name = $request->query['name'];
} else {
    $name = 'default name';
}
```

```php
$user = $this->getUser() ?? $this->createGuestUser();

/* 다음과 동일함 */

$user = $this->getUser();

if ($user === null) {
    $user = $this->createGuestUser();
}
```

```php
$pageTitle = $title ?? 'Default Title';

/* 다음과 동일함 */

$pageTitle = isset($title) ? $title : 'Default Title';
```


PHP 버전 7.4에서는 `??=` 구문을 사용하는 널 병합 할당 연산자가 도입되었다:


```php
// 다음 줄들은 동일한 작업을 수행함
$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';
// 긴 이름의 변수를 반복하는 대신, 병합 할당 연산자가 사용됨
$this->request->data['comments']['user_id'] ??= 'value';
```


### 파워셸
파워셸 7부터 `??` 널 병합 연산자가 이 기능을 제공한다.

```powershell
$myVar = $null
$x = $myVar ?? "something" # "something"을 할당함
```


### R
R 버전 4.4.0부터 `%||%` 연산자가 기본 R에 포함되었다(이전에는 rlang과 같은 일부 패키지의 기능이었다).


```r
> NULL %||% 2
[1] 2
```


### 러스트

러스트에는 `null`이 없지만, 동일한 목적을 위해 태그된 공용체가 사용된다. 예를 들어, `Result<T, E>` 또는 `Option<T>`가 있다.
Try 트레이트를 구현하는 모든 타입은 래핑을 해제(unwrap)할 수 있다.

`unwrap_or()`는 다른 언어의 널 병합 연산자와 유사한 목적을 수행한다. 또는 함수의 결과를 기본값으로 사용하기 위해 `unwrap_or_else()`를 사용할 수도 있다.


```rust
// Option
// Option은 Some(value) 또는 None일 수 있음
Some(1).unwrap_or(0); // 1로 평가됨
None.unwrap_or(0); // 0로 평가됨
None.unwrap_or_else(get_default); // get_default 함수를 호출한 결과로 평가됨

// Result
// Result는 Ok(value) 또는 Err(error)일 수 있음
Ok(1).unwrap_or(0); // 1로 평가됨
Err("oh no").unwrap_or(1); // 1로 평가됨
```


### SQL

오라클의 PL/SQL에서는 NVL() 함수가 동일한 결과를 제공한다:

```sql
NVL(possibly_null_value, 'value if null');
```


SQL 서버/트랜잭트 SQL에는 동일한 프로토타입 패턴을 따르는 ISNULL 함수가 있다:

```tsql
ISNULL(possibly_null_value, 'value if null');
```

ISNULL을 IS NULL과 혼동하지 않도록 주의해야 한다. 후자는 어떤 내용이 NULL로 정의되어 있는지 여부를 평가하는 데 사용된다.

ANSI SQL-92 표준에는 오라클, SQL 서버, PostgreSQL, SQLite, 그리고 MySQL에 구현된 COALESCE 함수가 포함되어 있다. COALESCE 함수는 null이 아닌 첫 번째 인수를 반환한다. 모든 항이 null이면 null을 반환한다.

```postgresql
COALESCE(possibly_null_value[, possibly_null_value, ...]);
```

ISNULL과 COALESCE의 차이점은 ISNULL에 의해 반환되는 타입은 가장 왼쪽 값의 타입인 반면, COALESCE는 null이 아닌 첫 번째 값의 타입을 반환한다는 것이다.

### 스위프트

스위프트에서 nil 병합 연산자는 `??`이다. 이는 옵셔널 타입을 래핑 해제할 때 기본값을 제공하기 위해 사용된다:


```swift
optionalValue ?? valueIfNil
```


예를 들어, 페이지 제목이 없을 때 기본 제목을 부여하는 스위프트 코드를 구현하려는 경우, 다음과 같은 문장을 사용할 수 있다:


```swift
var suppliedTitle: String? = ...
var pageTitle: String = suppliedTitle ?? "Default Title"
```


이는 다음과 같은 더 장황한 코드 대신 사용된다:

```swift
var pageTitle: String = (suppliedTitle != nil) ? suppliedTitle! : "Default Title";
```


## 같이 보기
* ?:
* 엘비스 연산자
* 널 조건부 연산자
* 연산자 (프로그래밍)

## 각주

분류:조건문
분류:연산자 (프로그래밍)
분류:이항연산