[궁금] super(props) 사용이유

JooSehyun·2023년 2월 8일
0

[궁금증]

목록 보기
1/4
post-thumbnail

[궁금] 🕵️‍♀️super(props) 사용이유

super를 호출해야하는 이유가 궁금해졌다.
super를 왜 호출해야할까? 호출하지 않으면 어떻게 될까? super를 호출하되 props 인자를 전달하지 않는다면 어떻게 될까?

자바스크립트에서 super는 부모클래스 생성자의 참조이다. 그리고 자바스크립트는 언어적 제약사항으로써 생성자에서 super를 호출하기 전에는 this를 사용할 수 없다.

예제 )

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

class PolitePerson extends Person {
  constructor(name) {
    this.greetColleagues(); // 🔴 This is disallowed, read below why
    super(name);
  }
  greetColleagues() {
    alert('Good morning folks!');
  }
}

super 호출 전에도 this를 사용하는 것이 가능하다고 가정해보자. 아직까지는 문제가 없어보인다. 하지만 몇달 후에 greetColleagues 함수가 아래와 같이 this.name을 사용하도록 변경되었다고 해보자

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

this.name이 초기화되기 전에 this.greetColleagues가 호출되었다. 코드를 이해하기가 상당히 어려워졌다. 이렇게 애매한 경우를 허용하지 않기 위해 자바스크립트는 언어 차원에서 this 사용 전에 super호출을 강제하는 것이다. 그리고 이 사항이 클래스 기반의 리액트 컴포넌트를 작성하는 데에도 동일하게 반영된 것이다.

constructor(props) {
    super(props);
    // ✅ Okay to use `this` now
    this.state = { isOn: true };
  }

super를 반드시 호출해야 하는 이유는 설명이 되었지만 아직 해결되지 않은 질문이 하나 남았다. 왜 props를 인자로 전달해야 할까?

아마도 React.Component객체가 생성될 때 props속성을 초기화하기 위해 부모 컴포넌트에게 props를 전달하는 것이구나 라고 쉽게 예상할 수 있을 것이다.

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

그 예상은 틀리지 않았다. 그리고 실제로 그렇게 동작한다.
그러나 props전달 없이 super()를 호출하더라도 render 함수 및 기타 메소드에서 여전히 this.props를 사용할 수 있습니다.

어떻게 그것이 가능할까? 리액트는 작성한 컴포넌트의 생성자 호출 이후 해당 객체에 props속성을 세팅해준다.

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

그래서 리액트는 propssuper의 인자로 전달하는 것을 실수로 빠뜨리더라도 정상적으로 동작되는 것을 보장해준다.

리액트가 처음 클래스를 지원하기로 했을 때 단지 ES6의 클래스만 지원하기로 했던 것은 아니었다. 최대한 범용적으로 여러가지 클래스 형태를 지원하고자 했다. 정확하지는 않지만 ClojureScript , CoffeeScript , ES6 , Fable , Scala.js , TypeScript 등에서 사용하기에도 문제가 없도록 하고자 했다. 그래서 리액트는 의도적으로 super를 사용하는데 주저하지 않았다.

그렇다면 이것이 super()를 사용하기 보다 super(props)를 사용해야하는 충분한 이유가 될까?

아마도 그렇지 않을 것이다. 여전히 충분히 납득이 안되는 사람도 있을 것이다. 리액트는 작성한 생성자 호출 이후에 props를 세팅해 준다. 생성자 내부에서 super()가 호출되고 생성자가 끝나기 전까지 this.propsunderfined가 될 것이다.

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

// Inside your code
class Button extends React.Component {
  constructor(props) {
    super(); // 😬 We forgot to pass props
    console.log(props);      // ✅ {}
    console.log(this.props); // 😬 undefined 
  }
  // ...
}

이렇게 this.propsundefined인 생성자 내부에서 다른 함수를 또 호출하는 경우에 이로 인해 발생하는 문제들을 디버깅하는 일은 매우 흥미진진한 일이 된다.

이것이 바로 super(props)를 꼭 호출해야만 하는 이유이다. 심지어 this.props를 굳이 사용하지 않는 경우라도 말이다.

class Button extends React.Component {
  constructor(props) {
    super(props); // ✅ We passed props
    console.log(props);      // ✅ {}
    console.log(this.props); // ✅ {}
  }
  // ...
}

이렇게 super(props)를 호출하는 것은 생성자 내부에서도 this.props를 정상적으로 사용할 수 있도록 보장해 준다.

아직 리액트 개발자들이 오랫동안 궁금해 했던 것이 아직 하나 남았다.

Context API를 사용할 경우 context가 생성자의 두번째 인자로 전달된다는 것을 알고 있었을 것이다. 그렇다면 왜 super(props,context)와 같이 사용하지는 않을까? 그렇게 해도 된다. 그러나 context는 많이 사용되지 않기 때문에 이로 인한 문제는 별로 발생되지 않을 것이다.

어쨌든 class fields proposal 를 공식적으로 사용할 수 있을 때가 되면 super(props)에 대한 복잡한 사항들은 더 이상 고민하지 않아도 될 것이다. 그때는 명시적인 생성자 선언 없이도 모든 인자를 편하게 사용할 수 있을 것이다.

This is what allows an expression like state = {} to include references to this.props or this.context if necessary.

Hooks 를 사용한다면 우린는 superthis에 대해 고민하지 않아도 된다. 하지만 그것은 나중에 다루어야 할 주제이다.

[출처]https://min9nim.github.io/2018/12/super-props/
이 글은 Dan Abramov의 Why Do We Write super(props)? 글을 충분한 의역으로 번역한 것입니다. 번역이 일부 자연스럽지 않은 부분이 있을 수도 있습니다. 정확한 내용은 원문을 참고하기 바랍니다

0개의 댓글