hooks가 최근에 핫하다고 들었다.

아이러니하지만 class 컴포넌트에 재밌는 사실을 설명하기 위해 블로그를 시작한다.

리액트를 더 잘 사용하기 위해서 이것을 아는것은 중요하지 않지만,
어떻게 동작하는지 깊이있게 파고드는 것을 좋아한다면, 재미있을 것이다.

첫 번째 예시를 보자.


나는 super(props) 에 대해 제대로 알기 전부터 사용해왔다.

class Checkbox extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isOn: true };
    };
    // ...
};

물론 클래스필드 문법을 사용하면 작성하지 않아도 된다.

class Checkbox extends React.Component {
    state = { isOn: true };
    // ...
};

클래스필드는 2015년 리액트 버전 0.13부터 기본 클래스 문법을 보조하기 위해 계획되었다. 클래스필드를 지원하기 전까지 constructor를 정의하고 super(props)를 호출하는 것이 항상 임시적인 대안이 되었다.

다시 ES6만을 사용하는 예제로 돌아가보자.

class Checkbox extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isOn: true };
    };
    // ...
};

super를 선업합니까, 선언하지 않으면 어떻게 됩니까, super를 사용했을때, props를 넣어주지 않으면 어떤 일이 발생합니까. 또다른 의견을 한번 알아보자.


자바스크립트 에서는 super는 부모 클래스 생성자를 가리킨다(리액트에서는 React.Component)

중요한것은, super(props) 선언전까지 constructor에서 this 키워드를 사용 할 수 없다.
자바스크립트에서는 허용되지 않는다.

class Checkbox extends React.Component {
    constructor(props) {
        // this 사용불가!
        super(props);
        // 여기서 부터 사용가능!
        this.state = { isOn: true };
    };
    // ...
};

왜 자바스크립트가 부모 constructorthis 사용전에 실행하는지에 대한 좋은 이유이다.
클래스의 체계를 생각해보자.

class Person {
    constructor(name) {
        this.name = name;
    };
};

const PolitePerson extends Person {
    constructor(name) {
        this.greetColleagues(); // 이것은 허용되지 않는다. 이유를 읽어보자
        super(name);
    };

    greetColleagues() {
        alert('Good morning folks!');
    };
};

super를 선언하기 전 this를 사용했을 경우를 가정해보자.

시간이 지난 뒤에 greetColleagues 메서드에 메세지를 추가 하여야 할 상황이다.

greetColleagues() {
    alert('Good morning folks!');
    alert('My name is' + this.name + ', nice to meet you');
}

그 사이 잊어버렸다. this.greetColleagues()super()가 불려오기 전에 실행되어 버렸다. this.name은 아직 변경되지 않았다. 이와 같은 코드를 보는 것은 논리를 굉장히 복잡하게 만들수 있다.

이런 단점을 보완하기 위해, 자바스크립트는 super 키워드 실행 이후 this를 사용할 수 있게 한다. 이것은 리액트에서 클래스 문법을 사용 할때도 적용된다.

constructor(props) {
    super(props);
    // this 사용 가능!
    this.state = { isOn: true };
};

이로인해 왜 props를 넣어줘야 하는가라는 질문이 떠오른다.


React.Component 에서 this.props에 초기 설정을 하기 위해 propssuper에 넣어준다고 생각 할수도 있다.

// Inside React
class Component {
    constructor(props) {
        this.props = props;
        // ...
    };
};

사실에 가깝다. 실제로 하는일이다.

그러나, 어떤 경우에는 super 키워드 안에 props를 작성하지 않아도 render 메서드에서 this.props를 사용 할 수 있다.(직접 해보면 알것!)

올바르게 작동하는가? 리액트는 constructor가 올바르게 불려지고 난 이후에 props를 불러온다.

// Inside React
const instance = new YourComponent(props);
instance.props = props;

그래서 만약에 super() 안에 props 추가하는 것을 기억하지 못하더라도, 리액트는 여전히 올바르게 작동한다. 이유가 있다.

리액트가 클래스 문법에 대한 지원이 추가 되었을때, 단지 ES6 클래스로써의 지원만을 한것은 아니였다. 클래스의 넓은 범위의 추상화를 지원하는 것이 목표였다.

ClojureScript, CoffeeScript, ES6, Fable, Scala.js, Typescript 및 기타 솔루션들이 클래스 구성 요소를 정의하는데 얼마나 성공적이었는지는 명확하지 않다.

그래서 리액트는 의도적으로 super() 를 작성하는것을 요구하였다. ES6의 클래스로써.
그래서 이것이 super(props) 대신 super()를 작성하여도 문제가 없는 것을 의미하는가?

아마도 그렇지 않을것이다. 혼란스럽게 할것이다.
물론 리액트는 constructor가 실행된후 this.props를 할당된다.
그러나 constructor에서 super()가 불려진 이후에도 this.props는 여전히 undefined 일 것이다.

// Inside React

class Component {
    constructor(props) {
        this.props = props;
        // ...
    };
};

// Inside your code
class Button extends React.Component {
    constructor(props) {
        super(); // props를 넣어주지 않았다
        console.log(props); // {}
        console.log(this.props); // undefined
    };
    // ...
};

이것은 constructor로부터 불린 메서드에서 디버깅을 어렵게 할 수 있다. 그래서 꼭 필요한 것은 아니지만, 항상 super(props)로 작성하는 것을 추천하다.

class Button extends React.Component {
    constructor(props) {
        super(props); // props 추가
        console.log(props); // {}
        console.log(this.props); // {}
    };
    // ...
};

이러한 코드는 constructor 사이클이 끝나기전 this.props 가 생성되는 것을 보장한다.


리액트 사용자들이 오랫동안 궁금해 하던 것이 하나 더 있다.

Context API를 클래스에서 사용 할 때(React 16.6에서 추가된 contextType 또는 레거시에 contextTypes) context가 생성자에 두번째 인수로 전달 되는 것을 알고 있을 것이다.

그렇다면 왜 super(props, context)를 써야 합니까? context는 사용 빈도가 적었기 때문에, 큰 문제 의식을 가지고 있지 않았다.

클래스 필드 문법은 이런 문제점들을 대부분 해결해 준다. 명시적인 constructor가 없으면, 모든 인자는 자동으로 전달된다. state = {} 가 필요하다면, this.props 또는 this.context를 참조 할 수 있도록 허용 한다.

hooks 와 함께라면 우리는 superthis를 사용하지 않을 것입니다. 다음에 알아보자!

원문 - Why Do We Write super(props)?