모던 리액트 Deep Dive

들어가면서

  • 해당 내용은 인강을 통해 들었던 내용이 있으나, 개념적으로 부족한 부분이 있는 것으로 판단되어, 조금 더 기본이 되는 근본적인 내용을 정리하기 위하여 모던 리액트 Deep Dive 초반 부분의 독서 및 정리한 내용이다.
  • 필요하거나, 중요하다고 생각하는 내용만을 담고 있다. 주의!
  • 챕터 상의 3차 목차에 대해서 제목으로 지정하며, 보고 알아둬야 하는 부분은 페이지까지 기록한다.

01 장 리액트 개발을 위해 꼭 알아야 할 자바스크립트

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

리액트와 함께 사용 가능한 다른 라이브러리들 21p~

  • 상태관리 : Redux, Zustand, Recoil, Jotai
  • 서버 사이드 렌더링 : Next.js, Remix, Hydrogen
  • 애니메이션 : Framer Motion, react-spring, React Move
  • 차트 : Rechats, visx, nivo
  • 폼 : React Hook Form, Fromik, React Final Form

JS에서 undefined, null 의 의미 21p~

  • undefined : 선언은 됐지만, 할당되지 않은 값
  • null : 명시적으로 값이 들어 있지 않음을 나타내는 값

truthy / falsy 22p~

  • 조건문 상에서 true나 false 값은 아니지만, 같은 취급을 받는 값
  • truthy
    • {}
    • []
    • 1
  • falsy
    • 0, -0, 0n, 0x0n
    • NaN
    • 빈 문자열(싱글, 쌍 따옴표, 백틱) : 문자열에 값이 없으면 falsy 취급을 받는다.
    • null
    • undefined

Symbol 26p~

  • ES6에서 추가된 7번째 타입. 고유한 값을 위해 만들어진 타입. 심볼타입은 같은 값을 인수로 넘겨줘도 온전히 고유한 값으로만 취급 받는다.
const key = Symbol('key')
const key2 = Symbol('key')

key === key2 // false

// 동일한 값을 사용하기 위해서는 
// 다음 static 메소드를 활용해야 한다.
Symbol.for('hello') === Symbol.for('hello') 
// true

객체 타입 26p~

  • typeof 데이터 타입을 알려주는 역할을 한다.
typeof [] === 'object' // true
typeof {} === 'object' // true

function hello() {}
typeof hello === 'function' // true

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

  • 두개의 인수를 받고, 인수가 두개가 동일한지를 확인하는 반환 메서드
  • 하지만 ==, === 와는 다른 점이 있다.
    • == 는 같음 비교 전에 양쪽 타입이 다르면 강제 형변환이 일어난다. 즉, 5 == ‘5’ 는 같음 취급을 한다. 하지만 Object.is 는 false 취급으로 === 와 같게 결과가 나온다.
    • === 의 경우와 다르게 좀더 개발자가 기대하는 방식으로 정확한 비교가 이루어진다.
      -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
    • ES6 에서 추가 되었으며, === 가 가지는 한계를 극복하기 위해 만들어졌으나, 자바스크립트의 특징으로 객체에서는 동일하게 작동한다.

리액트에서의 동등 비교 30p~

  • 리액트에서는 기본적으로 Object.is 를 기반으로 shallowEqual 라는 함수를 만들어 동등 비교를 진행한다.
  • Object.is 로 먼저 비교하고, 수행한 다음, Object.is에서 수행하지 못하는 비교, 즉, 객체 간의 얕은 비교를 한번더 수행하고, 첫 번째 깊이의 값을 확인하는 식으로 진행한다. 이렇게 되는 가장 중요한 포인트는 props 만 일차적으로 비교하면 되기 때문이다.
  • 그렇기 때문에 props가 깊어지는 경우, 즉 한 객체 안에 추가 객체가 있으면, 컴포넌트에서 실제로 변경된 값이 없음에도 불구하고 메모이제이션된 컴포넌트를 반환하지 못함.
  • 단, 이러한 점은 단점이라기 보단 내부 객체까지 완벽하게 재귀로 비교하는 식으로 구현하게 되었을 경우 있을 성능적 악영향 때문이다.

