자바스크립트 null 병합 연산자와 옵셔널 체이닝

김혜진·2020년 6월 27일
7

javascript

목록 보기
7/9
post-thumbnail

틀린 것 잘못된 것들은 올바르게 잡아주시면 감사하겠습니다.

 

Nullish coalescing operator (null 병합 연산자)

nullish coalescing operator ??은 nullish한 값(null 혹은 undefined)을 판별하여 or 연산을 하는 연산자이다.

왼쪽 피연산사가 nullish한 값(null 혹은 undefined)한 경우에 오른쪽 피연산자를, 그렇지 않은 경우에는 왼쪽 피연산자를 반환한다.

예제

const x = hello ?? hi();

따라서 위의 코드는 아래의 연산과 동일하다.

const x = (hello !== null && hello !== undefined) ?
    hello :
    hi();

foo (왼쪽의 피연산자)가 nullish하다면 hi()를 반환, 그렇지 않다면 hello를 반환한다.

여기까지만 보면 기존의 or 연산자 ||와 크게 다름 없어 보이는데, 실제로도 타입스크립트 문서에서도 기본 값으로 사용하는 경우??||를 대체할 수 있다고 쓰여있다.

The ?? operator can replace uses of || when trying to use a default value. For example, the following code snippet tries to fetch the volume that was last saved in localStorage (if it ever was);
-typescript 문서 중-

그리고 아래와 같은 예제 코드가 나와있는데...

function initializeAudio() {
    const volume = localStorage.volume || 0.5;

    // ...
}

localStorage에 저장된 volume 값이 있으면 해당 값을 변수 volume에 할당하고 그렇지 않은 경우에 0.5를 할당하라는 or 연산... 그러나 || 사용 시에는 버그가 발생할 수 있었으니..

자바스크립트의 truty, falsy 값에 혼란을 주는 것들이 몇몇 있다는 것..

대표적으로 빈 문자열 ''과 숫자 0, NaN

다른 언어 하시는 분들이 자바스크립트에 회의적인 것이.. 이해가 가기도 한다..ㅎㅎ..

 

?? !== ||

따라서 위의 예제에서 발생할 수 있는 버그는

const volume = localStorage.volume || 0.5;

localStorage.volume 값이 0이라면 falsy 값으로 판단되어 0.5가 할당되는 것이다.
음소거는 반영되지 않는다니... 안돼애..

해당 연산을 nullish operator를 사용하여 아래와 같이 변경한다면

const volume = localStorage.volume ?? 0.5;

localStorage.volume가 빈 문자열이나 0, NaN와 같은 falsy값이라 해도 nullish한 경우에만 0.5가 할당될 것이다.

When localStorage.volume is set to 0, the page will set the volume to 0.5 which is unintended. ?? avoids some unintended behavior from 0, NaN and "" being treated as falsy values.
-typescript 문서 중-

실제 falsy 값들로 인해서 의도치 않은 연산이 된 경우가 잦았던 것을 생각해보면 ?? 연산자로 확실히 더 명확하게 불필요한 타입들을 걸러낼 수 있게 된 것 같다.

 

nullish에서 null 제거

nullish한 값이 들어올 수 있는 경우 null의 경우에도 undefiend로 반환시켜 사용하기 위해 아래와 같은 방식으로 null을 제거할 수 있다.

const x = data ?? undefiend;

nullundefined 모두 값이 없음을 나타내는 데이터 타입이긴 하지만 null은 없는 값임을 할당하여 명시적으로 보여주는 것이므로 undefiend와는 엄연히 다르다.

실제로 변수 선언 후 값을 할당하지 않으면 undefined가 할당되어 값이 없음을 나타내지만 null은 변수에 직접 할당을 해주어야 한다.

let data;
data // undefined

data = null;
data // null

falsy 값들은 ! not 연산자로 묶어서 처리하던 이전의 나.. 반성하라....

 

Optional chaining

Optional chaining ?.. 체이닝과 동일한 기능을 하는데 다른 점은 참조 값이 nullish한 경우에 에러를 뿜지 않고 undefiend를 반환한다.

