1. Context API를 사용하는 것이 최선일까?
각각 EmailSection과 CertificationSection 컴포넌트이다. 현재 구조는 다음과 같다.
// page.tsx
<main>
<JoinTopNavigationBar />
<AuthTitleTextMessage text={`재학생 인증을 위해\\n학교 이메일을 입력해주세요`} />
<InputForm />
<NoticeSection />
</main>
그리고. InputForm컴포넌트는 다음과 같다.
// components/InputForm.tsx
<div>
<EmailSection
register={register}
handleSubmit={handleSubmit}
email={watch('email')}
isError={!!errors.email}
/>
<CertificationSection
register={register}
handleSubmit={handleSubmit}
certificateNumber={watch('certificateNumber')}
/>
</div>
여기서, 두 컴포넌트에 넘겨주는 props인 register, handleSubmit, watch, isError는 모두 react-hook-form의 훅인 useForm에서 받은 값이다. 두 컴포넌트에 넘겨주는 props또한 거의 일치한다. 그렇기에 Context로 묶으면 useForm에서 받은 methods만 context로 관리하면 된다. 이 방법이 정말 최선일까?
Context API를 사용하는 것이 가장 걱정되는 부분은 리렌더링 이슈이다. CertificationSection의 값을 다루는데 EmailSection이 리렌더링될 수 있다는 사실이다. 왜냐하면, Context API는 context값이 바뀌면 Context를 사용하고 있는 모든 컴포넌트가 리렌더링이 일어나기 때문이다.
props로 컴포넌트에 넘겨주고 있는 현재, 각 컴포넌트를 React.memo로 메모이제이션을 해두어서, 한 컴포넌트가 리렌더링이 일어날 때 다른 컴포넌트가 리렌더링이 일어나지 않는다.
사실, React.memo를 사용할 때 넘겨주는 props에 함수인 handleSubmit이 있어서 이 함수를 useCallback으로 메모이제이션을 해주어야 렌더링 이슈가 해결될 줄 알았다. handleSubmit을 메모이제이션을 하지 않았는데 어떻게 렌더링 이슈가 해결이 된 것일까?
본론으로 들어가서, 이 구조를 Context API를 사용하여 넘겨주는 props를 줄이는 것이 더 좋은 구조라고 할 수 있을까? props를 넘겨주는 코드와 컴포넌트의 로직이 분리되니 코드가 확실히 깔끔해지긴 할 것이다. 어떤 구조가 더 좋은 구조라고 할 수 있을까.
1. Context API 쓰는 게 맞을까?
- 위 페이지를 다음과 같은 구조로 작성하였다.
<CreateMeetingContextProvider>
<ImageSection />
<TitleSection />
<Spacing size={15} />
<DescriptionSection />
<DateSection />
<LocationSection />
<NumberSection />
<Spacing size={15} />
<SubmitSection />
</CreateMeetingContextProvider>
Context Provider로 각 input 컴포넌트를 감쌌다. 과연 이게 좋은 구조일까? Context값이 변경이 되면 모든 Context를 가지고 있는 컴포넌트가 리렌더링이 일어난다. 즉, 제목에 값을 입력하면 다른 모든 컴포넌트도 리렌더링이 일어난다는 것이다. 그래서, 각 컴포넌트에 memo를 이용하여 컴포넌트 리렌더링을 최소화하였다.
그럼에도 불구하고 swiper를 이용한 캘린더와 모임 인원 input의 리렌더링 이슈는 막지 못하였다.
다른 해결책이 필요하다.
이러한 단일 페이지에서 Context를 사용하는 것이 정말 최선일까?
이러한 경우에서 props drilling, 렌더링 이슈를 해결하기 위해 등장한 것이 바로 전역상태 관리 라이브러리가 아닐까?
2. Context API vs 전역 상태 관리 라이브러리
그렇다면, 언제 Context API를 쓰고, 언제 전역 상태 관리 라이브러리를 써야 할까?
JBEE님은 테마, 다국어 처리 처럼 전역에서 사용하는, 모든 컴포넌트에 변화가 생기는 상태만 전역 상태로 관리해야 한다고 이야기하신다. 너무 극단적인 의견 아닐까?
그렇다면, Context API는 전역 상태 관리 툴이 아닐까? Context API는 컴포넌트 트리 안에서 전역 상태를 공유할 수 있는 것으로, Provider로 감싼 컴포넌트 안에서만 context를 사용이 가능하다. 이는 종속성을 주입하기 위한 도구이기에 전역 상태 툴이라기엔 다소 에매한면이 있다.
Context API는 Context 값이 변경되면 해당 값이 useContext를 사용하는 컴포넌트가 모두 렌더링이 일어난다는 치명적인 단점이 있다. 이에 Context API를 되도록이면 쓰되, 복잡한 상태 관리는 서드파티 전역 상태 라이브러리를 사용하면 좋을 것 같다. 앞으로 이러한 점들을 항상 깊게 고민하며 상태 관리를 짜보도록 하자.
현재 상태
전역 상태 관리 라이브러리를 사용할 필요성을 크게 느끼지 못해, 상태가 여러 컴포넌트 계층에 걸처 관리가 필요하다면 Context API로 상태값을 주입시켜주고 있다. Context API는 상태 관리를 해주는 것이 아니라, useState, useReducer등으로 생성한 상태값을 주입시켜주는 것 뿐이다.
전역 상태관리 라이브러리가 등장한 이유가 Context의 리렌더링 이슈인데, 사용해도 성능 상에 문제가 없을까?