옵셔널 체이닝이라는 기능이 등장하기 전에는, 존재하지 않는 프로퍼티를 오류없이 확인하고
싶다면 &&
연산자를 사용해서 오류없이 접근 했습니다.
user
라는 빈 객체가 있다고 가정해봅시다. 이 객체에 대한 정보를 알아내기 위해서 일반적으로
접근하면 프로퍼티가 존재하지 않기 때문에 오류를 발생시킵니다.
let user = {}; // 빈 user 객체 선언 ( user 객체에는 어드레스나 스트릿같은 정보가 없다 )
alert(user.address.street); // 정보나 주소에 관한 프로퍼티가 없기에 오류 발생
또 다른 사례로는 브라우저에서 동작하는 코드를 개발할 때 발생할 수 있는 문제가 있습니다.
JS
를 사용해서 페이지에 존재하지 않는 요소에 접근하고, 정보를 가져오려고 할 때 문제가
발생합니다.
// querySelector(...) 호출 결과가 null인 경우 에러 발생
let html = document.querySelector('.my-element').innerHTML;
이처럼, 특정 객체의 프로퍼티에 접근할 떄 그 프로퍼티가 존재하지 않는다면 오류를 발생
시키기에 접근할 수 없습니다. 오류 없이 접근해서, 프로퍼티가 존재하는지 알아보기 위해서
&&
연산자를 사용해서 접근할 수 있었습니다.
let user = { };
alert( user && user.address && user.address.street ); // undefined 반환, 오류 발생 X
/* */
AND 연산자를 사용하면 존재하지 않는 프로퍼티에 접근해도 오류 대신 `null` 이나 `undefined`
를 반환하고 평가를 종료하기 때문에 프로퍼티가 존재하는지에 대한 유무를 파악할 수 있었습니다.
오류없이 말이죠! 다만, 최대 단점으로 코드의 길이가 너무 길어진다는 단점이 있기에 이 문제를
해결하기 위한 대안책이 탄생합니다. 그 이름이 옵셔널 체이닝이라는 기능 입니다.
?.
?.
체이닝을 사용해서 프로퍼티에 존재유무를 확인할 수 있습니다.
존재하지 않다면 오류 대신, undefined
를 반환하므로 프로퍼티에 안전하게 접근할 수 있다.
평가 대상은 ?.
체이닝 앞에 있는 대상만 평가합니다. 하나의 대상만 평가하므로 코드의
길이가 짧습니다.
평가 대상이 null
이거나 undefined
인 경우에는 즉시 평가를 중단하고 undefined
를 반환
합니다. 평가 대상이 값이 있다면 영향을 끼치지 않습니다.
let user = { }; // 빈 객체 생성, 존재함
alert( user?.address?.street ); // undefined 반환
/* */
(1) user 객체 평가 -> 존재 -> user.address 평가 -> 존재하지 않음 -> 평가 중단 -> undefined 반환
(2) 평가가 종료 됬기에 street는 평가하지 않는다.
address 프로퍼티가 없으면 street도 없는 것과 마찬 가지이므로 평가할 필요도 없다.
결론 : 개발자는 undefined가 반환되어서 street 라는 프로퍼티가 user에 없구나 라고 인식함
❤️ 옵셔널 체이닝은 없는 객체에 접근해도 오류를 발생하지 않는다.
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
/* */
?.은 자신의 바로 "앞에 있는 대상만" 평가합니다. 다른 대상은 평가하지 않습니다.
alert( user?.address );
(1) user 평가 > 값이 null이네? 평가 중단 > undefined 반환
alert( user?.address.street );
(1) user 평가 > 값이 null이네? 평가 중단 > undefined 반환
❤️ 평가 대상에 값이 있는 경우
let user = { };
alert( user?.address ); // undefined
alert( user?.address.street ); // error
/* */
alert( user?.address )
1. user 평가 -> 존재 한다.
2. user.address 접근 -> 존재하지 않음 -> undefined 반환 후 평가 종료
alert( user?.address.street );
1. user 평가 -> 존재 한다.
2. user.address 접근 -> 없는 존재이므로 undefined 반환
3. user.address.street 접근 -> 오류 발생 -> 2번에서 address가 undefined를 반환 했으므로
user.undefined.street로 접근 되기에 오류가 발생한다.
오류 발생없이, street에 접근하고 싶다면 어떻게 해야하는지 우리는 이미 답을 알고 있습니다.
adddress를 평가해서 undedfined를 반환하고 연산을 종료하면 됩니다. strret 까지 접근하지 않게 되죠
alert( user.address?.street ); // undefined 반환
⚠️ 옵셔널 체이닝은 가치가 없는 프로퍼티에만 사용하자
옵셔널 체이닝은 평가 대상의 값이 없다면 오류대신에undefined
를 반환 합니다.
실수로 인해 필수로 있어야 하는 객체에 값이 없고, 이 객체를 평가한다면 오류 대신
undefined
를 반환하므로, 실수를 확인하지 못하고 값을 할당하지 못하는 비상 사태가
발생할 수 있어야 합니다. 평가 대상은 값이 필수로 있어야 하는 대상으로는 사용하면
안됩니다. 없어도 되는 프로퍼티만 평가 대상으로 사용 합시다.
위 구문에서는 사용자의 주소를 알아내기 위해서는 사용자 객체user
가 필수이니
이 객체는 평가하면 안되겟죠.user.addres?.street
가 바람직합니다.
⚠️
?.
앞에는 변수가 존재해야 한다.
변수가 선언되어 있지 않으면 오류가 발생합니다.alert(user?.address); // 오류 발생
?.
는 평가대상에 값이 없으면 평가를 즉시 멈춥니다. 이런 프로세서를 단락평가라고 합니다.
그렇기 때문에 ?.
우측에 있는 동작들은 ?.
평가로 인해 멈춘경우 더는 수행하지 않습니다.
let user = null; // user는 값이 없음
let x = 0;
user?.sayHi(x++); // 아무 변화가 없다. (이 함수는 예시입니다)
alert(x); // 0
?.[]
?.
은 연산자가 아닙니다. ?.
은 함수나 대괄호와 함께 동작하는 특별한 문법 구조체입니다.
존재여부가 확실하지 않은 함수를 호출할 때 어떻게 ?.()
를 사용할 수 있는지 살펴보겠습니다.
let user1 = {
admin() {
alert('관리자 계정입니다');
}
}
let user2 = {};
user1.admin?.(); // 관리자 계정입니다.
user2.admin?.();
/* */
user1.admin?.();
1. user1 평가 > 존재 한다
2. user1.admin 평가 > 존재 한다
3. user1.admin() 메서드 호출 > '관리자 계정입니다'
user2.admin?.();
1. user2 평가 > 존재 한다
2. user2.admin 평가 > 존재하지 않음 undefined 반환 후 평가 종료
3. 즉 user2.admin() 메서드는 존재하지 않음
❤️ .점 대신 대괄호를 사용한 옵셔널 체이닝
let user1 = {
name: '철수'
};
let user2 = null;
let key = 'name';
alert( user1?.[key] ); // 철수 ( user1.name )
alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.exit ); // undefined
/* */
alert( user1?.[key] );
1. user1 평가 > 존재 한다
2. user1.name 평가 > 존재 한다 > '철수' 출력
alert( user2?.[key] );
1. user2 평가 > 없는 존재 > undefined 반환 후 평가 중단
alert( user1?.[key]?.something?.not?.exit );
1. user1 평가 > 존재 한다
2. user1.name 평가 > 존재 한다
3. user.name.something 평가 > 존재하지 않음 > undefined 반환 후 평가 종료
🔔
Delete
연산자와 같이 사용 가능delete user?.name; // user가 존재하면 user.name을 삭제합니다.
🔔 옵셔널 체이닝
?.
은 읽거나 삭제는 가능하지만 값을 할당할 수 없다.
?.
은 할당 연산자 좌측에서 사용할 수 없습니다.let user = { name:0 }; user?.name = 'name'; // user.name 객체가 있더라도 불가능
옵셔널 체이닝 문법은 3 가지 형태로 사용됩니다.
obj?.prop
: obj가 존재하면 obj.prop
를 수행합니다. 존재하지 않다면 undefined
반환obj?.[prop]
: obj가 존재하면 obj.[prop]
을 수행, 존재하지 않다면 undefined
반환obj?.method.()
: obj가 존재하면 메서드를 수행하고 아니면 undefined
반환?.
을 사용해서 존재하지 않는 중첩객체도 오류 없이 접근할 수 있습니다. 다만, 필수로
검사해야 하는 부분에서 사용하면 값이 없을 때 undefined
를 반환하므로, 없어도 되는
프로퍼티 키에만 사용합시다.