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

2024년 새해, 새로운 마음 가짐으로 모던 리액트 Deep Dive 책을 정독하기로 해본다. 프런트엔드 분야는 다른 기술 분야에 비해 진입장벽이 상대적으로 낮고 웹서비스를 손쉽고 빠르게 만들 수 있다는 장점 덕분에 많은 사람들, 특히 비전공자들이 진입하는 경우가 많다. 하지만, 리액트와 자바스크립트 사이에 겉으로 드러나지 않는 사실 을 완벽하게 이해하는 과정에 도전하게 된다면 다른 기술 분야와 마찬가지로 프런트엔드 역시 절대 쉽지 않다는 것을 깨닫게 된다. 이 책을 통해 리액트에 대해서 다시 살펴보며 생각을 정리해본다.

1장에서는 리액트 코드의 기반이 되는 자바스크립트에 대해 먼저 알아본다. 그러나 단순히 리액트 코드를 작성하는 데 그치지 않고, 웹 애플리케이션이 작동하는 이면에서 리액트가 수행하는 작업을 이해하려면 자바스크립트의 개념을 다시금 짚어볼 필요가 있다.

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

1.1.1 자바스크립트의 데이터 타입
객체 타입
typeof [] ==== 'object' // true
typeof {} === 'object' // true
function hello() {}
typeof hello === 'function' // true
const hello1 = function () {}
const hello2 = function () {}
// 객체인 함수의 내용이 육안으로는 같아 보여도 참조가 다르기 때문에 false가 반환된다.
hellol === hello2 // false
1.1.2 값을 저장하는 방식의 차이
let hello = 'hello world'
let hi = hello
console.log(hello === 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',
}
// 그러나 동등 비교를 하면 false가 나온다.
console.log(hello === hi) // false
// 원시값인 내부 속성값을 비교하면 동일하다.
console.log(hello.greet === hi.greet) // true
1.1.3 자바스크립트의 또 다른 비교공식, 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
1.1.4 리액트에서의 동등 비교
// 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
import { memo, useEffect, useState } from 'react'

type Props = {
counter: number
}

const Component = memo((props: Props) => {
    useEffect(() => {
        console.log('Component has been rendered!')
    })
    return <hl>{props.counter}</hl>
})

type DeeperProps = {
    counter: {
        counter: number
    }
}

const DeeperComponent = memo((props: DeeperProps) => {
    useEffect(() => {
        console.log('DeeperComponent has been rendered!')
    })
    return <hl>{props.counter.counter}</hl>
})

export default function App() {
    const [, setcounter] = useState(0)
    function handleClick() {
        setCounter((prev) => prev + 1)
    }
    return (
        <div className="App">
        <Component counter={100} />
        <DeeperComponent counter= />
        <button onClick={handleClick}>+</button>
        </div>
    )
}
1.1.5 정리

1.2 함수

1.2.1 함수란 무엇인가?
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
}
sum(10, 24) // 34
함수 표현식
함수 표현식과 선언 식의 차이
hello() // hello
function hello() {
    console.log('hello')
}
hello() // hello
Function 생성자


const add = new Fiinction('a', 'b', 'return a + b')
add(10, 24) // 34
화살표 함수


const add = (a, b) => {
    return a + b
}
const add = (a, b) => a + b

1.4 클로저

1.4.1 클로저의 정의
1.4.3 클로저의 활용
var counter = 0
function handleClick() {
    counter++
}
리액트에서의 클로저
function Component() {
const [state, setState] = useState()
function handleClickO {
    // usestate 호출은 위에서 끝났지만,
    // setState는 계속 내부의 최신값(prev)을 알고 있다.
    // 이는 클로저를 활용했기 때문에 가능하다.
    setState((prev) => prev + 1)
}
// ...
1.4.4 주의할 점
for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i)
        }, i * 1000)
}
// 일반적인 함수
const aButton = document.getElementByld('a')

function heavyJob() {
    const longArr = Array.from({ length: 10000000 }, (_, i) => i + 1)
    console.log(longArr.length)
}
aButton.addEventListener('click', heavyJob)
// 클로저라면?
function heavyJobWithClosure() {
    const longArr = Array.from({ length: 10000000 }, (_, i) => i + 1)
    return function () {
        console.log(longArr.length)
    }
}

const innerFunc = heavyJobWithClosure()
bButton.addEventListener('click', function () {
    innerFunc()
})

1.6 라액트에서 자주 사용하는 자바스크립트 문법

1.6.1 구조 분해 할당
배열 구조 분해 할당
const array = [1, 2, 3, 4, 5]
const [first, second, third, ...arrayRest] = array
// first 1
// second 2
// third 3
// arrayRest [4, 5]
const array = [1, 2, 3, 4, 5]
const [first, , , , fifth] = array // 2, 3, 4는 아무런 표현식이 없으므로 변수 할당이 생략돼 있다.
first // 1
fifth // 5
객체 구조 분해 할당
const object = {
    a: 1,
    b: 1,
    c1,
    d: 1,
    e1,
}
const { a, b, c, ...objectRest } = object
// a 1
// b 2
// c 3
// objectRest = {d: 1, e: 1}
const object = {
    a: 1,
    b: 1,
}
const { a: first, b: second } = object
// first 1
// second 2
const object = {
    a1,
    b: 1,
}
const { a = 10, b = 10, c = 10 } = object
// a 1
// b 1
// c 10
1.6.2 전개 구문
배열의 전개 구문
const arrl = ['a', 'b']
const arr2 = arr1

arrl === arr2 // true. 내용이 아닌 참조를 복사하기 때문에 true가 반환된다.
const arr1 = ['a', 'b']
const arr2 = [...arrl]

arrl === arr2 // false. 실제로 값만 복사됐을 뿐, 참조는 다르므로 false가 반환된다.
객체의 전개 구문
const newObj = { ...objl, ...obj2 }
// { "a": 1, "b": 2, "c": 3, "d": 4 }
const obj = {
    a: 1,
    b: 1,
    c: 1,
    d: 1,
    e1,
}

// {a: 1, b: 1, c: 10, d: 1, e: 1}
const aObj = {
    ...obj,
    c: 10,
}

// {c: 1, a: 1, b: 1, d: 1, e: 1}
const bObj = {
    c 10,
    ...obj
}

끝!