리액트의 Render 함수와 Component 방식의 차이점 Thumbnail

성능, 가독성 차이점

4 min read
FE

리액트에서 Render 함수와 Component 방식의 차이점을 알아보자

리액트에서 Render 함수와 Component 방식의 차이점을 알아보자리액트에서 Render 함수와 Component 방식의 차이점을 알아보자

상황

공통 컴포넌트(캘린더)를 만드는 중 currentView 상태에 따라 다른 뷰를 보여주는 컴포넌트를 만들어야 했다. currentView는

type CurrentView = 'year' | 'month' | 'day'

로 구성되어있다. 각 상태에 따른 서로 다른 뷰를 렌더링해야했기에 처음에 renderCalendar 함수를 만들어서 사용했다.

const renderCalendar = () => {
  switch (currentView) {
    case 'year':
      return <YearDateCalendar />;
    case 'month':
      return <MonthDateCalendar />
    case 'day':
      return <SingleDateCalendar />
    default:
      return null;
  }
};

처음 리액트를 배웠을 때도 render 함수를 사용해왔고, 규모가 크지 않다면 render 함수를 자주 사용했다. 예를 들면 아이콘을 렌더링해야하는 경우 같이 작은 규모라면 const renderIcon = (isChecked: boolean) => isChecked ? <IconA /> : <IconB /> 처럼 사용했던 경험이 많다.

성능 차이점

리렌더링

사실 두 방식을 이렇다 정해두고 써본 적은 없지만 성능차이가 크지 않을 것이라고 무의식적으로 생각하고 있었다. 하지만 알아보니 꽤 많은 차이가 생길 수 있다는 것을 알게 되었다.

두 방식의 가장 큰 차이점은 렌더 함수의 경우 감싸고 있는 부모 컴포넌트가 리렌더링될 경우 항상 함수가 재생성된다는 점이다. 이때 렌더 함수의 규모가 아이콘을 렌더링하는 정도로 작다면, 큰 상관은 없지만 나의 경우처럼 뷰를 선택적으로 렌더링하는 경우에는 큰 손실이 생길 수 있겠다고 생각했다.

함수 재생성, 꽤 많이 들어본 용어라고 생각한다. 그렇다면 이 함수 재생성의 비용은 어느정도일까? 함수 재생성이 매번 이루어지면 문제가 되는걸까?

답은 그렇지 않다. 함수 객체를 생성하는 것은 V8 엔진 단에서 매우 최적화되어 있다고 한다. 그래서 함수 재생성에 들어가는 코스트는 크지 않다. 함수 재생성에 들어가는 비용 때문에 문제가 생기는 것이 아니라, 이로 인해 발생하는 사이드 이펙트 때문이라고 한다.

대표적인 예시로 useEffect의 의존성배열에 함수가 있을 경우, 컴포넌트가 리렌더링될 때마다 함수의 참조가 새로 생성되어 effect가 다시 실행된다.

커스텀 훅 사용 가능 여부

render 함수 내부에서는 커스텀 훅을 사용할 수 없다. 내부에서 사용할 경우 커스텀 훅은 항상 최상위에서 사용되어야 한다는 리액트의 생애주기에 위반하기 때문에 에러가 발생한다.

component 내부에서는 커스텀 훅을 사용할 수 있다.

메모이제이션 적용

성능 최적화가 꼭 필요한 도메인이거나 규모가 큰 컴포넌트의 경우 React.memo로 컴포넌트를 메모이제이션할 수 있다. 여기서도 Render와 Component의 차이점이 생긴다.

Render 방식은 메모이제이션 불가능하지만 Component 방식은 가능하다.

// Component는 React.memo로 최적화 가능
const YearDateCalendar = React.memo(() => {
  // 불필요한 리렌더링 방지
});

// Render 함수는 memo 적용 불가
const renderCalendar = () => <YearDateCalendar />; // memo 효과 없음

가독성 차이점

매개변수

Render 함수

render 함수를 사용하면 매개변수를 최소화 할 수 있다는 것이 장점이다. 보통 render 함수는 컴포넌트 내부에 로직 형태로 작성되기 때문에 컴포넌트에서 사용하고 있는 상태를 전역 변수처럼 사용이 가능하다. 그렇기 때문에 단순히 render() 처럼 사용할 수도 있다.

Component

이에 비해 컴포넌트의 경우에는 네이밍과 전달되는 props를 통해서 해당 컴포넌트가 무슨 역할을 수행하는지 간단하게 파악을 할 수 있다는 점이 다르다.

const Parent = () => {
  const [date, setDate] = useState(new Date());
  
  // 같은 스코프의 date에 접근 가능
  const renderCalendar = () => {
    return <div>{date.toString()}</div>;  
  };
  
  // 하지만 Component는 명시적으로 props 전달 필요
  return <Calendar date={date} />;
};

결론

결론적으로, 대부분의 경우에 컴포넌트로 분리해서 사용하는 것이 훨씬 더 좋다.

성능적인 문제는 크지 않다. 하지만 유지보수성과 가독성 그리고 사이드이펙트를 방지하기 위해서 컴포넌트 방식을 사용해야겠다고 생각했다.

📌 Table of Contents

0
추천 글