본문 바로가기
Dev/JavaScript

[JavaScript] 옵셔널 체이닝 '?.'으로 객체 다루기

by 5kdk 2022. 12. 4.

✔ 들어가며

 

 프로퍼티가 없는 중첩 객체에 접근할 때, 에러 없이 안전하게 접근할 수 있는 방법이 필요한 경우가 있다. 프로퍼티의 여부를 확인하고 접근하는 방법도 있지만, 옵셔널 체이닝을 활용하면 표현식이 더 짧고 간단해진다.

 

 

✔ 옵셔널 체이닝이 필요한 상황들

 

💡 오브젝트 프로퍼티 접근

user.address.street로 접근할 때, 사용자들 중 몇몇은 주소 정보를 가지고 있지 않다면 에러가 발생한다.

let user = {}; // 주소 정보가 없는 사용자

alert(user.address.street); // TypeError: Cannot read property 'street' of undefined

 

💡  DOM 요소 접근

페이지에 존재하지 않는 요소에 접근하여 요소의 정보를 가져오려 하면 문제가 발생한다.

// querySelector(...) 호출 결과가 null인 경우 에러 발생
let html = document.querySelector('.my-element').innerHTML;

 

 

 

✔ 옵셔널 체이닝 살펴보기

 

?. ?. '앞’의 평가 대상이 undefined나 null이면 평가를 멈추고 undefined를 반환한다.

const nestedProp = obj.first?.second;

 

temp 변수가 실제로 생성되지 않는다는 점을 제외하면 다음과 같다.

const temp = obj.first;
const nestedProp =
  temp === null || temp === undefined ? undefined : temp.second;

 

배열 인덱스는 대괄호로 액세스해야 하므로 배열에 특히 유용하다.

function printMagicIndex(arr) {
  console.log(arr?.[42]);
}

printMagicIndex([0, 1, 2, 3, 4, 5]); // undefined
printMagicIndex(); // undefined; if not using ?., this would throw

 

🚫(주의) 옵셔널 체이닝 표현식의 결과에 할당을 시도하는 것은 유효하지 않다.

const obj = {};
obj?.property = 1; // SyntaxError: Invalid left-hand side in assignment

 

 

✔ 단락 평가

?.는 왼쪽 평가대상에 값이 없으면 즉시 평가를 멈춘다. 이런 평가 방법을 단락 평가(short-circuit)라고 부른다.

피연산자가 null 또는 undefined인 경우 표현식이 평가되지 않는다.

const potentiallyNullObj = null;
let x = 0;
const prop = potentiallyNullObj?.[x++];

console.log(x); // 0, x는 증가하지 않음

 

후속 프로퍼티 액세스도 평가되지 않는다.

const potentiallyNullObj = null;
const prop = potentiallyNullObj?.a.b;
// 평가가 이미 중지되었기 때문에 throw되지 않음
// 첫 번째 옵셔널 체인

 

이는 다음 코드와 같다.

const potentiallyNullObj = null;
const prop =
  potentiallyNullObj === null || potentiallyNullObj === undefined
    ? undefined
    : potentiallyNullObj.a.b;

 

 

✔ 옵셔널 체이닝 예시

 

💡 Basic 예시

맵에서 name해당 멤버의 속성 값을 찾는다. 해당 멤버가 없기 때문에 결과는 undefined다.

const myMap = new Map();
myMap.set("foo", { name: "baz", desc: "inga" });

const nameBar = myMap.get("bar")?.name;

 

💡 옵셔널 콜백 또는 이벤트 핸들러 다루기

만약 객체에서 구조 분해 할당으로 callbacks 또는 fetch 메서드를 사용한다면, 그 존재 여부를 테스트하지 않으면 함수로 호출할 수 없는 값이 있을 수 있다. ?. 을 사용하면, 다음 추가 테스트를 피할 수 있다.

// optional chaining 없이 작성된 코드
function doSomething(onContent, onError) {
  try {
    // 데이터로 무언가를 작업
  } catch (err) {
    // onError가 실제로 존재하는지 테스트
    if (onError) {
      onError(err.message);
    }
  }
}
// 함수 호출에 옵셔널 체이닝 사용
function doSomething(onContent, onError) {
  try {
    // 데이터로 무언가를 작업
  } catch (err) {
    onError?.(err.message); // onError가 undifined인 경우 예외 없음
  }
}

 

💡 옵셔널 체이닝 연산자 쌓기

중첩 구조를 사용하면 옵셔널 체이닝을 여러 번 사용할 수 있다

const customer = {
  name: "Carl",
  details: {
    age: 82,
    location: "Paradise Falls", // 상세 주소없음
  },
};
const customerCity = customer.details?.address?.city;

// 옵셔널 체이닝 함수 호출과도 작동함
const customerName = customer.name?.getName?.(); // 메서드가 존재하지 않음, customerName이 정의되지 않음

 

💡 null 병합 연산자와 같이 사용하기

null 병합 연산자는 optional chaining를 사용한 후에 아무 값도 찾을 수 없을 때 기본 값을 주기 위해 사용될 수 있다.

function printCustomerCity(customer) {
  const customerCity = customer?.city ?? "Unknown city";
  console.log(customerCity);
}

printCustomerCity({
  name: "Nathan",
  city: "Paris",
}); // "Paris"
printCustomerCity({
  name: "Carl",
  details: { age: 82 },
}); // "Unknown city"

 

 

✍ 요약

  • 옵셔널 체이닝 문법?. 의 세 가지 형태
    • obj?. prop – obj가 존재하면 obj.prop을 반환하고, 그렇지 않으면 undefined를 반환함
    • obj?.[prop] – obj가 존재하면 obj[prop]을 반환하고, 그렇지 않으면 undefined를 반환함
    • obj?.method() – obj가 존재하면 obj.method()를 호출하고, 그렇지 않으면 undefined를 반환함
  • 옵셔널 체이닝 문법은 꽤 직관적이고 사용하기도 쉽다. ?. 왼쪽 평가 대상이 null이나 undefined인지 확인하고 null이나 undefined가 아니라면 평가를 계속 진행한다
  • ?.를 계속 연결해서 체인을 만들면 중첩 프로퍼티들에 안전하게 접근할 수 있다
  • ?.은 ?.왼쪽 평가대상이 없어도 괜찮은 경우에만 선택적으로 사용해야 한다
  • null 병합 연산자와 함께 사용하면 default 값을 지정해 주는 방법으로 활용가능하다
  • 꼭 있어야 하는 값인데 없을 경우에 ?.을 사용하면 프로그래밍 에러를 쉽게 찾을 수 없으므로 이런 상황을 만들지 않도록 하자

 

 

 

📖 참고자료

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

https://ko.javascript.info/optional-chaining

 

댓글