<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dylan</title>
    <description>The latest articles on DEV Community by Dylan (@dylanju).</description>
    <link>https://dev.to/dylanju</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F355664%2Fa0b054ec-af1c-47c4-9ef1-c987ab0d7743.jpeg</url>
      <title>DEV Community: Dylan</title>
      <link>https://dev.to/dylanju</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dylanju"/>
    <language>en</language>
    <item>
      <title>[번역] Jest Mocks에 대한 이해</title>
      <dc:creator>Dylan</dc:creator>
      <pubDate>Mon, 22 Feb 2021 02:50:48 +0000</pubDate>
      <link>https://dev.to/dylanju/jest-mocks-18l9</link>
      <guid>https://dev.to/dylanju/jest-mocks-18l9</guid>
      <description>&lt;p&gt;Jest의 Mock 함수를 구현하는 3가지 방법에 대한 설명을 번역한 글입니다. 어느 상황에 어떤 Mock 함수를 써야할지 몰라서 헤매던 와중에 발견했습니다. 원문에 달려있는 &lt;code&gt;공식문서보다 이해하기 잘 쓰여진 글이다&lt;/code&gt; 라는 댓글에 백번 공감하며 번역해보았습니다.&lt;br&gt;
원문은 &lt;a href="https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c"&gt;https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c&lt;/a&gt; 에서 확인하실 수 있습니다.&lt;br&gt;
틀린 부분은 편하게 댓글 달아주시면 감사하겠습니다~!&lt;/p&gt;



&lt;p&gt;Mocking은 테스트를 독립시키기 위해 의존성을 개발자가 컨트롤하고 검사할 수 있는 오브젝트로 변환하는 테크닉입니다. 의존성은 무엇이든 될 수 있지만, 일반적으로 import 하는 모듈입니다.&lt;br&gt;
자바스크립트에는 &lt;a href="https://github.com/testdouble/testdouble.js"&gt;testdouble&lt;/a&gt; 과 &lt;a href="https://sinonjs.org/"&gt;sinon&lt;/a&gt;처럼 훌륭한 mocking 라이브러리가 있고, Jest는 기본적으로 제공하는 기능입니다.&lt;br&gt;
최근에 저는 &lt;a href="https://github.com/facebook/jest/issues?utf8=%E2%9C%93&amp;amp;q=involves%3Arickhanlonii"&gt;Jest의 이슈 트래커를 돕기위해&lt;/a&gt;  Collaborator로서 Jest팀에 참여했습니다. 거기서 많은 이슈들이 Jest에서 &lt;a href="https://github.com/facebook/jest/issues?utf8=%E2%9C%93&amp;amp;q=how+mock+"&gt;어떻게 mocking 하는지에 관한 질문&lt;/a&gt;이라는 것을 깨닫고, 이 것들을 한번에 설명하는 가이드를 작성하기로 했습니다.&lt;br&gt;
우리가 Jest에서 Mocking을 이야기할 때, 일반적으로 의존성을 &lt;a href="https://jestjs.io/docs/en/mock-function-api.html"&gt;Mock Function&lt;/a&gt;으로 대체하는 것에 대해 말합니다. 이 글에서 Mock 함수에 대해 리뷰해보고, 의존성을 대체하는 여러가지 방법으로 deep dive 해보겠습니다.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Mock Function
&lt;/h2&gt;

&lt;p&gt;Mocking의 목적은 우리가 컨트롤할 수 없는 무엇인가를 대체하는 것이기 때문에, 우리가 대체하는 것이 필요로하는 모든 기능을 갖고 있는게 중요합니다.&lt;br&gt;
Mock 함수는 다음 기능을 제공합니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;함수호출 Capture&lt;/li&gt;
&lt;li&gt;Return Value 설정&lt;/li&gt;
&lt;li&gt;구현 변경하기&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mock 함수 인스턴스를 만드는 가장 간단한 방법은 &lt;code&gt;jest.fn()&lt;/code&gt; 을 쓰는 것입니다.&lt;br&gt;
이 것과 &lt;a href="https://jestjs.io/docs/en/expect.html"&gt;Jest Expect&lt;/a&gt;를 쓰면, 함수호출을 Capture해서 쉽게 테스트할 수 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;그리고 Return Value, 구현, Promise Resolution을 바꿀 수도 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Mock 함수가 무엇이고 이 것으로 무엇을 할 수 있는지 알아봤습니다. 이제 어떻게 사용할지 알아봅시다.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  의존성 주입
&lt;/h2&gt;

