모던 리액트 Deep Dive | 1.1 자바스크립트와 동등 비교

2025. 3. 9. 20:13·FRONTEND/React

1.1 자바스크립트와 동등 비교

1.1.1 자바스크립트의 데이터 타입

자바스크립트의 모든 값은 데이터 타입을 갖고 있으며, 이 데이터 타입은 원시 타인과 객체 타입으로 나눌 수 있다.

 

원시타입(primitive type)

  • 객체가 아닌 다른 모든 타입, 메서드를 갖지 않는다.

Undefined

선언한후 값을 할당하지 않은 변수 또는 값이 주어지지 않은 인수에 자동으로 할당되는 값

let foo

typeof foo === 'undefined' //true

function bar(hello) {
	return hello
	}

typeof bar() === 'undefined' //true

후술 할 원시값 중 null과 undefined는 오직 각각 null과 undefined라는 값만 가질 수 있으며, 그 밖의 타입은 가질 수 있는 값이 두 개 이상(boolean의 true, false와 같이) 존재한다.

 

Null

아직 값이 없거나 비어 있는 값을 표현할 때 사용한다.

typeof null === 'object' //true?

null은 object type을 반환

undefined 선언됐지만 할당되지 않은 값

null은 명시적으로 비어 있음을 나타내는 값

 

Boolean

참(true), 거짓(false)만을 가질수 있는 데이터 타입

true, false의 boolean형의 값 외에도 true와 false처럼 취급되는 truthy, falsy 값이 존재

값 타입 설명
false Boolean false는 대표적인 falsy한 값이다.
0, -0, 0n, 0x0n Number, BigInt 0은 부호나 소수점 유무에 상관없이 falsy한 값이다.
NaN Number Number가 아니라는 것을 뜻하는 NaN(Not a Number)은 falsy한 값이다.
"" String 문자열이 falsy하기 위해서는 반드시 공백이 없는 빈 문자열이어야 한다.
null null null은 falsy한 값이다.
undefined undefined undefined는 falsy한 값이다.
if (1) {}
Boolean(1)

 

Number

자바스크립트는 모든 숫자를 하나의 타입에 저장(정수, 실수 구분 없음)

-(2^53-1) ~ 2^53 -1 사이 값을 저장

const maxinteger = Math.pow(2, 53)
maxinteger - 1 == Number.MAX_SAFE_INTEGER // true

const minlnteger = -(Math.pow(2, 53) - 1)
minlnteger === Number.MIN_SAFE_INTEGER // true

2진수, 8진수, 16진수 등의 별도 데이터 타입이 없음

const binary = 0b10
binary == 2 //true

const octal = 0o10
octal == 8 //true

const hex = 0x10
hex == 16 //true

 

Bigint

number 크기 제한 한계를 넘어선 더 큰 숫자를 저장 가능

//기존 number의 한계
9007199254740992 === 9007199254740993 //true, 더이상 다룰 수 없는 크기 이기 때문에

const maxinteger = Number.MAX_SAFE_INTEGER
console.log(maxinteger + 5 === maxinteger + 6) // true

const bigIntl = 9007199254740995n // 끝에 n을 붙인다
const bigInt2 = BigInt('9007199254740995') // BigInt 함수를 사용하면 된다

const number = 9007199254740992
const bigint = 9007199254740992n

typeof number // number
typeof bigint // bigint

number == bigint //true
number === bigint //false

BigInt 함수를 사용할때 Number 범위를 넘는 숫자를 사용하려면 따옴표를 붙여줘야 함

- BigInt('9007199254740995') (o)

- BigInt(9007199254740995) (x)

 

String

` , ‘ , “ 으로 사용가능

백틱(`)을 사용하여 표혐한 문자열을 템플릿 리터럴이라 함

줄 바꿈 가능, 문자열 내부 표현식 사용 가능

// '\\n안녕하세요.\\b'
const longText = `
안녕하세요.
`

// 오류 발생
const longText = "
안녕하세요.
"

