ABOUT ME

Today
Yesterday
Total
  • 모던 리액트 Deep dive - 클래스, 클로져
    React 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)
        }
    }

     

Designed by Tistory.