&lt;p&gt;Mock 함수를 사용하는 일반적인 방법 중 하나는 테스트하려는 함수로 arguments를 직접 전달하는 방식입니다. 이 것은 테스트를 실행시키고, Mock 함수가 어떤 arguments와 어떻게 실행됐는지 assert구문으로 확인해 볼 수 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;이 전략은 견고한 테스트를 만들지만 테스트코드가 의존성주입을 허용하도록 요구합니다. 종종 그럴 수 없는 경우에, 우리는 실제로 존재하는 모듈과 함수를 Mocking 해야 합니다.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  모듈과 함수를 Mocking하기
&lt;/h2&gt;

&lt;p&gt;Jest에서 모듈과 함수를 Mocking 하는 3가지 방법이 있습니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jest.fn: Mock a function&lt;/li&gt;
&lt;li&gt;jest.mock: Mock a module&lt;/li&gt;
&lt;li&gt;jest.spyOn: Spy or mock a function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 것들은 각각의 방식으로 Mock 함수를 만드는데, 어떻게 동작하는지 설명을 하기 위해 다음과 같은 폴더구조로 만들어 보겠습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;이 설정에서는 &lt;code&gt;math.js&lt;/code&gt; 함수를 실제로 호출하지 않고 &lt;code&gt;app.js&lt;/code&gt;를 테스트하면서, 함수가 예상대로 호출되는지 확인하기 위해 Spy를 하는 것이 일반적입니다. 예시들은 진부하지만 &lt;code&gt;math.js&lt;/code&gt; 의 함수들이 복잡한 계산을 하거나 개발자가 피하고싶은 IO를 만드는 요청이라고 상상해주세요.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
  
&lt;h2&gt;
  
  
  jest.fn으로 Mocking 하기
&lt;/h2&gt;

&lt;p&gt;가장 기본적인 전략은 함수를 Mock 함수로 재할당하는 것입니다. 재할당된 함수가 쓰이는 어디서든지 Mock 함수가 원래의 함수 대신 호출될 것입니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;이렇게 Mocking 하는 방식은 몇 가지 이유로 덜 쓰입니다.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jest.mock&lt;/code&gt; 은 자동적으로 모듈의 모든 함수를 Mocking 해줍니다.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jest.spyOn&lt;/code&gt; 도 마찬가지로 모든 함수를 Mocking 해주면서 원래의 함수를 다시 복원할 수도 있습니다.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  jest.mock으로 Mocking 하기
&lt;/h2&gt;

&lt;p&gt;좀 더 일반적인 접근법은 자동적으로 모듈이 exports하는 모든 것들을 Mocking 해주는 &lt;code&gt;jest.mock&lt;/code&gt; 을 쓰는 것입니다. 따라서 &lt;code&gt;jest.mock('./math.js')&lt;/code&gt; 를 해주면 본질적으로 &lt;code&gt;math.js&lt;/code&gt; 를 다음처럼 설정하는 것입니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;여기서부터 모듈이 exports 하는 모든 것들에 Mock 함수 기능을 쓸 수 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;이것은 가장 쉽고 일반적인 Mocking 방법입니다. (Jest의 &lt;code&gt;automock: true&lt;/code&gt; 설정 방식이기도 합니다)&lt;br&gt;
이 전략의 유일한 단점은 모듈의 원래 구현에 접근하기 어렵다는 것입니다. 이런 경우를 대비해 &lt;code&gt;spyOn&lt;/code&gt; 이 있습니다.  &lt;/p&gt;
&lt;h2&gt;
  
  
  jest.spyOn으로 Spy 혹은 Mocking하기
&lt;/h2&gt;