문자열은 원시 타입이며 변경 불가능함

const foo = 'bar'

console.log(foo[0]) // 'b'

foo[0] = 'a'

console.log(foo) // bar, 변경 불가

 

Symbol

중복되지 않는 고유한 값을 나타내기 위해 사용

Symbol() 함수를 사용해야만 만들 수 있음

const key = Symbol('key')
const key1 = Symbol('key')

key === key1 //false

Symbol.for('hello') === Symbol.for('hello') //true

 

객체타입(obejct/reference type)

  • 원시 타입 이외의 모든 것, 자바스크립트를 이루고 있는 대부분의 타입이 객체타입
  • 객체 타입은 참조를 전달한다고 해서 참조 타입으로도 불린다
typeof [] === 'object' //true
typeof {} === 'object' //true

function hello() {}
typeof hello === 'function' //true, 함수도 객체이지만 실제로는 일급 객체로 특별한 종류로 분류하고 있음

const hello1 = function() {}
const hello2 = function() {}

hello1 === hello2 // false, 같아 보이지만 참조가 다르기 때문에 false를 반환

 

1.1.2 값을 저장하는 방식의 차이

원시 타입과 객체 타입의 가장 큰 차이점은 값을 저장하는 방식의 차이

 

원시타입

  • 불변 형태의 값으로 저장
  • 변수 할당 시점에 메모리 영역을 차지하고 저장
let hello = 'hello world'
let hi = hello

console.log(hellow === hi) //true
let hello = 'hello world'
let hi = 'hello world'

console.log(hellow === hi) //true

값을 전달하는 방식이 아닌 각각 선언하는 방식도 동일한 값으로 인식

 

객체

const hello = {
	'greet' : 'hello, world'
	}
	
const hi = {
	'greet' : 'hellow, world'
	}
	
console.log(hello === hi) // false
consoel.log(hello.greet === hi.greet) //true

값은 같아도 참조하는 곳이 다르다, 참조를 전달할 경우 원시값에서와 동일한 결과를 보여준다.

const hello = {
	'greet' : 'hello, world',
}

const hi = hello

console.log(hi === hello) // true

 

1.1.3 자바스크립트의 또 다른 비교 공식 Object.is

Object.is 는 두 개의 인수를 받으며, 이 인수 두 개가 동일한지 확인하고 반환하는 메서드

-0 === +0 //true
Object.is(-0, +0) //false

Number.NaN === NaN //false
Object.is(Number.NaN, NaN) //true

NaN === 0/0 //false
Object.is(NaN, 0 / 0) //true

원시 타입의 경우 ==, === 에서의 부족한 부분을 채워 주지만 객체 타입에서는 동일하게 적용됨

Object.is({}, {}) //false

const a = {'hello' : 'hi'}
const b = a

Object.is(a, b) // true
a === b //true

 

1.1.4 리액트에서의 동등 비교

1. 리액트에서의 동등 비교 방식

리액트는 JavaScript의 일반적인 동등 비교 연산자(==, ===)를 사용하지 않고, Object.is를 사용합니다.

 

Object.is 사용 이유

  • JavaScript의 ===로는 특정 케이스(예: NaN, 0과 +0 비교)를 정확히 처리할 수 없음
  • 브라우저 호환성을 위해 폴리필 구현도 함께 사용

리액트의 objectIs 구현

function is(x, y) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y)
  );
}

const objectIs = typeof Object.is === 'function' ? Object.is : is;

 

2. 리액트의 얕은 비교(Shallow Equal)

리액트는 Object.is를 기반으로 얕은 비교를 수행하는 shallowEqual 함수를 구현하여 사용합니다.

 

shallowEqual 작동 방식

  • 먼저 Object.is로 두 값을 비교
  • 두 값이 객체인 경우 객체의 1-depth까지만 비교 (얕은 비교)
    • 객체의 키 개수 비교
    • 각 키에 해당하는 값들을 Object.is로 비교

얕은 비교의 예

