리액트와 상태 관리의 난제

단순한 컴포넌트 간의 결합을 넘어 복잡한 데이터 동기화와 깊은 Props 전달(Prop Drilling)을 제거하기 위해 프론트엔드 아키텍처에서 글로벌 상태 관리는 필수적인 요소로 정립되었습니다. 하지만 매년 새로운 도구와 패러다임이 추가되면서 개발자들에게는 또 다른 고민을 낳고 있습니다.

1. 전통과 신뢰의 Redux

단일 스토어와 불변 상태, 불변 업데이트를 지향하는 Flux 패턴의 근본적인 도구입니다. 엄격한 규칙으로 인해 버그 추적이 쉽고 미들웨어(Redux DevTools, RTK Query) 생태계가 완벽합니다. 단점은 가벼운 상태 변화 하나에도 액션 타입, 리듀서 등 지나친 보일러플레이트 코드를 작성해야 한다는 것입니다.

2. 리액트 친화적인 아토믹 패턴, Recoil / Jotai

상태 단위를 작은 'Atom' 단위로 선언하고 상태 구독 관계를 통해 연산 결과(Selector)를 효율적으로 전파합니다. 리액트의 Context API 대비 재조정 연산이 세밀하고 속도가 빠르며 훅처럼 가볍게 작성 가능합니다.

3. 트렌디한 선택, Zustand

보일러플레이트 코드가 거의 없으며, 단순한 팩토리 함수 호출만으로 스토어를 개설하고 모든 컴포넌트에서 자유롭게 구독할 수 있는 매우 직관적인 발행-구독 모델 도구입니다. 현재 대형 라이브러리들 사이에서 실무 생산성 관점으로 큰 호평을 얻고 있습니다.

상태 관리 도구를 고르는 기준

상태 관리 라이브러리는 팀의 문제를 줄여야지 새로운 규칙을 과도하게 늘리면 안 됩니다. 서버 데이터, URL 상태, 폼 상태, 전역 UI 상태를 먼저 분류하면 어떤 도구가 필요한지 훨씬 선명해집니다.

상태 분류법

  • 서버에서 가져온 데이터는 캐싱과 동기화가 핵심이므로 전용 데이터 패칭 도구를 고려합니다.
  • 모달, 토스트, 테마처럼 앱 전체에서 공유하는 UI 상태는 가벼운 전역 스토어가 적합합니다.
  • 입력 중인 폼 값은 전역으로 올리기보다 폼 라이브러리나 로컬 상태로 관리하는 편이 단순합니다.

Redux, Zustand, Jotai 중 무엇을 고르든 상태의 책임을 먼저 나누면 도구 선택이 훨씬 덜 혼란스럽습니다.

Frontend Note 실무 노트

상태 관리 도구를 고르기 전에 상태를 분류해야 합니다. 서버에서 가져온 데이터, URL에 담긴 상태, 입력 중인 폼 값, 모달 열림 여부, 테마 같은 전역 UI 상태는 서로 성격이 다릅니다. 이 구분 없이 모든 것을 하나의 전역 스토어에 넣으면 구조가 커질수록 디버깅이 어려워집니다.

상태 종류예시권장 위치
서버 상태목록, 상세, 프로필데이터 패칭 캐시
URL 상태검색어, 페이지 번호query string
폼 상태입력 중인 값컴포넌트 또는 폼 도구
전역 UI토스트, 테마가벼운 스토어

Redux는 규칙과 추적성이 강하고 큰 팀에 적합합니다. Zustand는 작고 빠르게 시작하기 좋습니다. Context는 빈번히 바뀌지 않는 설정값에 적합하지만 모든 상태 관리 문제를 해결하는 도구는 아닙니다. 중요한 것은 도구 이름보다 데이터 소유권입니다.

상태가 어디에서 만들어지고, 누가 변경하고, 어떤 화면이 구독하는지 설명할 수 있어야 합니다. 이 질문에 답하지 못하면 어떤 라이브러리를 써도 복잡도는 줄어들지 않습니다.

도구 선택보다 상태 소유권이 먼저다

상태 관리가 어려워지는 이유는 도구가 부족해서가 아니라 상태의 주인이 불분명해서입니다. 서버에서 온 데이터는 서버가 원본이고, URL 검색 조건은 주소가 원본이며, 폼 입력값은 사용자가 입력 중인 화면이 원본입니다. 이 원본을 무시하고 모두 전역 스토어에 복사하면 동기화 문제가 생깁니다.

예를 들어 상품 목록의 필터 조건은 URL에 두는 것이 좋습니다. 사용자가 새로고침하거나 링크를 공유해도 같은 화면을 볼 수 있기 때문입니다. 반면 모달 열림 여부는 URL보다 UI 상태로 충분합니다. 서버에서 가져온 목록은 전역 스토어에 직접 넣기보다 캐시와 재검증 정책을 가진 데이터 패칭 도구가 더 적합합니다.

const stateDecision = {
  url: ["keyword", "page", "sort"],
  server: ["products", "profile"],
  local: ["draftMessage", "focusedField"],
  globalUi: ["toast", "theme"]
};

