Frontend

자바스크립트와 리액트의 동등 비교

date
Feb 23, 2024
thumbnail
js-react-equality-comparison-thumbnail.png
slug
equality-comparisons-in-js-and-react
author
status
Public
tags
MIL
React.js
Javascript
summary
자바스크립트 객체 간 비교의 한계점과 리액트 컴포넌트 리렌더링
type
Post
category
Frontend

자바스크립트의 동등 비교


리액트 컴포넌트 렌더링이 일어나는 이유 중 하나가 바로 props의 동등 비교에 따른 결과다.
 
  • 렌더링 최적화를 위해서는 리액트 컴포넌트 렌더링이 어떻게 작동하는지 알아야 한다.
  • props의 동등 비교가 객체의 얕은 비교 기반으로 이루어진다.
  • 가상 DOM과 실제 DOM의 비교, 리액트 컴포넌트가 렌더링할지를 판단하는 방법, 변수나 함수의 메모이제이션 등 모든 작업은 자바스크립트의 동등 비교를 기반으로 한다.
 

원시 타입(primitive type)과 객체 타입(object/reference)의 차이점

가장 큰 차이점이 바로 값을 저장하는 방식이다.
 
원시 타입 불변 형태의 값으로 저장되며, 변수 할당 시점에 메모리 영역을 차지하고 저장된다.
let hello = 'hello world'; let hi = hello; console.log(hello === hi) // true
  • hellohello world라는 값이 hi에 복사해 전달되었으므로 true가 나온다.
 
let hello = 'hello world'; let hi = 'hello world'; console.log(hello === hi) // true
  • 값을 비교하기 때문에, 값을 전달하는 방식이 아닌 각각 선언하는 방식으로도 동일한 결과가 나온다.
 
 
객체 타입은 프로퍼티를 삭제, 추가, 수정할 수 있으므로 변경 가능한 형태로 저장되며, 값을 복사할 때도 값이 아닌 참조를 전달한다.
var hello = { greet: 'hello, world', } var hi = { greet: 'hello, world', } console.log(hello === hi) // false console.log(hello.greet === hi.greet) // true
  • 객체는 값을 저장하는 것이 아니라 참조를 저장한다.
 
var hello = { greet: 'hello, world', } var hi = hello console.log(hello === hi) // true
  • hihello와 동일한 주소를 가리키므로 true가 나온다
 
 
👀
객체 간 비교가 발생하면, 개발자가 이해하는 객체 내부의 값이 서로 같더라도 결과는 대부분 false라는 점을 인지해야 한다.
 
 

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

동등 비교를 위한 다른 방법으로 자바스크립트에서 제공하는 메서드인 Object.is==, ===의 차이점을 이해하고 있어야 한다.
 
==
  • 동등 비교 연산자
  • 타입 변환(형 변환, Type conversion)이 일어난 뒤 비교
    •  
===
  • 엄격한(strict) 동등 비교 연산자
  • 타입 변환(Type conversion)이 일어나지 않으며, 타입이 일치해야만 동등한 것으로 간주
    •  
Object.is
  • 동등 비교 메서드 (ES6+ 이후)
  • 동등 비교 ==의 한계를 극복하기 위해 만들어 졌으나, 객체 간 비교에서는 ===와 동일하게 동작함
  • 사람(개발자)이 이해하는 방식으로 결과를 내주는 경우가 많음
 
 

동등 비교 예제

'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
-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
var a = {} var b = {} a == b // false a === b // false var c = []; var d = []; c == d // false c === d // false
Object.is({}, {}) // false const a = { hello: 'hi', } const b = a Object.is(a, b) // true a === b // true
 
👀
리액트에서 동등 비교는 Object.is 메서드를 활용하여 구현되어 있다.
 
 

리액트에서의 동등 비교


꼭 알아야 할 것들

  • JSX Props는 객체로 취급한다.
  • Object.is는 참조가 다른 객체 간의 비교가 불가능하다.
  • 리액트 팀은 shallowEqual이라는 메서드를 구현하여 1depth 까지 객체 간 비교가 가능하게 만들었다.
    • Object.is로 먼저 비교를 수행한 다음 Object.is로 수행하지 못하는 객체 간 얕은 비교를 한 번 더 수행한다.
    • 리액트 JSX props는 객체이므로, 일차적으로 props만 비교하면 되기 때문에 얕은 비교까지만 구현되었다.
 
객체 간 얕은 비교(Shallow Comparison)란? 첫 번째 깊이(depth)에 존재하는 값만 비교한다는 것을 의미한다.
 

예제

type Props = { hello: string } function HelloComponent(props: Props) { return <h1>{hello}</h1> } //... function App() { return <HelloComponent hello="hi!" /> }
  • 여기서 props는 객체다.
  • 리액트는 props에서 꺼내온 값을 기준으로 렌더링한다.
  • 일반적인 케이스에서 얕은 비교로 충분하다.
  • 이러한 특성으로 props에 또 다른 객체를 넘겨주면 리액트 렌더링이 예상치 못하게 작동한다.
  • 객체 안의 객체는 자바스크립트, 리액트에서 동등 비교가 어렵다는 것을 이해해야 한다.
 
 
👀
리액트의 얕은 비교는 객체의 1 depth까지 비교할 수 있게 구현되어 있다는 점을 기억하자. 객체 안의 객체부터는 비교가 불가능하다.
 
👀
자바스크립트 객체 비교의 불완전성은 다른 함수형 언어에서 볼 수 없는 자바스크립트만의 특징이다. 이러한 언어적 한계를 뛰어넘을 수 없으므로, 리액트는 얕은 비교만을 사용해 필요한 기능을 구현한다는 점을 꼭 숙지하자.
 
 

참고 자료