React의 성능 최적화가 가장 중요한가? :: Pre-Onboarding

원티드 프리 온보딩 코스가 2주나 지났지만 마지막 1주가 윈티드와 겹친 기업협업 스케쥴과, 끝났지만 며칠 나와 인수인계해달라는 기업의 부탁, 이력서 등등으로 블로그 쓰는 걸 포기한 정도로 손을 놔버렸지만
이럴려고 이 수강과정을 지원한게 아니고 더 빠듯하고 더 발전한 주니어 개발자가 되기 위해서 였다는걸 망각 하지말고 이제라도 규칙적이게 블로그를 써볼려고 썸네일도 만들었습니다.🤗(뿌듯)

  • 주 마다 두 번씩하는 멘토님(예리멘토님) 수업에 관한 것
  • 기업 과제에 좋았던, 기록하고 싶은 코드와 방향성에 관한 것
    주로 이 두 주제에 관한 것을 적을려고 노력합니다.

사실 이 블로그 포스팅도 과제라는 건 안비밀ㅎㅎ 🤫

Let's dig in 🤟

코드 1000줄을 몇 십줄 줄였다고해서 사실 성능에 대한 큰 변화가 없다.
제일 중요한 건 DOM을 직접 컨트롤 하지 않는 것, 불필요한 재랜더가 없어야한다라는 것이다.

그럴려면

  • 데이터 타입의 참조형, 기본형
  • Avoid Object/Array Mutation
  • ref, useRef
  • React.PureComponent, React.memo
  • useCallback, useEffect

이런 것들을 잘 이해했고, 잘 다룰 수 있어야한다.

📕 데이터 타입의 참조형, 기본형

대부분 JavaScript의 첫 주제는 참조형과 기본형이 많이 나온다.
정확히 이 둘을 나누는 기준이 뭘까?

기본형은 값이 들어있는 데이터의 주소 값을 저장한다는 것
참조형은 값이 들어있는 데이터의 주소의 참조 주소를 저장한다는 것

처음 봤을 때는 도대체가 다른 건 이해하겠지만 정확히 어떻게 다른지 이 다름으로 인해 어떤 작동을 하는지는 바로 이해하기 힘들다.

기본형

let a = 10;
let b = a;
b = 15;

console.log(a === b); // false

참조형

let obj1 = {a: 1, b: 'bbb'};
let obj2 = obj1;

obj2.a = 20;

console.log(obj1.a === obj2.a); // true

JavaScript에선 어떤 데이터 타입이든 변수에 할당하기 위해서는 주솟값을 복사하는 것 이기에 참조형 데이터일 수 밖에 없지만 다른 점은 기본형은 주솟값 복사를 한 번, 참조형두 번을 한다는 점이다
그래서 기본형은 a, b 둘 다 10이 가진 주솟 값을 가졌었는데 b = 15를 해주면서 다른 주솟 값을 가지게 됨으로써 false라는 값이 나왔고
참조형은 값은 달라졌지만 주솟 값이 같기에 true로 나온 것이다.

이 기본적인 부분을 지금 다루는 이유는 React의 props와 state는 직접 접근해서 수정하면 안되는데 참조의 주솟 값이 바뀌기 때문이다.
거기에 더하여 모든 본질적인 이유는 데이터 타입에서 나오기 때문이다.
(역시 JavaScript의 기본기가 무엇보다 가장 중요한 것 같다)

이 부분은 다른 스터디에서 코어 자바스크립트 - 정재남님의 책을 격파 할 예정이므로 정말 가볍게 다뤘다.

📗 Avoid Object/Array Mutation

React가 state 객체의 변화를 빨리 구분하려면, 객체 자체를 접근해서 수정하지 말고, 새로운 객체를 만들어라.

앞 내용가 이어서 객체나 배열인 state, props를 직접 수정하지말고

  • spread syntax
  • Object.assign
  • object spread properties

등의 메소드를 이용하여 내용을 복사하거나 새로운 객체를 만들어 사용해야한다.

spread syntax (전개 구문)

...을 이용하여 내용을 펼쳐 사용한다라는 간단한 문법이다

let obj1 = {a: 1, b: 2};
let obj2 = {...obj1};

console.log(obj1 === obj2); // false

