<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Now or Never</title>
    <link>https://norwayy.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 12:27:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>GrapeMilk</managingEditor>
    <image>
      <title>Now or Never</title>
      <url>https://tistory1.daumcdn.net/tistory/3568892/attach/a64563e21652421388da82cae3eb1ae1</url>
      <link>https://norwayy.tistory.com</link>
    </image>
    <item>
      <title>Leetcode 2910. Minimum Number of Groups to Create a Valid Assignment</title>
      <link>https://norwayy.tistory.com/440</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/minimum-number-of-groups-to-create-a-valid-assignment/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문제 링크&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;- 그룹의 수를 최소화 해야하기 때문에 그룹안에 ball을 최대한 많이 넣어야 한다 (그리디)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;- 그룹핑을 할 때 나머지가 있으면 나머지로 그룹을 만들 수 있는지 확인해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738858401740&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function minGroupsForValidAssignment(balls: number[]): number {
  const map = new Map&amp;lt;number, number&amp;gt;();
  for (const n of balls) {
    map.set(n, (map.get(n) || 0) + 1);
  }

  let min = balls.length;
  for (const v of map.values()) {
    min = Math.min(min, v);
  }

  // size는 min 값부터 시작 (가장 큰 그룹을 만들 수 있는 크기, 그리디) 
  for (let size = min; size &amp;gt;= 1; --size) {
    const numGroups = groupify(map, size);
    if (numGroups &amp;gt; 0) return numGroups;
  }

  return balls.length;
}

