React Render Props Pattern

Haegul Pyun
6 min readJun 13, 2018

--

React에서 Component를 재사용 하기위해 많이 사용하는 기법중에HOC(Higher-Order Component)가 있다. 이와 마찬가지로 Component의 재사용성을 증가시켜 줄 Render Props Pattern에 대해 공부해 보도록 하자. 먼저 Render Props가 무엇인지 이해하기 위해 공식 문서를 번역해 보았다.

Render Props

“render props” 용어는 React component들 간에 코드를 공유하기 위해 함수형 props를 이용한 간단한 테크닉 입니다.

render props pattern으로 구현된 component는 자체적으로 rendering 로직을 구현하는 대신 react element 요소를 반환하고 이를 호출하는 함수를 사용합니다.

render props를 사용하는 라이브러리로는 React RouterDownshift가 있습니다.

이 문서에서는 왜 render props가 유용하고, 어떻게 여러분에 프로젝트에 적용할 수 있을지에 대해 이야기 하겠습니다.

횡단 관심사(Cross-Cutting Concerns)를 위한 render props 사용법

Component는 React에서 코드의 재사용성을 위한 주요 단위 입니다. 하지만한 컴포넌트에서 캡슐화 된 상태나 동작을, 같은 상태를 가진 다른 컴포넌트와 공유하는 방법이 항상 명확하지는 않았습니다.

예를 들면, 아래 Component는 웹 어플리케이션에서 마우스 위치를 추적하는 로직을 가지고 있습니다.

스크린 주위로 마우스 커서를 움직이면, component가 마우스의 (x, y)좌표를 <p>에 나타냅니다.

여기서 질문입니다: 다른 component에서 이 행위를 재사용하려면 어떻게 해야 할까요? 즉, 다른 component에서 커서 위치에 대해 알아야 할 경우, 해당 행위를 쉽게 공유할 수 있도록 캡슐화할 수 있습니까?

React에서 component는 코드 재사용의 기본 단위이므로, 우리가 필요로 하는 마우스 커서 트래킹 행위를 <Mouse> 컴포넌트로 캡슐화 하여 어디서든 사용할 수 있게 리팩토링 해보겠습니다.

이제 <Mouse> component는 mousemove event를 감지하고 마우스 커서의 (x, y)위치를 저장하는 행위를 캡슐화 했습니다. 그러나 아직 완벽하게 재사용할 수 있는 건 아닙니다.

예를 들어, 마우스 주위에 고양이 그림을 보여주는 <Cat> component를 생각해 보겠습니다. 우리는 <Cat mouse={{x, y}}> prop을 통해 Cat component에게 마우스 좌표를 전달해주고 화면에 어떤 위치에 이미지를 보여줄지 알려 주고자 합니다.

첫번째 방법으로는, 다음과 같이 <Mouse> component의 render method안에 <Cat> component를 넣어 랜더링하는 방법이 있을 겁니다.

이러한 접근 방법은 특정 사례에서는 적용할 수 있지만, 우리가 원하는 행위의 캡슐화(마우스 트랙킹)라는 목표는 달성하지 못했습니다. 이제 우리는 다른 use case에서도 언제든지 마우스 위치를 추적할 수 있는 새로운 component(<MouseWithCat>과 근본적으로 다른)를 만들어야 합니다.

여기에 render prop를 사용할 수 있습니다. <Mouse>component 안에 <Cat>component를 hard-coding해서 결과물을 바꾸는 대신에, <Mouse>에게 동적으로 rendering을 할 수 있도록 해주는 함수형 prop을 제공 할 수 있습니다. — 이것이 render prop의 개념입니다.

이제 <Mouse> component의 행위를 복제하기 위해 hard-coding을 할 필요 없이 render함수에 prop으로 전달해 줌으로서, <Mouse> component는 동적으로 트래킹 기능을 가진 component들을 rendering할 수 있습니다.

정리하자면, render prop은 무엇을 render할지 component에게 알려주는 함수 입니다.

이 테크닉은 행위(마우스 트래킹 같은)를 매우 쉽게 공유할 수 있도록 만들어 줍니다. 해당 행위를 적용하려면, <Mouse> 를 그리고 현재 (x, y) 커서위치에 무엇을 그릴지에 대한 정보를 prop을 통해 넘겨주기만 하면 됩니다.

render props에 대해 한가지 흥미로운점은 대부분의 higher-order components(HOC)에 render props pattern을 이식할 수 있습니다. 예를들면, 만약에 <Mouse> component보다 withMouse HOC를 더 선호한다면 render prop을 이용해서 다음과 같이 쉽게 HOC를 만들 수 있습니다.

(역자:: 불가피하게 HOC를 사용할 경우에도 쉽게 만들 수 있습니다!)

render props를 사용하면 두가지 모두 사용이 가능합니다.

Props를 사용하지 않은 패턴

여기서 중요하게 기억해야 할 것은, “render props pattern” 으로 불리는 이유 때문에 꼭 prop name으로 render를 사용할 필요는 없습니다. 사실, 어떤 함수형 prop이든 render prop이 될 수 있습니다.

위 예제에서는 render를 사용했지만, 우리는 children prop을 더 쉽게 사용할 수 있습니다.

실제로 JSX element의 “속성”목록에 하위 속성 이름(예를들면 render)을 지정할 필요는 없습니다. 대신에, element안에 직접 꽂아넣을 수 있습니다!

이 테크닉은 react-motion API에서 실제로 사용된 것을 볼 수 있습니다.

이 테크닉은 자주 사용되지 않기 때문에, API를 디자인 할 때 children은 function type을 가지도록 propTypes를 지정하는 것이 좋습니다.

주의 사항

React.PureComponent에서 render props pattern을 사용할 땐 주의 해주세요.

render props pattern을 사용하면 React.PureComponent를 사용할 때 발생하는 이점이 사라질 수 있습니다. shallow prop comparison은 새로운 prop에 대해 항상 false를 반환합니다. 이 경우 각 render마다 render prop으로 넘어온 값을 항상 새로 생성합니다.

위에서 사용했던 <Mouse> component를 이용해서 예를 들어보겠습니다. mouse에 React.Component 대신에 React.PureComponent를 사용하면 다음과 같은 코드가 됩니다.

이 예제에서 <MouseTracker>가 render 될때마다, <Mouse render>의 prop으로 넘어가는 함수가 계속 새로 생성됩니다. 따라서 React.PureComponent를 상속받은 <Mouse> component의 효과가 사라지게 됩니다.

이 문제를 해결하기 위해서, 다음과 같이 instance method를 사용해서 prop을 정의합니다.

만약 prop을 정적으로 정의할 수 없는 경우에는 <Mouse> component는 React.Component를 상속받아야 합니다.

render props 개념은 간단하다. 중복되는 Logic은 공유하고, 파생되는 UI는 props를 통해 분리하는 것이다. 이 간결하지만 강력한 패턴은 HOC와 더불어 생산성을 많이 향상시켜준다. HOC와 비교해서 알아두면 유용한데, 이 부분에 대해서는 다음에 따로 포스팅 하도록 하겠다.

--

--