안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

리액트 (React)

[React] 학습 중 자체 Q & A (지속적인 업데이트 예정)

피그브라더 2020. 9. 9. 14:19

* React를 공부하면서 생긴 궁금증들과 이에 대한 해답을 개인적으로 정리한 것입니다. 잘못된 내용이 있다면 지적 부탁드립니다.

 

1. <script> 태그로 Babel 라이브러리를 로드하는 경우 Babel은 어떠한 원리로 동작하는가? 

  • <script> 태그로 Babel 라이브러리를 로드한다.
  • <script type="text/babel"> 태그를 만들고 그 안에 ES6(+ JSX) 문법의 코드를 작성한다.
  • 브라우저는 <script type="text/babel"> 태그 안에 작성된 코드를 ES6(+ JSX) 문법으로 간주한다.
  • 그 코드들은 로드된 Babel 라이브러리에 의해 모든 브라우저에서 호환이 가능한 문법의 코드로 컴파일된다.
  • 따라서 이러한 런타임 변환은 서버에서 미리 컴파일된 코드를 제공하는 것보다 더 느릴 수밖에 없다.

 

2. key prop은 엘리먼트 배열을 사용할 때만 의미가 있는가?

  • React 공식 문서에 따르면, 프로그래밍적으로 엘리먼트 배열을 만들지 않고 단순히 엘리먼트들을 하드 코딩해서 나열한 경우에도 key prop을 사용하면 엘리먼트 간의 식별이 가능해진다고 한다. 생각해보면 당연하다. 프로그래밍적으로 만들든 하드 코딩을 하든 엘리먼트 배열이라는 것은 본질적으로 같기 때문이다. 실제로, 하드 코딩에 의해 컴포넌트의 자식으로 삽입된 여러 개의 엘리먼트들조차도 결국은 배열의 형태로 묶여서 해당 컴포넌트 props의 children 프로퍼티에 전달된다.
  • 따라서 key prop은 필수가 아니며(디폴트 값은 null), 단지 React에서 엘리먼트 배열을 프로그래밍적으로 만들 때 key prop을 설정하도록 권고하고 있을 뿐이다. 실제로 key prop에 대해 경고를 제공하는 부분은 렌더링 하는 부분이 아닌 map() 함수를 이용하여 엘리먼트 배열을 만드는 부분이다.
  • key prop의 용도 및 주의 사항
    • key prop은 재조정 과정에서 한 엘리먼트 안에 여러 자식 엘리먼트들이 존재할 때 비교를 위해 사용이 된다.
    • 기본적으로 React는 기존 트리와 새로운 트리의 자식 엘리먼트들을 순회하며 차이점을 계산하게 된다.
    • 이때 만약 key prop이 설정되어 있다면, 더 효율적이고 안정적인 방식으로 엘리먼트 간의 식별이 가능해진다.
    • 이를 잘 응용하면, 기존의 DOM 노드를 언마운트 하고 새로운 DOM 노드를 마운트 하도록 설계하는 것도 가능하다.
    • 또한 컴포넌트 엘리먼트의 경우, key prop 값을 기반으로 컴포넌트 인스턴스가 갱신되고 재사용된다.
    • 따라서 key prop을 잘못 설정하면 컴포넌트 인스턴스가 언마운트 되어 state가 유실될 수 있으니 주의해야 한다.

 

