React 기술면접 4-4. props drilling

강연주·2025년 1월 14일

🙋‍♀️ 기술면접

목록 보기
108/112

props drilling 개념, 해결법

  1. props drilling과 이를 해결하는 방법에 관하여 설명할 수 있습니다.
    • 답변)
      • 의도:
        • 지원자가 props drilling 문제를 이해하고, 이를 해결할 수 있는 방법을 알고 있는지 평가.
      • 팁:
        • props drilling의 문제점을 설명하세요.
        • props drilling을 해결하는 방법(Context API, 상태 관리 라이브러리 등)을 설명하세요.
        • props drilling 문제를 해결한 예제를 떠올려 보세요.
      • 모범답안:
        • props drilling은 깊게 중첩된 컴포넌트 트리에서 props를 전달해야 하는 문제를 말합니다.
        • Context API를 사용하면 중간 컴포넌트를 거치지 않고 데이터와 함수를 전달할 수 있습니다.
        • 상태 관리 라이브러리(Redux, Recoil 등)를 사용하여 전역 상태를 관리할 수 있습니다.

Props Drilling

React에서 상위 컴포넌트의 데이터를 하위 컴포넌트로 전달할 때,
중간에 있는 여러 컴포넌트들을 거쳐가야 하는 현상.
데이터가 여러 층을 '뚫고(drilling)' 내려가는 것과 같다고 해서 붙여진 이름이다.

최상위 컴포넌트에서 가장 하위 컴포넌트로 사용자 정보를 전달하려면, 그 사이에 있는 모든 컴포넌트들이 props를 받아서 다시 전달해야 하는 불편함이 생기고 코드의 가독성과 유지보수성을 저하시킨다.


해결 방안

1. Context API

React의 Context API로 중간 컴포넌트들을 거치지 않고도 직접 데이터를 전달 가능.

간단한 전역 상태 관리에 적합하지만,
너무 많은 Context를 사용하면 컴포넌트 재사용이 어려워질 수 있다.

2. 상태 관리 라이브러리 사용

Redux나 Recoil과 같은 상태 관리 라이브러리를 사용하면 전역 상태 효과적 관리 가능.

강력한 상태 관리 기능을 제공하지만,
작은 애플리케이션에서는 과도한 설정이 필요할 수 있다.

Redux 예제

🖥️ // userSlice.js

import { createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {
    name: 'John Doe',
    email: 'john@example.com'
  },
  reducers: {
    updateUser: (state, action) => {
      return { ...state, ...action.payload };
    }
  }
});

export const { updateUser } = userSlice.actions;
export default userSlice.reducer;

// store.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';

export const store = configureStore({
  reducer: {
    user: userReducer
  }
});

// App.js
import { Provider } from 'react-redux';
import { store } from './store';

function App() {
  return (
    <Provider store={store}>
      <MainLayout />
    </Provider>
  );
}

// UserProfile.js
import { useSelector, useDispatch } from 'react-redux';
import { updateUser } from './userSlice';

function UserProfile() {
  const user = useSelector(state => state.user);
  const dispatch = useDispatch();
  
  const handleUpdateName = () => {
    dispatch(updateUser({ name: 'Jane Doe' }));
  };
  
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <button onClick={handleUpdateName}>
        Change Name
      </button>
    </div>
  );
}

4. 컴포넌트 구조 개선
컴포넌트 구조 재설계도 Props Drilling을 피하는 방안이 될 수 있다.

  • 컴포넌트를 더 작은 단위로 분리
  • 상태를 사용하는 컴포넌트들을 가까운 곳으로 이동
  • 공통 부모 컴포넌트에서 상태 관리

가장 기본적인 해결책이지만, 항상 가능한 것은 아니라는 단점이 있다.


Context API

Context API는 React에서 제공하는 데이터 전달 방식으로,
"전역적인 데이터 공유 환경"을 만들어주는 기능이다.
마치 컴포넌트 트리 전체를 감싸는 하나의 데이터 우산을 만드는 것과 같다.

주요 구성 요소

1. Provider(제공자)
Context의 데이터를 제공하는 컴포넌트.
보통 애플리케이션의 최상위나 데이터가 필요한 컴포넌트 트리의 최상위에 위치한다.
value prop을 통해 공유할 데이터를 지정한다.

2. Consumer(소비자)
Context의 데이터를 사용하는 컴포넌트.
useContext 훅을 사용하여 데이터에 접근한다.
Provider가 제공하는 value를 받아서 사용할 수 있다.

작동 방식

  1. createContext()로 Context 객체 생성
  2. Provider로 컴포넌트 트리 감싸기
  3. value prop으로 공유할 데이터 전달
  4. 필요한 컴포넌트에서 useContext 훅으로 데이터 가져와 사용

사용해야 할 때

  • 여러 컴포넌트에서 공유해야 하는 전역 데이터가 있을 때 (예: 사용자 정보, 테마, 언어 설정)
  • Props를 여러 단계 거쳐서 전달해야 하는 경우
  • 특정 컴포넌트 트리 내에서만 공유되는 데이터가 필요할 때

장점

  • Props Drilling 문제 해결
  • 컴포넌트 재사용성 향상
  • 코드 가독성 개선
  • 상태 관리 로직 중앙화

주의사항

  • Context를 사용하면 컴포넌트 재사용이 어려워질 수 있다.
  • Context Provider 하위의 모든 컴포넌트는 Context 값이 변경될 때마다 리렌더링될 수 있다.
  • 너무 많은 Context를 사용하면 코드가 복잡해질 수 있다.

사용 예시

예를 들어, 다크 모드/라이트 모드 테마 설정을 구현할때,

  1. 최상위에서 ThemeProvider로 현재 테마 상태를 관리
  2. 버튼이나 설정 페이지에서 테마 변경 함수 호출
  3. 변경된 테마가 자동으로 전체 앱에 적용

코드

🖥️ // UserContext.js

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

const UserContext = createContext();

export function UserProvider({ children }) {
  const [user, setUser] = useState({
    name: 'John Doe',
    email: 'john@example.com'
  });

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

export function useUser() {
  return useContext(UserContext);
}

// App.js
function App() {
  return (
    <UserProvider>
      <MainLayout />
    </UserProvider>
  );
}

// MainLayout.js
function MainLayout() {
  return (
    <div>
      <Sidebar />
      <Content />
    </div>
  );
}

// Sidebar.js
function Sidebar() {
  return (
    <div>
      <Navigation />
    </div>
  );
}

// Navigation.js
function Navigation() {
  return (
    <div>
      <UserProfile />
    </div>
  );
}

// UserProfile.js
function UserProfile() {
  const { user, setUser } = useUser();
  
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <button onClick={() => setUser({ ...user, name: 'Jane Doe' })}>
        Change Name
      </button>
    </div>
  );
}
profile
아무튼, 개발자

0개의 댓글