본문 바로가기
React

React에서 Prop Drilling 문제 해결하기

by 굿센스굿 2024. 12. 13.
반응형

 

React 애플리케이션을 개발하다 보면 컴포넌트 간 데이터를 전달해야 할 때가 많습니다. 이 과정에서 흔히 겪는 문제 중 하나가 prop drilling입니다. prop drilling은 부모 컴포넌트에서 자식 컴포넌트로, 다시 그 자식 컴포넌트로 props를 계속해서 전달해야 하는 상황을 의미합니다. 이러한 방식은 코드의 가독성과 유지 보수성을 저하시킬 수 있습니다. 이 블로그 글에서는 prop drilling 문제를 해결할 수 있는 몇 가지 방법을 살펴보겠습니다.

Prop Drilling의 문제점

prop drilling은 작은 애플리케이션에서는 큰 문제가 되지 않을 수 있지만, 애플리케이션이 커지고 컴포넌트 계층 구조가 깊어질수록 다음과 같은 문제를 초래할 수 있습니다.

  1. 복잡성 증가: 많은 컴포넌트가 단순히 데이터를 전달하는 역할만 하게 되어 컴포넌트 트리가 복잡해집니다.
  2. 유지 보수 어려움: 데이터를 전달하는 경로가 길어질수록, 특정 데이터를 수정하거나 추적하기가 어려워집니다.
  3. 재사용성 저하: 데이터 전달을 위해 많은 컴포넌트를 수정해야 하므로, 컴포넌트를 재사용하기가 어려워집니다.

Prop Drilling 문제 해결 방법

prop drilling 문제를 해결하기 위해 여러 가지 접근 방법이 있습니다. 여기서는 Context API, 상태 관리 라이브러리 (Redux, MobX 등), 그리고 custom hooks를 이용한 방법을 소개하겠습니다.

1. Context API

React의 Context API는 prop drilling 문제를 해결하기 위한 강력한 도구입니다. Context를 사용하면 데이터를 필요한 컴포넌트에 직접 전달할 수 있습니다.

예제:

import React, { createContext, useContext, useState } from 'react';

// 1. Context 생성
const MyContext = createContext();

// 2. Context Provider 컴포넌트
const MyProvider = ({ children }) => {
  const [value, setValue] = useState('Hello World');
  return (
    <MyContext.Provider value={{ value, setValue }}>
      {children}
    </MyContext.Provider>
  );
};

// 3. Context 소비
const MyComponent = () => {
  const { value, setValue } = useContext(MyContext);
  return (
    <div>
      <p>{value}</p>
      <button onClick={() => setValue('Hello React!')}>Change Value</button>
    </div>
  );
};

// 4. Provider로 감싸기
const App = () => (
  <MyProvider>
    <MyComponent />
  </MyProvider>
);

export default App;

위 예제에서 MyContext를 생성하고, MyProvider를 통해 데이터를 공급하며, MyComponent에서 useContext를 사용하여 데이터를 소비합니다. 이렇게 하면 중간 컴포넌트들이 데이터를 전달할 필요가 없어 prop drilling을 피할 수 있습니다.

2. 상태 관리 라이브러리

Context API가 간단한 애플리케이션에 적합하다면, 더 큰 애플리케이션에서는 Redux나 MobX 같은 상태 관리 라이브러리를 사용하는 것이 좋습니다.

Redux 예제:

import React from 'react';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

// 1. 액션 타입 정의
const SET_VALUE = 'SET_VALUE';

// 2. 액션 생성자
const setValue = (value) => ({ type: SET_VALUE, payload: value });

// 3. 리듀서
const reducer = (state = { value: 'Hello World' }, action) => {
  switch (action.type) {
    case SET_VALUE:
      return { ...state, value: action.payload };
    default:
      return state;
  }
};

// 4. 스토어 생성
const store = createStore(reducer);

// 5. 컴포넌트
const MyComponent = () => {
  const value = useSelector((state) => state.value);
  const dispatch = useDispatch();
  return (
    <div>
      <p>{value}</p>
      <button onClick={() => dispatch(setValue('Hello Redux!'))}>Change Value</button>
    </div>
  );
};

// 6. Provider로 감싸기
const App = () => (
  <Provider store={store}>
    <MyComponent />
  </Provider>
);

export default App;

Redux를 사용하면 전역 상태를 관리할 수 있고, 필요한 컴포넌트에서 직접 상태를 읽고 수정할 수 있어 prop drilling 문제를 효과적으로 해결할 수 있습니다.

3. Custom Hooks

Custom hooks를 사용하면 반복적인 로직을 재사용 가능하게 만들 수 있습니다. 이를 통해 prop drilling 문제도 해결할 수 있습니다.

예제:

import React, { useState, useContext, createContext } from 'react';

const MyContext = createContext();

const useMyContext = () => {
  return useContext(MyContext);
};

const MyProvider = ({ children }) => {
  const [value, setValue] = useState('Hello World');
  return (
    <MyContext.Provider value={{ value, setValue }}>
      {children}
    </MyContext.Provider>
  );
};

const MyComponent = () => {
  const { value, setValue } = useMyContext();
  return (
    <div>
      <p>{value}</p>
      <button onClick={() => setValue('Hello Custom Hooks!')}>Change Value</button>
    </div>
  );
};

const App = () => (
  <MyProvider>
    <MyComponent />
  </MyProvider>
);

export default App;

Custom hooks를 사용하면 로직을 분리하고 재사용할 수 있어 코드의 가독성과 유지 보수성이 향상됩니다.

결론

prop drilling은 React 애플리케이션에서 흔히 발생하는 문제이지만, Context API, 상태 관리 라이브러리, custom hooks 등을 사용하면 효과적으로 해결할 수 있습니다. 각 방법의 장단점을 고려하여 애플리케이션에 가장 적합한 방법을 선택하는 것이 중요합니다. prop drilling 문제를 해결하면 더 깔끔하고 유지 보수하기 쉬운 코드를 작성할 수 있을 것입니다.

반응형