&lt;p&gt;때로 우리는 메소드가 실행되는 것을 지켜보길 원할뿐만 아니라, 기존의 구현은 보존하길 바랍니다. 구현을 Mocking하고 차후에 테스트구문에서 원본을 복원할 수 있습니다.&lt;br&gt;
이 경우에 &lt;code&gt;jest.spyOn&lt;/code&gt; 을 쓸 수 있습니다.&lt;br&gt;
단순히 math 함수에 "Spy"를 호출하고 원본 구현은 그대로 둘 수 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;이것은 실제로 함수를 대체하지 않고, 특정한 사이드 이펙트가 발생하는지 테스트하는 몇몇 시나리오에 유용합니다.&lt;br&gt;
함수를 Mocking하고 다시 원래 구현을 복원할 수도 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Jest는 각각의 테스트 파일이 샌드박스화 되어 있기 때문에, &lt;code&gt;afterAll&lt;/code&gt; 훅을 불필요하게 사용하지 않도록 하는 경우에 유용합니다.&lt;br&gt;
&lt;code&gt;jest.spyOn&lt;/code&gt; 는 기본적으로 &lt;code&gt;jest.fn()&lt;/code&gt; 의 사용에 대한 Sugar(일반적으로 말하는 Syntactic Sugar를 말하는 것 같습니다: 역자 주)라는 것이 키포인트 입니다. 우리는 기존의 구현을 저장하고, Mocking 했다가, 기존 구현을 재할당하는 방식으로 똑같은 목표를 달성할 수 있습니다.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;이 것이 실제로 &lt;code&gt;jest.spyOn&lt;/code&gt; 이 &lt;a href="https://github.com/facebook/jest/blob/e9aa321e0587d0990bd2b5ca5065e84a1aecb2fa/packages/jest-mock/src/index.js#L674-L708"&gt;구현된 방식&lt;/a&gt;입니다.  &lt;/p&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;이 글에서 우리는 Mock 함수가 무엇인지와 모듈과 함수 호출을 트래킹하고, 구현과 return value를 바꾸는 방법을 배웠습니다.&lt;br&gt;
저는 여러분이 Jest Mock을 쉽게 이해하고 고통없이 테스트를 작성하는데 더 많은 시간을 쓸 수 있도록 돕기를 바랍니다. Mocking에 대한 더 많은 정보와 best practice들은 &lt;a href="https://medium.com/@searls"&gt;Justin Searls&lt;/a&gt;에 의해 &lt;a href="https://www.youtube.com/watch?v=Af4M8GMoxi4"&gt;Don't Mock Me&lt;/a&gt; 라고 이름 붙여진 발표와 700장이 넘는 슬라이드를 확인해보세요.&lt;br&gt;
트위터와 스택오버플로우, 디스코드 채널로 무엇이든 물어보세요.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>jest</category>
      <category>testing</category>
      <category>mocking</category>
    </item>
    <item>
      <title>Storybook에서 nextjs 에러가 발생할 때</title>
      <dc:creator>Dylan</dc:creator>
      <pubDate>Sun, 13 Dec 2020 15:00:11 +0000</pubDate>
      <link>https://dev.to/dylanju/storybook-5f3c</link>
      <guid>https://dev.to/dylanju/storybook-5f3c</guid>
      <description>&lt;p&gt;스토리북을 쓰면서 nextjs mock module을 간단하게 구현했던 부분을 공유하려고 합니다. 이번 글은 nextjs에 관련된 내용이지만, 조금만 응용하면 storybook에서 발생하는 대부분의 외부 모듈 의존성 에러를 처리할 수 있지 않을까 생각합니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  문제 발생
&lt;/h2&gt;

&lt;p&gt;Storybook으로 컴포넌트의 UI를 테스트하다 보면, 외부모듈이 적절히 주입되지 않아서 에러가 발생할 때가 있습니다. 예를 들어 &lt;code&gt;next/router&lt;/code&gt;, &lt;code&gt;next/link&lt;/code&gt;, &lt;code&gt;next/image&lt;/code&gt;를 사용하면 storybook에서는 다음과 같은 에러가 발생합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next/router: Uncaught TypeError: Cannot read property 'pathname' of null&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isRoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// next/link: Uncaught TypeError: Cannot read property 'prefetch' of null&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/signup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Sign&lt;/span&gt; &lt;span class="nx"&gt;Up&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&amp;gt;&lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;
&lt;span class="c1"&gt;// next/image: http://localhost:6006/_next/image?url={src}&amp;amp;w=640&amp;amp;q=75 (404 Not Found)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  문제의 원인
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;nextjs&lt;/code&gt;는 redux, i18n, react-router처럼 개발자가 &lt;code&gt;&amp;lt;Provider /&amp;gt;&lt;/code&gt; 를 명시적으로 주입하지 않기 때문에, 적절한 &lt;code&gt;mock module&lt;/code&gt;을 만들어줘야 테스트 러너가 코드를 실행할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nextjs&lt;/code&gt;는 zero-configuration framework를 목표로 하다보니, 렌더링 하는 부분에서 nextjs Provider 주입을 자동으로 처리해주기 때문입니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next/next-server/server/render.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouterContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AmpStateContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ampState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HeadManagerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;headValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LoadableContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reactLoadableModules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LoadableContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/HeadManagerContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/AmpStateContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/RouterContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;반면 &lt;code&gt;storybook&lt;/code&gt;에서는 (당연히) 그런 nextjs의 Provider를 처리해주지 않죠.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// storybook/app/react/src/client/preview/render.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  해결방법
&lt;/h2&gt;