이것은 참조가 누락될 가능성이 있는 경우 연결된 속성으로 접근할 때 더 짧고 간단한 표현식이 생성된다. 어떤 속성이 필요한지에 대한 보증이 확실하지 않는 경우 객체의 내용을 탐색하는 동안 도움이 될 수 있다.
-MDN 문서 중-

예제

const person = {
  name: undefined
};

person.name // undefined
person.name.firstName // Error! Uncaught TypeError: Cannot read property 'firstName' of undefined
person.name?.firstName // undefined

 
error가 아닌 undefined를 반환하므로 코드를 간략히 하는데 요긴하게 사용되며, 나아가 일종의 에러 핸들링의 역할도 한다.

<div>
  {data && data.dogs && data.dogs.map(dog => (
    <option key={dog.id} value={dog.breed}>
      {dog.breed}
    </option>
  ))}
</div>

위의 로직는 아래의 동일하다.

<div>
  {data?.dogs?.map(dog => (
    <option key={dog.id} value={dog.breed}>
      {dog.breed}
    </option>
  ))}
</div>

 

함수 호출도 가능

someInterface내에 customMethod가 없는 경우!

const someInterface = {
  otherMethod: () => {
    return 'otherMethod called!';
  }
};

someInterface.customMethod(); // Error! Uncaught TypeError: someInterface.custromMethod is not a function
someInterface.customMethod?.(); // undefined

someInterface내에 customMethod가 있는 경우!

const someInterface = {
  customMethod: () => {
    return 'customMethod called!';
  }
};
someInterface.customMethod.(); // 'customMethod called!'
someInterface.customMethod?.(); // 'customMethod called!'

 

nullish operator와 함께 사용

const customer = {
  name: "Carl",
  details: { age: 82 }
};

const customerCity = customer?.city ?? "Unknown city";

console.log(customerCity); // Unknown city

 
apollo 쿼리를 사용하여 데이터를 받아오는 경우엔 아래와 같이 핸들링 할 수 있다.

function Dogs({ onDogSelected }) {
  const { loading, error, data } = useQuery(GET_DOGS);
  const dogs = data?.dogs ?? [];

  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;

  return (
    <select name="dog" onChange={onDogSelected}>
      {dogs?.map(dog => (
        <option key={dog.id} value={dog.breed}>
          {dog.breed}
        </option>
      ))}
    </select>
  );
}

The nullish coalescing operator is another upcoming ECMAScript feature that goes hand-in-hand with optional chaining, and which our team has been involved with championing in TC39.
-typescript 문서 중-

입사 후 타입스크립트를 사용하면서 처음 접한 연산자들라 타입스크립트에서만 사용되는 건줄 알았는데 optional chaining과 함께 새로운 자바스크립트의 기능이었다.. 읍! 재밌다

 

소소한 퀴이즈

(0 || undefined) ?? 'yejinh'

('0' && null) ?? 'yejinh'

null ?? (undefined && 0)

null ?? undefined ?? 0

const obj = {
  name: ''
};

obj.name ?? 0

obj.name || 0

obj?.age ?? 0

(obj.name ?? 0) && null

obj.name || (0 ?? null)

각각의 연산은 무엇을 반환할까요 ?

정답은 개발자도구 창에서 직접 확인해보세요오오

참고

MDN Nullish coalescing operator

MDN falsy값

MDN 논리 연산자

MDN Optional chaining

Apollo Client doc

Typescript doc

profile
꿈꿀 수 있는 개발자가 되고 싶습니다

3개의 댓글

comment-user-thumbnail
2020년 8월 29일

프로미스에 대한 것 검색하다 쉽게 풀어 쓴 글 보고 감탄! 받아 다른 글들도 찾아서 보는 중입니다. 개념 설명을 참 잘하세요! 감사드립니다

1개의 답글
comment-user-thumbnail
2021년 6월 16일

falsy 값들은 ! not 연산자로 묶어서 처리하던 이전의 나.. 반성하라....

이게 어떤 의미인지 여쭤봐도 될까요? ! 연산자로 묶는거랑 크게 차이가 없는것 같아 보여서요.

답글 달기