면접준비(3-1) 리액트

Edwin·2023년 5월 29일
0

면접준비

목록 보기
4/6
post-thumbnail

프론트엔드 개발에서 리액트가 강세인 이유

가장 큰 이유는 가상DOM을 채택함에서 오는 이점들 때문일 것이다. 첫째로, 가상 DOM은 리액트의 성능을 향상시키는 데 도움을 준다. 가상 DOM은 실제 DOM과 동기화되는 작업을 최소화하여 성능을 최적화한다. 변경된 부분만을 감지하고 업데이트하기 때문에, 필요한 부분만을 업데이트하고 리렌더링할 수 있다. 이를 통해 애플리케이션의 성능이 향상되며, 사용자 경험을 개선할 수 있는 것이다.

어플리케이션의 상태가 변경되었을 때, 개발자가 직접 DOM을 변경하는 대신 리액트가 이를 대신 처리한다. 이 과정에서 변경된 부분을 감지하여 해당 부분만 변경하기에, 상태 관리의 간평성은 물론, 어플리케이션의 동적인 동작을 쉽게 구현한다. 그러나 가장 큰 단점으로는 SEO(검색 엔진 최적화)와 관련하여, 페이지의 내용을 정확하게 파악하기 어렵게 만들 수 있다는 점이 뒤따른다.

리액트가 가상DOM을 채택하며 발생되는 리렌더링 단점

이번에 기술면접에서 내가 답변하지 못한 지점이다. 장점만을 생각했고, 단점으로는 SPA의 한계인 SEO문제를 언급하기는 했다. 그러나, 리렌더링의 단점에 대해서는 고민해보지 못했다.

가상 DOM을 채택하는 리액트가 성능 최적화에 도움을 주지만, 가상DOM을 사용했을 때 전체 컴포넌트 트리를 다시 렌더링하고 비교해야 하는 오버해드가 발생된다는 단점이 있다.

이를 이해하기 위해서는 브라우저가 DOM을 그리는 방식에 대해서 이해할 필요가 있다.

가상DOM에서 발생되는 오버헤드는 바로 "렌더트리" 시점과 관련되어 있다. 리액트는 상태가 변경될 때마다 변경된 부분을 탐지하고, 필요한 부분만 실제 DOM에 반영하는데, 이 과정에서 변경된 부분을 찾아내기 위해 DOM 트리를 비교해야 한다. 만약 프로젝트의 단위가 대규모라면, 상태 변경이 빈번하게 일어날 것이고, 이는 비교를 위핸 오버헤드를 크게 한다는 단점이 있다. 이를 위해서 상태가 변경될 때마다 비교 작업이 필요한 상황을 적절하게 배치하는 것이 필요하다.

컴포넌트 분리, 메모이제이션, 최적화 기법 등이 이러한 오버헤드를 최소화 하는 방법 중 하나이다.

  • React.memo 함수는 컴포넌트를 메모이제이션하여 불필요한 리렌더링을 방지합니다. 이 함수를 사용하면 컴포넌트의 속성(props)이 변경되지 않는 한 이전에 렌더링한 결과를 재사용하여 성능을 향상시킬 수 있습니다.
  • useCallback은 콜백 함수를 메모이제이션하여, 의존성이 변경되지 않는 한 이전에 생성한 함수를 재사용합니다. 이를 통해 부모 컴포넌트의 상태가 변경되어도 새로운 함수를 생성하지 않고 이전에 생성한 함수를 재사용할 수 있습니다.
  • useMemo는 계산 비용이 큰 결과 값을 메모이제이션하여, 의존성이 변경되지 않는 한 이전에 계산한 결과를 재사용합니다. 이를 통해 반복적인 계산을 피하고 성능을 개선할 수 있습니다.

React.memo, useCallback, useMemo 공부했지만

어떻게 사용하는지에 대해서는 사실 인식하지 못한 상태였다. 아래와 같이 사용할 수 있다.

props를 전달받는 자녀컴포넌트의 리렌더링

import React, {useState} from 'react';

const App = () => {
  const [name, setName] = useState('john')
  return <React.memo><MyComponent name={name} /></React.memo>;
};

export default App;

const MyComponent = ({ name }) => {
  // 컴포넌트 로직 및 렌더링
  return <div>{name}</div>;
};

이때 표현하는 방식은 2가지 일 수 있다. 일반적으로 사용되는 방식은 위와 같다. 위의 코드보다는 아래의 코드가 더 일반적이다.

import React, {useState} from 'react';

const App = () => {
  const [name, setName] = useState('john')
  return <MyComponent name={name} />;
};

export default App;

const MyComponent = React.memo(({ name }) => {
  return <div>{name}</div>;
});

두 코드의 차이는 React.memo를 어디에서 관리하는지이다. 첫번째는 App 컴포넌트에서 MyComponent를 <React.memo>를 감싸고 있다는 점이고, 두 번째 코드에서는 MyComponent 자체를 React.memo로 감싸고 있다는 점이다. 그러나 결과는 동일히다.

위의 단순한 코드는 useMemo를 통해서도 가능하다.

import React, {useState} from 'react';

const App = () => {
  const [name, setName] = useState('john')
  const memoizationName = useMemo((name)=> ,[name])
  return <MyComponent name={memoizationName} />;
};

export default App;

const MyComponent = ({ name }) => {
  return <div>{name}</div>;
};

상태를 인지하는 내용이 name 하나이기 때문에 이러한 경우에는 name을 메모이제이션하여 관리할 수도 있다. 그러나 자녀컴포넌트로 내려주는 props가 복수라면, 이 방법은 적절하지 않을 것이다. 자녀컴포넌트 전체를 메모이져이션 하여 관리하는 것이 더 바른 접근이다.

const MyComponent = React.memo(({ name, age }) => {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
    </div>
  );
});

전달받은 값이 name, age 복수일 때, 두 개 가운데 하나가 변경될 때 리렌더링 하도록 설정할 수 있는 접근이다.

profile
신학전공자의 개발자 도전기!!

0개의 댓글