모던 리액트 Deep dive - 클래스, 클로져
1. 클래스란?
- 특정한 객체를 만들기 위한 일종의 템플릿.
특정한 형태의 객체를 반복적으로 만들기 위해 사용되는 것이 바로 클래스.
다음은 클래스 예제이다. (우아한테크코스 로또 미션 중 일부)
1-1 정적 메서드(static)은 클래스에서 직접 호출 할 수 있다.
const { ErrorMessage } = require("./Error");
class Validate {
static purchase(callback) {
return (amount) => {
if (Number(amount) % 1000 !== 0) {
throw new Error(ErrorMessage.IS_CORRECT_AMOUNT);
}
if (isNaN(amount)) {
throw new Error(ErrorMessage.NOT_NUMBER);
}
callback(amount);
};
}
}
module.exports = Validate;
정적 메서드는 this에 접근할 수 없지만, 인스턴스를 생성하지 않아도 사용할 수 있다.
객체를 생성하지 않더라도 여러 곳에서 재사용이 가능하다.
따라서 전역에서 사용하는 유틸 함수를 정적 메서드로 많이 활용하는 편.
1-2 getter와 setter
getter란 클래스에서 무언가 값을 가져올 때 사용.
class Car {
constructor(name) {
this.name = name
}
get firstCharacter() {
return this.name[0]
}
}
const myCar = new Car('자동차')
myCar.firstCharacter //자
setter란 클래스 필드에 값을 할당할 때 사용
class Car {
constructor(name) {
this.name = name
}
get firstCharacter() {
return this.name[0]
}
set firstCharacter(char) {
this.name = [char, ...this.name.slice(1)].join('')
}
}
const myCar = new Car('자동차')
myCar.firstCharacter //자
// '차'를 할당
myCar.firstCharacter = '차'
console.log(myCar.firstCharacter, myCar.name) // 차, 자동차
1-3 상속
extends를 활용하면 기본 클래스를 기반으로 다양하게 파생된 클래스를 만들 수 있다.
(다음은 '김정완의 mvc패턴' 중 일부)
import { emit, on } from "../helpers.js";
const tag = "[View]";
export default class View {
constructor(element) {
if (!element) throw "no element";
this.element = element;
this.originalDisplay = this.element.style.display || "";
return this;
}
show() {
this.element.style.display = this.originalDisplay;
return this;
}
}
import View from "./View.js";
import { qs } from "../helpers.js";
export default class KeywordListView extends View {
constructor(element = qs("#keyword-list-view"), template = new Template()) {
super(element);
this.template = template;
}
show(data = []) {
this.element.innerHTML =
data.length > 0
? this.template.getList(data)
: this.template.getEmptyMessage();
super.show();
}
}
class Template {
getEmptyMessage() {
return `<div class="empty-box">추천 검색어가 없습니다</div>`
}
getList(data=[]) {
return `
<ul class="list">
${data.map(this._getItem).join("")}
</ul>
`
}
_getItem({id, keyword}) {
return `
<li data-keyword="${keyword}">
<span class="number">${id}</span>
${keyword}
</li>
`
}
}
1-4 클래스와 함수의 관계
클래스가 작동하는 방식은 JS의 프로토타입을 활용하는 것이라고 볼 수 있다.
** 클래스를 이해하고 나면 클래스형 컴포넌트에 어떻게 생명주기를 구현할 수 있는지, 왜 클래스형 컴포넌트 생성을 위해 React.Component나 Reac.pureComponent를 상속하는지, 메서드가 화살표 함수와 일반 함수일 때 어떤 차이가 있는지 등을 이해할 수 있다.
2. 클로저
2-1 클로저의 정의
클로저는 함수와 함수가 선언된 어휘적 환경의 조합 - mdn
2-2 변수의 유효 범위, 스코프
전역 스코프
전역 객체는 windows, Node.js 환경에서는 global
함수 스코프
자바스크립트는 기본적으로 함수 레벨 스코프를 따른다.
2-3 클로저의 활용
var counter = 0
function handleClick() {
counter ++
}
위 counter 변수는 전역에 있어서 누구나 수정할 수 있다는 큰 문제가 있음.
클로저를 활용한 코드로 변경해보자,
function counter () {
var counter = 0
return {
increase: function() {
return ++ counter
},
decrease: function() {
return -- counter
},
counter: function() {
return counter
}
}
}
리액트에서의 클로저
function Component() {
const [state, setState] = useState()
function handleClick() {
// useState 호출은 위에서 끝났지만,
// setState는 계속 내부의 최신값(prev)를 알고 있다.
// 이는 클로저를 활용했기 때문에 가능하다.
setState((prev) => prev + 1)
}
}