본문 바로가기
컴퓨터/Frontend

[React] 최적화(4) - 프롭스 드릴링(Props Drilling)

by stdFrog 2023. 3. 28.

프롭스 드릴링(Props Drilling)이란 상위 컴포넌트가 하위 컴포넌트로 Props를 전달할 때 발생하는 구조적 문제를 말한다.

예를 들어, 최상위 컴포넌트가 직계비속 중 사촌쯤 되는 하위 컴포넌트에게 데이터를 전달해야 한다고 생각해 보자.

이 데이터는 최상위 컴포넌트의 하위(자식) 컴포넌트를 경유하고 또 그 컴포넌트의 자식을 건너고, 또 일촌을 건너야 비로소 전달되는 구조적인 문제를 갖고 있다.

즉, 그저 경유하기만 하는 컴포넌트가 최소 2개나 있다는 뜻이다.



여기서 만약 상태 데이터의 이름이나 직/간접적으로 참조하고 있는 함수의 이름이 변경되어야 한다면 어떤 문제가 발생할까?

이 데이터가 경유하고 있는 모든 컴포넌트를 열어 하나씩 수정해야 하는 아주 끔찍한 일이 발생할 것이다.

이를 프롭스 드릴링이라 부르며 이러한 구조적 문제는 단방향 데이터 흐름만을 지원하는 리액트의 고질적인 문제라고 볼 수 있다.

시간과 비용을 절약하기 위해선 반드시 해결되어야 하는 문제인데 다행히 Provider 즉, 공급자 컴포넌트로 이를 해결할 수 있다.

이 공급자 컴포넌트는 자손에 해당하는 컴포넌트들에게도 별 다른 경유 없이 데이터를 전달해 주는데 사람으로 치면 큰 집에 살고 계신 큰 아버지라 생각하면 된다.

최상위 컴포넌트는 공급자 컴포넌트에게 모든 데이터를 전달하고 공급자 컴포넌트는 직통으로 하위 컴포넌트에게 이 데이터를 전달하게 된다.

이 공급자 컴포넌트는 리액트의 createContext 훅으로 생성할 수 있는데 아래 예시를 보면 금방 이해할 수 있다.

 

// App.js
export const ComContext = React.createContext(defaultValue);

const App = () =>{

return(
	<ComContext.Provider value = {/*Values to Share Globally*/ }>
	{/* 이 프로그램을 구성하는 요소 즉, 자식 컴포넌트들을 포함하면 된다.*/}
	</ComContext.Provider>
	);
}

export default App;


공급자 컴포넌트 태그 사이에 위치한 컴포넌트들은 태그에서 지정한 속성 값(또는 Props)을 전역적으로 공유하게 된다.

앞에서 말한 상태 데이터를 전달받는 컴포넌트가 있다면 해당 컴포넌트는 더 이상 Props를 따로 전달받을 필요가 없다.

아래 예시를 보자.

import LComp from "./LComp";

const layOver = (/*{onCreate, onTimer, onPaint}*/) => {
	return(
		<div>
			<h2>Example Code</h2>
			<div>
			/*{ExampleList.map((e)=>(<LComp val = {e.list1} create={onCreate} timer={onTimer} paint={onPaint}}))}*/
			{ExampleList.map((e)=>(<LComp val = {e.list1} />))}
			</div>
		</div>
	);
}

export default layOver;


주석 처리된 부분이 원래 작성하던 코드라 생각하면 된다.

여기서 주의할 건 직계, 그러니까 바로 아래의 하위 컴포넌트가 layOver 컴포넌트이고 이 컴포넌트가 Props로 받고 있는 onCreate, onTimer, onPaint 등의 함수는 더 하위의 컴포넌트로 전달될 값이다.

위 예시에서 주석 처리한 {ExampleList.map...} 구문을 보면 LComp 컴포넌트로 자신이 받은 함수를 그대로 전달하고 있다.

즉, layOver 컴포넌트는 최상위 컴포넌트로부터 LComp로 함수를 전달하기 위해 그저 경유하는 중간 다리 역할이란 뜻이다.

이제 LComp를 보자.

import React, {useContext} from "react";
import {ComContext} from "./App";

const LComp = (/*{onCreate, onTimer, onPaint}*/) =>{
	const {onCreate, onTimer, onPaint} = useContext(ComContext);

	// 사용중...
};

export default LComp;


layOver를 경유하여 전달받던 함수를 이제는 컨텍스트로 직접 받고 있다.

useContext 구문은 컨텍스트에 포함된 함수를 사용하겠다는 의사표시이며 이렇게 불러온 함수는 해당 컴포넌트 내에서 얼마든지 사용할 수 있다.

공급자 컴포넌트는 말 그대로 컴포넌트이기 때문에 함수와 상태 데이터를 따로 관리해야 하는 상황이라면 그 개수만큼 공급자 컴포넌트를 만들어 사용하면 된다.

반응형

댓글