3. React의 제어 컴포넌트와 HTML의 폼 엘리먼트는 무엇이 다른가?

  • HTML의 폼 엘리먼트는 사용자가 입력한 값을 기반으로 자체적인 내부 상태를 업데이트하고 관리한다.
  • 반면 React에서는 상태가 컴포넌트 인스턴스 내에서 관리되고 setState() 메소드에 의해 업데이트된다.
  • 이처럼 React의 상태를 신뢰 가능한 단일 출처(Single Source of The Truth)로 사용하는 폼 엘리먼트를 제어 컴포넌트라고 부른다.
  • 제어 컴포넌트의 경우 React가 폼 엘리먼트의 값을 제어하며, 그 값을 어느 곳에서든 쉽게 접근할 수 있게 된다.
  • 특히 <input type="text">, <textarea>, <select> 엘리먼트는 제어 컴포넌트로 구현하기 편리하다.
  • 반면 <input type="file"> 엘리먼트는 값이 읽기 전용이기 때문에 제어 컴포넌트로 구현할 수 없는 비제어 컴포넌트이다. 즉 프로그래밍적으로 값을 설정할 수 없고 사용자만이 값을 설정할 수 있다. 사용자가 설정한 값에 접근하려면 Ref 메커니즘을 사용하여 DOM 노드에 직접 접근해야 한다.
  • 하나의 핸들러로 여러 <input> 엘리먼트를 제어할 때, name 어트리뷰트의 값은 e.target.name을 통해 접근할 수 있다.
  • 제어 컴포넌트의 value prop을 설정하면 핸들러를 지정하지 않은 이상 사용자가 값을 변경할 수 없다. 단 null이나 undefined로 설정하면 사용자가 값을 변경할 수 있고, 그 경우 React는 이를 알아차리지 못한다.
  • 특별한 상황이 아니면 비제어 컴포넌트를 사용하지 않는 걸 권장한다. 그러나 제어 컴포넌트를 사용하는 것은 핸들러 작성, 폼 제출 처리, 유효성 검사 등의 측면에서 매우 불편할 수도 있다. 이에 대한 대안으로 사용할 수 있는 좋은 라이브러리가 바로 Formik이다. 충분히 익숙해진 뒤 사용하면 매우 유용할 것이다.

 

4. 컴포넌트의 componentDidMount(), componentDidUpdate() 생명 주기 메소드는 언제 호출되는가?

  • Virtual DOM(= 최종적인 엘리먼트 트리) 전체가 실제 DOM에 반영된 이후이다.
  • 모든 자식 컴포넌트들의 componentDidMount() 혹은 componentDidUpdate() 생명 주기 메소드가 호출되어야 부모 컴포넌트의 componentDidMount() 혹은 componentDidUpdate() 생명 주기 메소드가 호출이 된다. 따라서 항상 자식 컴포넌트의 메소드가 부모 컴포넌트의 메소드보다 먼저 호출된다(역순).
  • 하지만 그렇다고 해서 자식 엘리먼트에 해당하는 DOM 노드가 실제 DOM에 먼저 반영되는 것은 아니다. 실제 DOM은 일괄적으로 한 번에 마운트/업데이트된다. 가장 깊은 레벨에 있는 컴포넌트의 componentDidMount() 혹은 componentDidUpdate() 생명 주기 메소드가 호출되었다면, 실제 DOM은 이미 마운트/업데이트가 끝난 상황인 것이다.
  • 그래서 Ref에 DOM 노드 혹은 컴포넌트 인스턴스가 연결되는 것도 실제 DOM 노드가 반영된 이후 componentDidMount() 혹은 componentDidUpdate() 생명 주기 메소드가 호출되기 직전이다.

 

5. React.memo() 함수의 사용과 관련한 궁금증 (미해결)

  • 특정 조건을 만족했다면(EX. props가 변하지 않음) 단순히 렌더링을 스킵하고 재조정 과정을 끝내면 되지 않을까? shouldComponentUpdate() 메소드가 그렇듯 말이다. 그런데 굳이 왜 이전 렌더링 결과를 기억해서 재사용한다는 걸까?
  • React.memo() 함수에 의해 만들어진 래퍼 컴포넌트를 여러 군데에서 사용한다면, 하나의 함수 인스턴스를 여러 곳에서 사용하므로 문제가 되지 않을까? reselect 패키지의 createSelect() 함수가 만들어낸 Selector 함수처럼 함수 인스턴스 자체가 내부 상태를 가지고 있을 것이기 때문이다. (참고1) (참고2)
    • 만약 React.memo() 함수가 반환하는 것이 '클래스형' 컴포넌트이고 각각의 컴포넌트 인스턴스가 이전 렌더링 결과를 기억하는 것이라면 말은 된다. 그러나 아직 React.memo() 함수가 반환하는 컴포넌트의 타입을 알아내지 못하였다. (검색 실패)