React

모던 리액트 Deep dive - 클래스, 클로져

popeyes 2024. 2. 18. 14:04

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)
    }
}