1.3 클래스
16.8 버전 전까지 모든 리액트의 컴포넌트는 클래스였다.
🔖 함수형 컴포넌트로 변경된지 얼마 되지 않은 리액트를 더 깊이 있게 알아보자.
1.3.1 클래스란 무엇인가?
🔖 특정 객체를 만들기 위한 일종의 템플릿과 같은 개념
- 객체를 만들 때 필요한 데이터나 조작 코드를 추상화하여 더 편리하게 생성할 수 있도록 한다.
- JS에서 클래스로 하는 모든 것을 함수로 동일하게 할 수 있다.
🏷️ 클래스 작성법
class Car {
// constructor (생성자) 작성
// 최초 생성 시 어떤 인수를 받을지 결정 및 객체 초기화
constructor(name) {
this.name = name;
}
// 메서드
honk() {
console.log(`${this.name}이(가) 경적을 갈긴다.`);
}
// 정적 메서드
static hello() {
console.log("안녕? 난 차야");
}
// setter
set age(value) {
this.carAge = value;
}
// getter
get age() {
return this.carAge;
}
}
🏷️ 클래스 사용법
// 미리 정의한 Car 클래스를 이용해 객체 생성
const myCar = new Car("포르쉐");
// 메서드 호출
myCar.honk(); // 포르쉐이(가) 경적을 갈긴다.
// 정적 메서드는 클래스에서 직접 호출한다.
Car.hello(); // 안녕? 난 차야
// 정적 메서드는 클래스로 만든 객체에선 호출이 불가하다.
myCar.hello(); // UncaughtError
// setter를 사용해 값을 할당할 수 있다.
myCar.age = 32; // myCar.carAge = 32;
// getter를 사용해 값을 가져올 수 있다.
console.log(myCar.age, myCar.name); // 32 포르쉐
🔖 클래스 내부 특징
- constructor
- 생성자로, 객체를 생성하는데 사용
- 단 하나만 존재 가능(여러개 사용 시 에러)
- 수행할 작업이 없을 시 생략 가능
- 프로퍼티
- 인스턴스 생성 시 내부에 정의할 수 있는 속성값
- 타입스크립트 활용 시
private
,public
,prottected
활용 가능
- getter & setter
- 클래스에서 값을 가져오거나, 값을 할당할 때 사용
- 각각
get
과set
키워드를 사용한다.
- 인스턴스 메서드
- 클래스 내부에서 선언한 메서드 (
prototype
에 선언)- prototype에 선언했으므로, 클래스를 기반으로 만드는 객체들은 메서드 또한 사용 가능
- 비교를 위해
Object.getPrototypeOf
메서드를 사용 가능하다.
❗여기서 prototype
이란?
- 객체 상속 시 사용하는 객체를
prototype
이라 정의한다.- 모든 객체는 proto(비표준)이라는 속성을 가지며, 이는 프로토타입을 가리킨다.
- 객체의 속성이나 메서드를 검색하고, 없을 시 프로토타입을 참조한다. (프로토타입 체인)
- 정적 메서드
- 클래스의 인스턴스가 아닌, 이름으로 호출 가능한 메서드
클래스 자신을 가리키므로, this 사용이 불가하고 인스턴스에서도 호출 불가하다.
객체를 생성하지 않아도 여러 곳에서 사용 가능하여, 전역적 유틸 함수로 활용한다.
- 상속
- 부모-자식 관계와 같이 다른 클래스에 속성들을 상속시켜 확장하는 개념
- extends 키워드를 활용하여 다양하게 파생된 클래스 생성이 가능하다.
1.3.2 클래스와 함수의 관계
🔖 클래스가 작동하는 방식은 JS의 prototype
을 활용하는 것
// 프로토타입를 활용한 JS의 클래스 구현
const Car = (function () {
function Car(name){
this.name = name;
}
Car.prototpye.honk = function () {
console.log(`${this.name}이(가) 경적을 갈긴다.`);
}
Car.hello = function () {
console.log(`안녕? 난 차야`);
}
Car.defineProperty(Car, 'age'){
get: function () {
return this.carAge
},
set: function () {
this.carAge = value
}
}
return Car;
})()
1.3.3 책 정리 + 주관적인 정리
🔖 책 정리
- 다른 객체지향언어와 비슷한 수준으로 JS의 클래스도 여러가지 기능을 제공한다.
- 클래스 이해를 통해 클래스 컴포넌트에 생명주기 구현 및 상속, 함수 표현 방식에 따른 차이를 이해할 수 있다.
🏷️ 주관적인 정리
- 클래스 컴포넌트는 예전에 리액트 라이프 사이클을 공부할 때 잠깐 스쳐지나간 기억이 있는데, 이런 식으로 리액트 전반에 관련된 중요한 개념인 줄 몰랐다.
- 현재 우리가 사용하는 함수형 컴포넌트는 결국 클래스 컴포넌트와 동일한 메커니즘이므로, 클래스를 확실하게 이해한다면 함수형 컴포넌트 작성에도 매우 용이할 듯 하다.
- 더불어 JS 딥다이브에서 공부했던 프로토타입에 관해 다시 정리할 수 있어 좋았다.
1.4 클로저
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합이다.
🔖 함수형 컴포넌트 작동 방식, 구조, 훅, 의존성 배열 등이 모두 클로저에 의존한다.
1.4.1 클로저의 정의
🔖 클로저는 this
와 달리 코드가 작성된 순ㄴ간에 정적으로 결정되는 변수나 함수의 스코프를 사용하는 기법이다.
function add() {
const a = 10;
function innerAdd() {
const b = 20;
console.log(a + b); // 상위 스코프인 add()에 a 변수가 있기 때문에 가능
}
innerAdd();
}
add();
1.4.2 변수의 유효 범위, 스코프
🔖 JS의 다양한 스코프를 알아보자!
전역 스코프
- 전역 레벨에 선언하는 스코프
- 해당 스코프 선언 시 어디서든 호출 가능하다.
함수 스코프
- {}블록이 아닌 함수 레벨로 범위를 결정
- JS는 변수 호출 시 상위 스코프로 변수 탐색 범위를 점점 넓힌다.
1.4.3 클로저의 활용
🔖 환경을 기억하고 이를 사용하는 특징을 이용해 상태 유지나 정보 은닉에 사용
const increase = (function () {
let num = 0;
return function () {
return ++num;
};
})();
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
- 위 함수의
num
이 전역 변수였다면 어디서든 활용되었을 것이다. - 하지만 함수 안에 넣고, 클로저를 활용해
num
의 상태를 유지하면서 함수 안에서만 사용 가능하도록 변경하였다.
🔖 리액트에서의 사용
- 대표적으로 활용되는 것이 리액트의
useState
훅이다.
function Component () {
const [num, setNum] = useState();
function handleClick () {
...
setState((prevNum) => prevNum + 1); // 클로저를 이용해 사용 시의 환경을 기억
}
}
1.4.4 주의할 점
🔖 기본 개념인 함수와 함수가 선언된 렉시컬 환경의 조합을 항상 인지하고 사용
❗ 꼭 필요한 작업만 남겨두지 않으면 메모리를 불필요하게 소모
❗ 적절한 스코프로 제한하지 않으면 성능에 악영향을 끼침
// 좋지 않은 예시인 긴 작업을 클로저로 처리하는 경우
function heavyJobWithClosure() {
const longArr = Array.from({ length: 10000000 }, (_, i) => i + 1);
return function () {
console.log(longArr.length);
};
}
const innerFunc = heavyJobWithClosure();
bBtn.addEventListener("click", function () {
innerFunc();
});
- 위의 경우 내부 함수가 선언 환경을 기억해야 하므로 이러한 긴 작업 또한 메모리에 올리게 된다.
1.4.5 책 정리 + 주관적인 정리
🔖 책 정리
- 클로저는 함수형 프로그래밍의 주요 개념인 부수 효과를 줄이고 순수해야 한다라는 목적 달성을 위해 적극 사용되는 개념이다.
- 하지만 공짜는 아니므로 항상 사용에 주의할 필요가 있다.
🏷️ 주관적인 정리
- 클로저 자체는 JS 딥다이브 스터디 때 했던 발표로 어느정도 익숙한 개념이었지만 리액트에서 어떻게 이를 사용하는지도 알 수 있어 유익했다.
- 클로저의 자체적인 활용과 더불어 사용하는 비용에 대한 문제도 고민해볼 수 있었다. (아껴야 잘 산다!)
1.5 이벤트 루프와 비동기 통신의 이해
자바스크립트는 싱글 스레드 언어이고, 비동기 작업은 자바스크립트를 멀티 스레드 언어처럼 동작하게 해주는 것이다.
🔖 비동기를 이용해 여러가지 작업을 한번에 처리한다.
1.5.1 싱글 스레드 자바스크립트
🔖 스레드에 대한 이해
- 프로그램의 상태가 메모리상에서 실행되는 작업 단위인 프로세스보다 작은 실행 단위
- 하나의 프로세스는 여러개의 스레드를 만들 수 있다
- 스레드끼리 메모리를 공유하여 여러 작업을 동시에 수행 가능하다.
🔖 자바스크립트는 단순히 웹 조작을 보조하기 위한 용도로 만들어져 효율적인 싱글 스레드 언어로 구현되었다.
- 자바스크립트 코드는 하나의 스레드에서 순차적으로 실행
- 코드를 한 줄씩 실행한다.
- 하나의 작업이 끝나기 전까지 뒤의 작업이 시작되지 않는다.
Run-to-completion
, 동기식으로 순차 처리된다.
1.5.2 이벤트 루프란?
🔖 자바스크립트 런타임 외부에서 비동기 실행을 돕기 위한 장치
function bar() {
console.log("bar");
}
function baz() {
console.log("baz");
}
function foo() {
console.log("foo");
bar();
baz();
}
foo();
- 콜 스택 작업 예시
- 각각의 함수들은 순서대로 콜스택에 들어가고, 실행 후 콜스택에서 제거된다.
- 기본적인 스택의 LIFO 방식에 따라,
baz()
->bar()
->foo()
순으로 제거된다.
✅ 콜 스택이 비어있는지 검사하는 것이 바로 이벤트 루프이다.
function bar() {
console.log("bar");
}
function baz() {
console.log("baz");
}
function foo() {
console.log("foo");
setTimeout(bar(), 0);
baz();
}
foo();
- 콜스택과 이벤트 루프의 비동기 처리
- 마찬가지로 차례대로 콜 스택에 들어간다.
- 하지만
setTimeout
때문에,bar()
는 태스크 큐로 들어가고 콜 스택에서 제거된다. - 다른 함수들이 실행되고 콜 스택이 비워진게 확인되면 이벤트 루프가
bar()
를 콜 스택에 집어넣는다. - 함수들이 실행되고 콜 스택이 전부 비워진다.
🔖 이러한 비동기 처리 작업은 자바스크립트의 메인 스레드가 아닌, 태스크 큐가 할당되는 별도의 스레드에서 수행된다.
🔖 자바스크립트 자체 코드 처리를 제외한 외부 Web API는 브라우저나 Node.js가 비동기 처리한다.
1.5.3 태스크 큐와 마이크로 태스크 큐
🔖 이벤트 루프는 하나의 마이크로 태스크 큐를 가지며, 이는 기존의 태스크 큐보다 실행이 우선된다.
- 태스크 큐 : setTimeout, setInterval, setImmediate
- 마이크로 태스크 큐 : Promise, MutationObserver, etc..
✅ 각 마이크로 태스크 큐 작업이 종료될 때마다 한번 렌더링할 기회를 얻는다.
requestAnimationFrame()
을 사용한 예시
console.log("a");
setTimeout(() => {
console.log("b");
});
Promise.resolve().then(() => {
console.log("c");
});
window.requestAnimationFrame(() => {
console.log("d");
});
- 위 코드의 실행 순서는
a - c - d - b
이다. - 따라서 브라우저에 렌더링하는 작업은 마이크로 태스크 큐와 태스크 큐 사이에서 일어난다.
1.5.4 책 정리 + 주관적인 정리
🔖 책 정리
- 자바스크립트의 싱글 스레드만으로는 불가능한 비동기 처리를 브라우저와 Node.js, 이벤트 루프와 마이크로 태스크 및 태스크 큐들을 통해 가능케 한다.
🏷️ 주관적인 정리
- 이벤트 루프 자체의 개념은 어렴풋이 알고 있었지만 조금 더 자세하게 이를 들여다보고 각각의 비동기 작업들을 비교하며 알아볼 수 있어 도움이 됐다.
- 더불어 이벤트 루프와 비동기 처리를 더욱 자세히 알아볼 수 있는 계기가 됐다. 👍
'Front-End Study > 모던 리액트 딥다이브 스터디' 카테고리의 다른 글
모던 리액트 딥다이브 - 7회차 [3-1, 3-2, 3-3] (2) | 2024.06.19 |
---|---|
모던 리액트 딥다이브 6회차 [2-3, 2-4, 2-5] (0) | 2024.06.17 |
모던 리액트 딥다이브 - 5회차 [2-1, 2-2] (1) | 2024.06.10 |
모던 리액트 딥다이브 - 4회차 [1-6, 1-7] (1) | 2024.06.08 |
모던 리액트 딥다이브 - 2회차 [1-1, 1-2] (0) | 2024.06.01 |