Redux는 액션 기록과 디버깅이 중요한 큰 팀에 어울립니다. Zustand는 작은 전역 UI 상태를 빠르게 다루기 좋습니다. Context는 테마나 인증 사용자처럼 자주 바뀌지 않는 값을 내려줄 때 적합합니다. 도구를 섞어도 괜찮지만, 각 도구가 맡는 범위는 문서로 남겨야 합니다.

리뷰 질문

  • 이 상태의 원본은 어디인가?
  • 새로고침 후에도 유지되어야 하는가?
  • 여러 화면에서 동시에 변경하는가?
  • 서버 데이터와 UI 상태가 섞여 있지 않은가?

참고 자료와 업데이트 기준

Frontend Note의 글은 실무 적용 관점에서 작성하며, 관련 기술의 공식 문서와 브라우저 지원 현황이 바뀌면 내용을 다시 점검합니다. 특히 프레임워크 버전, API 정책, 성능 측정 방식은 시간이 지나며 달라질 수 있으므로 적용 전에는 최신 문서를 함께 확인하는 것을 권장합니다.

  • MDN Web Docs에서 웹 표준과 브라우저 동작을 확인합니다.
  • web.dev에서 성능, 접근성, 사용자 경험 기준을 확인합니다.
  • React 공식 문서에서 React 관련 API와 권장 패턴을 확인합니다.

상태 관리 도구보다 먼저 그릴 지도

상태 관리가 어려워지는 이유는 도구가 부족해서가 아니라 상태의 성격을 섞어 관리하기 때문입니다. 서버에서 가져온 데이터, 폼 입력값, 모달 열림 여부, URL 쿼리, 로그인 세션, 임시 선택값은 갱신 방식이 서로 다릅니다. 이들을 모두 하나의 전역 저장소에 넣으면 처음에는 편해 보이지만, 시간이 지나면 어떤 변경이 어느 화면에 영향을 주는지 알기 어려워집니다. 먼저 상태 지도를 그려서 소유자와 수명을 적어 두면 도구 선택이 쉬워집니다.

예를 들어 서버 상태는 캐시, 재요청, 낙관적 업데이트가 중요하므로 TanStack Query 같은 전용 도구가 잘 맞습니다. 입력 중인 폼 값은 해당 폼 컴포넌트 근처에 두는 편이 예측하기 쉽습니다. 테마, 인증 사용자, 전역 알림처럼 여러 곳에서 읽지만 자주 바뀌지 않는 값은 Context로 충분할 수 있습니다. 반면 복잡한 UI 편집기나 다단계 설정 화면처럼 여러 컴포넌트가 같은 임시 상태를 자주 수정한다면 Zustand 같은 가벼운 스토어가 도움이 됩니다. 중요한 것은 도구 이름보다 상태의 책임을 작게 유지하는 것입니다.

실무 적용 전 최종 점검

이 글의 내용을 프로젝트에 적용하기 전에는 현재 코드의 목적과 사용자 흐름을 먼저 확인해야 합니다. 같은 패턴이라도 관리자 화면, 공개 랜딩 화면, 입력이 많은 폼 화면에서는 우선순위가 다릅니다. 관리자 화면은 반복 작업 속도와 오류 복구가 중요하고, 공개 화면은 초기 로딩과 접근성이 중요하며, 폼 화면은 검증 메시지와 상태 보존이 중요합니다. 따라서 예제 코드를 그대로 붙이기보다 어떤 파일에서 책임을 나눌지, 어떤 값이 외부 입력인지, 실패했을 때 사용자가 무엇을 보게 되는지를 함께 검토하는 것이 좋습니다.

작업 후에는 변경 전후를 비교할 수 있는 작은 기록을 남깁니다. 수정한 컴포넌트, 확인한 브라우저, 실패 케이스, 되돌리는 방법을 적어 두면 다음 배포에서 같은 문제를 빨리 찾을 수 있습니다. 특히 React와 TypeScript 코드는 타입 오류가 사라져도 런타임 흐름이 틀릴 수 있고, CSS 수정은 특정 화면 폭에서만 깨질 수 있습니다. 그래서 로컬 확인, 빌드 확인, 실제 화면 확인을 분리해 진행하는 습관이 필요합니다. Frontend Note의 예시는 이 과정을 돕기 위한 출발점이며, 팀의 코드 스타일과 배포 방식에 맞게 작게 조정해서 사용하는 편이 안전합니다.

작게 적용하는 연습 방법

처음 적용할 때는 전체 화면을 한 번에 바꾸지 말고 작은 컴포넌트 하나를 골라 실험하는 편이 좋습니다. 변경 범위를 좁히면 원인을 추적하기 쉽고, 기존 사용자 흐름에 주는 영향도 줄일 수 있습니다. 예제 코드를 적용한 뒤에는 정상 케이스뿐 아니라 빈 데이터, 긴 텍스트, 느린 네트워크, 모바일 화면을 함께 확인합니다. 이런 조건에서 문제가 없다면 같은 패턴을 다른 화면으로 확장해도 유지보수 부담이 적습니다.