&lt;p&gt;Storybook의 &lt;a href="https://github.com/vercel/next.js/issues/15543#issuecomment-664955766"&gt;decorator를 활용해 Provider를 주입&lt;/a&gt; 해주는 방식도 있지만, 이번에는 조금 더 간단하게 &lt;a href="https://webpack.js.org/configuration/resolve/#resolvealias"&gt;webpack의 module.alias api&lt;/a&gt; 를 이용해 mock module을 만들어 해결해보겠습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// __mocks__/next/router.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;asPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// __mocks__/next/link.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// __mocks__/next/image.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;실제로 nextjs의 모듈 코드가 리턴하는 값과 유사한 type을 리턴해주시면 됩니다. &lt;br&gt;
폴더와 파일이름은 편한대로 설정해도 상관 없습니다. 저는 테스트에 필요한 mock code를 모아놓는 &lt;code&gt;__mocks__&lt;/code&gt; 폴더에 next 모듈과 동일한 방식을 차용했습니다.&lt;/p&gt;

&lt;p&gt;각각의 mock module을 만들어준 뒤에 storybook webpack 설정에 적용해주시면 됩니다. 컴포넌트를 렌더링 할 때, import 하는 모듈의 경로를 위에서 만든 mock module로 바꿔줍니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .storybook/main.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...your config&lt;/span&gt;
  &lt;span class="na"&gt;webpackFinal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../__mocks__/next/router.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../__mocks__/next/link.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../__mocks__/next/image.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이제 storybook을 다시 시작하면 위에서 발생했던 에러는 다시 나타나지 않습니다. 해당 모듈이 제공하는 기능은 동작하지 않겠지만, storybook의 UI 테스팅에는 필요한 기능이 아니기 때문에 테스트에는 영향이 없을 것 입니다.&lt;br&gt;
필요하다면 로깅같은 동작을 덧붙이거나, 다른 모듈 에러가 발생했을 때도 응용할 수 있다고 생각합니다.&lt;/p&gt;

&lt;p&gt;출처&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/63536822/how-to-mock-modules-in-storybooks-stories"&gt;https://stackoverflow.com/questions/63536822/how-to-mock-modules-in-storybooks-stories&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/vercel/next.js"&gt;https://github.com/vercel/next.js&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/storybookjs/storybook"&gt;https://github.com/storybookjs/storybook&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>storybook</category>
      <category>testing</category>
    </item>
    <item>
      <title>A neat way to add Redux computed value by custom hook</title>
      <dc:creator>Dylan</dc:creator>
      <pubDate>Sun, 20 Sep 2020 09:41:40 +0000</pubDate>
      <link>https://dev.to/dylanju/a-neat-way-to-add-redux-computed-value-by-custom-hook-4ea5</link>
      <guid>https://dev.to/dylanju/a-neat-way-to-add-redux-computed-value-by-custom-hook-4ea5</guid>
      <description>&lt;p&gt;Someone who starts &lt;code&gt;react&lt;/code&gt; project usually have to decide a global state library &lt;code&gt;redux&lt;/code&gt; or &lt;code&gt;mobx&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;redux&lt;/code&gt; has a more stars in github than &lt;code&gt;mobx&lt;/code&gt; but &lt;code&gt;mobx&lt;/code&gt; has a unique point with &lt;code&gt;@computed&lt;/code&gt;. It is an easy way to get computed value with javascript decorator pattern. Of course, &lt;code&gt;redux&lt;/code&gt; can do the same thing by using &lt;code&gt;reselect&lt;/code&gt; library. &lt;/p&gt;

&lt;p&gt;As &lt;code&gt;react&lt;/code&gt; implement 'hook', developers can make easily the computed value.&lt;/p&gt;

