<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>elin.log</title>
        <link>https://velog.io/</link>
        <description>아자아자!</description>
        <lastBuildDate>Fri, 02 Jul 2021 01:56:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. elin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/elin_me" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[React-Query 도입기]]></title>
            <link>https://velog.io/@elin_me/React-Query-%EB%8F%84%EC%9E%85%EA%B8%B0</link>
            <guid>https://velog.io/@elin_me/React-Query-%EB%8F%84%EC%9E%85%EA%B8%B0</guid>
            <pubDate>Fri, 02 Jul 2021 01:56:40 GMT</pubDate>
            <description><![CDATA[<p> 현재 개발하고 있는 서비스는 state관리를 위해 Redux + Redux-saga + Redux-Toolkit(RTK) 을 사용하고 있었다. 이 조합을 사용하는데에 있어서 익숙해 지다보니 크게 불편함은 없었지만 최근에 몇가지 단점이 제기되었다.</p>
<h4 id="📌--실제-서버-데이터-외에-loading-flag-error-flag-가-많다-🧐">📌  실제 서버 데이터 외에 Loading Flag, Error Flag 가 많다. 🧐</h4>
<ul>
<li><p>서버로부터 데이터를 가져오는중 (GET/POST/DELETE등 요청을 했는데 아직 response가 도착하기 전일 경우) 이라는 것을 UI에 Loader로 보여줘야 하기 때문에 필요했다. </p>
</li>
<li><p>Error 가 났으면 노티를 준다던지 page를 이동한다던지 하는 로직을 구현하기 위해 필요했다. </p>
</li>
</ul>
<h4 id="📌--서버와의-통신--데이터-가공을-위해-작성하는-똑같은-모양의-코드가-너무-많다-🤭">📌  서버와의 통신 + 데이터 가공을 위해 작성하는 똑같은 모양의 코드가 너무 많다. 🤭</h4>
<ul>
<li><strong><em>&quot;API 호출을 했는지 (즉, Action이 발생했는지) watch하고 있다가 -&gt; API 호출 -&gt; response 기다리는 중.. -&gt; 완료!&quot;</em></strong> 이런 반복되는 동작들을 처리하고 데이터를 가공하는데에 쓰이는 똑같은 모양의 코드가 너무 많았다. </li>
</ul>
</br>


<p> 이러한 단점으로 인해 Redux+Redux-saga를 대체할 새로운 라이브러리로 떠오른게 React-Query였다. </br>
 React-Query가 잘하는 것은 서버 상태를 캐싱하는 것이다. </br> 이 외에도 Devtool을 제공, API  call -&gt; 간단한 후처리 코드 작성, Options으로 Polling,Pagination 지원 등 다양한 장점이 있다. </p>
</br>
</br>


<p><span style="color:purple">*<em>이 포스트에서는 React-Query 를 도입하면서 겪었던 이슈들을 정리해보려고 한다. *</em></span></p>
<h1 id="react-query">React-Query</h1>
<p><a href="https://react-query.tanstack.com/">React-Query 공식 문서</a>
</br></p>
<p>React-Query란 공식 문서에 의하면 <strong>&#39;Fetch, cache and update data in your React and React Native applications all without touching any &quot;global state&quot;.&#39;</strong> 이다. 
즉, <span style="color:green"><strong>&#39;React Application에서 서버 상태를 가져오고, 캐시하고, 동기화하고, 업데이트하는 작업을 수월하게 해주는 라이브러리&#39;</strong></span> 이다. </p>
<p>React-Query를 사용해 서버 상태를 가져오기 위해서는 <strong>useQuery</strong>를 사용하면 된다.
만약 서버의 data를 수정하고자 한다면 <strong>useMutation</strong> 을 사용한다. 
요약하면 <strong>R</strong> ( Read ) 은 <strong>useQuery</strong> , <strong>CUD</strong> ( Create, Update, Delete )는 <strong>useMutation</strong>을 사용하면 된다. </p>
<p>자세한 사용법은 React-Query 공식 문서의 가이드 문서를 통해 확인할 수 있다. ( <a href="https://react-query.tanstack.com/guides/queries">React-Query 공식 가이드 문서 </a> )
</br></p>
<p>이 포스트는 React-Query 사용법을 소개하는 포스트는 아니고 실제 적용하면서 겪었던 것을 기록하고 React-Query를 도입하고자 하는 또 다른 누군가에게 참고가 되었으면 해서 적게 되었다. </p>
<h2 id="react-query를-도입하면서-겪었던-이슈들">React-Query를 도입하면서 겪었던 이슈들</h2>
<h3 id="1️⃣-refetch-가-된-후-loading-상태는-false-이다">1️⃣ refetch 가 된 후 loading 상태는 false 이다.</h3>
<p>useQuery의 return 값으로 API의 fetch 상태를 얻어올 수 있다.</p>
<pre><code class="language-javascript">  const { isLoading, error, data, isFetching } = useQuery(&quot;repoData&quot;, () =&gt;
    fetch(
      &quot;https://api.github.com/repos/tannerlinsley/react-query&quot;
    ).then((res) =&gt; res.json())
  );</code></pre>
<p>fetch를 아직 완료하지 않은 상태일 때, UI에 Loader를 그려주고자 isLoading을 사용할 수 있을 것이다. 
❗️하지만 주의해야할 점이 있다❗️
isLoading === true의 조건은 <strong>&#39;캐시된 data가 없고 isFetching === true 일 때&#39;</strong> 이다. 
즉, refetch 가 된 query 일 경우 isLoading 이 false 라는 얘기이다. 
만약 의도적으로 refetch 시에도 Loader를 그려주고 싶다면 isLoading이 아닌 isFetching을 사용해야 한다. </p>
<h3 id="2️⃣-usemutation-사용시-서버의-response를-받고-싶을-땐">2️⃣ useMutation 사용시 서버의 response를 받고 싶을 땐?</h3>
<p>useQuery를 사용하면 server의 reponse를 useQuery의 return 값인 data에서 받아올 수 있다. </p>
<pre><code class="language-javascript">  const fetchTodoList = () =&gt; {
    const { data } = await axios.get(
      &quot;https://...&quot;
    );
    return data;
  }

  const { data } = useQuery(&quot;todos&quot;, fetchTodoList);</code></pre>
<p>하지만 useMutation의 경우, CUD에 해당하는 API 콜 후, 서버의 response를 data에서 받아올 수 없다. ( 성공/실패 여부는 당연히 알 수 있다! )</p>
<ul>
<li><p><strong>const { data: todoList } = useQuery(&#39;todos&#39;, fetchTodoList);</strong> ⭕️</p>
<ul>
<li>todoList는 fetchTodoList의 결과값</li>
</ul>
</li>
<li><p><strong>const { data: todo } = useMutation(postTodo);</strong> ❌</p>
<ul>
<li>API 콜이 성공하더라도 undefined 혹은 unknown</li>
</ul>
</li>
</ul>
<p>React-Query 공식 문서에서도 useMutation의 return 값인 data의 타입은 &#39;undefined | unknown&#39; 으로 명시되어 있다. 만약 useMutation 사용시 response 가 필요한 경우라면 mutateAsync 로 얻어올 수 있다. 
mutateAsync는 Promise를 return 하게 되고 Promise result로 response를 가져올 수 있다. </p>
<pre><code class="language-javascript">const postTodo = (todo) =&gt; { 
  axios.post(&#39;/api/data&#39;, { todo }) 
}
const createTodo = useMutation(postTodo); 

createTodo.mutateAsync(todo).then((data) =&gt; {
   console.log(data);
   // console로 찍은 data가 서버의 response 값입니다.
});</code></pre>
<h3 id="3️⃣-useismutating-vs-queryclientismutating">3️⃣ useIsMutating vs queryClient.isMutating</h3>
<p>React-Query의 QueryClient 는 캐시를 이용해 여러 기능을 사용할 수 있게 해준다. 
그 중 하나인 queryClient.isMutating method는 현재 mutating 중 (진행 중..) 인 mutation의 수를 반환한다.</p>
<pre><code class="language-javascript">import { QueryClient } from &#39;react-query&#39;

 const queryClient = new QueryClient();
 const isMutating = queryClient.isMutating();</code></pre>
<p>useIsMutating 이라는 hook 역시 현재 mutating 중인 mutation의 수를 반환한다. </p>
<pre><code class="language-javascript"> import { useIsFetching } from &#39;react-query&#39;
 // How many queries are fetching?
 const isFetching = useIsFetching()</code></pre>
<p>얼핏 보면 두 가지 모두 같은 기능을 하는 것 처럼 보인다. 하지만 차이점이 있기 때문에 적절하게 사용하는 것이 필요하다.</br>
🟠  <strong>useIsMutating 같은 경우에는 component에서 값의 업데이트를 알 수 있다.</strong> *</p>
<ul>
<li>useIsMutating 내부적으로 MutationCache를 subscribe 하고 있기 때문에 mutating 중인 mutation 수에 변화가 있으면 component 에서 감지할 수가 있는 것이다. *<em>당연하게도 subscribe 하고 있던 component ( useMutating을 사용하던 component )가 unmount 되는 순간 unsubscribe 를 한다 *</em></li>
</ul>
</br>
예를 들어보겠다. 

<p>TodoList 를 관리하는 페이지가 있다고 해보자.
이 TodoList에 새로운 Todo를 추가하기 위해서는 &#39;Todo 추가하기&#39;라는 모달을 통해 추가하면 된다. 
( 임의로 만든 매우 이상한 모달이지만 어쨌든 이 모달로 예를 들겠다.😅) </p>
<p><img src="https://images.velog.io/images/elin_me/post/6418bcf1-a4c9-4e54-a86a-d8728f970c1d/image.png" alt=""></p>
<p>추가 할 일 입력 후 &#39;추가하기&#39; 버튼을 누르고 POST 요청이 성공하면 모달이 unmount 되고 TodoList에서는 방금 추가한 할 일이 추가되어 있어야 한다.</p>
<pre><code class="language-javascript">createTodo.mutate(todo, { 
  onSuccess: () =&gt; closeModal(); // 모달을 unmount 하는 함수
  onSettled: () =&gt; queryClient.refetchQueries(&#39;todos&#39;) // 완료 후 todoList refetch
});</code></pre>
<p>TodoList의 코드 내부에는 useIsMutating 을 사용해 POST 요청이 mutating 중인지 확인하고 Loader를 돌리는 로직이 존재한다고 가정한다. 그런데 위와 같이 로직을 짜면  무한으로 Loader가 보이는 현상이 발생할 수 있다. </p>
<p>❓ 그 이유는?</p>
<ul>
<li>useMutation을 통해 POST 요청을 하던 &#39;Todo  추가하기&#39; 모달이 onSettled가 완료되기 이전에 unmount 되었기 때문이다. 모달이 unmount 된 순간 unsubscribe를 할 것이고 isMutating 값이 업데이트되지 않을 수 있다. </li>
</ul>
<p>이런 실수를 방지하기 위해서는 onSuccess/onError/onSettled 내부에 unmount 하는 로직은 넣지 않는 것이 좋을 것 같다.</p>
<p>반면 queryClient.isMutating을 사용하면 바뀐 mutating값을 조회할 수 있다. 하지만  useIsMutating과 다르게 업데이트 감지는 안되기 때문에 주의해야 한다. </p>
<h3 id="4️⃣-redux를-사용해-관리했던-서버-상태가-아닌-global-state는">4️⃣ redux를 사용해 관리했던 서버 상태가 아닌 global state는?</h3>
<p> React-Query는 서버 상태를 관리하는데에 유용한 라이브러리이다 . 하지만 서버 상태가 아닌 global state 를 관리하기 위해서는 다른 라이브러리를 함께 사용하는 것도 좋을 것 같다. 
 나 역시 global state 를 관리할 수 있는 다른 라이브러리를 둘러보다가 constate를 발견하게 되었다. 실제로 도입하지는 않았지만 공부했던 내용에 대해서 다음 포스트에 정리해보도록 하겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Code Splitting]]></title>
            <link>https://velog.io/@elin_me/code-splitting-lls7eaea</link>
            <guid>https://velog.io/@elin_me/code-splitting-lls7eaea</guid>
            <pubDate>Fri, 18 Jun 2021 07:28:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/elin_me/post/6eae3eee-a4e7-4ee1-bb59-9cf81056f9d3/1_TIcRAC_SRp3zxhuTyfOHJw.png" alt=""></p>
<h4 id="react-코드-분할-공식-문서"><a href="https://ko.reactjs.org/docs/code-splitting.html">React 코드 분할 공식 문서 </a></h4>
<p>  업무를 진행하면서 자주 쓰이는 모듈이 중복으로 들어가있는 chunk나 너무 작은 chunk 들을 합치는, 혹은 너무 큰 chunk를 나누는 <strong>chunking 최적화</strong>를 해야만 했다. 사실 나는 chunking 최적화를 해본 적이 없고 넘나 생소해서 어떻게 해야하지??? 물음표만 가득했다. 다행히 먼저 작업해주신 팀원이 있었고 그 코드를 참고해서 작업을 진행하면 되었다.</p>
<p> 그런데!! webpack-analyzer 로 보니 내 결과물은 합칠게 할 게 없었다(?) 좋았다는 뜻이 아니라 그냥 chunk 파일 하나로 구성되어 있었다는 얘기이다. code splitting 을 하지 않았기 때문에 당연한 결과 였다. 
 그래서 나는</p>
<blockquote>
<p>*<em>1. code splitting 적용
2. chunking 최적화 *</em></p>
</blockquote>
<p>이 순서대로 진행했다. 이 포스트에서는 code splitting을 어떻게 했는지 적어보려고 한다. </p>
<h1 id="code-splitting-이란">Code Splitting 이란?</h1>
<p>React 공식 문서에 따르면 Code Splitting은 
<strong><em>&#39;런타임에 여러 번들을 동적으로 만들고 불러오는 것으로 Webpack, Rollup과 Browserify (factor-bundle) 같은 번들러가 지원하는 기능</em>&#39;</strong>이다. </p>
<p>여기에서 번들은 <strong><em>&#39;Webpack, Rollup 과 같은 툴을 사용해 여러 파일을 하나로 병합한 파일&#39;</em></strong> 을 의미한다.</p>
<p>앱의 사이즈가 커질 수록 당연히 번들의 사이즈도 커지게 된다. 번들 사이즈가 커지게 되면 첫 페이지 진입시 로딩이 늦게 되는 아주 불편한 경험을 하게 될 수도 있다. 
이에 대한 해결책이 바로 &#39;Code Splitting&#39; 이다.</p>
<p>*<em>Code Splitting은 말그대로 코드를 분할하는 것이며 각 시점에 필요한 모듈만 로딩 ( lazy-load ) 되도록 하여서 성능을 향상 시킬 수 있다. *</em></p>
<p>code splitting 을 적용하는 방법으로는 대표적으로 두 가지가 있다.  </p>
<blockquote>
<p>*<em>1. 기본적으로 React에서 제공하는 React.lazy 를 사용한다.
2. code splitting을 지원하는 라이브러리를 사용한다.  *</em></p>
</blockquote>
<p>나는 그중에서 2번을 택했고 여러 라이브러리들 중에 &#39;@loadable/component&#39; 를 사용하였다. </p>
<h1 id="loadable-component-라이브러리">loadable-component 라이브러리</h1>
<p><a href="https://github.com/gregberge/loadable-components/" title="link"><img src="https://raw.githubusercontent.com/gregberge/loadable-components/master/resources/loadable-components.png" alt="image"></a></p>
<p>가장 먼저 Loadable Component의 사용을 위해 패키지를 설치해야 한다. </p>
<pre><code>npm install @loadable/component</code></pre><p>yarn을  사용한다면,</p>
<pre><code>yarn add @loadable/component</code></pre><p>Loadable Component 의 사용법은 아주 간단하다. </p>
<pre><code>import loadable from &#39;@loadable/component&#39;
const OtherComponent = loadable(() =&gt; import(&#39;./OtherComponent&#39;))
function MyComponent() {
  return (
    &lt;div&gt;
      &lt;OtherComponent /&gt;
    &lt;/div&gt;
  )
}</code></pre><p>React Component의 동적 로딩을 위해서  Code Splitting을 위해서 dynamic import() 구문을 사용한다. dynamic import 를 사용하면 런타임에 필요한 모듈만 import 할 수 있다. </p>
<p>나는 페이지 전환이 이루어지는 화면, 그리고 Modal 화면에 Code Splitting을 적용하였다. React 공식 문서의 Route-based code splitting 의 코드를 가져와 예시를 들어보겠다. </p>
<pre><code>import React from &#39;react&#39;;
import loadable from &#39;@loadable/component&#39;;
import { BrowserRouter as Router, Route, Switch } from &#39;react-router-dom&#39;;

const Home = loadable(() =&gt; import(&#39;./routes/Home&#39;));
const About = loadable(() =&gt; import(&#39;./routes/About&#39;));

const App = () =&gt; (
  &lt;Router&gt;
     &lt;Switch&gt;
       &lt;Route exact path=&quot;/&quot; component={Home}/&gt;
       &lt;Route path=&quot;/about&quot; component={About}/&gt;
     &lt;/Switch&gt;
  &lt;/Router&gt;
);</code></pre><p>Home 컴포넌트와 About 컴포넌트를 import 하고 Route 기반으로 Code Splitting을 하는 코드이다. </p>
<p>이런 식으로 Code Splitting을 적용한 결과, 아래와 같이 파일이 나눠지는 것을 확인할 수 있었다. </p>
<h3 id="code-splitting-적용전">code splitting 적용전</h3>
<p><img src="https://images.velog.io/images/elin_me/post/5cd6f3ea-432d-4646-8edb-ab1586a15d7c/before_splitting.png" alt=""></p>
<h3 id="code-splitting-적용후">code splitting 적용후</h3>
<p><img src="https://images.velog.io/images/elin_me/post/b4f4d6fd-0182-46f8-9acb-b762774bcfdf/after_splitting.png" alt=""></p>
<p>이제 추가적으로 해야할 일은 이렇게 분할된 chunk 파일들을 적절히 합치거나 나누는 일이다. 이 작업이 바로 제일 처음 언급했던 &#39;chunking 최적화&#39; 이다. 기회가 된다면 chunking 최적화에 대해서도 기록해보도록 하겠다. </p>
]]></description>
        </item>
    </channel>
</rss>