← 블로그 목록
상태 관리React웹앱 아키텍처AI 코딩사이드 프로젝트

AI 웹앱의 상태 관리: Props Drilling vs Context vs 상태 라이브러리, 언제 무엇을 쓸까

Claude Code나 Cursor로 생성한 웹앱에서 상태 관리 복잡도가 증가할 때, Props Drilling·Context·상태 라이브러리 중 무엇을 선택할지 판단하는 기준을 제시합니다.

Vibeollio 팀-

AI 생성 코드에서 상태 관리가 복잡해지는 이유

Claude Code나 Cursor 같은 AI 코딩 도구로 웹앱을 빠르게 만들다 보면, 초기에는 간단한 상태 관리로 충분해 보입니다. 하지만 기능이 늘어나고 컴포넌트 깊이가 깊어지면 상태 전달 로직이 급격히 복잡해집니다. 특히 AI가 생성한 코드는 즉시 동작하는 것을 우선하기 때문에, 아키텍처 개선은 개발자가 직접 판단해야 합니다.

상태 관리 방식을 잘못 선택하면 유지보수 난이도가 급증하고, 새로운 기능 추가 시 기존 로직을 뜯어고쳐야 하는 상황이 발생합니다. 이 글에서는 세 가지 방식의 장단점과 실제 선택 기준을 제시합니다.

Props Drilling: 언제까지 버틸 수 있을까?

Props Drilling은 부모 컴포넌트에서 자식 컴포넌트로 상태와 핸들러를 계속 내려주는 방식입니다. 가장 단순하고 추적하기 쉬우며, 추가 라이브러리가 필요 없습니다.

적합한 경우:

  • 컴포넌트 깊이가 3단계 이하
  • 전달되는 상태 개수가 3개 이하
  • 프로토타입이나 학습 목적의 프로젝트

예를 들어, 간단한 투두 앱에서 App → TodoList → TodoItem으로 내려가는 구조라면 Props Drilling으로도 충분합니다:

// App.jsx
const [todos, setTodos] = useState([]);
return <TodoList todos={todos} onAdd={setTodos} />;

// TodoList.jsx
const TodoList = ({ todos, onAdd }) => (
  <div>{todos.map(t => <TodoItem key={t.id} todo={t} />)}</div>
);

문제가 되는 순간:

  • 같은 상태를 전달하는 컴포넌트 체인이 4단계 이상
  • 중간 컴포넌트가 그 상태를 실제로 사용하지 않음 (통로 역할만 함)
  • 상태 종류가 5개 이상으로 늘어남

이 지점에 도달하면 Context나 상태 라이브러리로 마이그레이션할 시점입니다.

Context API: 중소 규모 프로젝트의 실용적 선택

React 내장 Context API는 Props Drilling을 피하면서도 추가 의존성을 최소화합니다. createContext와 useContext로 전역 상태처럼 접근 가능합니다.

적합한 경우:

  • 컴포넌트 깊이가 5~7단계
  • 전역으로 공유할 상태가 2~4개 (예: 사용자 정보, 테마, 언어 설정)
  • 상태 업데이트 빈도가 낮음 (초당 여러 번 변경 없음)
  • 팀 규모가 작거나 개인 프로젝트

실제 구현 예시:

// ThemeContext.js
const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// 어디서나 사용
const MyComponent = () => {
  const { theme, setTheme } = useContext(ThemeContext);
  return <button onClick={() => setTheme('dark')}>Toggle</button>;
};

주의할 점:

  • Context 값이 변할 때마다 모든 구독 컴포넌트가 리렌더링됨 (성능 이슈 가능)
  • 복잡한 상태 로직(여러 필드 동시 변경, 조건부 업데이트)을 다루기 어려움
  • 여러 Context를 조합하면 코드가 중첩되고 복잡해짐 (Context Hell)

상태 라이브러리: 복잡도가 높을 때의 필수 선택

Zustand, Redux(Redux Toolkit), Jotai 같은 상태 라이브러리는 복잡한 상태 관리를 체계적으로 처리합니다.

Zustand (경량, 추천):

  • 번들 크기가 작음
  • 문법이 직관적이고 학습 곡선이 낮음
  • Context 없이 직접 구독 가능 (불필요한 리렌더링 방지)
import { create } from 'zustand';

const useTodoStore = create((set) => ({
  todos: [],
  addTodo: (text) => set((state) => ({
    todos: [...state.todos, { id: Date.now(), text }]
  })),
}));

// 컴포넌트에서
const TodoApp = () => {
  const todos = useTodoStore((state) => state.todos); // 필요한 값만 골라 구독
  return <div>{todos.map(t => <div key={t.id}>{t.text}</div>)}</div>;
};

Redux (대규모 팀 프로젝트):

  • 상태 변화를 명확하게 추적 가능 (디버깅 용이)
  • 미들웨어로 비동기 작업, 로깅 처리
  • 설정과 코드량이 상대적으로 많음 (Redux Toolkit을 쓰면 크게 줄어듦)

적합한 경우:

  • 실시간 협업 기능 (여러 사용자 상태 동기화)
  • 복잡한 폼 상태 (유효성 검사, 조건부 필드)
  • 앱 전체를 통과하는 상태가 10개 이상
  • 팀 규모가 크고 코드 일관성이 중요함

선택 기준: 의사결정 플로우

  1. 컴포넌트 깊이가 3단계 이하인가? → Props Drilling 유지
  2. 전역 상태가 2~4개이고 업데이트 빈도가 낮은가? → Context API
  3. 상태 개수가 5개 이상이거나 실시간 업데이트가 필요한가? → Zustand 시작
  4. 팀 규모가 크고 상태 변화 추적이 중요한가? → Redux 검토

AI 생성 코드에서는 보통 초기 Context API 수준으로 충분하지만, 기능이 늘어나면서 자연스럽게 Zustand로 마이그레이션하는 경로가 가장 현실적입니다.

Props Drilling에서 벗어나는 시점을 놓쳤다면?

AI 도구가 생성한 코드를 받아서 Props 체인이 길어진 상황이라면, 한 번에 전체를 리팩토링할 필요는 없습니다. 가장 자주 변경되는 상태부터 Context나 Zustand로 추출하면, 점진적으로 코드 품질을 개선할 수 있습니다.

상태 라이브러리로 마이그레이션할 때 주의할 점은?

상태 라이브러리 도입이 항상 정답은 아닙니다. 간단한 프로젝트에 Redux를 도입하면 오히려 복잡도가 증가합니다. 현재 상태 관리의 고통이 명확할 때(예: Props 체인 5단계 이상, 상태 버그가 자주 발생)만 라이브러리 도입을 고려하세요.

AI 코딩 도구에서 생성된 상태 관리 코드를 바로 신뢰해도 되나요?

AI 도구는 빠르게 동작하는 코드를 생성하는 데 집중하므로, 장기 유지보수성을 고려한 아키텍처는 제시하지 않습니다. 생성된 코드가 Props Drilling 중심이라면, 프로젝트 규모와 팀 상황에 맞게 수동으로 리팩토링해야 합니다.


상태 관리 방식을 선택한 후, 실제 구현 과정에서 막히는 부분이 있다면 Vibeollio에 프로젝트를 등록 하고 커뮤니티 피드백을 받아보세요. 다른 개발자들의 의견을 통해 아키텍처 개선점을 발견할 수 있습니다.