모던자바스크립트, 옵셔널체이닝

Edwin·2023년 3월 9일
0

  • 본 포스트은 모던자바스크립트 Deep Dive 9장 4.2를 기반으로 정리했습니다.

9.4 단축평가

논리연산 가운데 논리곱(&&)은 A && B => A and B 모두를 부합하는 값을 논리적으로 연산할 때 사용된다. 그리고 결과는 B를 반환한다. 이번주 리액트를 다루면서, 조건문에서 else 구분이 필요없는 경우, 논리곱 연산을 통해서 코드를 기록했었다.

// 리액트 컴포넌트 
const Home = () => {
	const [isDone, setIsDone] = useState(true);
 
	return (
      {isDone && <div>"이게 논리곱 연산을 사용하는 경우야"</div>}
    )
}  

const isDone이 true 이기 때문에, 해당 컴포넌트에 따라서 뷰(view)에서는 "이게 논리곱 연산을 사용하는 경우야" div 태그가 생성될 것이다.

// 리액트 컴포넌트 
const Home = () => {
	const [isDone, setIsDone] = useState(false);
 
	return (
      {isDone && <div>"이게 논리곱 연산을 사용하는 경우야"</div>}
    )
}  

반면에, isDone(false)이기 때문에 뷰에는 아무것도 표시되지 않을 것이다. 그런데 이를 사용하기 위해서는 아래와 같이 !를 통해서 값을 변경해 주면 된다.

// 리액트 컴포넌트 
const Home = () => {
	const [isDone, setIsDone] = useState(true);
 
	return (
      {!isDone && <div>"이게 논리곱 연산을 사용하는 경우야"</div>}
    )
}  

isDone의 상태는 false이지만, !isDone이 선언되면 true가 되기 때문에 뷰(view)에 표현되는 것이다. 즉 아래와 같이 사용하면 둘 다 뷰(view)에 표현되는 것이다.

// 리액트 컴포넌트 
const Home = () => {
	const [isDone1, setIsDone] = useState(true);
	const [isDone2, setIsDone] = useState(false);
 
	return (
	<>
      {isDone1 && <div>"이건 true의 상태를 사용한 경우야"</div>}
      {!isDone2 && <div>"이건 false의 상태를 사용한 경우야"</div>}
    </>
    )
}  

단축평가란?

  • 모던자바스크립트 딥다이브(p.119)

위의 사례에서 본 것처럼 논리곱과 논리합은 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반영하는 반환하는데, 이를 단축평가라고 한다. 딥다이브는 이를 아래와 같이 서술한다.

"단출평가는 표현식을 평가하는 도중에 평가 결과가 확정된 경우 나머지 평가과정을 생략하는 것을 말한다"

위의 이미지에서 논리합(||)을 보면 A or B 에서 true인 값을 하나라도 찾으면 평가를 종료하기에, 'cat'가 결과로 반환되는 것이고, false || 'Dog'에서는 true인 'Dog'가 결과로 반환된 것이다. 딥다이브에서도 이런 단축평가를 활용하요 if문을 대체하는 경우에 대해서 소갷나다.

옵셔널 체이닝 연산자( ?. )

옵셔널 체이닝( ?. )은 ES11(2020년)에 들어서 도입된 연산자 이다. 이는 좌항의 피연산자가 null 또는 undefined인 경우 undefined를 반환하고, 값이 존재하면 우항의 정보를 반환한다.

말이 조금 어려운데, MDN-optional Chaining을 살펴보자. 개발의 상황에서 참조한 자료가 nullish(null 또는 undefined)이라면, 해당 연산자를 사용하기 전에는 Error가 발생되었다. 리액트의 상황에서는 화면이 표시되지 않을 것이다. 반면에 해당 연산자를 사용하면, 에러가 아니라 값을 undefined로 반환되기 때문에 내용이 반영되지 않을 뿐, 뷰 동작한다.

해당 연산자를 사용하는 경우에 대해서 MDN은 참조하는 자료의 보증이 확실하지 않은 경우라고 정의한다. MDN의 코드사례를 리뷰해보자. 콘솔에 아래와 같이 입력해보자. (Mac 기준 단축키는 ⌘ + ⌥ + C)

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const dogName = adventurer.dog.name;
console.log(dogName);

