Published on

react - 렌더 프롭스 패턴

Authors

출처

학습 배경

프로젝트를 진행하며, 상태를 공유하는 컴포넌트 간의 재사용성과 유연성을 향상시키기 위해 다양한 디자인 패턴을 학습하게 되었다. 특히 렌더 프롭스 패턴은 컴포넌트의 재사용성을 높이는 데 매우 유용하다고 느꼈다.

렌더 프롭스 패턴이란?

렌더 프롭스 패턴은 컴포넌트의 자식으로 함수를 전달하여, 이 함수에서 컴포넌트의 내부 상태를 외부에서 제어할 수 있도록 하는 패턴이다. 이를 통해 컴포넌트는 자신만의 렌더링 로직을 구현하지 않고, 전달된 함수(렌더 프롭)를 호출하여 그 결과를 렌더링한다. 즉, 로직에 해당하는 부분을 자신이 아닌 부모에게 위임한다.

예시: Title 컴포넌트

다음의 Title 컴포넌트는 단순히 전달받은 렌더 프롭을 호출하여 그 결과를 렌더링한다.

const Title = (props) => props.render()

render(
  <div className="App">
    <Title render={() => <h1>I am a render prop!</h1>} />
  </div>,
  document.getElementById('root')
)

이렇게 하면 Title 컴포넌트는 렌더 프롭을 통해 전달받은 내용을 렌더링한다.

렌더 프롭스 패턴의 유연성

렌더 프롭스 패턴은 컴포넌트를 재사용하기 쉽게 만들어준다. 동일한 컴포넌트를 여러 번 사용하면서 각각 다른 렌더 프롭을 전달할 수 있다.

render(
  <div className="App">
    <Title render={() => <h1>✨ First render prop! ✨</h1>} />
    <Title render={() => <h2>🔥 Second render prop! 🔥</h2>} />
    <Title render={() => <h3>🚀 Third render prop! 🚀</h3>} />
  </div>,
  document.getElementById('root')
)

렌더 프롭은 반드시 "render"라는 이름일 필요는 없다. 어떤 프롭이든 JSX를 반환하는 함수라면 렌더 프롭으로 간주될 수 있다.

커스텀 이름 사용 예시

const Title = (props) => (
  <>
    {props.renderFirstComponent()}
    {props.renderSecondComponent()}
    {props.renderThirdComponent()}
  </>
)

render(
  <div className="App">
    <Title
      renderFirstComponent={() => <h1>✨ First render prop! ✨</h1>}
      renderSecondComponent={() => <h2>🔥 Second render prop! 🔥</h2>}
      renderThirdComponent={() => <h3>🚀 Third render prop! 🚀</h3>}
    />
  </div>,
  document.getElementById('root')
)

렌더 프롭스 패턴의 활용

렌더 프롭스를 사용하면 컴포넌트가 단순히 렌더링 로직을 호출하는 것 이상의 역할을 할 수 있다. 보통 컴포넌트는 내부 상태나 데이터를 렌더 프롭을 통해 전달할 수 있다.

function Component(props) {
  const data = { key: 'value' }
  return props.render(data)
}

;<Component render={(data) => <ChildComponent data={data} />} />

온도 변환 예시

다음은 렌더 프롭스 패턴을 사용하여 섭씨 온도를 화씨와 켈빈으로 변환하는 간단한 애플리케이션이다.

// App.js
import React, { useState } from 'react'
import './styles.css'

function Input(props) {
  const [value, setValue] = useState('')

  return (
    <>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Temp in °C"
      />
      {props.render(value)}
    </>
  )
}

export default function App() {
  return (
    <div className="App">
      <h1>☃️ Temperature Converter 🌞</h1>
      <Input
        render={(value) => (
          <>
            <Kelvin value={value} />
            <Fahrenheit value={value} />
          </>
        )}
      />
    </div>
  )
}

function Kelvin({ value }) {
  return <div className="temp">{parseInt(value || 0) + 273.15}K</div>
}

function Fahrenheit({ value }) {
  return <div className="temp">{(parseInt(value || 0) * 9) / 5 + 32}°F</div>
}

이렇게 하면 사용자가 입력한 섭씨 온도가 화씨와 켈빈으로 자동 변환되어 표시된다.

자식 함수로서의 렌더 프롭

렌더 프롭을 사용하여 상태와 데이터를 공유할 수 있지만, 때로는 렌더 프롭을 명시적으로 전달하는 대신 자식 함수로서 전달할 수도 있다.

export default function App() {
  return (
    <div className="App">
      <h1>☃️ Temperature Converter 🌞</h1>
      <Input>
        {(value) => (
          <>
            <Kelvin value={value} />
            <Fahrenheit value={value} />
          </>
        )}
      </Input>
    </div>
  )
}

function Input(props) {
  const [value, setValue] = useState('')

  return (
    <>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Temp in °C"
      />
      {props.children(value)}
    </>
  )
}

렌더 프롭스 패턴의 장단점

장점:

  • 재사용성: 렌더 프롭스 패턴을 사용하면 로직을 여러 컴포넌트에서 재사용할 수 있다. 이는 특정 기능을 독립된 컴포넌트로 분리하여 재사용성을 높이는 데 유리하다.
  • 유연성: 부모 컴포넌트가 자식 컴포넌트에 렌더링할 내용을 제어할 수 있어 매우 유연하다. 이는 다양한 렌더링 요구사항을 충족하는 데 도움이 된다.
  • 로직 분리: 렌더링 로직과 UI를 분리할 수 있어 코드의 가독성과 유지보수성을 향상시킨다. 로직은 렌더 프롭스를 통해 전달되고, UI는 자식 컴포넌트에서 정의된다.

단점:

  • 코드 복잡도 증가: 렌더 프롭스 패턴을 남용하면 코드가 복잡해질 수 있습니다. 여러 레이어의 함수 호출이 발생할 수 있어 가독성이 떨어질 수 있다.
  • 성능문제: 렌더 프롭스를 사용하면 매 렌더링마다 새로운 함수가 생성될 수 있어 성능에 영향을 미칠 수 있습니다. 이는 불필요한 리렌더링을 초래할 수 있습니다.
  • 로직 분리: 렌더링 로직과 UI를 분리할 수 있어 코드의 가독성과 유지보수성을 향상시킨다. 로직은 렌더 프롭스를 통해 전달되고, UI는 자식 컴포넌트에서 정의된다.

결론

렌더 프롭스 패턴은 컴포넌트의 재사용성과 유연성을 극대화할 수 있는 강력한 디자인 패턴이다. React Hooks가 등장하면서 일부 사용 사례는 줄어들었지만, 여전히 많은 상황에서 유용하게 사용할 수 있다. 특히, 컴포넌트의 상태나 데이터를 외부로 전달하여 다양한 방식으로 렌더링할 때 매우 유용하다. 렌더 프롭스를 사용하면 더 깨끗하고 관리하기 쉬운 코드를 작성할 수 있으며, 팀 협업 시 컴포넌트의 역할이 명확해져 개발 효율성을 높일 수 있다.