function groupify(map: Map&amp;lt;number, number&amp;gt;, size: number): number {
  let groups = 0;
  const next = size + 1;

  for (const value of map.values()) {
    const numGroups = Math.floor(value / next);
    const remaining = value % next;

    if (remaining === 0) {
      groups += numGroups;
    } else if (numGroups &amp;gt;= size - remaining) {
      // 그룹 하나 더 만들 수 있는지 확인
      // 더 만들 수 있는지 확인에는 next가 아닌 size를 비교. 
      groups += numGroups + 1;
    } else {
      // size로 쪼개질 수 없는 그룹
      return 0;
    }
  }

  return groups;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/Algorithm Practice</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/440</guid>
      <comments>https://norwayy.tistory.com/440#entry440comment</comments>
      <pubDate>Fri, 7 Feb 2025 01:18:26 +0900</pubDate>
    </item>
    <item>
      <title>전역 상태 라이브러리를 왜 사용할까?</title>
      <link>https://norwayy.tistory.com/439</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단한 전역 객체를 만들다가 들었던 생각을 공유해 봅니다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;javascript에서 모듈안에 생성된 객체/배열은 다른 모듈에서 import할 때 새 인스턴스가 생기지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;9ff2f300-c065-4672-b355-644c35ba0e91&quot; data-message-author-role=&quot;user&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를들어 module.js라는 공유되는 모듈이 있고&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1738679773385&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.js

export const arr = [];&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A.js 모듈에서 arr를 import한 뒤 숫자 1을 push 하는 코드가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1738679831957&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { arr } from &quot;./module&quot;; 

arr.push(1);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;B.js 모듈에서 arr를 콘솔에 찍어보면 push된 값이 반영된 걸 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738680416037&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// B.js
import { arr } from &quot;./module&quot;;

console.log(&quot;B.js에서 arr:&quot;, arr); // [1] (A.js에서 변경한 값이 반영됨)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참조의 공유&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈에서 선언한 객체/배열을 export하면 참조를 공유하기 때문에 import 한 모듈사이에서 값을 공유할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(단, let을 통해 값을 &quot;재할당&quot;하면 안됨)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 모듈 시스템을 통해 간단하게 전역에서 공유하는 객체를 생성할 수 있는데 &lt;b&gt;왜 우리는 전역 상태 라이브러리를 사용할까?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;React 상태 관리와 모듈 시스템&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러가지 이유가 있지만 일단 모듈 시스템만으로는 React 상태 관리를 할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈로 import한 값이 변경된다고 React 라이프 사이클을 타지 않는다 따라서 상태 변경을 감지하려면 useState나 useEffect와 같이 사용해야한다. 전역 상태 라이브러리는 이런 작업을 내부적으로 구현해서 사용하기 편리하게 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Zustand의 경우 내부적으로 &lt;b&gt;구독(subscribe) 시스템&lt;/b&gt;을 사용하여 상태 변경을 감지하고 React 컴포넌트를 자동으로 업데이트한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래는 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Zustand의 상태 관리 코드의 단순화 버전이라고 하는데(by GPT) 정확한 구현은 시간될 때 뜯어봐야 할 것 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1738680682443&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useSyncExternalStore } from &quot;react&quot;;

// Zustand 내부에서 실행되는 상태 관리 로직
const createStore = (createState) =&amp;gt; {
  let state = createState((newState) =&amp;gt; {
    state = { ...state, ...newState };
    listeners.forEach((listener) =&amp;gt; listener(state));
  });

  let listeners = new Set();

  return {
    getState: () =&amp;gt; state,
    setState: (newState) =&amp;gt; {
      state = { ...state, ...newState };
      listeners.forEach((listener) =&amp;gt; listener(state));
    },
    subscribe: (listener) =&amp;gt; {
      listeners.add(listener);
      return () =&amp;gt; listeners.delete(listener);
    },
  };
};

// Zustand의 상태 변경 감지 로직
const useStore = (selector) =&amp;gt; {
  const store = createStore((set) =&amp;gt; ({ count: 0, increase: () =&amp;gt; set({ count: state.count + 1 }) }));

  return useSyncExternalStore(
    store.subscribe,
    () =&amp;gt; selector(store.getState()), 
    () =&amp;gt; selector(store.getState()) 
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>토막지식시리즈/React 토막지식</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/439</guid>
      <comments>https://norwayy.tistory.com/439#entry439comment</comments>
      <pubDate>Tue, 4 Feb 2025 23:54:16 +0900</pubDate>
    </item>
    <item>
      <title>useEffect vs useLayoutEffect</title>
      <link>https://norwayy.tistory.com/437</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useScrollRestoration Hook을 구현하면서 발생한 문제를 기록 해봤습니다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;useScrollRestoration훅은 특정 페이지에서 스크롤 이벤트가 발생할 때마다 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;scrollY값을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;sessionStorage에 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;무한스크롤로 구현된 목록 페이지에서 상세페이지로 이동한뒤 다시 목록에 돌아왔을 때 scrollY값을 유지함으로써 좋은 탐색 UX를 위해 자주 사용되는 기능이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 훅을 구현하면서 발생한 이슈가 있었는데 useEffect vs useLayoutEffect 관점에서 기록해보려고 한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useScrollRestoration훅 초기 버전&lt;/p&gt;
&lt;pre id=&quot;code_1738323964916&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useLayoutEffect } from 'react'
import { useRouter } from 'next/router'
import { throttle } from 'lodash-es'

const THROTTLE_TIME = 300

export function useScrollRestoration() {
  const router = useRouter()
  const asPath = router.asPath

  const saveScrollHeight = (asPath: string) =&amp;gt; {
    sessionStorage.setItem(
      `scrollHeight:${asPath}`,
      window.scrollY.toString(),
    )
  }

  useEffect(() =&amp;gt; {
    const scrollHeight = sessionStorage.getItem(`scrollHeight:${asPath}`)

    if (scrollHeight) {
      window.scrollTo(0, parseInt(scrollHeight, 10))
    }
  }, [])

  useEffect(() =&amp;gt; {
    const saveScrollPosition = throttle(() =&amp;gt; {
      saveScrollHeight(asPath)
    }, THROTTLE_TIME)

    window.addEventListener('scroll', saveScrollPosition)
    return () =&amp;gt; {
      window.removeEventListener('scroll', saveScrollPosition)
    }
  }, [])
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 훅을 구현하고 테스트해보니 sessionStorage에 저장된 window.scrollY 값이 상세 페이지에 진입할 때 0으로 리셋되는 현상이 있었다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;의심되는 부분은saveScrollHeight를 실행하는 scroll 이벤트 부분. clean up 시점에 콘솔을 찍어보니 window.scrollY가 0으로 찍혔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;console.log를 적용한 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738324103638&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    const saveScrollPosition = throttle(() =&amp;gt; {
      saveScrollHeight(asPath)
    }, THROTTLE_TIME)

    window.addEventListener('scroll', saveScrollPosition)
    return () =&amp;gt; {
      console.log(window.scrollY, &quot; : debug&quot;) // clean up시에 0으로 찍히는 scrollY 값...
      window.removeEventListener('scroll', saveScrollPosition)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;왜 이런 현상이 발생할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래 사진 처럼 useEffect의 clean up 시점은 Browser Paint가 발생한 뒤이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;862&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjk86v/btsL2HA8Qir/xtKhoBRjf16qHo4bxIrh8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjk86v/btsL2HA8Qir/xtKhoBRjf16qHo4bxIrh8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjk86v/btsL2HA8Qir/xtKhoBRjf16qHo4bxIrh8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjk86v%2FbtsL2HA8Qir%2FxtKhoBRjf16qHo4bxIrh8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;552&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;862&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;추측컨데 상세페이지로 이동하면서 Brower paint가 다시 발생했고 변경된 페이지의 scrollY는 0부터 시작하므로(왜 0부터 시작하는지는 파악해봐야 한다, next.js의 기본 동작인 것 같다)&amp;nbsp;window.scrollY는 0으로 다시 그려졌을 것이다. 그 시점의 값을 useEffect의 clean up에 정의한 함수가 참조하면서 sessionStorage의 값도 0으로 바꿨을 것.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Browser paint 이전에 effect를 실행하는 useLayoutEffect로 변경해서 이슈를 해결했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738324387234&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

export function useScrollRestoration() {
  ...
  useLayoutEffect(() =&amp;gt; {
    const saveScrollPosition = throttle(() =&amp;gt; {
      saveScrollHeight(asPath)
    }, THROTTLE_TIME)

    window.addEventListener('scroll', saveScrollPosition)
    return () =&amp;gt; {
      window.removeEventListener('scroll', saveScrollPosition)
    }
  }, [])
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>토막지식시리즈/React 토막지식</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/437</guid>
      <comments>https://norwayy.tistory.com/437#entry437comment</comments>
      <pubDate>Fri, 31 Jan 2025 20:55:26 +0900</pubDate>
    </item>
    <item>
      <title>javascript import overhead</title>
      <link>https://norwayy.tistory.com/436</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://es-toolkit.slash.page/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;es-toolkit&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;의 PR을 구경하다가 &lt;/span&gt;&lt;a href=&quot;https://github.com/toss/es-toolkit/pull/629&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;bench: remove import overhead&amp;nbsp;&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;라는 제목의 PR을 보게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 PR에는 아래와 같은 수정이 있는데 이게 import의 overhead를 어떻게 줄일 수 있는지 궁금해졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 gpt에게 물어봤다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 질문&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1번에서&amp;nbsp;2번으로&amp;nbsp;코드를&amp;nbsp;수정했는데&amp;nbsp;import&amp;nbsp;overhead가&amp;nbsp;줄었어&amp;nbsp;어떤&amp;nbsp;차이야?&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736043423943&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1번
import { after as afterToolkit } from 'es-toolkit';
import { after as afterLodash } from 'lodash';

2번
import { after as afterToolkit_ } from 'es-toolkit';
import { after as afterLodash_ } from 'lodash';

const afterToolkit = afterToolkit_;
const afterLodash = afterLodash_;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GPT 첫 번째 답변&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. &lt;b&gt;첫 번째 코드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1737018890715&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { after as afterToolkit } from 'es-toolkit'; 
import { after as afterLodash } from 'lodash';&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 코드는 es-toolkit와 lodash 모듈에서 각각 after라는 함수를 가져와 &lt;b&gt;직접&lt;/b&gt; afterToolkit과 afterLodash에 할당합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;결과:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import 구문은 &lt;b&gt;모듈의 모든 코드&lt;/b&gt;를 실행한 뒤, 필요한 부분(after)을 가져옵니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 두 모듈(es-toolkit과 lodash)이 크거나 불필요한 부분을 포함하고 있다면 &lt;b&gt;초기 로드 시간&lt;/b&gt;과 &lt;b&gt;메모리 사용량&lt;/b&gt;이 증가할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. &lt;b&gt;두 번째 코드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1737018925590&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { after as afterToolkit_ } from 'es-toolkit'; 
import { after as afterLodash_ } from 'lodash'; 

const afterToolkit = afterToolkit_;
const afterLodash = afterLodash_;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 코드는 es-toolkit와 lodash에서 after를 각각 afterToolkit_과 afterLodash_라는 이름으로 가져오고, 이후에 &lt;b&gt;변수 할당&lt;/b&gt;으로 afterToolkit과 afterLodash를 정의합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;결과:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import 구문은 여전히 모듈을 실행하고 필요한 after를 가져오지만, 변수 할당을 통해 &lt;b&gt;모듈 초기화 과정에서의 직접 참조를 피합니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;차이가 발생하는 이유&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. &lt;b&gt;모듈 실행 및 바인딩&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 코드: afterToolkit과 afterLodash는 &lt;b&gt;직접적으로 모듈에서 가져온 값&lt;/b&gt;에 바인딩됩니다. 이런 방식은 모듈 초기화 시점에 더 많은 &lt;b&gt;메타데이터 관리 비용&lt;/b&gt;이 발생할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 코드: afterToolkit_과 afterLodash_를 가져온 뒤, 이를 &lt;b&gt;로컬 변수에 다시 할당&lt;/b&gt;합니다. 이 과정은 런타임에서 &lt;b&gt;바인딩 메타데이터를 단순화&lt;/b&gt;시켜 약간의 &lt;b&gt;오버헤드 감소&lt;/b&gt;를 유발할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. &lt;b&gt;V8 엔진 최적화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;V8 같은 자바스크립트 엔진은 &lt;b&gt;직접 import한 값&lt;/b&gt;과 &lt;b&gt;로컬 변수에 재할당한 값&lt;/b&gt;을 다르게 처리합니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 방식은 &lt;b&gt;모듈 네임스페이스 참조 비용&lt;/b&gt;이 더 클 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 방식은 &lt;b&gt;로컬 변수 접근이 더 빠릅니다&lt;/b&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결론&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 방식은 &lt;b&gt;재할당을 통해 import 바인딩의 최적화&lt;/b&gt;를 유도하며, &lt;b&gt;모듈 참조 비용을 줄이는 효과&lt;/b&gt;가 있습니다. 따라서 &lt;b&gt;초기화 속도와 런타임 접근 성능&lt;/b&gt;에서 약간의 이점을 얻을 수 있습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 질문&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;aaa2634d-1b0c-458e-a81d-0c777f61afd6&quot; data-message-author-role=&quot;user&quot;&gt;
&lt;div&gt;import 구문은 모듈의 모든 코드를 실행한 뒤, 필요한 부분(after)을 가져옵니다. 이 문장을 더 구체적으로 설명해줘&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GPT 두 번째 답변&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;1. &lt;/span&gt;&lt;b&gt;모듈 시스템의 동작 방식&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;c5bd479f-2923-4a65-946a-9bac21bb8456&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 **ES 모듈(ESM)**은 다음 단계를 통해 동작합니다:&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1 &lt;b&gt;모듈 로드 및 파싱 단계&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import 구문이 실행되면, 자바스크립트 엔진은 해당 모듈 파일(예: es-toolkit 또는 lodash)을 찾습니다.&lt;/li&gt;
&lt;li&gt;모듈 파일을 읽어 들이고 &lt;b&gt;전체 코드를 파싱&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;필요한 경우 **의존성(다른 import된 모듈)**도 재귀적으로 로드합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2 &lt;b&gt;모듈 실행 단계&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈이 파싱되면 &lt;b&gt;모든 코드가 실행&lt;/b&gt;됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, es-toolkit이나 lodash 모듈 안에 함수 정의, 변수 초기화, 객체 생성, 로직 실행 등이 있으면 이들이 모두 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행 결과로 &lt;b&gt;모듈의 export 객체&lt;/b&gt;가 만들어집니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 객체는 모듈이 제공하는 모든 export된 값(함수, 변수, 클래스 등)을 포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.3 &lt;b&gt;필요한 부분 가져오기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import { after } from 'lodash';처럼 &lt;b&gt;부분 import&lt;/b&gt;를 사용할 경우, 엔진은 모듈의 export 객체에서 after라는 이름의 값을 찾아 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;실제 동작 예시&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 lodash 모듈 구조 (가상 예시)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1736043793291&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lodash/index.js
export const after = (n, func) =&amp;gt; { /* 정의 */ };
export const chunk = (array, size) =&amp;gt; { /* 정의 */ };
export const cloneDeep = (value) =&amp;gt; { /* 정의 */ };

// 추가적인 내부 로직 (모듈 실행 시 실행됨)
console.log('lodash 모듈이 로드되었습니다.');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 import 구문의 동작&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1736043804485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { after } from 'lodash';&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행하면 다음이 발생합니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;lodash/index.js 파일이 로드되고 &lt;b&gt;전체 코드&lt;/b&gt;가 실행됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 console.log('lodash 모듈이 로드되었습니다.');도 실행됩니다.&lt;/li&gt;
&lt;li&gt;after, chunk, cloneDeep 함수가 메모리에 정의됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행 결과로 export 객체가 생성됩니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1736043839484&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  after: [Function: after],
  chunk: [Function: chunk],
  cloneDeep: [Function: cloneDeep]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3. import { after }는 이 export 객체에서 after를 찾아 가져옵니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;모듈 실행의 부작용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 모듈의 코드를 실행하는 이유는 모듈이 **부작용(side effect)**을 포함할 수 있기 때문입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시: 부작용이 있는 모듈&lt;/h4&gt;
&lt;pre id=&quot;code_1736043964696&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// example.js
console.log('Example 모듈 실행 중...');
export const exampleFunction = () =&amp;gt; console.log('Hello, world!');

// main.js
import { exampleFunction } from './example.js';&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main.js를 실행하면, &lt;b&gt;example.js의 모든 코드가 실행&lt;/b&gt;되기 때문에 다음이 출력됩니다:
&lt;div&gt;
&lt;div&gt;Example 모듈 실행 중...&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;이후 exampleFunction만 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. &lt;b&gt;요약&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import 구문은 &lt;b&gt;모듈의 전체 코드를 실행&lt;/b&gt;하여 필요한 export 객체를 생성합니다.&lt;/li&gt;
&lt;li&gt;필요한 부분만 가져오더라도 &lt;b&gt;모듈 전체 실행&lt;/b&gt;은 피할 수 없습니다.&lt;/li&gt;
&lt;li&gt;따라서 큰 모듈에서는 &lt;b&gt;불필요한 실행 및 메모리 사용&lt;/b&gt;이 성능에 영향을 줄 수 있습니다. 이를 최적화하려면 필요한 부분만 export하거나, 로직을 나눠서 import하는 방법이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 세 번째 질문&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;두 번째 코드도 결국 import에서 모든 모듈을 실행하는데 import overhead가 줄어드는 이유는 뭐야?&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GPT 세 번째 답변&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;9d37c08e-2e1a-4d2d-947e-dbc953ba2191&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 질문입니다! 두 번째 코드에서 &lt;b&gt;import overhead&lt;/b&gt;가 줄어드는 이유는, &lt;b&gt;모듈의 실행 자체&lt;/b&gt;가 아니라, &lt;b&gt;변수 접근과 바인딩 방식&lt;/b&gt;의 차이에 있습니다. 이를 자세히 설명하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;두 코드의 비교&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;첫 번째 코드:&lt;/h4&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1736044219385&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { after as afterToolkit } from 'es-toolkit';
import { after as afterLodash } from 'lodash';&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;두 번째 코드:&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1736044258827&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { after as afterToolkit_ } from 'es-toolkit';
import { after as afterLodash_ } from 'lodash';

const afterToolkit = afterToolkit_;
const afterLodash = afterLodash_;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 코드 모두 &lt;b&gt;모듈 전체를 실행&lt;/b&gt;하는 것은 동일합니다. 하지만 &lt;b&gt;import된 값을 사용하는 방식&lt;/b&gt;에서 차이가 발생하여 성능에 영향을 미칩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;import 구문과 바인딩&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;첫 번째 코드의 동작&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import { after as afterToolkit }는 es-toolkit 모듈에서 after를 가져와 &lt;b&gt;모듈 네임스페이스와 강하게 바인딩&lt;/b&gt;합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;afterToolkit은 항상 &lt;b&gt;모듈 네임스페이스&lt;/b&gt;를 통해 값을 참조합니다.&lt;/li&gt;
&lt;li&gt;이는 모듈 내부에서 값이 변경될 가능성을 고려한 동작으로, &lt;b&gt;런타임 시점마다 모듈 네임스페이스를 확인&lt;/b&gt;하게 되어 약간의 오버헤드가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;두 번째 코드의 동작&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import { after as afterToolkit_ }로 가져온 afterToolkit_은 모듈 네임스페이스와 바인딩되지만, 이후:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1736044294856&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const afterToolkit = afterToolkit_;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 변수 afterToolkit에 재할당됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 변수는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;모듈 네임스페이스와의 연결이 끊어지며&lt;/b&gt;, 단순히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;메모리 주소만을 참조&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;로컬 변수 접근은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;더 빠르고 메타데이터 오버헤드가 적습니다&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;성능 차이의 원인&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1 &lt;b&gt;모듈 네임스페이스 접근 비용&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈에서 값을 직접 import할 경우, 엔진은 &lt;b&gt;모듈 네임스페이스 객체&lt;/b&gt;를 통해 값을 참조해야 합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 객체에는 &lt;b&gt;export된 모든 값&lt;/b&gt;과 관련된 메타데이터가 포함되어 있으며, 이로 인해 접근 비용이 조금 더 높아집니다.&lt;/li&gt;
&lt;li&gt;특히, 반복적으로 함수나 변수를 호출하거나 참조할 때 이 오버헤드가 누적될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2 &lt;b&gt;로컬 변수 접근 비용&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 번째 코드에서는 import한 값을 &lt;b&gt;로컬 변수로 재할당&lt;/b&gt;했기 때문에, 이후에는 단순히 &lt;b&gt;로컬 스코프의 변수&lt;/b&gt;로 접근합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 변수는 &lt;b&gt;모듈 네임스페이스와 독립적&lt;/b&gt;으로 작동하며, 접근 속도가 더 빠릅니다.&lt;/li&gt;
&lt;li&gt;V8 같은 자바스크립트 엔진은 로컬 변수에 대해 더 효율적으로 최적화를 수행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. &lt;b&gt;실제 성능 차이&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;첫 번째 코드&lt;/b&gt;는 모듈 네임스페이스를 통해 값을 참조하기 때문에, &lt;b&gt;런타임 접근 비용&lt;/b&gt;이 조금 더 높습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;두 번째 코드&lt;/b&gt;는 import된 값을 로컬 변수에 재할당하여, &lt;b&gt;더 빠른 변수 접근&lt;/b&gt;을 가능하게 합니다.&lt;/li&gt;
&lt;li&gt;이 차이는 모듈 내 &lt;b&gt;큰 함수 호출이나 반복문에서 더욱 두드러지게 나타납니다&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. &lt;b&gt;왜 import overhead가 줄어드는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, 두 번째 코드에서 import overhead가 줄어드는 이유는 다음과 같습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;모듈 네임스페이스 참조 대신 로컬 변수 사용&lt;/b&gt;으로 인해 접근 비용이 줄어듭니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;엔진 최적화&lt;/b&gt;가 더 효율적으로 적용됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 변수는 스코프와 메모리 구조가 간단하여 최적화가 빠르게 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모듈 실행 자체&lt;/b&gt;는 동일하지만, &lt;b&gt;런타임 접근 속도&lt;/b&gt;가 개선되어 반복적인 참조에서 성능 향상을 얻을 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. &lt;b&gt;적용 시점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 특히 다음과 같은 경우 유용합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈에서 가져온 함수나 변수를 &lt;b&gt;반복적으로 사용&lt;/b&gt;하는 경우.&lt;/li&gt;
&lt;li&gt;성능 최적화가 중요한 대규모 프로젝트에서 &lt;b&gt;미세한 개선&lt;/b&gt;이 누적될 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>토막지식시리즈/javascript 토막지식</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/436</guid>
      <comments>https://norwayy.tistory.com/436#entry436comment</comments>
      <pubDate>Sun, 5 Jan 2025 11:31:55 +0900</pubDate>
    </item>
    <item>
      <title>for of vs for</title>
      <link>https://norwayy.tistory.com/435</link>
      <description>&lt;div&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;d32a66e3-9405-4c2e-9109-9b0bd2b95c75&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;javaScript에서 for...of와 일반적인 for 루프의 성능 차이는 사용하는 용도에 따라 다르지만 &lt;b&gt;보통 for의 성능이 좋다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. &lt;b&gt;for&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;: for 루프는 인덱스를 기반으로 배열이나 반복 가능한 객체에 직접 접근한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;성능&lt;/b&gt;: 배열의 길이가 크고 단순히 인덱스를 사용해 값을 순회할 때, 최적화가 잘 되어 있기 때문에 성능 면에서 이점이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1725847744719&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4]; 

for (let i = 0; i &amp;lt; arr.length; i++) { 
	console.log(arr[i]); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. &lt;b&gt;for...of&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;: for...of는 반복 가능한 객체(iterable)에서 값 자체를 순회한다. 배열 외에도 Map, Set 같은 다른 이터러블 객체에도 사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;성능&lt;/b&gt;: for...of는 내부적으로 이터레이터를 생성하고 그 값을 가져오기 때문에, 단순 배열 순회에 대해서는 for 루프보다 약간 느릴 수 있다. 하지만 코드의 가독성과 간결성 면에서는 장점이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1725847793499&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4]; 
for (const value of arr) {
	console.log(value); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;성능 비교&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;작은 배열&lt;/b&gt;: 배열의 크기가 작을 경우 두 루프의 성능 차이는 미미합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;큰 배열&lt;/b&gt;: 큰 배열을 순회할 때는 for 루프가 성능 면에서 더 효율적일 수 있다. 특히 반복 횟수가 많을 때는 차이가 더  커진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;가독성&lt;/b&gt;: 성능을 크게 고려하지 않는다면, 가독성 면에서 for...of가 더 직관적일 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span style=&quot;color: #000000;&quot;&gt;예시: &lt;a href=&quot;https://github.com/toss/es-toolkit/pull/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;es-toolkit의 sum 함수를 for..of에서 for로 개선한 PR&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;for...of가 성능이 느린 이유(이터레이터)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;javaScript에서 for...of는 &lt;/span&gt;&lt;b&gt;이터러블 객체&lt;/b&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;를 순회할 때 &lt;b&gt;이터레이터(iterator)&lt;/b&gt;를 사용한다. 이터레이터는 값을 순차적으로 접근할 수 있는 객체이며, for...of 루프는 이터레이터의 동작을 간결하게 해주는 문법이다.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;fdb18ff9-6ed8-44e9-b5d9-451ff8e5c475&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. &lt;b&gt;이터러블과 이터레이터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이터러블은 이터레이터를 반환할 수 있는 객체이다. Symbol.iterator 메서드를 가지며, 이 메서드는 이터레이터를 반환한다. 이터레이터는 next() 메서드를 통해 순차적으로 값을 반환하는 객체이다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1725847978153&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4];

// 이터레이터 생성
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: undefined, done: true }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;arr[Symbol.iterator]()를 호출하면 배열의 이터레이터가 반환된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;iterator.next()는 이터레이터를 통해 값을 하나씩 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;next() 메서드는 { value, done } 객체를 반환하며, value는 다음 값이고 done은 이터레이션이 끝났는지 여부를 나타낸다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. &lt;b&gt;for...of로 이터레이터 사용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;for...of는 위와 같은 이터레이터 로직을 내부적으로 자동으로 처리해준다. 즉, 개발자가 직접 next() 메서드를 호출할 필요 없이 값만 쉽게 순회할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1725848025265&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4];

for (const value of arr) {
  console.log(value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 코드는 내부적으로 arr[Symbol.iterator]()를 호출하여 이터레이터를 생성하고, next()를 반복적으로 호출해 값을 순차적으로 가져온다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;done이 true가 될 때까지 자동으로 순회가 이루어진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 과정이 index로 단순히 값을 가져오는 for 보다 성능을 느리게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. &lt;b&gt;이터레이터를 수동으로 사용한 for...of의 동작 원리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;for...of가 내부적으로 하는 작업을 수동으로 구현한 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1725848091352&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4];

// 이터레이터 생성
const iterator = arr[Symbol.iterator]();

let result = iterator.next();
while (!result.done) {
  console.log(result.value);
  result = iterator.next();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드는 for...of와 동일한 결과를 얻을 수 있다. 직접 next()를 호출하고 이터레이션을 제어한다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>토막지식시리즈/javascript 토막지식</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/435</guid>
      <comments>https://norwayy.tistory.com/435#entry435comment</comments>
      <pubDate>Mon, 9 Sep 2024 11:15:33 +0900</pubDate>
    </item>
    <item>
      <title>리액트에서 &amp;quot;렌더링(rendering)&amp;quot;이란?</title>
      <link>https://norwayy.tistory.com/434</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프론트엔드에서 렌더링은 범용적인 용어이다, 브라우저에서도 리액트에서도 렌더링이 발생한다. 리액트에서 렌더링은 무엇일까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;TL;DR 렌더링이란?&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;브라우저 DOM을 직접적으로 수정하는 비용을 줄이기 위해 React에서 수행하는 작업이며 브라우저 DOM을 업데이트할 범위를 결정하고 반영하는 과정.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Trigger, Render, Commit&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;리엑트는 &quot;렌더링과정&quot;을 통해 브라우저 DOM을 업데이트할 범위를 결정하고 반영한다. 여기서 렌더링과정은 &quot;Trigger&quot;, &quot;Render&quot;, &quot;Commit&quot; 단계로 나뉜다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Trigger&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;렌더링을 유발하는 단계로 두 가지 상황이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;1. 앱 시작 초기(initial render)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;initial render는 target DOM node(root)와 함께 createRoot를 호출한 뒤 render 함수를 실행하는 과정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&amp;nbsp;* createRoot는 인수로 전달된 요소를 리액트 앱의 루트로 만들어 반환한다. 트리 형태의 Virtual Dom에서 루트 요소가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724422826464&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Image from './Image.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(&amp;lt;Image /&amp;gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t1wYQ/btsJfErufaK/IGZRpmp65nwZLWbaF9ZRs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t1wYQ/btsJfErufaK/IGZRpmp65nwZLWbaF9ZRs1/img.png&quot; data-alt=&quot;렌더링 결과 (공식문서 참고)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t1wYQ/btsJfErufaK/IGZRpmp65nwZLWbaF9ZRs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft1wYQ%2FbtsJfErufaK%2FIGZRpmp65nwZLWbaF9ZRs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;280&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;렌더링 결과 (공식문서 참고)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;2. state의 변경이 있을 때(re-render)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;initial render 이후에는 set(State) 함수를 통해 state를 변경하여 리렌더링 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;b&gt;Render&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;화면에 그릴 요소를 계산하기 위해 리엑트는 components를 호출하는데 이를 Rendering 이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&amp;nbsp;- initial render에서는 root component를 호출한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&amp;nbsp;- 리렌더시에는 state가 변경된 component를 호출한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;이런 호출은 재귀적으로 반복된다. 호출된 컴포넌트가 다른 컴포넌트를 return 한다면 리액트는 다음 스텝으로 return된 컴포넌트를 렌더한다. 이 호출은 더이상 반환되는 컴포넌트가 없을 때까지 반복된다. 이런 과정을 통해 리액트는 어떤 요소를 하면에 그려야 하는지 알 수 있다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724423985946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Gallery.js

export default function Gallery() {
  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h1&amp;gt;Inspiring Sculptures&amp;lt;/h1&amp;gt;
      &amp;lt;Image /&amp;gt;
      &amp;lt;Image /&amp;gt;
      &amp;lt;Image /&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}

function Image() {
  return (
    &amp;lt;img
      src=&quot;https://i.imgur.com/ZF6s192.jpg&quot;
      alt=&quot;'Floralis Gen&amp;eacute;rica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals&quot;
    /&amp;gt;
  );
}

// index.js

import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(&amp;lt;Gallery /&amp;gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;- 리액트는 initial render에서 section, h1, 세개의 img Dom nodes를 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;- 리렌더시에는 이전 렌더와 현재 렌더를 비교하여 변화된 부분을 계산한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;b&gt;Commit&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;Render 단계를 통해 호출된 컴포넌트들이 브라우저 DOM에 적용되는 단계.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&amp;nbsp;- initial render시에는 DOM API인 appendChild() 함수를 사용하여 render를 통해 생성한 노드들을 돔에 반영한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&amp;nbsp;- 리렌더시에는 변화된 부분만 DOM에 반영한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;리액트는 render시에 변화된 부분이 있는 경우에만 DOM을 업데이트한다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에는 부모에게 time 값을 전달받아 props가 변경되어 리렌더링이 일어나는 Clock 컴포넌트가 있다. 매초마다 변경된 time을 받기 때문에 Clock도 매초마다 리렌더링된다. 이 때 input 컴포넌트에는 변화가 없다. 리액트는 이전, 이후 렌더에서 input 컴포넌트에 변화가 없다고 판단하기 때문에 input에 어떤 값을 입력해도 리셋되지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1724427935397&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function Clock({ time }) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;h1&amp;gt;{time}&amp;lt;/h1&amp;gt;
      &amp;lt;input /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f172a; text-align: left;&quot;&gt;Browser paint&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f172a; text-align: left;&quot;&gt;리엑트가 렌더링을 마치고 DOM을 업데이트 한 뒤, 브라우저는 화면을 repaint 한다. 즉 &quot;브라우저 렌더링&quot;이 시작된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f172a; text-align: left;&quot;&gt;ref&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f172a; text-align: left;&quot;&gt;리액트 공식문서,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://beta.reactjs.org/learn/render-and-commit&quot;&gt;https://beta.reactjs.org/learn/render-and-commit&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f172a; text-align: left;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f172a; text-align: left;&quot;&gt;직접 답변한 커리어리 글 &lt;a href=&quot;https://careerly.co.kr/qnas/1400&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://careerly.co.kr/qnas/1400&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>토막지식시리즈/React 토막지식</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/434</guid>
      <comments>https://norwayy.tistory.com/434#entry434comment</comments>
      <pubDate>Fri, 23 Aug 2024 23:07:48 +0900</pubDate>
    </item>
    <item>
      <title>HTTP 요청 생성 및 분석</title>
      <link>https://norwayy.tistory.com/433</link>
      <description>&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc example.com 80
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nc (netcat) 명령어를 터미널에 입력하면 간단하게 TCP 연결을 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;GET / HTTP/1.0
Host: example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 GET 요청을 작성한 뒤 엔터를 누르면 response를 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XkTTY/btsJbL68ww5/qVV0uTM0t0J7uuXmnWcTL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XkTTY/btsJbL68ww5/qVV0uTM0t0J7uuXmnWcTL1/img.png&quot; data-alt=&quot;nc &amp;amp;amp; http 요청 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XkTTY/btsJbL68ww5/qVV0uTM0t0J7uuXmnWcTL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXkTTY%2FbtsJbL68ww5%2FqVV0uTM0t0J7uuXmnWcTL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;690&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nc &amp;amp; http 요청 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 터미널에서 명령어를 통해 간단하게 TCP 연결 및 HTTP 요청을 수행했다! HTTP 요청과 응답을 하나씩 뜯어보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;request header 분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET / HTTP/1.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET: HTTP method를 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/: 요청 url&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP/1.0: HTTP 버전&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Host: example.com&lt;/b&gt;: 요청 도메인 이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response header 분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP/1.0 200 OK&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP/1.0: HTTP 버전&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;200 OK: 응답 상태 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외 key:value 형식으로 다양한 응답값이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response에는 header와 payload가 존재한다. payload는 위의 사진에 일부 잘린 html이며 header와 엔터로 구분된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP 1.0과 1.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 1.0은 많은 단점이 있는데 그 중 하나는 multiple request가 되지 않는 것이다. 위에서 실습한 nc 명령어에 HTTP 버전을 1.1로 바꾸면 request한 뒤 한번더 요청이 가능하다. (1.0으로 연결하면 한 번 요청을 보내고 연결이 끊어진다) 이러한 단점은 TCP handshake 시간지연에 따른 비효율로 1.1에서 개선되었다.&lt;/p&gt;</description>
      <category>컴퓨터 공학(CS)/네트워크 기본 정보</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/433</guid>
      <comments>https://norwayy.tistory.com/433#entry433comment</comments>
      <pubDate>Fri, 23 Aug 2024 01:59:58 +0900</pubDate>
    </item>
    <item>
      <title>(Graphql Apollo) dataIdFromObject와 keyFields</title>
      <link>https://norwayy.tistory.com/432</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;deprecated된 dataIdFromObject를 keyFields로 변경하면서 생긴 이슈 기록&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Apollo Graphql은 Object 타입을 캐시할 때 기본 식별자로 id 필드를 사용한다. Object에 id필드가 없거나 다른 필드를 식별자로 사용하고 싶은 경우 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.apollographql.com/docs/react/caching/cache-configuration/#customizing-identifier-generation-globally&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;커스텀 할 수 있다.&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724310515534&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Configuring the Apollo Client cache&quot; data-og-description=&quot;keyFields: [&amp;quot;title&amp;quot;, &amp;quot;author&amp;quot;, [&amp;quot;name&amp;quot;]],&quot; data-og-host=&quot;www.apollographql.com&quot; data-og-source-url=&quot;https://www.apollographql.com/docs/react/caching/cache-configuration/#customizing-identifier-generation-globally&quot; data-og-url=&quot;https://www.apollographql.com/docs/react/caching/cache-configuration&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cFGbn8/hyWSnjyH9t/jQQk8y1cEHx0j4Hq0F4kPk/img.png?width=1280&amp;amp;height=669&amp;amp;face=0_0_1280_669&quot;&gt;&lt;a href=&quot;https://www.apollographql.com/docs/react/caching/cache-configuration/#customizing-identifier-generation-globally&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.apollographql.com/docs/react/caching/cache-configuration/#customizing-identifier-generation-globally&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cFGbn8/hyWSnjyH9t/jQQk8y1cEHx0j4Hq0F4kPk/img.png?width=1280&amp;amp;height=669&amp;amp;face=0_0_1280_669');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Configuring the Apollo Client cache&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;keyFields: [&quot;title&quot;, &quot;author&quot;, [&quot;name&quot;]],&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.apollographql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 때 사용하는 것이 dataIdFromObject 함수 또는 KeyField이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공식문서에서는 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;dataIdFromObject의 세 가지 결점을 이유로 &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;KeyFields 사용을 권장한다.&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;dataIdFromObject의 세 가지 결점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;1) It's sensitive to&amp;nbsp;aliasing&amp;nbsp;mistakes.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;2) It does nothing to protect against undefined object properties.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;3) Accidentally using different key&amp;nbsp;fields&amp;nbsp;at different times can cause inconsistencies in the cache.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;오늘 기록할 이슈는 dataIdFromObject의 2번째 결점과 관련이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;keyField로 변경하기 전 다음과 같이 dataIdFromObject 코드를 사용하고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724311007356&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; dataIdFromObject(responseObject) {
    switch (responseObject.__typename) {
      case 'Book':
        return `Path:${responseObject.slug}`
      case 'LessonNote':
        return `LessonFile:${JSON.stringify(responseObject)}`
      case 'Course':
        return `KDCCourseInfo:${responseObject.courseId}`
      default:
        return defaultDataIdFromObject(responseObject)
    }
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Book 타입은 id가 따로 없기 때문에 고유한 slug 필드가 식별자로 사용될 수 있도록 작업되어 있었고, keyField를 사용하여 다음과 같이 변경해주었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724311453553&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Book: {
  keyFields: ['slug'],
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;keyFields을 바꾸니 갑자기 Book 상세 페이지 데이터를 가져오는 Query에서 에러가 발생했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Uncaught Invariant Violation: Missing field 'slug' while extracting keyFields from {&quot;__typename&quot;:&quot;BookDetail&quot;}&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;같은 조건에서 바뀐건 dataIdFromObject -&amp;gt; keyFields 밖에 없었기 때문에 코드를 다시 되돌리니 에러는 발생하지 않았다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런데 되돌린 코드의 쿼리 요청을 보니 상세페이지에 접근할 때 마다 캐싱된 데이터를 사용하지 않고 계속 서버에 데이터를 요청해서 받아오고 있었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;에러 로그를 다시 확인해봤다. Book 타입이 아닌 &lt;span style=&quot;background-color: #fcfcfc; text-align: left;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;BookDetail&lt;/span&gt; 타입을 가져오는 과정에서 slug를 찾지 못하고 있었다. 갑자기 BookDetail 관련 에러는 뭐지..? &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; text-align: left;&quot;&gt;BookDetail은 Book을 implement한 타입이다. &lt;b&gt;이를 통해 interface에 keyFields를 적용하면 implements의 식별자도 변경되는 것을 알 수 있었다.&lt;/b&gt; &lt;span style=&quot;background-color: #fcfcfc; color: #000000; text-align: start;&quot;&gt;dataIdFromObject를 사용할 때는 typename을 'Book'으로 특정하고 있었기 때문에 slug 식별자로 변경한 코드는 BookDetail에 적용되지 않았다. keyFields를 적용한 순간 implements인 BookDetail도 식별자가 수정되었고, slug 필드를 갖고있지 않았기 때문에 에러를  발생시키고 있었다. &lt;span style=&quot;background-color: #fcfcfc; color: #000000; text-align: left;&quot;&gt;BookDetail 타입에 slug를 추가하면 문제는 해결된다. 하지만 캐싱을 못하는건 뭐람..?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #000000; text-align: start;&quot;&gt;알고보니 이전 코드인 dataIdFromObject를 사용했을 때부터 있던 이슈였다. BookDetail 타입은 id를 식별자로 사용하고 있었는데 막상 필드에서는 id를 요청하고 있지 않았다. 즉, 상세 페이지의 데이터가 캐싱되지 않고 항상 Query를 재요청하고 있던 것이다. keyField를 사용했다면 id가 없을 때 에러를 발생시켰겠지만 &lt;span style=&quot;background-color: #fcfcfc; color: #000000; text-align: start;&quot;&gt;dataIdFromObject의 특성상 undefined인 식별자 필드를 무시해서 데이터는&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;캐싱되지도 않고, 캐싱된 데이터를 찾을 수도 없었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>토막지식시리즈/GraphQL 토막지식</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/432</guid>
      <comments>https://norwayy.tistory.com/432#entry432comment</comments>
      <pubDate>Thu, 22 Aug 2024 17:11:42 +0900</pubDate>
    </item>
    <item>
      <title>What Is Presigned Url</title>
      <link>https://norwayy.tistory.com/431</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Presigned Url&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A &lt;b&gt;Presigned URL&lt;/b&gt; is a URL that provides temporary, secure access to a specific resource in cloud storage (like Amazon S3) without needing the client to have full access credentials. It is commonly used in scenarios where a client needs to upload or download files directly to/from cloud storage, while the server still controls access.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;presignedUrl.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/891nT/btsI8kfF8Fk/kzwujgU2wcpb2Oaromki1K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/891nT/btsI8kfF8Fk/kzwujgU2wcpb2Oaromki1K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/891nT/btsI8kfF8Fk/kzwujgU2wcpb2Oaromki1K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F891nT%2FbtsI8kfF8Fk%2FkzwujgU2wcpb2Oaromki1K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;400&quot; data-filename=&quot;presignedUrl.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;How Presigned URLs Work&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;When a presigned URL is generated, it includes:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The resource's location (bucket, key, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A signature created using the server's access credentials.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A defined expiration time, after which the URL becomes invalid.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The server typically generates this URL, embedding the necessary permissions and expiration time into it. The client can then use the URL to directly upload a file (for a PUT request) or download a file (for a GET request) without needing further authentication.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Common Use Cases&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Direct File Uploads&lt;/b&gt;: Allow users to upload files directly to cloud storage from their browser or application without routing through your server, reducing load and complexity.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Secure File Sharing&lt;/b&gt;: Temporarily grant access to a file (like a report or media file) without exposing your credentials or full access permissions.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;API-Driven File Access&lt;/b&gt;: In microservices or serverless architectures, presigned URLs can be used to allow other services to interact with specific resources securely.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Benefits&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Security&lt;/b&gt;: Access is restricted by both permissions and expiration time.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Performance&lt;/b&gt;: Clients upload or download files directly from storage, reducing server bandwidth and processing load.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Simplified Permissions&lt;/b&gt;: You don&amp;rsquo;t need to manage complex access controls for each client interaction&amp;mdash;just generate the presigned URL with the right permissions.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Limitations&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Temporary Access&lt;/b&gt;: The URL is only valid for a limited time.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Sensitive Handling Required&lt;/b&gt;: If the URL is exposed, unauthorized users can access the resource until it expires.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;In summary, a presigned URL is a powerful tool for secure, temporary access to cloud storage resources, enabling efficient file transfers and fine-grained control over access.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 서버의 이미지 serving 부담을 줄여주고 클라이언트가 직접 파일 업로드함으로서 파일명 수정등의 소통비용도 줄일 수 있는 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>토막지식시리즈</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/431</guid>
      <comments>https://norwayy.tistory.com/431#entry431comment</comments>
      <pubDate>Sun, 18 Aug 2024 15:54:43 +0900</pubDate>
    </item>
    <item>
      <title>20240814 TIL</title>
      <link>https://norwayy.tistory.com/430</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;React Graphql Masterclass&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;32. Data Resolver - Overriding A Field&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GraphQL 서버는 graphql 요청시 scalar 타입이면 값을 그대로 response하고 object 타입이면 object 타입의 각 필드마다 관련된 매핑 함수가 있는지 확인한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어 아래와 같은 데이터 구조에서&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723567553099&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Query {
    sayHello: String
    customers: [Customer]!
}

type Customer {
    id: ID
    name: String
    age: Int
    city: String
}

public class Customer {
    private Integer id;
    private String name;
    private Integer age;
    private String city;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 쿼리 요청시에 sayHello 필드는 string을 바로 내려주지만, Customer 필드를 확인할 때 해당 필드는 Object 타입이므로 각 필드가 scalar를 리턴해도 관련 매핑 함수가 있는지 확인한 뒤 실행한 값을 내려준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래와 같은 age관련 함수가 있다면 항상 100을 내려줄 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723567667077&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @SchemaMapping(typeName = &quot;Customer&quot;)
    public Mono&amp;lt;Integer&amp;gt; age() {
        return Mono.just(100);
    }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Study/TIL (Today I Learned)</category>
      <author>GrapeMilk</author>
      <guid isPermaLink="true">https://norwayy.tistory.com/430</guid>
      <comments>https://norwayy.tistory.com/430#entry430comment</comments>
      <pubDate>Wed, 14 Aug 2024 01:49:18 +0900</pubDate>
    </item>
  </channel>
</rss>