-
11/16 TIL | React 상태 관리 라이브러리 useStore-ts! 마이크로스토어로 가는 길🚶🏻♀️📝 기록/매일의 기록 2022. 11. 16. 19:11
이번 주에 React 상태 관리를 배우는 주간! useStore라는 Flux 패턴의 Store를 직접 만들어 사용하는 방식으로 강의가 진행되었는데, 이번 FEConf 2022에 홀맨님이 발표하신 useStore-ts의 개념을 가지고 강의에서는 조금 더 단순하게 제작을 하는 방식으로 진행되었다.
👇 FEConf 2022 홀맨님 세션에 대해서 기록했던 10월 8일의 TIL 보러 가기
오늘 열린 수요 지식회에서는 아샬님이 useStore-ts 라이브러리의 활용 방법을 설명해주셨다. 참고로 useStore-ts는 React 상태 관리 라이브러리로, 자세한 설명은 아샬님의 트윗으로 대체한다! (참고로 이 트윗에서는 TypeScript 전용이라 적혀 있지만 지금은 JavaScript까지 지원한다고 한다!)
🎏 전반적인 흐름
📂 src > App.jsx
import { useEffect } from 'react'; import Posts from 'components/Posts'; import usePostStore from 'hooks/usePostStore'; export default function App() { const [, store] = usePostStore(); // 1️⃣ useEffect(() => { store.fetchPosts(); // 2️⃣ }, []); return ( <Posts /> ); }
1️⃣ 우선 store를 usePostStore로부터 가져온다.
2️⃣ store에 있는 fetchPosts 메서드를 사용해 페이지 최초 렌더링 시 posts를 불러온다.
📂 src > hooks > usePostStore.jsx
import { useStore } from 'usestore-ts'; // 1️⃣ import PostStore from 'stores/PostStore'; const postStore = new PostStore(); export default function usePostStore() { return useStore(postStore); // 2️⃣ }
1️⃣ & 2️⃣ useStore 라이브러리에서 제공하는 useStore를 사용하여 postStore를 만들어준다.
📂 src > components > Posts.jsx
import usePostStore from 'hooks/usePostStore'; export default function Posts() { const [{ posts }] = usePostStore(); // 1️⃣ if (!posts.length) { return ( <p>게시물이 없습니다</p> ); } return ( <ul> {posts.map((post) => ( <li key={post.id}> <div> {post.title} by {post.author} </div> <div> {post.body} </div> </li> ))} </ul> ); }
1️⃣ PostStore에서 가지고 있는 posts를 가져온다.
이때 만약 posts가 없으면 게시물이 없다는 내용을 담은 태그를 반환하고, 있으면 map을 돌아 posts를 그려준다.
📂 src > stores > PostStore.js
import { Store, Action } from 'usestore-ts'; // 1️⃣ import { apiService } from 'services/ApiService'; @Store() // 2️⃣ class PostStore { posts = []; async fetchPosts() { const posts = await apiService.fetchPosts(); this.setPosts(posts); } @Action() // 3️⃣ setPosts(posts) { this.posts = posts; } } export default PostStore;
1️⃣ useStore에서 제공하는 Store와 Action 데코레이터를 가져온다. (참고로 데코레이터는 TypeScript에서 지원하는 일종의 선언 기능으로 Java의 어노테이션이랑 비스무리한 것이라고 생각하면 된다.)
2️⃣ 이때 PostStore 클래스에 @Store() 데코레이터로 Store임을 선언한다.
3️⃣ 이때 setPosts메서드에 @Action() 데코레이터로 Action임을 선언한다.
💡 다만 이때 왜 Action 데코레이터를 fetchPosts가 아닌 setPosts 메서드에 선언하였는지 의문스러울 수도 있다! 이는 fetchPosts가 비동기 함수이기 때문인데, async는 Promise 객체를 반환한다. 확실한 처리를 보장하기 위해서 setPosts에 Action 데코레이터를 선언하고 posts를 불러온 후 setPosts를 처리하는 것이다.
추가로 데코레이터인 Store와 Action이 어떻게 구현되어 있는지 살펴보면 아래와 같다.
📂 useStore-ts > src > decorators.ts (🔗 Github 링크)
import { STORE_GLUE_PROPERTY_KEY } from './contants'; import StoreGlue from './StoreGlue'; type Klass = { new (...args: any[]): {} }; export function Store() { // 1️⃣ return function decorator<T extends Klass>(klass: T) { return class extends klass { constructor(...args: any[]) { super(...args); const glue = new StoreGlue(this); Reflect.set(this, STORE_GLUE_PROPERTY_KEY, glue); glue.update(this); } }; }; } export function Action() { // 2️⃣ return ( target: object, propertyKey: string, descriptor: PropertyDescriptor, ) => { const method = descriptor.value; descriptor.value = function decorator(...args: unknown[]) { const returnValue = method.apply(this, args); Reflect.get(this, STORE_GLUE_PROPERTY_KEY).update(this); return returnValue; }; }; }
1️⃣ Store는 glue 인스턴스를 만들어 this의 STORE_GLUE_PROPERTY_KEY에 glue를 할당하고 glue를 업데이트하는 로직을 가지고 있다.
2️⃣ Action은 STORE_GLUE_PROPERTY_KEY에 해당하는 프로퍼티를 가져와서 update에 this를 넣어서 실행시키는 로직을 가지고 있다.
⚠️ 중요! Immutable
또한 Store의 속성은 전부 레퍼런스로 변화를 감지하기 때문에 불변성을 유지해야 한다.
추가로 React 18 버전에서 useExternalSync와 같이 사용하게 되면 전체적으로 화면에 보이는 것을 맞추기 위해 퍼포먼스는 조금 떨어질 수 있다고 덧붙이셨다. 그렇기 때문에 Store는 작으면 작을수록 좋다. Store를 최대한의 효율을 낼 수 있게 설계 전략을 잘 짜야한다!
마지막 쯤에는 React 메모이제이션에 대해서도 말씀해주셨는데 이는 내일 TIL로 한번 정리해보려고 한다!(to be continued...)
근 2주 만에 열린 수요 지식회였는데, 너무 유익했던 시간이었다! 더군다나 이번 주차 강의와 맞물려서 느껴지는 게 남달랐고, Redux 쓰면서 고통받았던 지난날이 갑자기 떠오르는데 그때에 비해 상태 관리에 대해서 내가 많이 성장했구나.. 뿌듯하면서도 동시에 이런 설계를 잘 짤 수 있으려면 어떤 노력을 해야 할지에 대해서 고민했던 시간들이었다. 근데 명쾌한 해결책은.. 결국 코딩의 신 아샬님의 흐름을 따라가는 것! 좋은 설계를 많이 보고 익히자!!! 아자아자 화이자~💪
'📝 기록 > 매일의 기록' 카테고리의 다른 글
11/18 TIL | React 성능 최적화 2탄. 참조형 타입 & 얕은 비교 (0) 2022.11.18 11/17 TIL | React 성능 최적화 1탄. React.memo & React.PureComponent (0) 2022.11.17 11/15 TIL | React의 Virtual DOM은 과연 빠른가? (0) 2022.11.15 11/14 TIL | React Virtual DOM의 재조정(Reconsiliation) (0) 2022.11.14 11/13 TIL | Flux 패턴이란? (0) 2022.11.13