읽은 책들/모던 리액트 DeepDive

[모던 리액트 Deep Dive] 1장. 리액트 개발을 위해 꼭 알아야 할 자바스크립트

today is ? 2024. 10. 29. 11:23

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

더보기
더보기

의존성 배열에 대한 고민 : 보통 리액트에서 제공하는 eslint-react-config에 선언돼 있는 react-hooks/exhaustive-deps의 도움을 받아 해당 배열을 채우는데, 실제로 이것이 어떤 식으로 작동하는지, 또한 왜 이러한 변수들을 넣어야 하는지 이해하지 못하는 경우가 많음

렌더링 관점 : 리액트 컴포넌트의 렌더링이 일어나는 이유 중 하나가 바로 props의 동등 비교에 따른 결과

 

리액트의 가상 DOM과 실제 DOM의 비교, 리액트 컴포넌트가 렌더링할지를 판단하는 방법, 변수나 함수의 메모이제이션 등 모든 작업은 자바스크립트의 동등 비교를 기반으로 함

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

원시타입
(primitive type)
boolean 참과 거짓만을 가질 수 있는 데이터 타입
null 아직 값이 없거나 비어 있는 값을 표현할 때 사용
undefined 선언한 후 값을 할당하지 않은 변수 또는 값이 주어지지 않는 인수에 자동으로 할당되는 값
number 모든 숫자를 하나의 타입에 저장
string 텍스트 타입의 데이터 저장
symbol 중복되지 않는 어떠한 고유한 값을 나타내기 위해 만들어짐
bigint number의 한계를 넘어서 더 큰 숫자를 저장
객체타입
(object/reference type)
object 자바스크립트를 이루고 있는 대부분의 타입
배열, 함수, 정규식, 클래스 등이 포함
더보기
더보기
  • 후술한 원시값 중 null과 undefined는 오직 각각 null과 undefined라는 값만 가질 수 있으며, 그 밖의 타입은 가질 수 있는 값이 두 개 이상 존재
  • 다른 원시값과 다르게 typeof로 null을 확인했을 때 해당 타입이 아닌 'object'라는 결과 반환
  • 자바스크립트 문자열은 원시 타입이며 변경 불가능

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

원시 타입

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

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

 

객체 타입

  • 프로퍼티를 삭제, 추가, 수정할 수 있으므로 원시 값과 다르게 변경 가능한 형태로 저장
  • 값을 복사할 때도 값이 아닌 참조를 전달
var helllo = {
	greet: 'hello, world',
}

var hi = {
	greet: 'hello, world',
}

console.log(hello === hi)

console.log(hello.greet === hi.greet) // false

 

 

자바스크립트 개발자는 항상 객체 간에 비교가 발생하면, 이 객체 간의 비교는 우리가 이해하는 내부의 값이 같다 하더라도 결과는 대부분 true가 아닐 수 있다는 것을 인지해야 함

 

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

Object.is

  • 리액트에서 사용하는 동등 비교는 ==나 ===가 아닌 Object.is

리액트에서의 비교를 요약하자면 Object.is로 먼저 비교를 수행한 다음에 Object.is에서 수행하지 못하는 비교, 즉 객체 간 얕은 비교를 한 번 더 수행하는 것을 알 수 있음

// Object.is는 참조가 다른 객체에 대해 비교 불가능
Object.is({hello: 'world'}, {hello: 'world'}) // false

// 리액트 팀에서 구현한 shallowEqual은 객체의 1 depth까지는 비교 가능
shallowEqual({ hello: 'world'}, { hello: 'world' }) // true

// 그러나 2 depth까지 가면 이를 비교할 방법이 없으므로 false를 반환
shallowEqual({ hello: { hi: 'world'}), { hello: { hi: 'world'} }) // false

 

객체의 얕은 비교까지만 구현한 이유?

type Props = {
	hello: string
}

function HelloComponent(pops: Props) {
	return <h1>{props.hello}</h1>
}

// ...

function App() {
	return <HelloComponent hello="hi!" />
}
  • 리액트에서 사용하는 JSX props는 객체이고, 여기에 있는 props만 일차적으로 비교하면 되기 때문
  • 리액트는 props에서 꺼내온 값을 기준으로 렌더링을 수행하기 때문에 일반적인 케이스에서는 얕은 비교로 충분할 것
  • props에 또 다른 객체를 넘겨준다면 리액트 렌더링이 예상치 못하게 작동한다는 것을 알 수 있음

pops가 깊어지는 경우, 즉 한 객체 안에 또다른 객체가 있을 경우 컴포넌트에 실제로 변경된 값이 없음에도 불구하고 메모이제이션된 컴포넌트를 반환하지 못함

더보기
더보기

만약 내부에 있는 객체까지 완벽하게 비교하기 위한 재귀문까지 넣었다면?

객체 안에 객체가 몇 개까지 있을지 알 수 없으므로 이를 재귀적으로 비교하려 할 경우 성능에 악영향을 미칠 것

1.1.5 정리

  • 자바스크립트에서 객체 비교의 불완전성은 스칼라나 하스켈 등의 다른 함수형 언어에서는 볼 수 없는 특징
  • 자바스크립트를 기반으로 한 리액트의 함수형 프로그래밍 모델에서도 이러한 언어적인 한계를 뛰어넘을 수 없으므로 얕은 비교만을 사용해 비교를 수행

참고

https://product.kyobobook.co.kr/detail/S000210725203

 

모던 리액트 Deep Dive | 김용찬 - 교보문고

모던 리액트 Deep Dive | 요즘 프런트엔드 개발은 자바스크립트와 리액트부터 시작한다는 말이 있을 정도로 최근 몇 년간 프런트엔드 생태계에서 리액트의 비중은 날이 갈수록 커지고 있습니다.

product.kyobobook.co.kr