객체 adventurer를 살펴보면 해당 프로퍼티에 dog는 존재하지 않기에, 해당 프로퍼티를 담은 식별자 dogName를 콘솔에 기록하면, 값이 반환되는 것이 아니라 Error가 발생되는 것을 볼 수 있다. 객체가 아니라고 기록된다. 탐색을 하니 찾을 수 없었다는 말이다. 만약 프로그램이 실행되는 상황이었다면, 해당 구문에서 javascript 엔진은 멈추고 이후의 값을 실행하지 못한다.

이런 문제를 해결하고자 나온 개념이 옵셔널 체이닝인 것이다. 찾고자 하는 부분 앞에 ?를 달아보자.(그러나 옵셔널 체이닝은 ?. 이다. 아마도 중복되는 도트(.) 때문에 하나만 기록했는지는 의문이다.)

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const dogName = adventurer.dog?.name;
console.log(dogName);

이러한 기술적인 동작에 대해서 MDN-optional Chaining은 이렇게 말한다.

"optional chaining은 선언되지 않은 루트 객체에 사용할 수 없지만, 정의되지 않은 루트 객체와는 함께 사용할 수 있다."

?. 에서 기존의 도트(.)를 사용하는 예에 대해서

MDN-optional Chaining은 이렇게 설명한다.

". 대신에 ?. 연산자를 사용함으로써, 자바스크립트는 obj.first.second에 접근하기 전에 obj.first가 null 또는 undefined가 아니라는 것을 암묵적으로 확인하는 것을 알고 있다. 만약 obj.first가 null 또는 undefined이라면, 그 표현식은 자동으로 단락되어 undefined가 반환된다."

옵셔널 체이닝과 함수에 대해서; 비동기 처리

// Written as of ES2019
function doSomething(onContent, onError) {
  try {
    // ... do something with the data
  }
  catch (err) {
    if (onError) { // Testing if onError really exists
      onError(err.message);
    }
  }
}

데이터를 가져오기 위해서 시도(try)를 하고 선언된 성공하면, onContent 콜백함수를 실행할 것이다. 그러나 onError 실패하면(catch) onError 콜백함수를 실행할 것인데, 내용을 보면 에러로 설정한 message를 뱉어낼 것이다. 그러나 이를 옵셔널체이닝을 사용하면 아래와 같이 간편하게 서술할 수 있다.

// Using optional chaining with function calls
function doSomething(onContent, onError) {
  try {
   // ... do something with the data
  }
  catch (err) {
    onError?.(err.message); // no exception if onError is undefined
  }
}

ES11 이전, 이었다면 조건문을 사용해서 해당 내용에 접근해야 했지만, 옵셔널체이닝은 ?. 좌항의 값을 검사하고, 내용을 실행하는 것으로 보면 될 것 같다.

모던자바스크립 딥다이브의 코드예시

// 사례 01
const str = '';
const length = str && str.length;
console.log(length); // ''

// 사례 02
const str = 'a';
const length = str && str.length;
console.log(length); // 1

// 사례 03
const str = '';
const length = str?.length;
console.log(length); // 0 

설명에 따르면, 논리곱(&&)은 값이 좌항의 값이 false로 평가되는 Falsy 값(false, undefined, null, 0, -0, NaN, '')이라면 좌항 피연산자가를 반환한다. 즉 위의 코드는 식별자 length 안에 '' 이 담겨진 것이다. 즉 비어있는 값이 반환되기에 접근할 수 없는 것이다. 그러나 사례 02를 보자. 보면 값이 있기에 우항의 결과가 식별자 length에 담겨져서 콘솔에 결과를 반환한 것이다.

그러나 이에 옵셔널 체이닝을 사용했을 때는 달라지는데, 이는 예시가 '' 또는 0 을 사용했을 때만 기대할 수 있는 예이다. 모던다이브는 이를 "원시 값과 래퍼 객체"라고 서술한다.(21장.3절) 이에 따라서, 이는 객체로 취급이 되고, 객체의 길이를 구한 것인데, 그 값으로 0을 반환했다고 한다.

const str = {};
console.log(str.length) // undefined

그러나 위와 같이 기록해서, undefined가 반환되는데, 이것이 0이 되는 것은 이유를 모르겠다. 오늘은 여기까지이다.

arthor. EDWIN
date. 23/03/10

profile
신학전공자의 개발자 도전기!!

0개의 댓글