<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Persistence</title>
    <link>https://developer-alle.tistory.com/</link>
    <description>개발 TIL, 개발 관련 글 업로드 블로그</description>
    <language>ko</language>
    <pubDate>Sat, 9 May 2026 15:51:23 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Alledy</managingEditor>
    <item>
      <title>Java는 설치가 필요하고, Javascript는 설치가 필요하지 않은 이유</title>
      <link>https://developer-alle.tistory.com/458</link>
      <description>&lt;h2&gt;Q. 왜 자바는 JDK, JRE와 같은 것을 설치해야 하고, 자바스크립트는 설치가 필요없는걸까?&lt;/h2&gt;
&lt;p&gt;이것에 대해 이해하려면 일단 Java와 Javascript의 언어 차이를 이해해야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;자바스크립트는 인터프리터 언어이고 자바는 컴파일 언어이다.&lt;ul&gt;
&lt;li&gt;인터프리터 언어와 컴파일 언어의 차이는 무엇일까? &amp;quot;인터프리트&amp;quot;는 사전에 머신코드나 바이트코드로 변경하는 일 없이 &lt;strong&gt;&amp;quot;line by line&amp;quot;&lt;/strong&gt;으로 읽고 실행한다는 뜻이다. &lt;strong&gt;&amp;quot;컴파일&amp;quot;은 런타임 전에 머신코드나 바이트코드로 변경하는 중간 스텝이 한번 더 추가&lt;/strong&gt;되고, 이 컴파일된 코드를 머신이나 가상 머신이 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자바스크립트의 런타임 환경은 웹 브라우저이다. 그리고 웹 브라우저 내에는 JS engine (e.g. 크롬 V8)이 내장되어 있다. 이 엔진이 자바스크립트 코드를 해석, 실행하므로 웹 브라우저가 있다면 별도로 자바스크립트를 다운로드할 필요가 없다.&lt;/li&gt;
&lt;li&gt;컴파일 언어인 자바는 일단 프로그램을 실행하기 위해서는 사전에 컴파일을 해야하고, 이 컴파일을 해줄 컴파일러(javac)가 필요하다. JDK(Java Development Kit)은 컴파일러를 포함하고 있다. 또한 컴파일된 바이트코드를 JVM이라는 가상 머신 위에서 실행시키는데, 이 JVM을 포함하고 있는 것이 JRE(Java Runtime Environment)이다. 이 JVM이 os나 device에 맞게 해석, 실행하므로 java는 플랫폼 중립적으로 사용할 수 있다는 장점이 있다.&lt;ul&gt;
&lt;li&gt;그래서 자바는 JDK, JRE 다운로드가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;보통 자바는 컴파일 언어, 자바스크립트는 인터프리터 언어라고 하기 때문에 그러면 자바스크립트는 컴파일을 안하고 자바는 인터프리트를 안하나? 이분법적으로 생각할 수도 있다. 하지만 그것은 전통적인 구분이고 현대에 와서는 자바스크립트도 자바도 JIT(Just in time) 컴파일러(런타임 컴파일러)를 사용한다. 자바는 jvm에서 바이트코드를 실행할 때 JIT compiler를 사용하거나 인터프리트하여 코드를 실행한다. JIT를 사용하는 이유는 그냥 인터프리트 하는 것보다 성능을 향상시킬 수 있기 때문이다.&lt;/li&gt;
&lt;li&gt;그러므로 인터프리터 언어와 컴파일 언어 구분이 있다고 해서 이분법적으로 해석해서는 안되고, 그 둘의 메인 차이점은 런타임 전에 코드를 컴파일 하는 단계가 있느냐 없느냐 로 봐야한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>인터프리터 언어</category>
      <category>컴파일 언어</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/458</guid>
      <comments>https://developer-alle.tistory.com/458#entry458comment</comments>
      <pubDate>Fri, 6 Oct 2023 23:08:30 +0900</pubDate>
    </item>
    <item>
      <title>[React Navigation] Nested Navigator</title>
      <link>https://developer-alle.tistory.com/457</link>
      <description>&lt;h2&gt;Nested Navigator&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;네비게이터는 own 히스토리를 갖는다.&lt;ul&gt;