&lt;p&gt;Assume you have &lt;code&gt;fruitStore&lt;/code&gt; keeping price and discount data fetched by your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FruitStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;grape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This data will be displayed in 3 ways at &lt;code&gt;Tag&lt;/code&gt; Component.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;discounted price&lt;/li&gt;
&lt;li&gt;final billing price to the payment&lt;/li&gt;
&lt;li&gt;readable discount value with '%'&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;for this, you have to calculate as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If have multiple components using these values, you have &lt;code&gt;ctrl + c&lt;/code&gt;, &lt;code&gt;ctrl + v&lt;/code&gt;. It makes it harder to read and re-write code.&lt;br&gt;
You may think the store keeps these all data in redux. But it may force the nested object structure and become more complex as view component incremented.&lt;/p&gt;

&lt;p&gt;Then let's write simple and efficient code with &lt;code&gt;useMemo&lt;/code&gt; and custom hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// custom hook&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useFruitTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSelect&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;fruitStore&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruitStore&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// component&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;AppleTag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFruitTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Apple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This way makes computed value without another library &lt;code&gt;reselect&lt;/code&gt; or &lt;code&gt;mobx&lt;/code&gt;. By using &lt;code&gt;useMemo&lt;/code&gt;, you can use cached value and maintain easily code even if view and fruits incremented. And also, the most important thing I think is that you can distinguish various logic as you want.&lt;/p&gt;

&lt;p&gt;p.s&lt;br&gt;
This article means not that &lt;code&gt;useMemo&lt;/code&gt; can replace &lt;code&gt;reselect&lt;/code&gt; or what. You can read more details from &lt;a href="https://github.com/reduxjs/reselect/issues/386"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>hook</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Redux에 custom hook으로  Computed value 달아주기</title>
      <dc:creator>Dylan</dc:creator>
      <pubDate>Sun, 20 Sep 2020 07:46:07 +0000</pubDate>
      <link>https://dev.to/dylanju/redux-custom-hook-computed-value-29a1</link>
      <guid>https://dev.to/dylanju/redux-custom-hook-computed-value-29a1</guid>
      <description>&lt;p&gt;React를 쓰는 많은 분들이 프로젝트를 시작하기 전 첫 번째로 고민하게 되는 기술스택이 글로벌 상태관리 라이브러리 &lt;code&gt;redux&lt;/code&gt; 와 &lt;code&gt;mobx&lt;/code&gt; 중 어떤 것을 선택할까가 아닐까 합니다.&lt;br&gt;
깃헙 스타수로 보면 리덕스의 인기가 좀 더 많은 것을 알 수 있습니다. 저도 리덕스를 주로 쓰지만 &lt;code&gt;mobx&lt;/code&gt; 의 장점 중 하나는 데코레이터 문법과 computed value 에 있다고 생각합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;computed&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;이런식으로 &lt;code&gt;store&lt;/code&gt;에 데코레이터 문법으로 getter를 달아주는 방식입니다. &lt;code&gt;redux&lt;/code&gt;에는 api가 없지만 &lt;code&gt;reselect&lt;/code&gt; 라는 라이브러리를 통해서 비슷한 방식을 구현할 수 있습니다.&lt;br&gt;
이제 &lt;code&gt;react&lt;/code&gt;에 훅이 도입되면서 추가 라이브러리 없이도 computed value를 쉽게 구현할 수 있게 되었습니다.&lt;/p&gt;

&lt;p&gt;스토어 &lt;code&gt;fruitStore&lt;/code&gt;에 서버로부터 과일의 할인율과 가격 데이터를 받아 저장되어 있다고 해보겠습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FruitStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;grape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;저장된 데이터는 &lt;code&gt;Tag&lt;/code&gt; 컴포넌트에 3가지 방법으로 표현됩니다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;할인된 가격을 표현해줍니다.&lt;/li&gt;
&lt;li&gt;할인이 적용된 최종 결제가격을 보여줍니다.&lt;/li&gt;
&lt;li&gt;할인율을 읽기 쉽게 %로 보여줍니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;데이터를 view에 표현하기 위해서는 항상 아래와 같이 값을 변환해주어야 합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;만약에 위의 데이터를 쓰는 컴포넌트가 여러개라면 각 컴포넌트마다 같은 코드를 복사/붙여넣기 해야 할 것이고 유지보수하기도 점점 어려워질 것입니다.&lt;br&gt;
스토어에서 3가지 데이터를 모두 저장하는 것도 생각해볼 수 있습니다. 하지만 이런 경우에 nested 된 형태로 스토어를 관리해야 될 가능성이 높아지고, view의 형태가 다양해지면 복잡성이 기하급수적으로 커지게 됩니다.&lt;/p&gt;

