본 포스팅은 JavaScript에서 물음표 연산자 사용 방법을 소개합니다.
개요
JavaScipt에서 물음표 연산자는 주로 if…else문을 간결하게 만들어주는 삼항 연산자로 사용된다. 그러나 물음표 연산자는 삼항 연산자 이외에도 두 가지 용도(Nullish 병합 연산자, 옵셔널 체이닝)로 사용할 수 있다.
1. 삼항 연산자(Ternary Operator)
“삼항”이라는 용어는 세 개의 항이 있다는 뜻이며, 삼항 연산자는 3개의 피연산자를 필요로 하는 연산자를 말한다. 여기서 세 개의 피연산자는 조건식, 참일 경우의 값 or 연산식, 거짓일 경우의 값 or 연산식을 나타낸다.
조건식 ? (참일 경우의 값 or 연산식) : (거짓일 경우의 값 or 연산식)
조건식이 참일 경우 참일 경우의 값 or 연산식이 실행되고 조건식이 거짓일 경우 거짓일 경우의 값 or 연산식이 실행된다. 예를 들어, 다음과 같이 조건식에 따라 반환 결과를 다르게 설정할 수 있다.
let age = 30;
let message = age >= 19 ? '성인' : '청소년'
console.log(message); // 성인
위 예시를 if…else문으로 표현하면 다음과 같다.
let age = 30;
let message = '';
if (age >= 19) {
message = '성인';
} else {
message = '청소년';
}
console.log(message); // 성인
삼항 연산자를 사용하면 코드가 간결해지고 가독성이 향상될 수 있지만, 조건문이 지나치게 중첩된 경우에는 가독성 문제로 if…else문이 더 적합할 수 있다.
예를 들어, 계절이 여름인지 확인한 다음 온도를 체크하는 조건문을 삼항 연산자로 작성하면 아래와 같다.
const weather = {
season: 'summer',
temperature: 31
};
let message = weather.season === 'summer' ?
weather.temperature >= 30 ? '폭염' : '정상'
:
'여름 아님';
console.log(message); // 폭염
중첩된 조건문을 삼항 연산자로 작성했을 때, 가독성이 좋지 않은 것을 볼 수 있다. 대부분의 JavaScript 개발자들은 가독성 문제로 중첩된 조건문은 if…else문으로 작성하도록 권장한다.
위 예시를 if…else문으로 표현하면 다음과 같다.
const weather = {
season: 'summer',
temperature: 31
};
let message = '';
if(weather.season === 'summer') {
if(weather.temperature >= 30) {
message = '폭염';
} else {
message = '정상';
}
} else {
message = '여름 아님';
}
console.log(message); // 폭염
2. Nullish 병합 연산자(Null Coalescing Operator)
이중 물음표 연산자(??)인 Nullish 병합 연산자는 왼쪽 피연산자가 null 또는 undefined일 때, 오른쪽 피연산자를 반환하고 그렇지 않으면 오른쪽 연산자를 반환하는 논리 연산자다.
다음은 Nullish 병합 연산자를 사용하는 간단한 예시다.
const a = null;
const b = 'Hello';
const result = a ?? b;
console.log(result); // Hello
Nullish 병합 연산자는 언뜻 보기에 OR 연산자(||)와 비슷해 보일 수 있지만, 처리 방식이 다르다.
- Nullish 병합 연산자: null, undefined 값에 대해 처리한다.
- OR 연산자: Boolean 값에 대해 처리한다.
JavaScript는 null과 undefined를 falsy한 값으로 간주하므로 왼쪽 피연산자가 null 또는 undefined인 경우 Nullish 병합 연산자와 OR 연산자의 실행 결과가 동일하다.
const a = null;
const b = 'Hello';
const nullishResult = a ?? b;
const orResult = a || b;
console.log(nullishResult); // Hello
console.log(orResult); // Hello
하지만 null 또는 undefined가 아닌 falsy한 값(”, 0, NaN, false)인 경우 실행 결과가 다른 것을 알 수 있다.
const a = NaN;
const b = 'Hello';
const nullishResult = a ?? b;
const orResult = a || b;
console.log(nullishResult); // NaN
console.log(orResult); // Hello
그리고 Null 병합 연산자와 AND 연산자, OR 연산자를 혼합해서 사용할 경우에는 반드시 소괄호를 사용하여 연산자 우선 순위를 설정한다.
const result = (null ?? false) || 'Hello';
console.log(result); // Hello
소괄호를 사용하지 않고 Null 병합 연산자와 AND 연산자를 혼합해서 사용하면 SyntaxError가 발생한다.
null ?? false || 'Hello'; // Uncaught SyntaxError: Unexpected token '||'
null || false ?? 'Hello'; // Uncaught SyntaxError: Unexpected token '??'
3. 옵셔널 체이닝(Optional Chaining)
옵셔널 체이닝(?.)은 객체의 속성을 접근할 수 없는 상황일 때, 오류가 발생하지 않도록 해주는 연산자다. JavaScript에서 객체의 속성을 접근했을 때, TypeError가 발생하는 상황은 다음과 같다.
- 객체 변수가 객체가 아닌 값으로 할당되었을 경우
- 중첩된 객체에 존재하지 않는 속성을 접근하는 경우
다음과 같이 person이라는 변수를 빈 객체로 선언하고 name 속성을 접근하는 경우 undefined를 반환한다.
const person = {};
console.log(person.name); // undefined
하지만, 다음과 같이 person이라는 변수를 도중에 null로 할당하고 name 속성을 접근하는 경우 TypeError가 발생한다.
let person = {};
person = null;
console.log(person.name);
// Uncaught TypeError: Cannot read properties of null (reading 'name')
그리고 다음과 같이 중첩된 객체에 존재하지 않는 속성을 접근하는 경우에도 TypeError가 발생한다.
let person = {};
console.log(person.name.firstName);
// Uncaught TypeError: Cannot read properties of undefined (reading 'firstName')
이러한 경우 옵셔닐 체이닝 연산자인 ?.를 사용한다. 옵셔널 체이닝을 사용하면 TypeError가 발생하는 대신 undefined를 반환한다.
person이라는 변수는 중간에 null로 할당되었으며, name이라는 속성이 존재하지 않지만 옵셔널 체이닝 연산자를 사용했으므로 undefined를 반환한다.
let person = {};
person = null;
console.log(person?.name); // undefined
person이라는 변수에 name이라는 속성이 존재하지 않으므로 person.name은 undefined를 반환한다. 그리고 옵셔널 체이닝 연산자를 사용해서 undefined인 name 속성의 firstName 속성을 접근한다.
let person = {};
console.log(person.name?.firstName); // undefined
옵셔널 체이닝을 사용하여 객체의 속성을 접근할 때, TypeError를 방지할 수 있지만 지나치게 남용하는 경우 코드의 흐름을 이해하기 어렵게 만들 수 있으므로 필요한 경우에만 사용하는 것이 좋다.
참고
- What IS the ?? Nullish Coalescing Operator in JavaScript?
- 3 Uses of ‘?’ in JavaScript ?
- How the Question Mark (?) Operator Works in JavaScript
- JavaScipt I How does the Question Mark(?) Work? (examples)
- [JavaScript]물음표 사용 방법, Null 병합 연산자, 옵셔널 체이닝