본 포스팅 시리즈는 📚'모던 리액트 Deep Dive'를 주간 별로 1장씩 공부하며
* 새롭게 알게 된 것들
* 평소 알고 있다고 생각했지만 이번에 제대로 알게 된 것들
* 궁금한 부분에 대해 딥다이브한 것들
등을 기재하기 위해 시작되었다.
리액트 컴포넌트는 기본적으로 아래와 같은 경우에 리렌더링된다.
리액트는 이전 props와 새로운 props를 비교한 뒤, 값이 다르다고 판단되면 해당 컴포넌트를 리렌더링한다.
리액트는 props를 비교할 때 객체나 배열을 참조(메모리 주소)로만 비교한다. 객체 내부 값까지 깊게 비교하지 않기 때문에, 값이 같더라도 참조가 다르다면 다른 객체로 인식한다.
function Child({ user }) {
console.log("Child 렌더링!");
return <div>{user.name}</div>;
}
function Parent() {
const user = { name: "Alice" }; // 렌더링 때마다 새 객체 생성
return <Child user={user} />;
}
위 예시 코드를 보자.
Parent 가 리렌더링될 때마다 user 객체가 새로 생성되고, 이에 따라 Child 는 props가 달라졌다고 판단해서 리렌더링이 발생하게 된다.
물론❗️ 리액트의 리렌더링 규칙에 의해, user 객체를
Child컴포넌트에 전달하지 않더라도 부모 컴포넌트가 리렌더링되면 자식 컴포넌트 함수도 호출된다.
이건 props가 바뀌었는지 여부와 상관없이, 리액트가 트리를 다시 그려야 하기 때문에 자식 컴포넌트도 렌더링하는 것이다.
typeof null === 'object' // true
다른 타입들은 typeof 연산자를 활용해 타입을 확인했을 때 해당 타입이 반환되나, null은 'object'를 반환한다.
이는 초창기 자바스크립트가 값을 표현하는 방식 때문에 발생한 문제이며, 호환성 등의 이슈로 이 문제는 해결되지 않은 채 남겨졌다.
함수 선언문은 자바스크립트에서 함수를 선언할 때 가장 일반적으로 사용하는 방식으로, 다음과 같이 선언한다.
function add(a, b) {
return a + b
}
함수 선언문을 변수에 할당하는 것도 가능하다.
const add = function add(a, b) {
return a + b
}
add(3, 4) // 7
함수 표현식에서 함수는 다음과 같이 선언하며, 보통 할당하려는 함수의 이름을 생략한다.
const sum = function (a, b) {
return a + b
}
sum(3, 4) // 7
함수 선언문과 함수 표현식의 가장 큰 차이는 호이스팅(Hoisting)이다. 호이스팅은 함수에 대한 선언을 함수 실행 전에 미리 메모리에 등록하는 작업을 의미한다.
함수 선언문은 호이스팅 덕분에 코드의 순서에 관계 없이 함수의 호출을 자유자재로 할 수 있지만, 함수 표현식은 함수 선언문의 호이스팅과 다르게, 호이스팅되는 시점에서 var 의 경우 undefined로 초기화한다는 차이가 있다.
console.log(typeof hello === 'undefined') // true
hello() // Uncaught TypeError
var Hello = function () {
console.log("Hello")
}
함수의 부수 효과(side-effect)란, 함수 내의 작동으로 인해 함수가 아닌 함수 외부에 영향을 끼치는 것을 의미한다.
부수 효과를 처리하는 훅인 useEffect 의 작동을 최소화하는 것이 그 일환이라고 할 수 있다.
useEffect 의 작동을 최소화하는 것은 구체적으로 어떻게 하는 것일까useEffect 는 렌더링 이후 실행되는 부수 효과를 다루기 위한 훅이므로, 렌더링과 직접 관련 없는 계산이나 상태 업데이트 로직을 넣지 않고 DOM 조작, 외부 API 호출, 구독과 같은 진짜 부수 효과만 처리해야 한다.
또한 의존성 배열을 올바르게 관리해 불필요한 재실행을 줄이고, useMemo, useCallback 등을 사용해 참조 값이 불필요하게 변경되지 않도록 최적화하는 것이 중요하다.
useEffect(function apiRequest() {
// ... do something
}, [])
콜백 함수에 이름을 붙이는 것에 대해 생각해본 적 있는가? 일단 지금까지의 나에게는 없었다.
useEffect 나 useCallback 등의 훅에 사용하는 콜백 함수에 이름을 붙여준다고 한들, 이 콜백 함수를 훅 외부에서 호출할 수 있는 것은 아니다. 그러나 네이밍을 통해, 훅 내 콜백 함수의 코드를 유심히 살펴보지 않더라도 이 함수가 어떤 일을 하는지, 어떻게 작동하는지 등을 빠르게 파악하는 데 도움이 될 것이다.
자바스크립트에서는 다른 언어처럼 접근 제한자가 완벽하게 지원되는 것은 아니지만, # 을 붙여서 private 프로퍼티를 선언하는 방법이 ES2019에 추가되었다. 기본적으로는 모든 프로퍼티가 public으로 선언된다.
class Person {
#name; // private field
constructor(name) {
this.#name = name;
}
getName() {
return this.#name; // 클래스 내부에서 접근 가능
}
}
또한 타입스크립트를 활용하면 다른 언어와 마찬가지로 private, protected, public을 사용할 수 있다.
class Animal {
public name: string; // 어디서나 접근 가능
private age: number; // 클래스 내부에서만 접근 가능
protected species: string; // 클래스와 하위 클래스에서만 접근 가능
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
public getInfo() {
return `${this.name} is ${this.age} years old.`;
}
}
class Car {
constructor(name) {
this.name = name
}
get firstCharacter() {
return this.name[0]
}
}
const myCar = Car("자동차")
console.log(myCar.firstCharacter) // 자
getter/setter는 메서드처럼 생겼지만 사실 클래스의 프로퍼티이기 때문에, 실제로 사용할 때 괄호를 붙이지 않고 사용한다.
class Car {
static hello() {
console.log("안녕하세요!")
}
}
const myCar = Car()
myCar.hello() // Uncaught TypeError
Car.hello() // 안녕하세요!
클래스에서 정적 메서드는 static 키워드로 선언한 메서드를 말한다. 정적 메서드는 클래스의 인스턴스가 아닌 클래스의 이름으로만 호출할 수 있다는 특징을 가지고 있는데, 이 메서드에서 this 는 클래스로 생성된 인스턴스가 아닌 클래스 자신을 가리키기 때문에 this 를 사용할 수 없다.
🧐 그럼 정적 메서드는 왜 사용할까?
this 에 접근할 수 없지만, 인스턴스를 생성하지 않아도 사용할 수 있다.따라서 애플리케이션 전역에서 사용하는 유틸 함수를 정적 메서드로 많이 활용하는 편이다.
var와 let의 차이 (p.65-66)0부터 시작해 1초 간격으로 0, 1, 2, 3, 4를 차례대로 출력하는 코드를 작성해보자.
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
위 코드는 우리가 의도한대로 동작하지 않고, 0, 1, 2, 3, 4초 뒤에 모두 5가 출력된다.
그 이유는 자바스크립트는 기본적으로 함수 레벨 스코프를 따르기 때문에, var 키워드로 선언한 제어 변수 i가 전역 변수로 작동해서 for문을 모두 순회한 이후 태스크 큐에 있는 setTimeout 함수를 실행하려고 했을 때 이미 i는 5로 바뀌어있기 때문이다.
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
따라서 위와 같이 var를 블록 레벨 스코프를 가지는 let 으로 수정한다면, 문제를 해결할 수 있다. 위 코드를 실행시키면 우리가 의도한대로 0~4가 1초에 하나씩 차례대로 출력된다.