🚀 들어가며
이번 주 멘토님과의 커피챗 시간에 유익한 질문이 나왔다. React에서 제공하는 Context API가 있음에도 다른 상태 관리 라이브러리가 필요한 이유와 어떤 경우에 둘을 사용해야할지에 대한 질문이었다. 많은 개발자들이 상태 관리 라이브러리를 사용하고는 있지만 Context API와 무엇이 다른지에 대해 깊게 고민하지 않았을 것이다. 나 또한 그랬다. 그저 상태 관리 라이브러리를 사용하는 것이 무조건적으로 옳다고만 생각했었다. 이번 포스팅에서는 Context API와 상태 관리 라이브러리의 차이점에 대해서 다뤄보려고 한다.
✅ 01. Context API
Context – React
A JavaScript library for building user interfaces
ko.legacy.reactjs.org
리액트 공식 문서에서는 context에 대해 아래와 같이 소개하고 있다.
- context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
- 일반적인 리액트 애플리케이션에서 데이터는 위에서 아래로 props를 통해 전달된다. context를 이용하면 애플리케이션 안의 여러 컴포넌트에 props로 전달할 필요없이 여러 컴포넌트가 값을 공유하도록 할 수 있다.
이처럼 context를 이용하면 애플리케이션의 상위 컴포넌트의 값을 깊이가 깊은 하위 컴포넌트에서 사용해야 할 때, props drilling 없이 간단하게 값을 공유받고 사용할 수 있다.
하지만 props drilling을 방지하기 위해 리액트 공식 문서에서 Context API 사용보다 우선적으로 제안하는 방식이 있는데 바로 합성 컴포넌트이다. 합성 컴포넌트를 사용하면, props drilling을 방지하면서 깊이가 깊은 하위 컴포넌트에 값을 전달할 수 있다. Context API를 사용하면 컴포넌트를 재사용하기 어려워지기 때문에 리액트는 합성 컴포넌트 사용을 우선적으로 추천하고 있다.
▪︎ Context API 사용을 적극 고려해야 하는 경우
그렇다면 합성 컴포넌트을 사용하지 않고 Context API를 사용해야 하는 경우는 어떤 경우일까? 아래의 3가지 조건을 만족하는 상황의 경우 Context API 사용을 고려해보자.
- 깊은 props drilling이 발생할 경우
- 합성 컴포넌트를 사용하면 로직이 복잡해질 경우
- 같은 데이터를 깊은 컴포넌트 트리 안의 여러 컴포넌트들에게 주어야 할 경우
✅ 02. Context API 와 상태 관리 라이브러리의 차이
흔히들 착각하는 것이 Context API가 상태 관리 도구라는 것이다. (나 역시 그렇게 생각했었다..) 하지만, Context API는 상태 관리 도구가 아니다. 그저, 전역적으로 State를 공유하는 기능만 수행한다. 상태 관리는 사실상 useState 혹은 useReducer가 해준다.
그렇다면 실제 프로젝트에서 프론트엔드 개발자들이 상태 관리 라이브러리 없이 리액트 Context API와 useState로 상태 관리를 할까? 아니다. 아마도 거의 대부분의 프론트엔드 개발자들이 프로젝트를 진행할 때 상태 관리를 외부 라이브러리를 설치해서 관리할 것이다. 왜 그럴까? 여러 상태 관리 라이브러리들은 Context API의 단점을 보완하고 있고, 사용이 편리하기 때문이다. 그렇다면 리액트 자체에서 제공하는 Context API와 상태 관리 라이브러리는 어떻게 다를까?
나는 이전 팀에서 상태 관리 라이브러리로 Redux Toolkit을 사용했었다. 사용법을 익히는 데에 어려움이 있었지만 적응하고나니 효율적으로으로 상태를 관리할 수 있었다. 지금부터 대표적인 상태 관리 라이브러리인 Redux와 Context API를 비교해보자.
▪︎ 대표적인 상태 관리 라이브러리, Redux
Redux는 데이터의 흐름이 단방향으로 흐르는 Flux 아키텍처 구조이다.
- Store : Store 객체에 전역 상태를 저장한다.
- Reducer : Reducer 함수 내에 상태를 변경하는 로직을 작성한다. Action의 결과로 상태를 어떻게 바꿀지 구체적으로 정의하며, 이전 상태를 가공하여 새로운 상태를 반환한다.
- Action : 상태를 업데이트하기 위해 Action 객체를 사용한다. Reducer는 Action에 따라 Store에 접근하여 Action에 따라 정해진 로직을 수행하게 된다. 즉, 트리거 역할을 한다.
- Dispatch : 상태를 변경하기 위해서 Dispatch 함수를 사용한다. Dispatch 함수는 Action을 전달받아 이에 대한 상태 업데이트를 Reducer에 요청한다.
Redux는 위와 같은 구조를 가진다. 이전 프로젝트를 진행하며 내가 느낄 수 있었던 Redux의 가장 큰 장점은 여러 사람과 함께 하나의 애플리케이션을 만들 때 상태 관리가 매우 편리하다는 것이다. Action을 생성하고 Reducer에 상태 변경 로직을 작성한 후 분리된 Reducer들을 하나의 Reducer로 합침으로써 전역 상태 관리를 편리하게 할 수 있었다.
또, Redux를 사용하면 State를 컴포넌트에 종속시키지 않고 외부에서 처리하기 때문에 리렌더링 면에서 이점이 있다.
▪︎ 정리하자면,
Context API는 context의 상태가 바뀌면 해당 context의 Provider 내부의 컴포넌트들이 모두 리렌더링이 되는 반면, Redux 등의 외부 상태 관리 라이브러리는 실제 상태가 바뀔때만 컴포넌트가 리렌더링된다. 또한, 상태 관리 라이브러리는 사용이 편리한 다양한 기능을 제공하고, 큰 규모의 프로젝트를 여러 사람들과 함께 진행할 때 편리하다.
✅ 03. Context API 로 페이지네이션 구현하기
번외로, 이번 프로젝트에서는 리액트에서 제공하는 Context API를 활용하여 페이지네이션을 구현했다. 앞서 정리한 Context API를 사용해야 하는 위 3가지 조건을 만족했기 때문에 Context API를 활용했고, props drilling을 방지할 수 있었다.
▪︎ Context 생성하기
Context를 생성하고, 상태 변경 로직을 작성한다.
▪︎ 하위 컴포넌트들을 Provider로 감싸기
상태 공유가 필요한 하위 컴포넌트들을 Provider로 감싼다.
▪︎ 하위 컴포넌트에서 Context 전달받기
useContext() 훅을 통해 생성한 context를 전달 받을 수 있다.
🛸 마치며
이전까지 상태 관리를 위해 상태 관리 라이브러리를 사용만 했었지 리액트에서 제공하는 Context API와의 차이점은 전혀 생각하지 않았다. 생각보다 많은 차이점이 있었고, 왜 상태 관리 라이브러리가 필요하고 각광받는지 이번 기회에 느낄 수 있었다. 앞으로도 단순히 기술을 사용하는 데에 그치지 않고 '왜?' 라는 질문을 던지는 개발자로 성장해야겠다.
'프로그래머스 데브코스' 카테고리의 다른 글
[Tanstack Query / 트러블 슈팅] useSuspenseQuery로 데이터 로딩 상태 처리하기 (0) | 2024.03.22 |
---|---|
[Tanstack Query / 트러블 슈팅] invalidateQueries로 쿼리 무효화 하기, new QueryClient()와 useQueryClient() 차이 (0) | 2024.03.17 |
협업 잘하는 개발자는 어떤 모습일까? (2) | 2024.03.06 |
[YIL] GitHub 깃허브 1일 1잔디 1년 심은 후기 (1) | 2024.03.06 |
[MIL-4] 231226 ~ 240123 프론트엔드 데브코스 회고 / 첫 팀 프로젝트 (1) | 2024.01.23 |