일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 캠스터디
- Chrome Extension
- TypeScript
- popup
- discord.js
- Message Passing
- content script
- 프로그래머스 #정수삼각형 #동적계획법
- C언어로 쉽게 풀어쓴 자료구조
- 백준
- react
- 포도주시식
- 파이썬
- 동적계획법
- 백준 #7568번 #파이썬 #동적계획법
- webpack
- 디스코드 봇
- 공부시간측정어플
- 백준 7579
- 자료구조
- 크롬 익스텐션
- 2156
- background script
- supabase
- X
- nodejs
- 갓생
- 크롬 확장자
- Today
- Total
히치키치
zustand 도입과 최적화 본문
[1] Intro
1. 왜 상태 관리 라이브러리가 필요한가?
현재 "whaat2buy" 프로젝트는 리액트 프레임워크를 이용해 개발하고 있다. 리액트 프레임워크는 데이터 변경 때마다 해당 내용이 들어가는 템플릿 방식으로 HTML을 작성하는 선언적인 방식으로 만들어졌다. 그러나 많은 데이터, 산발적인 로직은 템플릿과 컴포넌트 또한 복잡해진다. 따라서 데이터만 받아서 (readonly) 보여주는 presentor형 컴포넌트와 데이터 조작을 다루는 container형 컴포넌트를 분리해 개발하고자 하였다. container에서 props를 presentor로 내려주면서 view를 재사용해도 충분하다고 생각했었다.. 하지만 생각보다 분리한 컴포넌트 사이에 중간 컴포넌트가 많이 존재하게 되면서 오히려 분리한 컴포넌트가 더 결합할 수 밖에 없고, 중간에 props를 사용하지 않아도 중간 컴포넌트에게 이를 전달해야하는 props drilling 문제가 있었기 때문이다.
2. 사용할 상태 관리 라이브러리의 조건
러닝커브가 낮아야 한다.
프로젝트 마감까지 시간이 얼마 남지 않아 새로운 기술을 습득하는데 여유가 부족했다. 또, 리액트 기반의 간결한 문법과 데이터를 기록하고 변경을 감지해 view로 전달하는 주요 기능만 요구되는 상황이다.
이미 react에서 생태계가 있어야 한다.
개발 과정에서 프론트 담당자가 혼자였기 때문에 참고할 문서나 질문/답변이 풍부해야 하고 꾸준히 유지보수 되는 것이었으면 좋겠다고 생각했다.
3. Zustand 선택한 이유
보일러플레이트 코드가 복잡한 redux를 제외한 독보적인 다운로드 수와 성장세
hook을 수단으로 상태를 이용하며 랜더링을 일으키지 않고 컴포넌트에 적용함
4. Zustand란?
A small, fast and scalable bearbones state-management solution using simplified flux principles.
작은 단위로 사용되며 빠르고 redux보다 조금 더 심플한 flux 패턴의 구현체
5. Flex 패턴이란?
기존 리액트의 MVC 패턴
단점 : 상위 컴포넌트의 상태를 하위 컴포넌트에 전달하기 위해 중간 컴포넌트에 상태를 전달/선언 해야함
Flux 패턴
사용자 인터렉션을 기반으로 action을 만들고 action을 dispatcher에 전달해 store의 데이터를 변경 후 view에 반영하는 단방향의 흐름이며, view에서 새로운 변경이 생기면 다시 이 순서대로 실행됨
[2] 적용하기
1. 어디에 필요한가? - 사례 1개만 ㅎㅎ
다음은 유저가 사이트에 처음으로 들어와서 검색창에 자신이 찾고자하는 옷을 입력하는 페이지다. 여기서 검색창에 사용자가 입력한 내용은 최초요구사항 (query)이다.
다음 화면의 좌측에는 유저의 최초요구사항을 충족하는 상품 목록을, 우측에는 특정 상품을 선택하고 이에 대해 질의응답을 할 수 있는 채팅칸이다. 채팅칸 상단에는 유저가 어떤 최초요구사항을 기반으로 상품 탐색과 질의응답이 진행되는지 보여줘야 한다.
또 유저가 채팅칸에서 둘러보면서 선택한 상품이 최초요구사항과 매핑되어 서버 디비에서 저장되어야 한다.
최초요구사항 (query)가 반복적으로 이용되면서 이 값을 얻기 위해서 거쳐야하는 중간 컴포넌트가 많아지면서 props drilling이 우려되어 상태관리에서 관리해야하는 값으로 선정하였다.
2. Zustand 설치
npm i zustand
3. Store 선언
// query.tsx
query: 초기 상태 값
setQuery: 인자로 전달 받은 string형태의 input을 query 값으로 설정하는 함수
resetQuery: 현재 query 값을 초기 상태로 초기화 하는 함수
set을 이용하면 간단하게 작성할 수 있다!!
4. store 내보내기
// query.tsx
useQuery: query를 다른 곳에서 사용할 수 있도록 query를 훅 형태로 내보내냄
5. store 불러와 사용하기
다음과 같이 store에서 생성한 useQuery를 불러와 query를 사용할 수 있다.
6. Devtools 사용해 상태 debugging 확인하기
Zustand는 middleware로 devtools를 지원하고 있다. 여기서 다운받아서 크롬 확장프로그램을 우선 설치한다.
그리고 코드에서 zustand/middleware에서 devtools를 import 해와서 set으로 작성한 부분을 devtools( ) 안에 들어가게 감싸주면 끝이다!!!
검색창에 최초요구사항(query)를 입력하고 해당 크롬 익스텐션을 켜면 그 값이 zustand의 query 값에 잘 들어간 것을 확인할 수 있다!!
[3] 개선하기
불필요한 렌더링을 줄여보자
문제 상황 1
다음과 같이 query값을 사용하기 위해 QueryStore 전체를 구독하고 내보내고 가져와 사용하면 불필요한 렌더링이 발생한다. 가져온 것 중에 일부만 새 객체를 반환하고 나머지는 내용이 동일해도 모두 변경되었다고 간주하기 때문이다.
해결 방안 1: custom hooks만 내보내기
사용할 query만 선택해 개별적 selector로 분리해서 내보낸다.
해결 방안 2: shallow 이용
shallow를 사용해 zustand의 state 값을 비교할 수 있다.
문제 해결을 위한 선택:
매번 전체 store에서 사용할 것을 dot notation으로 가져오고 shallow를 추가하기보다는 사용할 state만 개별적으로 선택해 내보내고 기존의 다른 훅 코드와 일관성있게 작성할 수 있는 해결 방안 1을 적용했다.
문제 상황 2
setQuery와 resetQuery는 엄연히 state가 아니라 store 값을 업데이트하는 action이다. 정적이고 절대 변하지 않는 함수(객체)이기 때문이다.
해결 방안 1 : state와 action 분리
스토어에서 setQuery와 resetQuery를 별도의 객체인 action으로 구성하여 성능에 영향을 주지 않도록 하였다. 또 action만 useQueryActions 훅으로 내보내 사용할 수 있도록 하였다.