&lt;p&gt;커스텀훅과 &lt;code&gt;useMemo&lt;/code&gt;를 조합해 computed value를 구현해보겠습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// custom hook&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useFruitTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSelect&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;fruitStore&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruitStore&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// component&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;AppleTag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFruitTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Apple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;discountedPrice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;billingPrice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;이런 방식으로 &lt;code&gt;reselect&lt;/code&gt;나 &lt;code&gt;@computed&lt;/code&gt; 없이도 computed value를 쉽게 만들어 쓸 수 있습니다. &lt;code&gt;useMemo&lt;/code&gt;를 이용해서 캐싱된 값을 쓰기도 쉽고, 과일의 종류가 늘어나거나 대응해야 하는 view가 늘어나더라도 쉽게 처리할 수 있습니다. 무엇보다 관심사별로 로직이 분리되면서 코드를 읽기 쉬워진다는 게 가장 큰 장점인 것 같습니다.&lt;/p&gt;

&lt;p&gt;p.s&lt;br&gt;
&lt;code&gt;useMemo&lt;/code&gt;가 &lt;code&gt;reselect&lt;/code&gt;를 완전히 대체한다는 뜻은 아닙니다. 관련된 내용은 &lt;u&gt;&lt;a href="https://github.com/reduxjs/reselect/issues/386"&gt;여기&lt;/a&gt;&lt;/u&gt; 에 잘 설명되어 있습니다.&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>useRef()가 순수 자바스크립트 객체라는 의미를 곱씹어보기</title>
      <dc:creator>Dylan</dc:creator>
      <pubDate>Mon, 17 Aug 2020 04:45:00 +0000</pubDate>
      <link>https://dev.to/dylanju/useref-3j37</link>
      <guid>https://dev.to/dylanju/useref-3j37</guid>
      <description>&lt;p&gt;리액트에서는 DOM 노드나 React 엘리먼트에 접근하기 위한 방법으로 Ref라는 API를 제공해왔습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CustomTextInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;focusTextInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
          &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
          &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Focus the text input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focusTextInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;일반적으로는 이렇게 DOM에 접근하기 위해 &lt;code&gt;Ref&lt;/code&gt;를 사용해왔습니다.&lt;br&gt;
React 16.8 버전부터 새롭게 등장한 &lt;code&gt;hook&lt;/code&gt;을 이용해 &lt;code&gt;useRef&lt;/code&gt; 라는 API가 공개됐습니다. 이전 버전의 &lt;code&gt;createRef&lt;/code&gt;를 단순히 &lt;code&gt;hook&lt;/code&gt; 으로 처리했다라고만 알고 있었습니다. 그런데 &lt;code&gt;react-redux&lt;/code&gt; 의 &lt;code&gt;useSelector&lt;/code&gt; 소스코드에서 &lt;code&gt;useRef&lt;/code&gt; 를 적극적으로 사용하는 것을 보고 의문이 생겼습니다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EBsdy_bt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x883dh2aw6q9ny1drs9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EBsdy_bt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x883dh2aw6q9ny1drs9w.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
공식문서에 설명된 목적 (DOM 노드나 React 엘리먼트에 접근하기 위한 방법)이 아니라 일종의 변수를 관리하기 위한 목적으로 사용하고 있었습니다.&lt;br&gt;
&lt;code&gt;react-redux&lt;/code&gt; 팀이 공식문서에서 벗어난 방법을 쓸 것 같지는 않아서 기존에 봐왔던 &lt;code&gt;createRef&lt;/code&gt; 가 아니라 &lt;code&gt;useRef&lt;/code&gt; 문서를 찾아보았습니다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;useRef() is useful for more than the ref attribute. It's handy for keeping any mutable value around similar to how you'd use instance fields in classes.&lt;br&gt;
The useRef() Hook isn't just for DOM refs. The "ref" object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class.&lt;/p&gt;

&lt;p&gt;This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.&lt;br&gt;
Keep in mind that useRef doesn't notify you when its content changes. Mutating the .current property doesn't cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;useRef()&lt;/code&gt; 는 DOM 뿐만이 아니라 어떤 값이든 저장할 수 있는 일반적인 자바스크립트 객체라는 표현을 찾을 수 있었습니다. 또한 부연설명으로 매번 렌더링할 때 동일한 객체를 제공한다는 점과 값이 변경될 때 리렌더링이 발생하지 않는다는 점도 써있었습니다. 만약에 DOM 노드에 ref 를 더하거나 제거할때 어떤 코드를 실행시키고 싶다면 &lt;code&gt;uesRef&lt;/code&gt; 가 아니라 기존의 &lt;code&gt;callback ref&lt;/code&gt; 를 사용하라고도 합니다.&lt;/p&gt;



