티스토리 뷰

JavaScript를 처음 접하거나 조금 더 깊게 이해하려는 개발자들이 흔히 겪는 고통 포인트는 다음과 같다:

 

  • 숫자, 문자열, 배열, 객체 사이의 동작 차이비교 결과가 직관과 다르게 나타나는 경우가 많음.
  • 동적 타이핑 언어 특성으로 인해 변수의 타입이 실행 중에 바뀔 수 있어 예상치 못한 버그가 발생함.
  • 원시 타입과 참조 타입의 비교 결과가 값(value)참조(reference) 중 어떤 기준으로 평가되는지 명확하지 않음.

예를 들어, `1 + "2"`는 `"12"`가 되고, 배열과 객체는 `typeof`가 모두 `"object"`를 반환해 혼란을 야기할 수 있음. 이런 현상은 JavaScript의 암시적 형 변환(type coercion)참조 동작 방식 때문임.

 

 

JavaScript의 타입 시스템 메커니즘

JavaScript는 동적(dynamic)이고 약 타입(weakly typed) 언어임. 즉, 변수는 선언 시 명시적 타입을 갖지 않고, 실행 시점(runtime)에 값에 따라 타입이 결정됨. 여기에 더해, JavaScript는 암시적 형 변환을 허용하기 때문에 종류가 다른 값들 사이의 연산 시 타입이 자동으로 바뀌기도 함.

 

JavaScript의 데이터 타입은 크게 두 가지 범주로 나뉨:

 

  • 원시(Primitive) 타입: 값 자체를 직접 표현하는 불변 데이터 (숫자, 문자열 등)
  • 객체(Object) 타입: 객체, 배열 등 복합 구조로 동작하며 참조 방식으로 저장됨

다음은 JavaScript에서 중요한 타입별 특징과 메커니즘 요약임:

 

  • Number: 64비트 부동소수점(IEEE 754) 숫자형. 정수/실수 구분 없음. 특수값(`NaN`, `Infinity`) 포괄.
  • String: 문자들의 시퀀스. 불변적이며 템플릿 리터럴(``` `...${expr}` ```)로 값 삽입 가능.
  • Array: JavaScript의 Array 객체. 크기 변경 가능하고 여러 타입 혼합 저장 허용. 인덱스는 0부터 시작함.
  • Object: 키-값 쌍의 컬렉션. 값은 어떤 타입도 가능하며 동적으로 수정 가능.

 

타입 비교 및 처리 가이드

특성 Number String Array Object
메모리 저장 값(value) 값(value) 참조(ref) 참조(ref)
변경 가능성 불변 불변 가변 가변
typeof 결과 "number" "string" "object" "object"
비교 (`===`) 값 비교 값 비교 참조 비교 참조 비교

위 비교표는 원시 타입과 참조 타입 사이의 근본적인 차이를 보여줌. 특히 배열과 객체는 메모리 참조 주소가 다르면 같은 값 구조라도 `===` 비교가 false가 됨.

  1. 숫자 처리: 산술 연산 전 명시적 타입 검증. 예를 들어, 문자열 `"123"`을 숫자 `123`으로 변환하기 위해 `Number()`, `parseInt()`, `parseFloat()` 사용. 필요 시 0.1+0.2 같은 부동소수점 오차를 보정하기 위해 `toFixed(2)`와 같은 출력 포맷 메서드 활용.
  2. 문자열 처리: 템플릿 리터럴과 `String()` 함수로 다양한 문자열 조작. 인덱스로 접근 시 새로운 문자열 객체가 생성되는 불변성 유지.
  3. 배열 비교: 얕은 비교로는 배열 요소가 같아도 참조가 다르면 false. `JSON.stringify()` 또는 lodash `_.isEqual()` 같은 깊은 비교 사용 권장.
  4. 객체 구조: 객체 속성 추가/삭제 가능하며 동작 중 구조 변경이 성능에 영향을 줄 수 있으므로 미리 설계 또는 `Object.freeze()`로 불변화 옵션 고려.

 

흔한 오해와 주의사항

  • `typeof null`의 결과가 `"object"`인 이유: 역사적 유산 때문에 `typeof null`은 `"object"`를 반환함. null을 검사할 때는 반드시 `value === null`을 사용할 것.
  • 암시적 형 변환 경계: `==`는 자동 형 변환을 수행하므로 예기치 않은 결과를 초래함. 항상 `===`, `!==`를 사용해 비교 안전성을 확보할 것.
  • BigInt와 Number는 혼합 연산 금지: 둘을 동일 연산에서 섞으면 TypeError가 발생하므로 적절히 변환 후 처리.
  • 배열과 객체의 참조 복사: 얕은 복사(`slice()`, spread `[...]`)는 중첩 객체 내부까지 복사하지 않음. 필요 시 깊은 복사 도구를 적용할 것.

정성 들여 쓴 제 글이 여러분의 북마크 한구석에 저장되어, 필요할 때마다 꺼내 볼 수 있는 유용한 도구가 되길 바랍니다.