본문 바로가기

react

[react] 상태관리 라이브러리는 정말로 필요한가?(context api)

react를 배우고 규모가 있는 웹을 만들 때면 자연스레 듣게 되는 단어가 바로 상태관리 라이브러리다. 이것이 왜 필요한지 묻는다면 일반적으로는 "리엑트의 하향식 데이터 전달방식"은 규모가 큰 앱에서 버그가 발생하기 쉽고 생산성과 유지보수에 문제가 되기 때문이라고 말해준다.

 

그런데, "react의 하향식 데이터 전달방식" 이 왜 "문제"를 일으킬까. 그리고 정말로 상태관리 라이브러리라는 대안이 정말로 불가피한 걸까?

 

1. react는 props drilling 문제를 일으킨다?


react로 큰 규모의 앱을 만들 때 으레 예상되는 문제가 바로 props drilling 이다.

 

props drilling

간단히 말하자면, react는 state를 부모 컴포넌트에서 자식 컴포넌트로만 전달할 수 있기 때문에, state가 생성된 부모 컴포넌트와 state를 사용하는 자식 컴포넌트 간에 tree상 거리가 멀다면 경유지에 위치한 중간 컴포넌트들이 그 state를 사용하지도 않으면서 단지 전달만을 위해 props로 받아야 한다. 이런 일이 잦아지면, 중간 컴포넌트들은 사용하지 않는 props가 많아져서 코드가 지저분해지고 재사용성이 떨어진다.

 

리엑트만으로는 해결방법이 없다?

과거에는 이런 문제를 리엑트만으로 해결할 수 없었고, 그런 상황 속에서 등장한 redux는 유일무이한 대안으로 부상하였다. 추측컨대, 이런 맥락 때문에, "리엑트+ 상태관리 라이브러리"가 공식처럼 자리잡은 것 같다. 하지만, 그 이후로 react에서도 전역 상태 관리를 위한 대안을 내놓았으니 바로 context api였다.

 

context api(+ useReducer)

이 훅은 props drilling 없이도 하위 컴포넌트에 상태를 전달할 수 있는 대안이었고, useReducer와 같이 사용하면, state setter 로직을 단일하게 관리함으로써 flux 디자인에 가까운 구조를 만들 수 있었다. 때문에, 전역 상태 관리를 리엑트 자체적으로 할 수 있는 좋은 대안으로 각광받았다.

 

2. 그러면 상태관리 라이브러리는 필요없는거 아닌가?


이렇게 보면, 상태 관리 라이브러리를 보편적으로 사용하는 생태계에 의문이 들 수 밖에 없다. 왜 context api는 늦은 등장이었지만 그 유용성에도 불구하고 선발 주자인 redux를, 그리고 뒷따라오는 jotai, zustand 등의 라이브러리를 대체하지 못했을까.

 

불완전한 기능 

context api는 전역 상태 관리 기능을 할 수 있지만, 거기에 필요한 부가 기능이 불완전했다. 전역 상태는 모든 컴포넌트에 사용될만큼 크고 복잡한 객체 구조일 가능성이 크다. 따라서, 잦은 업데이트가 일어나며 context를 구독하는 모든 컴포넌트(정확히는 useContext를 사용하는 컴포넌트)에서 리렌더링이 발생할 것이다. 그러다보면 전역 상태의 극히 작은 부분만을 구독해도 잦은 리렌더링이 발생한다. 

이를 해결하려면 구독한 이전값과 새 전역 상태값을 비교하고 리렌더링을 중단하는 기능이 필요하다. redux는 useSelector의 비교 함수 인자로 구현했고, 다른 라이브러리도 내장이 되어있지만, context api는 이런 기능을 직접 구현해야 한다.

 

바로 이런 문제 때문에 리엑트의 context api가 상태 관리 라이브러리를 대체하지 못한 것 아닐까.

이건 마치 "바닐라 자바스크립트로 직접 가상 돔을 구현하면 되는데 왜 react나 vue를 사용하는가?"라는 질문과 비슷한 맥락이다. 개인이 노력하면 구현할 수 있겠지만 굳이 여러 추가 기능을 안정적으로 제공하는 라이브러리가 있는데 직접할 필요가 있을지 회의적인 대답이 생기기 때문이다.

다만, 이런 결론은 상태관리 라이브러리를 단순히 전역 상태 관리 정도로만 사용한다는 건 별 의미가 없다는 말이기도 하다. 이들이 제공하는 부가 기능이 context api의 전역 관리의 단점을 어떤 식으로 보완하는지를 이해해야 한다.

 

예컨대, react의 문제점 중 하나는 state를 업데이트하는 로직이 각 컴포넌트마다 소산되어 있어서 state 간의 의존적인 업데이트 흐름을 파악하기 불편하다는 문제점이 있다. 이런 데이터 흐름을 추적하는 측면에서는 redux가 탁월한 선택이다. redux는 각 컴포넌트에서 dispatch를 통해 action을 보내면 이것들을 중앙의 reducer에서 업데이트하고 단일한 store만을 관리하기 때문이다.

반면, context api의 최적화를 위해 분할하여 사용하는 경우에는 복수의 provider 들을 서로 감싸면서 가독성이 떨어지는 문제가 생기는데, 이는 recoil, zotai, zustand 등에서는 이런 생성과정을 생략할 수 있고 selector로 state간 의존성을 간단하게 처리가능하다. 

 

참고자료

https://velog.io/@velopert/react-context-tutorial

 

https://velog.io/@ckstn0777/Context-API%EC%9D%98-%EC%B5%9C%EB%8C%80-%EB%8B%A8%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

 

https://velog.io/@velopert/using-redux-in-2021