&lt;p&gt;일반적인 프로그래밍 언어는 &lt;code&gt;heap&lt;/code&gt; 영역과 &lt;code&gt;stack&lt;/code&gt; 영역에서 메모리를 관리합니다(code와 data 영역이 있지만 이 글에서는 주제를 벗어나니 생략하겠습니다). &lt;code&gt;stack&lt;/code&gt; 공간은 자바스크립트의 single thread call stack을 말하는 그 stack으로, 함수가 실행될 때 메모리에 할당됐다가 종료되면서 한꺼번에 해제됩니다.&lt;/p&gt;

&lt;p&gt;반면 &lt;code&gt;heap&lt;/code&gt; 은 전역변수와 참조타입의 변수를 할당하고 가비지 컬렉터를 이용해 사용하지 않는(=참조되지 않는) 메모리를 해제시킵니다. 즉 우리가 자바스크립트 객체로 만드는 변수들은 모두 &lt;code&gt;heap&lt;/code&gt; 공간에 할당되었다가 해제됩니다.&lt;/p&gt;



&lt;p&gt;다시 &lt;code&gt;useRef&lt;/code&gt; 의 설명으로 돌아가보겠습니다.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. &lt;code&gt;useRef()&lt;/code&gt; 는 일반적인 자바스크립트 객체입니다.
&lt;/h4&gt;

&lt;p&gt;즉 &lt;code&gt;heap&lt;/code&gt; 영역에 저장되는 변수입니다.&lt;/p&gt;
&lt;h4&gt;
  
  
  2.매번 렌더링할 때 동일한 객체를 제공합니다.
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;heap&lt;/code&gt;에 저장되어 있기 때문에 어플리케이션이 종료되거나 가비지 컬렉팅될 때 까지, 참조할때마다 같은 메모리 값을 가진다고 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;
  
  
  3.값이 변경되어도 리렌더링이 되지 않습니다.
&lt;/h4&gt;

&lt;p&gt;같은 메모리 주소를 갖고있기 때문에 자바스크립트의 &lt;code&gt;===&lt;/code&gt; 연산이 항상 &lt;code&gt;true&lt;/code&gt; 를 반환합니다. 즉 변경사항을 감지할 수 없어서 리렌더링을 하지 않는다는 뜻입니다.&lt;/p&gt;

&lt;p&gt;실제로 &lt;code&gt;useRef&lt;/code&gt; 가 어떻게 만들어져 있는지 코드를 열어보았습니다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NF7SAFtE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s5xnnzf5qbdt2zw32lqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NF7SAFtE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s5xnnzf5qbdt2zw32lqf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
useRef는 &lt;code&gt;resolveDispatcher&lt;/code&gt;로 만들어진 useRef() 값을 return 합니다. 이 &lt;code&gt;resolveDispatcher&lt;/code&gt; 코드를 따라가보면&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j3vJGX9_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tq2g1evfp3qnzfhupn8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j3vJGX9_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tq2g1evfp3qnzfhupn8d.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;resolveDispatcher&lt;/code&gt;는 &lt;code&gt;ReactCurrentDispatcher&lt;/code&gt;로 만들어지고&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MwemmTmF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lxzyf9sfjg56raovp3px.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MwemmTmF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lxzyf9sfjg56raovp3px.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jL5zXyP1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/awhcanmkafpn9z5hi1i3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jL5zXyP1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/awhcanmkafpn9z5hi1i3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;ReactCurrentDispatcher&lt;/code&gt;는 &lt;code&gt;{ current: null }&lt;/code&gt; 이라는 자바스크립트의 plain object 로서, ReactSharedInternal에 저장되어 사용됩니다. 즉 &lt;code&gt;useRef&lt;/code&gt;는 공식문서의 설명 그대로 plain object로 만들어진 &lt;code&gt;hook&lt;/code&gt; 이었습니다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;createRef&lt;/code&gt; 가 잘 동작하는데, React팀은 왜 &lt;code&gt;useRef&lt;/code&gt;를 만들었을까 추측해보았습니다. 함수형 컴포넌트는 인스턴스를 리턴하는 클래스형 컴포넌트와 조금 다르게 작동합니다. 렌더링 될때마다 매번 새로운 변수를 스택에 할당해 값이 초기화되기도 하고, 불필요한 성능낭비를 하게 될 수 있습니다. 클래스 컴포넌트는 인스턴스를 생성해서 렌더링 메소드만 재실행하는 구조였다면, 함수형 컴포넌트는 매번 함수(=함수형 컴포넌트의 렌더링) 를 실행하기 때문입니다.&lt;/p&gt;

