Frontend 스터디/이것저것 스터디

[이것저것 스터디] 전역 상태 관리 라이브러리 분석

gugu76 2024. 7. 28. 02:35

이번에는 또 다른 스터디다. 살려주세요

리액트 딥다이브 책 같이 읽은 분들과 아쉬워서 Next.js 공부도 살짝? 하고 결국 이 스터디까지 이어지게 됐다.

이것저것 스터디라는 이름답게 진짜 이것저것 다 공부한다.

 

요즘 디자인 시스템 최적화와 관련된 전반적인 지식과 컴포넌트 개발만 하다 보니, 전체적인 구조를 설계하는 능력이 떨어짐을 몸소 느껴버렸다.

이걸 회사 과제 테스트 보면서 느껴버려서 좀 더 많이 와닿았던 거 같기도?

어쨌든 그래서 뭔가 특단의 대책 같은 게 필요했고, 단순히 기한에 맞춰서 제대로 된 설계도 없이 프로젝트 개발만 하는 거 말고, 좀 내부 구조도 생각해 보고 설계도 고민을 한참 해볼 수 있는 능력이 필요하다고 느꼈다.

 

그래서 나와 다른 스터디원들이 많이 부족하다고 느끼는 것들 위주로 한 주차씩 주제를 잡았다.

아무래도 주니어들의 꾸준한 고민인 상태 관리부터 시작이다.

1주 차부터 2-3주간은 아마 상태 관리에 대해서 고민해 볼 거 같다.

1주 차는 zustand, jotai가 기존 전역 상태 관리 라이브러리와 어떤 차이점이 있는지 위주로 알아봤다.

 

zustand

zustand는 요즘 진짜 많이 쓰는 전역 상태 관리 라이브러리다.

나도 CMC 과제할 당시 처음 써봤는데 보일러 플레이트도 적고 프로바이더 없이 사용할 수 있다는 큰 장점을 느끼고 지금까지 잘 사용하고 있었다.

 

일단, zustand의 내부 구조를 파악하기에 앞서 주요 특징을 알아보도록 하자.

- redux처럼 중앙 집중식, 즉 하나의 스토어를 이용해서 전역 상태를 관리한다.

- 내부적으로 context api를 사용하지 않는다. (별도의 Provider가 필요 없다)

- zustand는 렌더링 최적화를 하기 위해서는 수동으로 selector를 통해 필요한 상태만 불러와야 한다.

 

사실상 이거 세 개만 알면 다 끝났다.

조금만 더 자세히 알아보도록 하자.

 

첫 번째 특징이 가장 중요하다.

첫 번째 특징을 이해하면 세 번째 특징도 바로 따라온다.

zustand에서는 하나의 스토어를 사용하기 때문에 selector 없이 상태를 불러오게 되면 불필요한 상태들이 포함된다.

이렇게 사용하지 않는 상태들을 불러오게 되면 해당 상태마저 변경 사항을 추적해서 불필요한 렌더링이 일어난다.

그런데 이때 selector를 활용하게 되면 selector를 통해 가져온 상태만 추적해서 렌더링 최적화를 할 수가 있다.

 

그럼 이제 두 번째 특징, context api를 사용하지 않는다는 중요한 특징에 대해 알아보자.

zustand를 제외한 거의 대부분의 전역 상태 관리 라이브러리를 사용하려면 전역에 Provider 컴포넌트로 감싸주는 작업이 필요하다.

이는 내부적으로 context api를 사용하기 때문인데, context api를 사용한다면 context api의 단점까지도 고스란히 들고 오게 된다.

 

context api는 변경 사항 발생 시 포함된 하위 모든 컴포넌트가 다시 렌더링 된다.

물론 라이브러리에 따라서 이런 부분들을 최적화해 주었을 수도 있다.

하지만 애초에 context api를 쓰지 않는다면 이런 단점을 보완할 필요도 없지 않을까?라는 생각이 든다.

최적화 비용도 있을 테니...

 

또, context api를 사용해 보면 알겠지만 보일러 플레이트 코드가 생각보다 많다.

이 부분은 크리티컬 하진 않지만 누구나 보일러 플레이트가 적은 코드를 좋아할 거라고 생각한다.

 

어쨌든 zustand는 context api를 사용하지 않고 전역 상태 관리 라이브러리를 구현해 냈다.

context api 대신 클로저를 활용해서 스토어 내부 상태를 관리한다.

 

코드가 궁금하다면, 아래 링크를 들어가 보도록 하자.

https://github.com/pmndrs/zustand

 

zustand 코드를 보다 보면 observer 패턴을 사용한 것도 알 수 있을 거 같다.

시간이 있다면 꼭 한 번 살펴보자.

생각보다 정말 짧다.

 

아 그리고 zustand는 패키지 크기마저 가볍다.

https://www.npmjs.com/package/zustand

 

jotai

jotai는 zustand와 완전 다르다.

 

jotai의 주요 특징은 다음과 같다.

- recoil처럼 atom 방식으로 동작한다. (다만, jotai는 atom의 객체 참조 id에 의존하지만 recoil은 atom의 문자열 key에 의존한다)

- zustand처럼 별도의 수동 렌더링 최적화가 필요하지 않다. 

- context api를 사용한다.

- 여러 Provider 컴포넌트를 통해 각 서브트리 하위에서 별도의 상태를 관리할 수 있다.

 

좀 더 자세히 알아보도록 하자.

jotai는 atom 방식으로 동작하기 때문에 상태를 가져올 당시 zustand / redux처럼 추적하지 않아야 되는 상태를 가져오지 않는다.

따라서 selector 등 별도로 렌더링 최적화를 해줄 필요가 없다.

 

그리고 네 번째 특징이 상당히 인상적이었는데, 전역 상태 관리 라이브러리임에도 context api처럼 여러 Provider 컴포넌트를 통해서 각 서브트리 하위에서 별도의 상태를 관리할 수 있었다.

다만, 이게 가능한 이유가 jotai가 내부적으로 context api로 구현됐기 때문이다.

그런데, context api와 다른 점은 불필요한 리렌더링 관련 최적화가 이루어졌다는 것이다.

이걸 보면서, 만약 context api처럼 컴포넌트 서브 트리에서 별도의 상태가 필요하다면 context api 대신 jotai를 써보면 좋을 것 같다는 생각이 엄청 들었다.

 

한편, 컴포넌트 라이브러리에서 RadioGroup, RadioButton과 같은 관계에 있는 경우 보통 context api를 많이 쓴다.

이때는 별도의 상태 관리 라이브러리를 사용하기보다는 context api를 사용해서 해결해 보는 것이 라이브러리 의존도도 낮을 것 같다는 생각이 들었다.

이외에는 jotai를 써볼 것 같다.

 

jotai도 zustand보다는 아니지만 못지않게 패키지 크기가 작다.

https://www.npmjs.com/package/jotai

 

약간 zustand와 jotai를 비교해 보니까 전역 상태 관리 라이브러리에 대해서 구조를 조금 더 파악해 볼 수 있었던 거 같다.

이제 어떤 상황에서 각 라이브러리를 써야 할지 명확해진 거 같다.

 

다음 주에는 이번 주 내용을 바탕으로 좀 더 상태 관리 기준에 대해서 명확하게 생각해보려고 한다.

 

참고 자료

https://docs.pmnd.rs/zustand/getting-started/introduction

https://commerce.nearform.com/blog/2021/stores-no-context-api

https://github.com/pmndrs/zustand/blob/main/src/vanilla.ts

https://www.patterns.dev/vanilla/observer-pattern

https://jotai.org/