// 기본 Object.is 비교
Object.is({ hello: 'world' }, { hello: 'world' }) // false (참조가 다름)

// 리액트의 shallowEqual 비교
shallowEqual({ hello: 'world' }, { hello: 'world' }) // true (1-depth까지 비교)

// 중첩 객체는 비교 불가
shallowEqual({ hello: { hi: 'world' } }, { hello: { hi: 'world' } }) // false

 

3. 왜 얕은 비교만 구현했을까?

리액트가 얕은 비교만 구현한 이유

  1. 성능 최적화: 깊은 비교는 재귀적으로 수행해야 하며 성능에 악영향을 줄 수 있음
  2. 일반적 사용 패턴: 리액트 props는 주로 1-depth 객체로 전달되는 경우가 많음
  3. 예측 가능성: 얕은 비교는 동작이 명확하고 예측 가능함

4. React.memo에서의 동등 비교 문제

React.memo는 내부적으로 shallowEqual을 사용하기 때문에, 중첩된 객체를 props로 전달할 경우 메모이제이션이 제대로 작동하지 않을 수 있습니다.

 

예시 문제

// 정상 작동하는 경우 (1-depth props)
<Component counter={100} />

// 메모이제이션이 제대로 작동하지 않는 경우 (2-depth props)
<DeeperComponent counter={{ counter: 100 }} />

이유

  • Component의 경우: counter가 변경되지 않았으므로 리렌더링 방지
  • DeeperComponent의 경우: 매 렌더링마다 { counter: 100 }이 새로운 객체로 생성되어 얕은 비교에서 항상 다른 것으로 인식됨

5. 주의사항 및 최적화 팁

  1. 중첩 객체 전달 지양하기 : props로 중첩 객체를 전달하는 것은 피하는 것이 좋음
  2. 원시 값 사용하기 : 가능한 원시 값(string, number, boolean)을 props로 전달
  3. 메모이제이션 활용 : 객체는 useMemo, 함수는 useCallback을 사용하여 참조 일관성 유지
  4. 커스텀 비교 함수 사용 : 필요한 경우 React.memo의 두 번째 인자로 커스텀 비교 함수 제공

'FRONTEND > React' 카테고리의 다른 글

리액트 훅 | useState에 대해 알아보기  (4) 2025.08.08
부모 요소 패딩 값에 영향을 받지 않고 전체 너비 구분선 그리기  (0) 2025.04.04
리액트 훅(React Hook) 이해하기  (1) 2025.01.19
리액트 컴포넌트 기반 구조 이해하기(WITH 재사용성, 유지보수성)  (0) 2025.01.05
React 프로젝트 생성 with Vite & Tailwind  (2) 2024.12.26
'FRONTEND/React' 카테고리의 다른 글
  • 리액트 훅 | useState에 대해 알아보기
  • 부모 요소 패딩 값에 영향을 받지 않고 전체 너비 구분선 그리기
  • 리액트 훅(React Hook) 이해하기
  • 리액트 컴포넌트 기반 구조 이해하기(WITH 재사용성, 유지보수성)
uoaheu
uoaheu
uoaheu 님의 블로그 입니다.
  • uoaheu
    uoaheu 님의 블로그
    uoaheu
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • 알고리즘 (7)
      • CS (9)
      • FRONTEND (9)
        • React (12)
        • Kotlin (1)
        • JS (5)
        • HTML (2)
      • SQL (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    멀티캠퍼스it부트캠프
    리액트usestate
    mysql로 피벗테이블만들기
    boj25418
    useactionstate
    토스 앱 분석
    BFS
    엘지유플러스유레카프론트엔드
    혼자서 공부하는 네트워크
    백준1926번
    이더넷프레임
    유레카3기
    부트캠프후기
    toss 분석
    mysql
    토스분석
    mysql 피벗테이블
    알고리즘
    toss uiux
    토스 uiux
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
uoaheu
모던 리액트 Deep Dive | 1.1 자바스크립트와 동등 비교
상단으로

티스토리툴바