이전에 기록해놨던 코드가 떠올랐다.
const [currentTeam, setCurrentTeam] = useState(
user?.currentTeam ?? (teams.length > 0 ? teams[0].id : undefined)
);
처음엔 그냥 잘 넘어갔던 코드인데, 어느 순간 user.currentTeam
이 빈 문자열인데도 기본값으로 바뀌어버리는 상황을 겪고 나서 이 코드가 눈에 밟히기 시작했다. 처음에는 그냥 ||
를 써도 무방하다고 생각했지만, 의외로 작은 차이가 UX와 버그 발생에 직접적인 영향을 줄 수 있다는 것을 경험하게 되었다.
이 글은 그 경험을 바탕으로 ??
, ||
그리고 관련 문법을 정리해보려 한다.
??
와 ||
, 뭘 기준으로 다르게 동작할까?
??
(null 병합 연산자)는 값이 null
또는 undefined
인 경우에만 대체 값을 사용한다. 반면, ||
(논리 OR 연산자)는 모든 falsy 값(false
, 0
, ""
, null
, undefined
, NaN
)을 없는 값으로 간주해 오른쪽 값을 사용한다.
예를 들어보자.
const name = userInput ?? "Guest"; // 입력이 null/undefined일 때만 "Guest"
const name = userInput || "Guest"; // 입력이 '', 0, false여도 "Guest"로 대체됨
userInput
이 빈 문자열이면 ??
는 그대로 ""
를 사용하지만, ||
는 이를 무시하고 "Guest"
를 사용한다. 이 차이는 사용자 입력이나 설정값처럼 "의도적으로 비워놓은 값"을 허용할지 말지에 큰 영향을 준다.
실전에서 벌어지는 문제들
입력값이 무시되는 문제
const nickname = user.nickname || "Anonymous";
사용자가 일부러 ""
를 입력했는데도 "Anonymous"로 나와버린다면, 꽤 어색한 상황이 된다. 이런 경우에는 ??
가 더 적절하다.
숫자 0
이 무시되는 문제
const pageSize = config.limit || 10;
limit
이 0이라면? ||
는 이를 falsy로 간주해 10
을 사용하게 된다. 하지만 0은 분명히 의미 있는 값이다.
그래서?
const nickname = user.nickname ?? "Anonymous";
const pageSize = config.limit ?? 10;
처럼 null
이나 undefined
만 처리하고, 나머지는 존재하는 값으로 인정해야 한다.
Optional chaining (?.
)과 함께 쓰면 더 강력해진다
자바스크립트에서는 중첩된 객체를 안전하게 접근하기 위해 다음과 같이 사용한다.
const teamId = user?.currentTeam ?? teams[0].id;
여기서 user?.currentTeam
은 user
가 nullish일 경우 에러를 발생시키지 않고 undefined
를 반환한다. 이 방식은 ??
와 아주 잘 어울린다. 왜냐하면 이후에 ??
로 기본값을 줄 수 있기 때문이다.
??=
연산자
ES2021에서 도입된 ??=
연산자는 아래처럼 쓸 수 있다.
config.timeout ??= 5000;
위 코드는 다음과 같다:
if (config.timeout === null || config.timeout === undefined) {
config.timeout = 5000;
}
단순하고 명확하게 기본값을 설정하는 문법이며, 값이 이미 존재한다면 덮어쓰지 않는다.
사용 케이스
React 상태 초기화 시
const [limit, setLimit] = useState(config.itemsPerPage ?? 10);
→ 0도 허용하려면 ??
, 무조건 fallback이 필요하다면 ||
.
서버 응답 처리
const userName = response?.data?.user?.name ?? "비회원";
→ 객체가 깊이 중첩되어도 안전하게 기본값 제공.
안전한 프로퍼티 할당
settings.timeout ??= 3000;
settings.language ??= "ko";
→ 이미 값이 설정돼 있다면 건드리지 않음.
6. 결론: 어떤 상황에서 어떤 연산자를 써야 할까?
- 값이 없을 수 있고, 0/false/""는 유효한 값일 때 →
??
- 값이 비어 있거나 falsy하면 무조건 기본값으로 대체할 때 →
||
마무리
자바스크립트의 연산자들은 겉보기엔 비슷해 보여도, 실제 동작은 아주 다르다. ??
와 ||
는 단순한 선택의 문제가 아니다. 이 둘의 차이는 기본값을 어떻게 정의하느냐, 어떤 값을 유효하다고 판단하느냐에 대한 개발자의 철학이 반영되는 지점이다.
실제로 코드를 리뷰하다 보면 ||
로 처리한 부분이 0
이나 false
때문에 문제가 되지 않을까? 싶은 경우가 아주아주 드물게 있었다. 그래서 최근에는 우선 ??
부터 고려하고, 정말 ||
가 필요한 상황만 선택적으로 사용하고 있다.
무심코 써왔던 연산자지만, 혹시나 이 글을 보게 되셨다면 한 번쯤은 고민해 보시길!!