JavaScript · 2장 기본
논리 연산자와 ??로 기본값 다루기
"회원이면서 성인일 때", "이름이 없으면 손님으로"처럼 조건을 묶고 기본값을 채우는 일은 거의 모든 코드에 나와요. 논리 연산자가 실제로 무엇을 돌려주는지 알면, 짧고 정확한 코드를 쓸 수 있어요.
01. &&, ||, !로 조건 묶기
&&(그리고)는 양쪽이 모두 참이어야, ||(또는)는 한쪽만 참이어도 참이에요. !(아니다)는 참과 거짓을 뒤집어요. 섞어 쓰면 ! → && → || 순서로 계산되는데, 읽는 사람이 순서를 따지지 않아도 되게 괄호로 묶어 쓰는 게 좋아요.
const isMember = true;
const age = 17;
console.log(isMember && age >= 19); // false — 회원이지만 성인이 아니에요
console.log(isMember || age >= 19); // true
console.log(!isMember); // false
console.log((isMember || age >= 19) && age > 10); // true
02. 답이 정해지면 멈춰요: 단락 평가
||와 &&는 왼쪽부터 값을 보다가 답이 정해지는 순간 멈추고, 그때 본 값을 그대로 돌려줘요. 결과가 꼭 true · false인 게 아니에요.
A || B: A가 참 같은 값이면 A를, 아니면 B를 돌려줘요. → "A가 없으면 B"A && B: A가 거짓 같은 값이면 A를, 아니면 B를 돌려줘요. → "A가 있을 때만 B"
console.log('' || '이름 없음'); // 이름 없음
console.log('하늘' || '이름 없음'); // 하늘
console.log(0 && '여기는 안 봐요'); // 0
console.log(1 && '여기까지 봐요'); // 여기까지 봐요
const isAdmin = false;
isAdmin && console.log('관리자 메뉴를 보여요'); // 아무것도 출력하지 않아요
console.log('끝');
멈춘 뒤의 코드는 실행조차 되지 않아요. 그래서 isAdmin && showMenu()처럼 "조건이 맞을 때만 함수 부르기"를 한 줄로 쓰기도 해요. 다만 읽기 쉬운 건 여전히 if라서, 화면 템플릿(React의 JSX 등)처럼 식만 쓸 수 있는 자리가 아니면 if를 권해요.
03. || 대신 ??: 0과 빈 문자열 지키기
||로 기본값을 채우면 문제가 하나 있어요. 0, '', false도 거짓 같은 값이라 정상 값인데도 기본값으로 바꿔 버려요. 음량 0, 할인율 0, 빈 메모는 "값이 없음"이 아니라 "0이라는 값"인데도요.
??(널 병합 연산자)는 왼쪽이 null이나 undefined일 때만 오른쪽을 써요. 그래서 기본값을 채울 때는 ??가 맞는 경우가 대부분이에요.
const settings = { volume: 0, nickname: '' };
console.log(settings.volume || 50); // 50 — 0을 "없음"으로 봤어요
console.log(settings.volume ?? 50); // 0
console.log(`[${settings.nickname || '손님'}]`); // [손님]
console.log(`[${settings.nickname ?? '손님'}]`); // [] — 빈 문자열을 지켰어요
console.log(settings.theme ?? 'light'); // light — 없는 속성은 undefined라서 기본값
앞 글의 객체 표에도 바로 쓸 수 있어요. STATUS_LABEL[status] ?? '알 수 없는 상태'처럼요. 한 가지 규칙이 있어요. ??를 || · &&와 괄호 없이 섞으면 문법 오류예요. 어느 쪽을 먼저 계산할지 괄호로 밝혀야 해요.
const a = null, b = 0;
console.log(a ?? b || 'c');
04. ??=, ||=, &&=
"비어 있으면 기본값을 넣어 두기"는 자주 하는 일이라 줄임 표기가 있어요. x ??= 값은 x가 null · undefined일 때만 값을 넣어요. 같은 방식으로 ||=와 &&=도 있어요.
const options = { retries: 0 };
options.retries ??= 3; // 0은 값이 있는 거라 그대로
options.timeoutMs ??= 5000; // 없던 속성이라 채워요
console.log(options); // { retries: 0, timeoutMs: 5000 }
할인율을 주지 않으면 10%를 기본으로 할인하는 함수예요. 그런데 "할인 없음"을 뜻하는 0을 넣어도 10%가 할인돼요. 한 곳을 고쳐서 세 줄이 차례로 8000, 9000, 10000이 나오게 해 보세요.
function finalPrice(price, discountRate) {
const rate = discountRate || 0.1;
return price * (1 - rate);
}
console.log(finalPrice(10000, 0.2));
console.log(finalPrice(10000));
console.log(finalPrice(10000, 0));
해답 보기
discountRate || 0.1은 0을 거짓으로 보고 0.1로 바꿔요. ??로 바꾸면 할인율을 주지 않았을 때(undefined)만 0.1을 써요.
const rate = discountRate ?? 0.1;
매개변수 기본값 function finalPrice(price, discountRate = 0.1)로 써도 같은 결과예요. 이건 함수 글에서 다뤄요.
05. 실전 포인트
- 언제 쓰나요? 조건을 묶을 땐
&&·||를 괄호와 함께 써요. 서버 응답 · 설정 · 함수 인자에서 빠진 값을 채울 땐??를 기본으로 써요. - 대신 쓰는 것:
||기본값은 "빈 문자열도 없는 것으로 치고 싶을 때"처럼 의도가 분명할 때만 써요. 함수 인자의 기본값은 매개변수 기본값(= 0.1)이 더 읽기 쉬워요. 깊은 속성을 꺼낼 땐 옵셔널 체이닝?.(4장)과 함께 써요. - 흔한 실수:
count || 1로 0을 1로 바꾸는 것,&&로 함수를 부르는 줄이 늘어나 읽기 어려워지는 것,??와||를 괄호 없이 섞어 문법 오류가 나는 것이에요.
정리
&&는 둘 다,||는 하나라도,!는 뒤집기예요. 섞을 땐 괄호로 순서를 밝혀요.||·&&는 답이 정해지면 멈추고, 그때 본 값을 그대로 돌려줘요.- 기본값은
??로 채워요.null·undefined일 때만 바꾸고 0 ·''·false는 지켜요. x ??= 값은 비어 있을 때만 값을 넣어요.