1.1.5 정리 34p

  • 자바스크립트에서 객체 비교의 불안전성은 반드시 개발자가 알아둬야 하는 지점이다.
  • 리액트는 이러한 언어적 한계를 뛰어넘을 수 없어 얕은 비교만을 사용해 비교를 수행해 필요한 기능을 구현하고 있다.
  • 따라서 이러한 특징의 숙지는 향후 함수형 컴포넌트에서 사용되는 훅의 의존성 배열의 비교, 렌더링 방지를 넘어선 useMemo, useCallback의 필요성, 렌더링 최적화를 위해 꼭 필요한 React.memo를 올바르게 작동시키기 위해 고려해야 할 것들을 쉽게 이해할 수 있을 것이다.

1.2 함수

1.2.1. 함수란 무엇인가 34p

//  JS의 기본적인 함수 형태 
function sum(a, b) {
	return a + b
}

// React 컴포넌트의 기본 형태 
function Component(props) {
	return <div>{props.hello}</div>
}

1.2.2 함수를 정의하는 4가지 방법

  • 함수 선언문
    // 일반문 방식
    function add(a, b) {
    	return a + b
    }
    // 선언문 방식 
    const sum = function sum(a, b) {
    	return a + b
    }
  • 함수 표현식
    • 일급 객체 : 다른 객체들에게 일반적으로 적용 가능한 연산을 모두 지원하는 객체. ≓ 자바스크립트 함수
    // 함수 표현식에서 할당하려는 함수의 이름은 
    // 생략하는 것이 일반적
    // 이렇게 하는 이유는 혼란 방지용이다. 
    // console.log(typeof sum) // 에러가 발생한다. 
    // ReferenceError: Cannot access 'sum' before initialization
    
    // sum()
    
    const sum = function (a, b) {
    	return a + b 
    }
    
    console.log(sum(4, 5)) // 9
    
    console.log(typeof hello === 'undefined') // true
    
    // hello() // Uncaught TypeError: hello is not a function
    
    var hello = function () {
    	console.log('hello')
    }
    
    hello()
    • 두 방식의 차이는 호이스팅과 관련있다.
    • 함수의 호이스팅은 한마디로 미리 메모리에 등록하는 작업으로, 코드의 순서와 상관없이 동작하는 형태를 말한다.
    • 이때 함수 선언문의 방식은 미리 저장된다.
    • 하지만, 함수 표현식 var의 경우 undefined 로 초기화되어 함수로 사용 자체는 안된다.
      • 추가로 const의 경우 아예 호이스팅에 포함되지않는다.(이는 선언시의 var, const의 차이에서 온다. )
    • 둘은 서로 장단점이 있으니 현재의 개발 환경, 본인이나 프로젝트 상황에 맞춰서 작성법을 일관하게 사용하면 충분하다.
  • Function 생성자
    const add = new Function ('a', 'b', 'return a + b')
    
    add(10, 24) // 34
    • 그냥 쓰지 말자.
  • 화살표 함수
    • 글자수를 줄일 수 있다는 점 등의 측면으로 많이 사용된다.
    const add = (a, b) => {
    	return a + b
    }
    
    const add = (a, b) => a + b
    • 단, 특징적인 차이점이 상당히 있다.
      • 생성자 함수로 사용이 불가능하다.
      • arguments 존재하지 않음
        • 이는 함수 내에서 사용 가능한 유사 배열 객체로, 따로 함수로 전달된 인수를 적지 않아도, 해당 키워드로 가변 배열로 값들을 받아와서 사용이 가능하다.
        • 이 외에도 기존 함수 오버로드, 특정 인자수 미리 알수 없는 함수 구현 등에서 쓰일 수 있다.
      • this 바인딩에서 차이가 발생함. 기본적으로 this 는 함수가 어떻게 호출 되느냐에 따른 동적 바인딩의 구조였지만, 화살표 함수는 함수 자체의 바인딩을 갖지 않고, 상위 스코프의 this를 따른다(!)
// class 형 컴포넌트에서 일반 함수와 화살표 함수로 state 를 갱신하는 예지 
class Component exteds React.Component {
	constructor(props) {
		super(props)
		this.state = {
			counter: 1,
		}
	}

	functionCountUp() {
		console.log(this) // undefined
		this.setState((prev) => ({ counter: prev.counter + 1}))
	}

	ArrowFunctionCountUp = () => {
		console.log(this) // class component
		this.setState((prev) => ({ counter: prev.counter + 1}))
	}

	render() {
		return (
			<div>
				{/* Cannot read properties of undefined (reading 'setState')*/}
				<button onClick={this.functionCountUp}>일반함수</button>
				{/* 정상적으로 작동한다. */}
				<button onClick={this.ArrowFunctionCountUp}>화살표 함수</button>
				
			</div>
		)
	}
}