본문 바로가기

Front-End: Web/JavaScript

[js] '?.' : 옵셔널 체이닝(optional chaining)

반응형

옵셔널 체이닝 '?.'

  • 스펙에 추가된 지 얼마 안된 문법. 따라서 구식 브라우저는 폴리필이 필요함.

 

옵셔널 체이닝이 필요한 이유 (with 예제)

ex 1) 사용자가 여러 명 있는데, 그 중 몇 명은 주소 정보를 가지고 있는 경우를 생각하자. 이 때, user.address.street로 접근하면 에러가 발생한다.

let user = {}; // 주소 정보가 없는 사용자
alert(user.address.street); // TypeError: Cannot read property 'street' of undefined

ex 2) 브라우저에서 동작하는 코드를 개발할 때 문제가 발생하는 경우도 있다. 자바스크립트로 페이지에 존재하지 않는 요소(Element)에 접근하여 요소의 정보를 가져오려고 하는 경우다.

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

 

그래서 이런 문제들을 해결하기 위해 && 연산자를 사용하곤 했었다.

let user = {}; // 주소 정보가 없는 사용자
alert( user && user.address && user.address.street ); // undefined, 에러가 발생하지 않습니다.

하지만 &&를 사용하면 문제점이 있다.

  • 원하는 프로퍼티에 접근하기 위해서 모든 구성요소들을 거쳐야하기 때문에 AND로 모두 연결하고, 원하는 프로퍼티가 있는지 없는지 확인하는 방법을 사용했었는데,
  • 이렇게 AND로 연결하면 코드가 으어어어어엄청 길어질 수 있다.

 

옵셔널 체이닝의 등장

옵셔널 체이닝의 역할

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

 

옵셔널 체이닝 사용해보기

옵셔널 체이닝을 사용했을 때 앞의 오류 예제들이 어떻게 되는지 다시 한 번 살펴보자.

예제 1) user.address.street에 안전하게 접근할 수 있다!

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

alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.

user 객체가 존재하지 않아도 user?.address를 읽더라도 에러가 발생하지 않는다.

let user = null;

alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
  • 위 예시를 보면 ?.은 '앞' 평가 대상에만 동작하고, 확장은 되지 않는다
  • 위의 user?.는 user가 null 혹은 undefined인 경우만 처리할 수 있다.
  • 만약, user가 null이나 undefined가 아니고 값이 존재한다면 user.address 프로퍼티가 있어야 한다. 그렇지 않으면 user?.address.street의 두 번째 연산자에서 에러가 발생한다.
🔶옵셔널 체이닝을 남용하지 않기!
- 존재하지 않아도 괜찮은 대상에만 사용하자.
- 예제 1에서는 user는 반드시 있는 값이고, address가 있는지 없는지 모르는 상황이다. 그래서 user.address?.street 라고 사용하는 게 좋다.
🔶 옵셔널 체이닝의 평가대상인 ?. 앞의 변수는 꼭 선언되어 있어야만 한다!
- 옵셔널 체이닝은 선언이 완료된 변수를 대상으로만 동작한다.
- 변수 user를 letm const, var로 선언(정의)하지 않고 user?.anything 을 평가하면 에러가 발생한다.
- ReferenceError: user is not defined

 

단락 평가

  • 평가대상인 ?. 앞에 값이 없으면 즉시 평가를 멈춘다. 이런 평가 방법을 '단락 평가(short-circuit)'라 한다.
  • 아래 예제는 ?.의 평가대상이 null 혹은 undefined이므로 평가가 멈춘다. 그래서 오른쪽에 있는 부가 동작은 일어나지 않는다.
let user = null;
let x = 0;

user?.sayHi(x++); // 아무 일도 일어나지 않음
alert(x); // 0, x가 증가하지 않음

 

?.() : 존재가 확실치 않은 함수 호출하기

  • ?.은 연산자가 아니라, 함수대괄호와 함께 동작하는 특별한 문법 구조체(syntax construct)다.
  • 함수 관련 예제) - 존재 여부가 확실치 않은 함수를 호출하는 경우
let user1 = {
	admin() {
    	alert('관리자 계정입니다.');
    }
}

let user2 = {};

user1.admin?.(); // 관리자 계정입니다.
user2.admin?.();

user1, user2 객체가 존재하므로 admin 프로퍼티는 .만 사용하여 접근한다.(남용하지 않기!)

그리고 ?.()를 사용해서 admin의 존재 여부를 확인했다. user1엔 admin이 정의되어 있어서 메서드가 그대로 호출되었고, user2는 admin이 정의되지 않아서 메서드를 호출하면 에러 없이 그냥 평가가 멈춘다.

옵셔널 체이닝으로 안전하게 함수를 읽을 수 있다!

 

?.[] : 존재가 확실치 않은 객체 프로퍼티 접근하기

  • 객체 프로퍼티 관련 예제) - 객체 존재 여부가 확실치 않은 프로퍼티에 접근하는 경우
let user1 = {
	firstName: 'Violet'
};

let user2 = null;

let key = 'firstName';

alert(user1?.[key]); // Violet
alert(user2?.[key]); // undefined

alert(user1?.[key]?.something?.not?.existing); // undefined

user1과 user2 둘 다 정의가 됐지만, key값 'firstName'의 프로퍼티는 user1에만 정의되어 있다. 그래서 user1만 프로퍼티값을 가져오고, user2는 undefined를 반환한다.

user1에 firstName 뒤에 값은 없다. 그래서 없는 프로퍼티에 접근한 [key]?.부분에서 바로 평가를 멈추고 undefined를 반환한다.

 

delete와 조합하여 사용하기

delete user?.name; // user가 존재하면 user.name을 삭제함

 

🔶?.은 읽거나 삭제할 순 있지만, 쓰기는 할 수 없다.
- ?. 은 왼쪽에서 사용할 수 없다. 아래 예제는 user가 존재하는 경우에 user.name에 값을 쓰려는 의도로 작성한 코드이다.
user?.name = "Violet"; // SyntaxError: Invalid left-hand side in assignment​

 

이렇게 작성하면 에러가 발생하는데, 왜냐하면 undefined = 'Violet'가 되기 때문이다.

 

Recap

옵셔널 체이닝은 왼쪽 평가 대상이 null 혹은 undefined인지 확인하고, 아니라면 평가를 계속 진행하고, 맞다면 평가를 멈추고 undefined를 반환한다.

옵셔널 체이닝 문법은 세 가지 형태로 사용할 수 있다.

  1. obj?.prop ➡️ obj가 존재하면 obj.prop를 반환하고, 아니면 undefined를 반환함
  2. obj?.method() ➡️obj가 존재하면 obj.method()를 호출하고, 아니면 undefined를 반환함
  3. obj?.[prop] ➡️ obj가 존재하면 obj[prop]를 반환하고, 아니면 undefined를 반환함

?.를 계속 연결해서 체인을 만들면 중첩 프로퍼티에 안전하게 접근할 수 있다.

?.은 왼쪽 평가대상이 없어도 괜찮은 경우에만 선택적으로 사용해야 한다. 만약 왼쪽 평가대상이 꼭 있어야 하는 값인데 옵셔널 체이닝을 쓰면, 왼쪽 평가대상이 없는 경우에 프로그래밍 에러를 쉽게 찾지 못하기 때문이다.

 


참고 자료

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

반응형