&lt;p&gt;함수형 컴포넌트에서 변수를 다루기 쉽게 하기위해 (마치 클래스의 인스턴스 변수처럼) 만들어진 API 입니다. 다른 변수선언 방법과 차이를 비교해보면 다음과 같습니다.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. hook 기반의 &lt;code&gt;useState&lt;/code&gt; 혹은 &lt;code&gt;useContext&lt;/code&gt; 로 선언
&lt;/h4&gt;

&lt;p&gt;이렇게 선언한 변수들은 값이 바뀔때마다 re-rendering을 유발합니다. 렌더링과 상관없는 변수를 선언하기에 적당하지 않습니다.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. 함수형 컴포넌트 내부에 &lt;code&gt;const&lt;/code&gt; 혹은 &lt;code&gt;let&lt;/code&gt; , &lt;code&gt;var&lt;/code&gt; 로 선언
&lt;/h4&gt;

&lt;p&gt;렌더링 될 때마다 값이 초기화 됩니다. 컴포넌트의 생애주기 동안 관리해야하는 변수를 선언하기에 적당하지 않습니다.&lt;/p&gt;
&lt;h4&gt;
  
  
  3. 컴포넌트 바깥에 &lt;code&gt;const&lt;/code&gt; 혹은 &lt;code&gt;let&lt;/code&gt; , &lt;code&gt;var&lt;/code&gt; 로 선언
&lt;/h4&gt;

&lt;p&gt;불필요한 렌더링을 유발하지도 않고, 렌더링될 때 값이 초기화 되지도 않습니다. 하지만 컴포넌트를 재사용하면서 값을 각각 따로 관리하는게 불가능합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;componentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;componentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;이 경우 MyComponent 를 여러번 재사용하더라도 componentId 는 어플리케이션 내에 단 1개만 존재합니다.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. &lt;code&gt;useRef&lt;/code&gt; 를 이용해 선언
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;useRef&lt;/code&gt; 를 통해 선언된 변수는 리렌더링을 유발하지도 않고, 리렌더링될 때도 이전의 값을 기억하고 있으며, 컴포넌트마다 각각의 값을 가질 수 있습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useRef&lt;/code&gt;는 클래스의 인스턴스 프로퍼티와 같다고 생각하시면 됩니다. 컴포넌트 내부에서 관리하는 변수인데, 값이 바뀔 때마다 렌더링이 필요하면 &lt;code&gt;useState&lt;/code&gt; 를 쓰면 되고 아닐 경우 &lt;code&gt;useRef&lt;/code&gt; 를 써야한다고 생각하시면 간단할 것 같습니다.&lt;/p&gt;




&lt;p&gt;출처&lt;br&gt;
리액트 공식문서&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/54620698/whats-the-difference-between-useref-and-createref"&gt;https://stackoverflow.com/questions/54620698/whats-the-difference-between-useref-and-createref&lt;/a&gt;&lt;br&gt;
&lt;a href="https://blog.bitsrc.io/react-useref-and-react-createref-the-difference-afedb9877d0f"&gt;https://blog.bitsrc.io/react-useref-and-react-createref-the-difference-afedb9877d0f&lt;/a&gt;&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/57530446/difference-between-useref-and-normal-variable"&gt;https://stackoverflow.com/questions/57530446/difference-between-useref-and-normal-variable&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.codementor.io/@dhananjaykumar/useref-hook-as-mutable-ref-object-ykb77fwvk"&gt;https://www.codementor.io/@dhananjaykumar/useref-hook-as-mutable-ref-object-ykb77fwvk&lt;/a&gt;&lt;br&gt;
&lt;a href="https://blog.logrocket.com/deep-dive-into-react-fiber-internals/"&gt;https://blog.logrocket.com/deep-dive-into-react-fiber-internals/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;제가 틀린 부분이 있다면 적극적으로 알려주시면 감사하겠습니다^^&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>useref</category>
      <category>hook</category>
    </item>
  </channel>
</rss>
