산술·비교·논리 연산자 완전 정복
JavaScript 산술 연산자의 형변환 함정(+ 이중 역할), 비교 연산자의 문자열 사전순 정렬, 논리 연산자의 단락 평가와 반환값, 증감·나머지·지수 연산자를 정리합니다.
지난 글에서 ==, ===, Object.is()의 차이를 살펴봤습니다. 이번에는 JavaScript의 산술·비교·논리 연산자를 체계적으로 정리합니다. 연산자 하나하나는 쉬워 보이지만, 형변환 규칙이 얽히면 예상치 못한 결과가 나옵니다.
+ 연산자의 이중 역할
+는 JavaScript에서 가장 복잡한 연산자입니다. 두 피연산자 중 하나라도 문자열이면 **문자열 연결(concatenation)**을 수행합니다. 그렇지 않으면 덧셈을 합니다.
1 + 2; // 3 (숫자 덧셈)
1 + '2'; // '12' (문자열 연결)
'1' + 2; // '12'
'1' + '2'; // '12'
나머지 산술 연산자(-, *, /, %, **)는 항상 숫자 컨텍스트입니다. 피연산자를 숫자로 변환 후 연산합니다.
'3' - 1; // 2 (문자열 → 숫자 변환)
'3' * '2'; // 6
'hello' - 1; // NaN (변환 실패)
빈 배열과 객체의 + 동작은 컨텍스트에 따라 극단적으로 다릅니다.
[] + []; // '' (ToPrimitive: []→'')
[] + {}; // '[object Object]'
{} + []; // 0 또는 '[object Object]' (파서가 {} 을 블록으로 해석할 수 있음)
비교 연산자 — 문자열 함정
<, >, <=, >=는 두 피연산자가 모두 문자열이면 **사전순(Unicode 코드 포인트)**으로 비교합니다. 숫자와 문자열이 섞이면 숫자로 변환 후 비교합니다.
10 > 9; // true (숫자 비교)
'10' > '9'; // false (사전순: '1' < '9')
'10' > 9; // true (숫자로 변환)
// 배열 정렬 시 흔한 버그
[1, 10, 9, 2].sort(); // [1, 10, 2, 9] ← 사전순!
[1, 10, 9, 2].sort((a, b) => a - b); // [1, 2, 9, 10] ← 숫자 정렬
null >= 0이 true이지만 null == 0이 false인 이유는 >=의 비교에서 null이 0으로 변환되지만, ==에서 null은 오직 undefined에만 동등하다는 특수 규칙이 적용되기 때문입니다.
논리 연산자 — 불리언이 아닌 값 반환
&&와 ||는 결과를 불리언으로 변환하지 않고 평가를 중단한 시점의 값을 반환합니다. 이것이 단락 평가(short-circuit evaluation)입니다.
&& (AND): 왼쪽이 falsy면 왼쪽을, truthy면 오른쪽 값을 반환합니다.
'hello' && 42; // 42 (왼쪽 truthy → 오른쪽 반환)
null && 42; // null (왼쪽 falsy → 단락)
0 && 'text'; // 0
|| (OR): 왼쪽이 truthy면 왼쪽을, falsy면 오른쪽 값을 반환합니다.
'hello' || 'default'; // 'hello'
null || 'default'; // 'default'
0 || 'default'; // 'default' (0은 falsy!)
! (NOT): 피연산자를 불리언으로 변환 후 부정합니다. !!로 불리언 변환에 자주 사용됩니다.
!0; // true
!null; // true
!'hello'; // false
!!0; // false (불리언 변환)
!!''; // false
!![]; // true (빈 배열도 truthy!)
연산자 우선순위
우선순위가 헷갈릴 때는 괄호를 사용하는 것이 최선입니다. 기본 순서:
단항(!、+、-、typeof) > 산술(*, /, %) > 산술(+, -) > 비교(<, >, <=, >=) > 동등(==, ===) > && > || > 삼항(?:) > 대입(=, +=)
// && 가 || 보다 우선순위 높음
true || false && false; // true (false && false = false, true || false = true)
(true || false) && false; // false
// 실수 예시: && 의도했지만 || 와 혼용
const a = 1;
if (a === 1 || a === 2 && a > 0) { } // (a===1) || (a===2 && a>0)
증감 연산자 (++, —)
++와 --는 전위와 후위 두 가지 형태가 있습니다. 전위는 값을 먼저 바꾼 후 반환하고, 후위는 현재 값을 먼저 반환한 후 바꿉니다.
let i = 5;
console.log(i++); // 5 (현재값 반환 후 i=6)
console.log(++i); // 7 (i=7로 먼저 바꾼 후 반환)
나머지(%) 와 지수(**) 연산자
%는 나눗셈의 나머지를 반환합니다. 결과의 부호는 **왼쪽 피연산자(피제수)**를 따릅니다.
10 % 3; // 1
-10 % 3; // -1 (음수)
10 % -3; // 1 (양수)
**는 ES2016에 추가된 지수 연산자입니다. Math.pow()보다 간결하고 **우결합성(right-associative)**입니다.
2 ** 3; // 8
2 ** 3 ** 2; // 2 ** 9 = 512 (우결합: 3**2 먼저)
(2 ** 3) ** 2; // 64
복합 대입 연산자
+=, -=, *=, /=, %=, **= 등은 연산 후 대입을 한 번에 처리합니다.
let x = 10;
x += 5; // x = x + 5 = 15
x *= 2; // x = 15 * 2 = 30
x **= 2; // x = 30 ** 2 = 900
논리 연산자도 복합 대입을 지원합니다(&&=, ||=, ??= — ES2021). 이 세 가지는 다음 글에서 자세히 다룹니다.
지난 글: 동등 비교 완전 정리
다음 글: 단락 평가, Nullish 병합, 논리 대입 연산자
읽어주셔서 감사합니다. 😊