내용을 펼쳐 복사하여 새로운 객체나 배열을 만들기 때문에 서로 다른 독립적인 객체, 배열이 된다.

Object.assign

object spread properties

매계변수 활용👇

function studyCrew( leader, ...members ) {
  return {leader: leader, members: members}
}

const myTeam = studyCrew('sunju','yujung','junhee','jimin','gunwoo');
const notMyTeam = studyCrew('minji','hana','mingki','mimi');

console.log(myTeam); // {leader: "T1", members: Array(4)}
console.log(notMyTeam); // {leader: "Damwon", members: Array(3)}

📘 ref, useRef를 남용하지 말자

앞서 성능을 최적화하려면 DOM을 직접적으로 컨트롤하는 것을 지향해야한다고 했다.

예전 첫 코딩을 할 때 JQuery로 만든 포트폴리오 사이트를 애니메이션 동작들 크기 늘어나거나 줄어드는에 대한 코드 더벅이로 만들었었다. 그 때는 왜 이렇게 느리게 돌아가는지 몰랐었는데 이런 이유였구나 생각을 해본다

React는 props로 자식 컴포넌트를 다루지만 예외적으로 직접 다뤄야 할 때가 있다.

예를 들면

import React, { useRef } from 'react';

export default function App() {
  const inputRef = useRef(null);

  function handleFocus() {
    inputRef.current.disabled = false;
    inputRef.current.focus();
  }

  function handleReset() {
    inputRef.current.disabled = true;
    inputRef.current.value = '';
  }

  return (
    <>
      <input disabled type='text' ref={inputRef} />
      <button onClick={handleFocus}>활성화</button>
      <button onClick={handleReset}>초기화</button>
    </>
  );
}

활성화, 초기화버튼으로 input의 컨트롤 할 수 있는 것을 예시로 둘 수 있다.

📙 React.PureComponent, React.memo

항상 모든 분들이 말하시길 공식문서에 해답이 있고 react 공식문서를 친절하다 하지만
왜 나에겐 너무 불친철한지,,, (내 마음이 삐뚤어진걸까?🥲)

호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다. - React.공식문서

React에서 재랜더링 될 때에는
1. state가 바뀔 때
2. props가 바뀔 때
가 있는데 이 말은 내 의도에 맞춰 재랜더링 하는게 아닌 부모 컴포넌트가 바뀔 때에도 재랜더링이 된다는 소리이다 고로 방지하기 위하여 위의 방법을 쓰는 것이다.

주의 할 점은 React.memo는 props 변화에만 영향을 준다는 것!!!
useState, useReducer 또는 useContext 훅을 사용한다면, 여전히 state나 context가 변할 때 재랜더링 된다.

메서드는 오직 성능 최적화를 위하여 사용된다. 렌더링을 방지용이 아니기에 한다면 버그를 만들 수 있다.

Tip : 예리멘토님의 말씀은 사용할 때에는 일반적으로 쓰다가 자식 컴포넌트는 바뀔일이 별로 없지만 부모 컴포넌트의 state, props가 자주 바뀌는 상황이 올 때 바꾸는 것이 좋다고 하였다.

📓 useEffect

useEffect의 의존성 배열을 관리하는게 중점이므로 생각해야 되는 부분이 꽤 있다.

의존성 배열을 되도록 짧게 만들자

의존성 배열이 길어질 수록 관리하게 힘들기 때문에 최대한 짧게, 안쓰는 방법으로 생각해야한다.

useEffect(() => {
  setInterval(() => {
    setCount(count + 1)
  },1000)
}), [count]);

useEffect(() => {
  setInterval(() => {
    setCount(num => num + 1)
  },1000)
}),[]);

위 방법처럼 의존성 배열에 count를 넣지않는 방법이 있다.

useCallback

useCallback은 콜백의 메모이제이션된 버전을 반환한다. 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다

함수형 컴포넌트의 속성은 랜더 될 때마다 안에 있는 변수, 함수가 다시 재생성되는데 매번 생성하는 것을 방지하지 위해서 의존성 배열 안의 내용이 바뀔 때마다 실행되는 useCallback을 사용한다.


참조한 사이트
https://www.daleseo.com/react-refs/
https://bbaktaeho-95.tistory.com/96

좋은 웹페이지 즐겨찾기