&lt;li&gt;뒤로가기를 하면 parent 네비게이터가 있더라도 nested stack 내부의 전 스크린으로 이동한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네비게이터는 own 옵션을 갖는다. title 옵션을 child navigator에서 지정하더라도 parent에게는 영향을 주지 않는다.&lt;/li&gt;
&lt;li&gt;네비게이터는 own param을 갖는다.&lt;ul&gt;
&lt;li&gt;nested 네비게이터 안의 스크린에 패스된 파라미터는 parent나 child 네비게이터의 라우트 파라미터에 접근이 불가능하다. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네비게이터 액션은 현재 네비게이터에 의해 핸들링되고 만약 핸들링 불가능한 경우 버블링된다.&lt;ul&gt;
&lt;li&gt;예를 들어 nested 네비게이터의 첫번째 페이지에 있는 경우 뒤로가기를 하면 부모 네비게이터로 이동한다.&lt;/li&gt;
&lt;li&gt;예를 들어 아래 구조에서 Feed 스크린에서 Messages로 네비게이트를 시도했을 때는 nested 내에서 핸들링이 가능하지만 Settings로 네비게이팅은 핸들링을 못해서 parent 네비게이터가 실행한다.&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Home&lt;/code&gt; (&lt;code&gt;Tab.Navigator&lt;/code&gt;)&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Feed&lt;/code&gt; (&lt;code&gt;Screen&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Messages&lt;/code&gt; (&lt;code&gt;Screen&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Profile&lt;/code&gt; (&lt;code&gt;Screen&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Settings&lt;/code&gt; (&lt;code&gt;Screen&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;child 네비게이터는 parent 네비게이터의 메서드를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;nested 네비게이터는 parent의 이벤트를 받지 않는다.&lt;ul&gt;
&lt;li&gt;예를 들어 stack 네비게이터가 tab 네비게이터 안에 있다고 할 때, tab 네비게이터가 tabPress 이벤트를 emit해도 stack 네비게이터는 이 이벤트를 listen할 수 없다.&lt;/li&gt;
&lt;li&gt;parent로 부터 이벤트를 받으려면 navigation.getParent 를 명시적으로 사용해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;parent 네비게이터의 UI는 child 네비게이터 UI의 상위에 그려진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;네비게이팅&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;parent 네비게이터에서 child 네비게이터로 이동하면 해당 네비게이터의 initial 스크린이 보여지게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;만약 child 네비게이터의 initial 스크린이 아닌 특정 스크린으로 이동하고 싶다면 두번째 파라미터로 스크린을 지정해줘야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;navigate(&amp;#39;Root&amp;#39;, { screen: &amp;#39;Profile&amp;#39;, params: { user: &amp;#39;JANE&amp;#39; } })&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네비게이터에 지정된 이니셜 라우트 렌더링하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네비게이터 내의 스크린으로 이동할 때 이 스크린이 initial 스크린으로 사용되고 네비게이터에 지정된 이니셜 라우트는 무시된다.&lt;/li&gt;
&lt;li&gt;만약 이 이니셜 라우트를 살리고 싶으면 initial 옵션을 false로 주면 된다. 그러면 뒤로 가기를 할 때 이니셜 스크린이 있다면 그리로 이동하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;navigate(&amp;#39;Root&amp;#39;, { screen: &amp;#39;Profile&amp;#39;, initial: false })&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ref&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://reactnavigation.org/docs/nesting-navigators&quot;&gt;https://reactnavigation.org/docs/nesting-navigators&lt;/a&gt;&lt;/p&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <category>중첩된 네비게이션</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/457</guid>
      <comments>https://developer-alle.tistory.com/457#entry457comment</comments>
      <pubDate>Wed, 17 May 2023 21:05:28 +0900</pubDate>
    </item>
    <item>
      <title>[React Native] ScrollView keyboardShouldPersistTaps</title>
      <link>https://developer-alle.tistory.com/456</link>
      <description>&lt;ul&gt;
&lt;li&gt;키보드가 올라와있을 때는 원래는 버튼을 클릭할 수 없다. 키보드가 올라와있을 때 화면을 터치하면 키보드를 dismiss하는 것만 가능하다. &lt;/li&gt;
&lt;li&gt;다만 키보드가 올라와있는 상태에서 버튼을 클릭하게끔 하고 싶다면 ScrollView로 해당 컴포넌트를 감싼 다음, keyboardShouldPersistTaps를 handled로 주면 된다.  &lt;/li&gt;
&lt;li&gt;handled는 ScrollView의 children에서 tap이 있을 때는 키보드를 dismiss시키지 않고 tap을 허용한다.&lt;/li&gt;
&lt;li&gt;다만 ScrollView가 nested돼 있다면 parent ScrollView에도 해당 prop이 동일하게 적용돼있어야 정상 작동한다. &lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/456</guid>
      <comments>https://developer-alle.tistory.com/456#entry456comment</comments>
      <pubDate>Wed, 12 Apr 2023 13:21:50 +0900</pubDate>
    </item>
    <item>
      <title>[http-proxy-middleware] path rewrite, target</title>
      <link>https://developer-alle.tistory.com/455</link>
      <description>&lt;pre&gt;&lt;code&gt;const proxy = require(&amp;#39;http-proxy-middleware&amp;#39;); 

module.exports = function(app) { 
    app.use( proxy(&amp;#39;/posts&amp;#39;, { 
        target:&amp;#39;https://target.domain.com&amp;#39;, 
        changeOrigin: true 
        }) 
    ); 
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;흔히 cors 문제를 해결하기 위해 프록시를 사용할 수 있는데, http-proxy-middleware를 사용해서 위처럼 설정하면 /posts로 시작하는 API는 target 필드에 설정한 서버로 호출하게 된다. 즉 /posts/1 을 호출하면, &lt;a href=&quot;https://target.domain.com/posts/1&quot;&gt;https://target.domain.com/posts/1&lt;/a&gt; 을 호출하게 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const proxy = require(&amp;#39;http-proxy-middleware&amp;#39;); 

module.exports = function(app) { 
    app.use( proxy(&amp;#39;/old-posts&amp;#39;, { 
          pathRewrite: {
            &amp;#39;^/old-posts&amp;#39;: &amp;#39;/new-posts&amp;#39;
        },
        target:&amp;#39;https://target.domain.com&amp;#39;, 
        changeOrigin: true 
        }) 
    ); 
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;pathRewrite 객체는 key에 매핑된 api path를 value로 치환해준다. 위의 경우에는 /old-posts를 호출하면 &lt;a href=&quot;https://target.domain.com/new-posts&quot;&gt;https://target.domain.com/new-posts&lt;/a&gt; 를 호출하게 된다.&lt;/p&gt;
&lt;h2&gt;Ref&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/chimurai/http-proxy-middleware#tldr-&quot;&gt;https://github.com/chimurai/http-proxy-middleware#tldr-&lt;/a&gt;&lt;/p&gt;</description>
      <category>공부일지(TIL)/Web</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/455</guid>
      <comments>https://developer-alle.tistory.com/455#entry455comment</comments>
      <pubDate>Mon, 27 Mar 2023 21:33:33 +0900</pubDate>
    </item>
    <item>
      <title>[React] re-render가 일어나는 경우와 불필요한 리렌더를 줄이는 법</title>
      <link>https://developer-alle.tistory.com/454</link>
      <description>&lt;h2&gt;리렌더: 이미 스크린에 그려진 컴포넌트가 다시 렌더링되는 것&lt;/h2&gt;
&lt;h3&gt;리렌더링은 언제 일어나는가?&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;컴포넌트의 state이 변했을 때, 해당 컴포넌트가 리렌더링됨&lt;/li&gt;
&lt;li&gt;부모가 리렌더링됐을 때, 자식 컴포넌트도 리렌더링됨 (엣지 케이스가 있으나 일반적으로 그러함)&lt;/li&gt;
&lt;li&gt;Context Provider 값이 바뀌었을 때 그 컨텍스트를 사용하는 &lt;strong&gt;모든&lt;/strong&gt; 컴포넌트가 리렌더링됨.&lt;/li&gt;
&lt;li&gt;훅 내부에서 state 변동이 있거나 context 값 변동이 있으면 그 훅을 소비하는 컴포넌트가 리렌더링 됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Parent() {
    const [state, setState] = useState(); // state이 변하면 Parent 컴포넌트 리렌더링됨
    // Parent 컴포넌트가 리렌더링되면 Child 컴포넌트도 리렌더링됨
    return &amp;lt;Child /&amp;gt; 
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;반대로 리렌더링을 유발하지 않는 것은?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;props 변동 (메모이제이션된 컴포넌트는 예외임)&lt;ul&gt;
&lt;li&gt;props가 변동하기 위해서는 부모 컴포넌트의 값이 업데이트되어야 하고 이것이 리렌더링을 유발하고 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 리렌더링되지만 이것은 props 변동이 원인인 것이 아님. &lt;/li&gt;
&lt;li&gt;값의 업데이트가 일어나면서 리렌더링과 props변동 둘 다 일어나는 것뿐. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;컴포넌트 합성으로 리렌더링 방지하기&lt;/h3&gt;
&lt;h4&gt;안티패턴&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트 안에서 새로운 컴포넌트를 생성해서 리턴하기&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Component() {
  // 컴포넌트 안에서 컴포넌트 생성 후 리턴
  function SlowComponent() {
      return &amp;lt;Something /&amp;gt;
  }
  return &amp;lt;SlowComponent /&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
위 케이스는 Component가 리렌더링 될 때마다 SlowComponent가 re-mount되기 때문에 단순한 리렌더링보다 더 느리다. 하지만 굳이 이렇게 코딩하는 경우는 크게 없을 듯. &lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;state를 밑으로 내리기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;state가 변동하면 해당 컴포넌트가 리렌더링되고 모든 Child가 또 리렌더링되기 때문에, state를 국한지을 수 있다면 가급적 부모보다는 낮은 위계에 분리시키는 것이 불필요한 리렌더링을 줄일 수 있다. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// 이 경우 state가 변경되면 children인 VerySlowComponent도 리렌더링된다. 
function Component {
    const [open, setOpen] = useState(false);
    return (
        &amp;lt;Container&amp;gt;
            &amp;lt;Button onClick={() =&amp;gt; {setOpen(true)}} /&amp;gt;
            {isOpen &amp;amp;&amp;amp; &amp;lt;ModalDialog /&amp;gt;}
            &amp;lt;VerySlowComponent /&amp;gt;
        &amp;lt;/Container&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// children의 리렌더링은 parent에게 영향을 주지 않으므로, 이 케이스에서는 VerySlowComponent가 영향받지 않는다. 
function ModalOpenButton() {
    const [open, setOpen] = useState(false);

    return (
        &amp;lt;&amp;gt;
            &amp;lt;Button onClick={() =&amp;gt; {setOpen(true)}} /&amp;gt;
            {isOpen &amp;amp;&amp;amp; &amp;lt;ModalDialog /&amp;gt;}
        &amp;lt;/&amp;gt;
    )
}

function Component {
    return (
        &amp;lt;Container&amp;gt;
            &amp;lt;ModalOpenButton /&amp;gt;
            &amp;lt;VerySlowComponent /&amp;gt;
        &amp;lt;/Container&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Children as prop&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;이것도 state를 내리는 기법과 비슷하다. state를 보다 작은 부분에 캡슐화해서 분리하는 방법이기 때문이다. &lt;/li&gt;
&lt;li&gt;그리고 props가 리렌더링이랑 관련없다는 사실도 기억해야 한다. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Component() {
    const [state, setState] = useState()

    return (
        &amp;lt;div onScroll={(e) =&amp;gt; setState(e)}&amp;gt;
            &amp;lt;VerySlowComponent /&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Scroll({ children }) {
    const [state, setState] = useState()
    return (
        &amp;lt;div onScroll={(e) =&amp;gt; setState(e)}&amp;gt;
            {children}
        &amp;lt;/div&amp;gt;
    )
}

// 리렌더링은 Scroll에서만 일어난다. 이 리렌더링은 props.children인 VerySlowComponent에는 영향을 주지 않는다. 
function Component() {
    return (
        &amp;lt;Scroll&amp;gt;
            &amp;lt;VerySlowComponent /&amp;gt;
        &amp;lt;/Scroll&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Component as props&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;위에와 아주 비슷한 패턴이다. 다만 children이 아닌 props일 뿐. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Component() {
    const [state, setState] = useState()

    return (
        &amp;lt;div onScroll={(e) =&amp;gt; setState(e)}&amp;gt;
            &amp;lt;VerySlowComponentOne /&amp;gt;
            &amp;lt;Something /&amp;gt;
            &amp;lt;VerySlowComponentTwo /&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function Scroll({ left, right }) {
    const [state, setState] = useState()
    return (
        &amp;lt;div onScroll={(e) =&amp;gt; setState(e)}&amp;gt;
            {left}
            &amp;lt;Something /&amp;gt;
            {right}
        &amp;lt;/div&amp;gt;
    )
}

// 리렌더링은 Scroll에서만 일어난다. 이 리렌더링은 props.left, props.right인 VerySlowComponent들에는 영향을 주지 않는다. 
function Component() {
    return (
        &amp;lt;Scroll 
            left={&amp;lt;VerySlowComponentOne /&amp;gt;}
            right={&amp;lt;VerySlowComponentTwo /&amp;gt;}
        /&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;props가 리렌더링에 영향을 주는 예외 케이스는 메모이제이션된 컴포넌트일 경우다. 이 케이스는 다음 편에 마저 작성할 예정. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ref&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.developerway.com/posts/react-re-renders-guide&quot;&gt;https://www.developerway.com/posts/react-re-renders-guide&lt;/a&gt;&lt;/p&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <category>react</category>
      <category>rerender</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/454</guid>
      <comments>https://developer-alle.tistory.com/454#entry454comment</comments>
      <pubDate>Sat, 18 Mar 2023 00:19:34 +0900</pubDate>
    </item>
    <item>
      <title>[React Query] Tracked Query</title>
      <link>https://developer-alle.tistory.com/453</link>
      <description>&lt;pre&gt;&lt;code&gt;export const useTodosQuery = (select) =&amp;gt;
  useQuery({ queryKey: [&amp;#39;todos&amp;#39;], queryFn: fetchTodos, select })
export const useTodosCount = () =&amp;gt; useTodosQuery((data) =&amp;gt; data.length)

function TodosCount() {
  const todosCount = useTodosCount()

  return &amp;lt;div&amp;gt;{todosCount.data}&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 쿼리는 백그라운드에서 refetch할 때마다 두번씩 컴포넌트 리렌더링을 트리거한다. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ status: &amp;#39;success&amp;#39;, data: 2, isFetching: true }
{ status: &amp;#39;success&amp;#39;, data: 2, isFetching: false }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;isFetching의 상태값이 바뀌기 때문이다. &lt;/p&gt;
&lt;p&gt;위와 같은 리렌더링을 피하고 싶다면 notifyOnChangeProps 를 사용할 수 있다.&lt;br&gt;이름에서 알 수 있듯이, 이것이 바뀔 때에만 옵저버에게 inform 하라는 것이다 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;export const useTodosQuery = (select, notifyOnChangeProps) =&amp;gt;
  useQuery({
    queryKey: [&amp;#39;todos&amp;#39;],
    queryFn: fetchTodos,
    select,
    notifyOnChangeProps,
  })
export const useTodosCount = () =&amp;gt;
  useTodosQuery((data) =&amp;gt; data.length, [&amp;#39;data&amp;#39;])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;하지만 위 방식의 단점은 화이트리스트로 관리된다는 점이다.&lt;br&gt;만약 에러가 일어났을 때 리렌더링을 하고 싶으면 &amp;#39;error&amp;#39; 도 넣어줘야 하고, isLoading도 사용하고 싶으면 그것도 넣어줘야 한다. 누락됐을 때 문제가 생길 수 있다. 이게 불필요한 리렌더보다 더 최악이다. &lt;/p&gt;
&lt;p&gt;이것을 해결할 수 있는 것이 있다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      notifyOnChangeProps: &amp;#39;tracked&amp;#39;,
    },
  },
})
function App() {
  return (
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      &amp;lt;Example /&amp;gt;
    &amp;lt;/QueryClientProvider&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;쿼리 클라이언트 설정에서 위처럼 tracked 값을 설정하면 우리가 사용하는 필드에 대해서만 tracking하게 되며, array에 일일이 리스트한 것과 동일한 효과를 낸다. &lt;/p&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/453</guid>
      <comments>https://developer-alle.tistory.com/453#entry453comment</comments>
      <pubDate>Thu, 16 Mar 2023 17:30:48 +0900</pubDate>
    </item>
    <item>
      <title>[React] usePrevious hook</title>
      <link>https://developer-alle.tistory.com/452</link>
      <description>&lt;h2&gt;usePrevious&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const usePrevious = value =&amp;gt; {
    const ref = useRef();
    useEffect(() =&amp;gt; {
        ref.current = value;
    })
    return ref.current;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state&quot;&gt;react docs&lt;/a&gt;에 보면 이런 코드에 대한 힌트를 얻을 수 있다. 이 훅은 전(Previous) state나 props의 값을 저장한다. 하지만 이 코드가 어떻게 전 value를 저장하는 결과를 가져오는 것일까?&lt;/p&gt;
&lt;p&gt;첫 번째 포인트는 ref 값의 변화는 re-render를 트리거하지 않는다는 점이다. 반면 state의 변화는 re-render를 일으킨다. 예를 들어&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const Counter = () =&amp;gt; {
    const ref = useRef(0);
    const [counter, setCounter] = useState(0);

    return (
        &amp;lt;&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; setCounter(counter + 1)}&amp;gt;state counter&amp;lt;/button&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; { ref.current ref.current + 1 }}&amp;gt;ref counter&amp;lt;/button&amp;gt;
            state couter value: {counter}
            ref counter value: {ref.current}
        &amp;lt;/&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 코드에서 아무리 ref counter의 버튼을 눌러서 값을 증가시켜도 ref value의 변화는 리렌더링을 일으키지 않으므로 화면에 보이는 값은 갱신되지 않는다. 만약 그 상태에서 state counter를 증가시키면 리렌더링이 일어나면서 ref counter 값 또한 갱신된다. &lt;/p&gt;
&lt;p&gt;두 번째 포인트는, useEffect는 렌더링이 끝나면 트리거된다는 점이다. &lt;/p&gt;
&lt;p&gt;즉, 초기값이 0인 카운터를 가정했을 때 이런 플로우로 실행된다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트가 처음 마운트하면 렌더링이 끝나고 useEffect는 실행되고(2번째 포인트) 그 콜백에 의해서 ref.current는 초기값 0을 저장할 것이다. &lt;/li&gt;
&lt;li&gt;만약 state가 1로 증가하면, 다시 리렌더링이 되면서 변화된 상태값 1이 화면에 표시된다. 이 리렌더링이 될 당시 ref.current에 저장된 값은 아직 0 이다. 리렌더링이 끝나고 useEffect가 실행되면서 콜백에 의해 변화된 상태값 1을 저장한다. ref 값의 변화는 리렌더링을 일으키지 않으므로 ref.current가 1로 갱신돼도 화면에서는 아무런 변화가 나타나지 않는다. &lt;/li&gt;
&lt;li&gt;즉 state 변화 -&amp;gt; 화면에 리렌더링 (이때 ref value는 여전히 이 전값을 가리킴) -&amp;gt; 리렌더링 후 useEffect가 실행되며 ref.current값을 최신으로 갱신 -&amp;gt; 하지만 이는 리렌더링을 일으키지 않음 -&amp;gt; 반복...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ref&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.developerway.com/posts/implementing-advanced-use-previous-hook&quot;&gt;https://www.developerway.com/posts/implementing-advanced-use-previous-hook&lt;/a&gt;&lt;/p&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <category>usePrevious</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/452</guid>
      <comments>https://developer-alle.tistory.com/452#entry452comment</comments>
      <pubDate>Mon, 13 Feb 2023 13:42:10 +0900</pubDate>
    </item>
    <item>
      <title>[React Query] active하고 stale한 쿼리의 refetch</title>
      <link>https://developer-alle.tistory.com/451</link>
      <description>&lt;h2&gt;상황&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React Native에서 react navigation 스택을 사용함&lt;/li&gt;
&lt;li&gt;퍼널 앞에서 호출한 쿼리를 퍼널 뒤에 새로운 화면에서 호출했는데 refetch하지 않고 기존 데이터를 재사용함.&lt;/li&gt;
&lt;li&gt;캐시타임 내에 호출이 되어서 그런 줄 알고 cacheTime을 0으로 주고 했는데 동일한 현상 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;이유&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일단 cacheTime은 inactive한 쿼리에 대해서만 의미가 있는데, 그 쿼리는 계속해서 active한 쿼리였다. 왜냐하면 스택 네비게이션에서는 계속해서 컴포넌트가 마운트된 상태로 쌓이기 때문이다. &lt;/li&gt;
&lt;li&gt;챰고로 inactive 쿼리는 옵저버가 없는 쿼리를 의미하며, devtool로도 확인할 수 있고 Query Cache를 조회해서 옵저버 필드를 확인해볼 수도 있다.&lt;/li&gt;
&lt;li&gt;stale time은 디폴트가 0이므로 그 쿼리의 상태는 stale하고 active한 상태였다. devtool을 사용하면 확인하기 쉬웠겠지만 현재 react-native-debugger를 사용해서 react query devtool을 사용할 수 없었던 상태였다.&lt;/li&gt;
&lt;li&gt;즉 cacheTime을 0으로 준 것은 어차피 active 쿼리니까 상관이 없었다.&lt;/li&gt;
&lt;li&gt;그리고 프로젝트에서 사용하는 리액트 쿼리의 디폴트 옵션이 refetchOnMount: false 였다.&lt;/li&gt;
&lt;li&gt;그래서 최초 쿼리한 스크린이 계속 마운트되어 있는 한 캐시타임과 관계 없이 refetch되지 않았다.&lt;/li&gt;
&lt;li&gt;뒤로 가기를 눌러 최초 쿼리한 스크린을 언마운트 시킨 뒤 다시 마운트 시키면 이 때는 캐시타임이 지났는지에 따라 refetch를 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ref&lt;/h2&gt;
&lt;p&gt;refetching 조건&lt;br&gt;&lt;a href=&quot;https://velog.io/@apro_xo/react-query-%EC%BA%90%EC%8B%B1feat.-refetch&quot;&gt;https://velog.io/@apro_xo/react-query-%EC%BA%90%EC%8B%B1feat.-refetch&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;important default&lt;br&gt;&lt;a href=&quot;https://tanstack.com/query/latest/docs/react/guides/important-defaults&quot;&gt;https://tanstack.com/query/latest/docs/react/guides/important-defaults&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;동일한 렌더링 주기에서는 같은 요청을 중복해서 보내지 않음 (active and stale query 케이스)&lt;br&gt;&lt;a href=&quot;https://github.com/TanStack/query/discussions/2018#discussioncomment-645456&quot;&gt;https://github.com/TanStack/query/discussions/2018#discussioncomment-645456&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;react navigation 라이프 사이클&lt;br&gt;&lt;a href=&quot;https://reactnavigation.org/docs/3.x/navigation-lifecycle&quot;&gt;https://reactnavigation.org/docs/3.x/navigation-lifecycle&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;옵저버가 없는 쿼리가 inactive 쿼리다&lt;br&gt;&lt;a href=&quot;https://tkdodo.eu/blog/inside-react-query&quot;&gt;https://tkdodo.eu/blog/inside-react-query&lt;/a&gt;&lt;/p&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/451</guid>
      <comments>https://developer-alle.tistory.com/451#entry451comment</comments>
      <pubDate>Wed, 11 Jan 2023 23:55:41 +0900</pubDate>
    </item>
    <item>
      <title>ISR (Incremental Static Regeneration)</title>
      <link>https://developer-alle.tistory.com/450</link>
      <description>&lt;h2&gt;ISR을 알기 전 Next.js 선수지식&lt;/h2&gt;
&lt;h3&gt;렌더링&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CSR, SSR, SSG&lt;/li&gt;
&lt;li&gt;렌더링이란 UI를 HTML로 나타내는 과정이다. 렌더링은 클라이언트에서 일어날 수도 있고 서버에서 일어날 수도 있다. &lt;/li&gt;
&lt;li&gt;클라이언트에서 렌더링이 일어나는 것을 Client Side Rendering이라고 한다. 서버로부터 빈 HTML과 UI를 구성하기 위한 정보가 담긴  javascript 파일을 받아서 유저 디바이스에서 렌더링 작업을 시작한다. &lt;/li&gt;
&lt;li&gt;이것과 대비되는 개념으로 서버에서 렌더링이 일어나는 것을 Pre-rendering이라고 한다. 클라이언트에 전송되기 전에 미리(Pre) HTML을 만들어서 전송한다는 의미이다. CSR을 사용하려면 useEffect나 useSWR을 사용해서 클라이언트에서 data fetching을 한다. &lt;/li&gt;
&lt;li&gt;Pre-rendering에는 Static Site Generation과 Server Side Rendering이 있다. Nextjs의 디폴트 동작은 Pre rendering이다. &lt;/li&gt;
&lt;li&gt;둘 다 HTML을 미리 만든 뒤 클라이언트에 전송한다는 점에서 공통점이 있지만, 차이점은 런타임에 만드느냐 빌드타임에 만드느냐이다. &lt;/li&gt;
&lt;li&gt;Static Site Generation은 빌드타임에 HTML을 만들어놓고 배포된 뒤 유저 요청이 올때마다 CDN에서 저장된 HTML을 서빙한다. &lt;strong&gt;즉 런타임에는 서버가 없다.&lt;/strong&gt; SSG를 사용하려면 getStaticProps를 사용한다. &lt;/li&gt;
&lt;li&gt;Server Side Rendering은 SSG와 달리 런타임에 유저 요청이 있을 때마다 서버에서 HTML을 만들어서 보내준다. HTML과 javascript instruction, json data를 보내서 HTML을 먼저 보여주고 거기에 이벤트 핸들러를 붙여서 interactive하게 만드는 hydration 과정을 거친다. SSR을 사용하려면 getServersideProps를 사용한다. &lt;/li&gt;
&lt;li&gt;ref: &lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/rendering&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/rendering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ISR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;static page generation은 빌드 타임에 모든 페이지를 미리 생성한다. &lt;/li&gt;
&lt;li&gt;isr은 모든 페이지를 생성하지 않고 페이지 단위로 갱신을 가능하게 한다. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;적용 방법&lt;/h2&gt;
&lt;p&gt;적용하는 방법은 getStaticProps 함수 리턴문 내에 revalidate 필드를 포함시키면 된다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;export async function getStaticProps() {
    const res = await fetch(&amp;#39;https://.../posts&amp;#39;)
    const posts = await res.json()

    return {
        props: {
            posts,
        },
        revalidate: 10 // 초 단위
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;revalidate에 매핑되는 시간 동안 페이지는 캐싱되고, 그 시간이 지난 이후 접속한 유저가 있으면 새로운 페이지를 뒷단에서 생성한다. 이 생성이 성공적이면 캐시를 무효화하고 새로 갱신한 페이지를 보여준다. &lt;/p&gt;
&lt;p&gt;revalidate을 추가하면 x-nextjs-cache 필드가 response 헤더에 추가된다. 이 값은 &lt;code&gt;MISS&lt;/code&gt;, &lt;code&gt;STALE&lt;/code&gt;, &lt;code&gt;HIT&lt;/code&gt; 3가지 중 하나이다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MISS: 캐시에 이 path가 없다. 처음 방문 시 최대 한 번 발생.&lt;/li&gt;
&lt;li&gt;STALE: 캐시에 있으며, revalidate 타임이 지나서 stale한 상태이므로 백그라운드에서 갱신이 진행될 것이다.&lt;/li&gt;
&lt;li&gt;HIT: 캐시에 있으며, revalidate 타임을 초과하지 않았다. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;export async function getStaticPaths() {
    const res = await fetch(&amp;#39;https://.../posts&amp;#39;)
    const posts = await res.json()

    return { paths: posts.map(x =&amp;gt; ({ params: id: x.id }), fallback: &amp;#39;blocking&amp;#39; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getStaticPaths에 정의된 path들만 빌드 타임에 pre render를 진행하고 fallback 필드에 정의된 값에 따라 서버 렌더링을 해서 페이지를 갱신한다. &lt;/p&gt;
&lt;p&gt;fallback 값으로 올 수 있는 값은 &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;&amp;#39;blocking&amp;#39;&lt;/code&gt; 이다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-false&quot;&gt;false&lt;/a&gt;: getStaticPaths로부터 리턴되지 않은 path는 404 페이지를 보여준다. &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-true&quot;&gt;true&lt;/a&gt;: getStatisPaths로부터 리턴되지 않은 path는 404를 보여주는 게 아니라 fallback 페이지를 보여주다가  백그라운드에서 html을 생성한 다음 유저에게 보이는 페이지를 갈아낀다. 이 새로운 페이지로 pre render 리스트에 추가한다. &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-blocking&quot;&gt;blocking&lt;/a&gt;: getStatisPaths로부터 리턴되지 않은 path는 유저 리퀘스트가 왔을 때 페이지를 만든 다음에 완성되면 클라이언트에 표시한다. true랑의 차이는 fallback이 보여지느냐 아니냐이다. &lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부일지(TIL)/JS Framework + Library</category>
      <category>isr</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/450</guid>
      <comments>https://developer-alle.tistory.com/450#entry450comment</comments>
      <pubDate>Mon, 28 Nov 2022 18:13:31 +0900</pubDate>
    </item>
    <item>
      <title>next.js module not found (module resolve 에러)</title>
      <link>https://developer-alle.tistory.com/449</link>
      <description>&lt;h2&gt;상황&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;../../.yarn/cache/keen-slider-npm-6.8.3-1dae0bc90d-b798545bd6.zip/node_modules/keen-slider/react.js:1:74
Module not found: Can&amp;#39;t resolve &amp;#39;react&amp;#39;
Did you mean &amp;#39;./react&amp;#39;?
Requests that should resolve in the current directory need to start with &amp;#39;./&amp;#39;.
Requests that start with a name are treated as module requests and resolve within module directories (node_modules, /Users/dayoung.kang/toss/frontend/services/homepage/src).
If changing the source code is not an option there is also a resolve options called &amp;#39;preferRelative&amp;#39; which tries to resolve these kind of requests in the current directory too.

Import trace for requested module:
./src/pages/main/components/review-section/ReviewSection.tsx
./src/pages/main/Main.tsx
./pages/index.ts

https://nextjs.org/docs/messages/module-not-found

error - Error: keen-slider tried to access react, but it isn&amp;#39;t declared in its dependencies; this makes the require call ambiguous and unsound.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;keen-slider라는 패키지를 설치하고 keen-slider/react에서 모듈을 임포트했는데 react를 resolve할 수 없다고 나옴. 해당 패키지의 package.json 을 보니 react가 devDependency로만 설치되어 있고 peerDependency 정의가 없음.&lt;/p&gt;
&lt;h2&gt;해결방법&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;yarn의 인터널 설정 파일인 .yarnrc.yml 에 아래처럼 추가해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;packageExtensions:
  keen-slider@*:
    peerDependencies:
      react: &amp;quot;*&amp;quot;
      react-dom: &amp;quot;*&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ref&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://yarnpkg.com/configuration/yarnrc&quot;&gt;https://yarnpkg.com/configuration/yarnrc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부일지(TIL)/Error case</category>
      <author>Alledy</author>
      <guid isPermaLink="true">https://developer-alle.tistory.com/449</guid>
      <comments>https://developer-alle.tistory.com/449#entry449comment</comments>
      <pubDate>Thu, 27 Oct 2022 17:49:28 +0900</pubDate>
    </item>
  </channel>
</rss>