<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>코드에 빠지다</title>
    <link>https://codiving.tistory.com/</link>
    <description>개인 프로젝트 정리 및 개발 관련 지식 공유를 위한 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 20 Apr 2026 11:47:06 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>구하천포</managingEditor>
    <image>
      <title>코드에 빠지다</title>
      <url>https://tistory1.daumcdn.net/tistory/4548259/attach/b040909171774132955ab71f1a9e3fb4</url>
      <link>https://codiving.tistory.com</link>
    </image>
    <item>
      <title>[eslint] interface, type Pascal Case로 강제하기</title>
      <link>https://codiving.tistory.com/241</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript로 개발할 때, &lt;code&gt;interface&lt;/code&gt;와 &lt;code&gt;type&lt;/code&gt;을 이용하여 타입을 선언하는 경우가 많습니다.&lt;br /&gt;이때 일반적으로 &lt;code&gt;Pascal Case&lt;/code&gt;를 사용하여 타입 이름을 작성하는데, ESLint를 이용해 이를 강제하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;{
  rules: {
    '@typescript-eslint/naming-convention': [
      'error',
      {
        selector: ['interface', 'typeAlias'], 
        format: ['PascalCase'],
        custom: {
          regex: '^[A-Z][a-zA-Z0-9]*$',
          match: true,
        },
      },
    ],
  },
} &lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@typescript-eslint/naming-convention&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@typescript-eslint/naming-convention은 TypeScript 코드에서 이름 규칙을 설정하는 규칙입니다.&lt;br /&gt;위 설정에서는 &lt;code&gt;selector&lt;/code&gt;를 통해 &lt;code&gt;interface&lt;/code&gt;와 &lt;code&gt;typeAlias&lt;/code&gt;를 선택합니다.&lt;br /&gt;format 옵션을 &lt;code&gt;PascalCase&lt;/code&gt;로 설정하여 Pascal Case로 작성되도록 강제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 규칙을 적용하면, Pascal Case를 따르지 않는 타입이나 인터페이스에 대해 에러가 발생하여 코드 스타일을 일관되게 유지할 수 있습니다.&lt;/p&gt;</description>
      <category>공유/ESLint</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/241</guid>
      <comments>https://codiving.tistory.com/241#entry241comment</comments>
      <pubDate>Thu, 14 Nov 2024 12:50:51 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] undefined 할당 금지</title>
      <link>https://codiving.tistory.com/240</link>
      <description>&lt;h3&gt;undefined 할당 금지&lt;/h3&gt;
&lt;h3&gt;undefined&lt;/h3&gt;
&lt;p&gt;선언했으나 &lt;strong&gt;초기화하지 않은 변수&lt;/strong&gt;, 함수의 반환 값이 없을 때, 객체에 존재하지 않는 속성 등에 &lt;strong&gt;자바스크립트가 자동으로 할당하는 값&lt;/strong&gt; 입니다.&lt;br&gt;코드에서 명시적으로 할당하지 않도록 합니다.&lt;/p&gt;
&lt;h3&gt;null&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;개발자가 의도적&lt;/strong&gt;으로 값이 없음을 표현하고 싶을 때 사용합니다.&lt;br&gt;예를 들어, 객체의 속성을 아직 할당하지 않았거나 특정 변수에 값이 없음을 명확히 하기 위해 null을 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-undefined&amp;quot;: &amp;quot;error&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;no-undefined&lt;/h3&gt;
&lt;p&gt;undefined를 할당하지 못 하게 하는 규칙입니다.&lt;/p&gt;</description>
      <category>공유/ESLint</category>
      <category>ESLint</category>
      <category>eslint undefined</category>
      <category>no-undefined</category>
      <category>null</category>
      <category>undefined</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/240</guid>
      <comments>https://codiving.tistory.com/240#entry240comment</comments>
      <pubDate>Thu, 14 Nov 2024 12:50:01 +0900</pubDate>
    </item>
    <item>
      <title>[TS] 타입스크립트에서 객체의 특정 키 값을 안전하게 변경하는 방법</title>
      <link>https://codiving.tistory.com/242</link>
      <description>&lt;p&gt;타입스크립트에서는 객체의 특정 key에 대한 값을 변경할 때, 타입 안전성을 유지하는 것이 중요합니다.&lt;br&gt;이번 글에서는 타입 안전성을 유지하면서 객체의 값을 수정하는 두 가지 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h3&gt;방법 1: 객체 확장 방식&lt;/h3&gt;
&lt;p&gt;아래 코드는 객체의 특정 key에 따라 값을 수정하는 예제입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;interface User {
  addr: string;
  name: string;
  age: number;
  gender: &amp;quot;M&amp;quot; | &amp;quot;W&amp;quot;;
}

type UserKey = keyof User;
type UserValue = User[UserKey];

const user: User = {
  addr: &amp;quot;&amp;quot;,
  name: &amp;quot;&amp;quot;,
  age: 0,
  gender: &amp;quot;M&amp;quot;,
};

const onChangeUser = (user: User, key: UserKey, value: UserValue) =&amp;gt; {
  return {
    ...user,
    [key]: value,
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 코드에서는 &lt;code&gt;key&lt;/code&gt;와 &lt;code&gt;value&lt;/code&gt;를 받아 객체를 반환하는데, &lt;strong&gt;타입 안전성에 문제가&lt;/strong&gt; 있습니다.&lt;br&gt;예를 들어, 아래와 같이 &lt;code&gt;addr&lt;/code&gt;에 &lt;code&gt;number&lt;/code&gt; 타입이 잘못 들어가도 타입 오류가 발생하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;onChangeUser(user, &amp;quot;addr&amp;quot;, 1);  // &amp;#39;addr&amp;#39;은 string이어야 하지만, number가 들어감&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;방법 2: 제네릭을 활용한 타입 안전성 강화&lt;/h3&gt;
&lt;p&gt;타입 안전성을 강화하기 위해 제네릭을 사용할 수 있습니다.&lt;br&gt;제네릭을 활용하면 &lt;code&gt;key&lt;/code&gt;가 설정될 때 해당 &lt;code&gt;key&lt;/code&gt;에 맞는 타입이 &lt;strong&gt;자동으로 추론&lt;/strong&gt;되므로, 잘못된 타입의 값이 들어갈 수 없게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;interface User {
  addr: string;
  name: string;
  age: number;
  gender: &amp;quot;M&amp;quot; | &amp;quot;W&amp;quot;;
}

interface ObjectHandler&amp;lt;T&amp;gt; {
  &amp;lt;K extends keyof T&amp;gt;(obj: T, key: K, value: T[K]): void;
}

const user: User = {
  addr: &amp;quot;&amp;quot;,
  name: &amp;quot;&amp;quot;,
  age: 0,
  gender: &amp;quot;M&amp;quot;,
};

const onChangeUser: ObjectHandler&amp;lt;User&amp;gt; = (obj, key, value) =&amp;gt; {
  return {
    ...obj,
    [key]: value,
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 작성하면 &lt;code&gt;addr&lt;/code&gt; 필드에는 &lt;code&gt;string&lt;/code&gt;만 들어갈 수 있으므로, &lt;code&gt;onChangeUser(user, &amp;quot;addr&amp;quot;, 1)&lt;/code&gt;처럼 잘못된 타입이 들어가는 경우 &lt;strong&gt;타입 에러가 발생&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;h3&gt;결론&lt;/h3&gt;
&lt;p&gt;제네릭을 활용하여 객체의 &lt;code&gt;key&lt;/code&gt;와 &lt;code&gt;value&lt;/code&gt; 타입을 강제하면 타입 안전성을 확보할 수 있습니다.&lt;br&gt;특히, 리액트에서 객체 상태를 업데이트할 때 이 방식을 사용하면 타입 오류를 방지하고 코드의 가독성을 높일 수 있습니다.  &lt;/p&gt;
&lt;p&gt;리액트에서는 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const onChangeUser: ObjectHandler&amp;lt;User&amp;gt; = (key, value) =&amp;gt; {
  setUser(prev =&amp;gt; ({
    ...prev,
    [key]: value,
  }));
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 방법을 통해 객체의 상태를 안전하고 일관되게 관리할 수 있습니다.&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>react dialog 상태 관리</category>
      <category>react modal 상태 관리</category>
      <category>react 객체 관리</category>
      <category>react 객체 상태 관리</category>
      <category>typescript</category>
      <category>typescript 객체</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/242</guid>
      <comments>https://codiving.tistory.com/242#entry242comment</comments>
      <pubDate>Thu, 14 Nov 2024 12:50:00 +0900</pubDate>
    </item>
    <item>
      <title>[JS] ?? 와 || 연산자 차이</title>
      <link>https://codiving.tistory.com/239</link>
      <description>&lt;h3&gt;nullish coalescing operator (??)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;??&lt;/strong&gt; 연산자는 &lt;strong&gt;null, undefined&lt;/strong&gt; 인 경우 기본값을 제공하는 연산자입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const value = variable ?? &amp;quot;default&amp;quot;;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;variable&lt;/strong&gt; 값이 &lt;strong&gt;null, undefined&lt;/strong&gt; 인 경우 &lt;strong&gt;default&lt;/strong&gt; 값으로 설정됩니다.&lt;/p&gt;
&lt;h3&gt;logical or operator (||)&lt;/h3&gt;
&lt;p&gt;falsy 값인 경우 기본값을 제공하는 연산자입니다.&lt;br&gt;즉, 개발자의 의도와 맞지 않게 false, 0, &amp;#39;&amp;#39;, NaN 과 같은 값에서도 default 값으로 설정될 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const value = 0 || &amp;quot;default&amp;quot;; // default
const value = 0 ?? &amp;quot;default&amp;quot;; // 0&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;strictNullChecks 설정&lt;/h3&gt;
&lt;p&gt;tsconfig.json 파일 &lt;strong&gt;strictNullChecks 옵션&lt;/strong&gt;을 true로 설정해두시는 것을 추천드립니다.&lt;br&gt;TypeScript가 더 엄격하게 null, undefined 값을 검사합니다.&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>??</category>
      <category>?? || 차이</category>
      <category>?? 연산자</category>
      <category>falsy</category>
      <category>Logical OR operator</category>
      <category>Nullish coalescing operator</category>
      <category>strictNullChecks</category>
      <category>||</category>
      <category>|| 연산자</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/239</guid>
      <comments>https://codiving.tistory.com/239#entry239comment</comments>
      <pubDate>Tue, 22 Oct 2024 18:27:14 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] 불필요한 타입 단언 (as) 찾기</title>
      <link>https://codiving.tistory.com/238</link>
      <description>&lt;h3&gt;불필요한 타입 단언, 불필요한 as 찾기&lt;/h3&gt;
&lt;p&gt;TypeScript로 개발 시 as의 사용은 버그를 발생시킬 수 있기 때문에 지양하게 됩니다.&lt;br&gt;그러나 개발하다보면 어쩔 수 없이 as를 사용하게 되는 부분이 있습니다.&lt;br&gt;이때 타입 추론이 되었는데 작성하는 경우도 있습니다.&lt;br&gt;이렇게 &lt;strong&gt;불필요하게 작성된 as를 찾아주는 규칙&lt;/strong&gt;을 알아보겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;@typescript-eslint/no-unnecessary-type-assertion&amp;quot;: &amp;quot;error&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;no-unnecessary-type-assertion&lt;/h3&gt;
&lt;p&gt;해당 규칙은 타입을 명확히 알고 있음에도 불구하고 as를 이용하여 &lt;strong&gt;타입 단언&lt;/strong&gt;한 경우를 찾아줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const someValue: string = &amp;quot;Hello&amp;quot;;
const value = someValue as string; // 불필요한 타입 단언&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>as</category>
      <category>eslint as</category>
      <category>no-unnecessary-type-assertion</category>
      <category>typescript as</category>
      <category>타입 단언</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/238</guid>
      <comments>https://codiving.tistory.com/238#entry238comment</comments>
      <pubDate>Tue, 22 Oct 2024 18:26:54 +0900</pubDate>
    </item>
    <item>
      <title>[JS,TS] 문자열에 있는 uuid 제거하기</title>
      <link>https://codiving.tistory.com/237</link>
      <description>&lt;h3&gt;문자열에 있는 uuid 제거하기&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;window.location.pathname&lt;/strong&gt;으로 주소창에 있는 데이터를 사용할 일이 있었습니다.&lt;br&gt;이 경우 다이나믹 라우팅을 위해 uuid가 있는 경우, 해당 &lt;strong&gt;uuid를 제거&lt;/strong&gt;하여 보여주어야 했습니다.&lt;br&gt;uuid를 간단하게 제거할 수 있는 함수를 구현해봅시다.   &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const removeUUID = (str: string) =&amp;gt; {
  // UUID 감지 정규식 (8-4-4-4-12)
  const uuidRegex =
    /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/;

  return url.replace(uuidRegex, &amp;quot;&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위처럼 &lt;strong&gt;정규식&lt;/strong&gt;을 이용하면 간단하게 uuid를 제거할 수 있습니다.&lt;br&gt;만약 다른 형식의 uuid인 경우 위 정규식을 이용하여 해당 형식에 맞게 수정해주시면 됩니다.   &lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>js uuid</category>
      <category>js uuid 정규식</category>
      <category>js uuid 제거</category>
      <category>UUID</category>
      <category>uuid 정규식</category>
      <category>uuid 제거</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/237</guid>
      <comments>https://codiving.tistory.com/237#entry237comment</comments>
      <pubDate>Mon, 21 Oct 2024 19:03:48 +0900</pubDate>
    </item>
    <item>
      <title>[JS,TS] 문자열로 되어 있는 new Date 함수 실행하기</title>
      <link>https://codiving.tistory.com/236</link>
      <description>&lt;h3&gt;문자열로 되어 있는 new Date 함수 실행하기&lt;/h3&gt;
&lt;p&gt;로그를 이용하여 사용자 행동 패턴 분석을 확인할 수 있는 서비스를 구현하고 있습니다.&lt;br&gt;프론트에서 aggregation 명령어를 작성하여 문자열 그대로 서버에 전송하여, 서버에서 문자열을 파싱하여 제대로 된 aggregation 명령어로 변환 후 실행합니다.&lt;br&gt;&lt;br /&gt;&lt;br&gt;이 경우 프론트에서 &lt;strong&gt;new Date(&amp;quot;날짜&amp;quot;)&lt;/strong&gt; 함수를 작성하게 되면 문자열이기 때문에 서버에서 제대로 처리를 할 수 없습니다.&lt;br&gt;모든 경우가 아래처럼 &lt;strong&gt;key, value 형태인 객체&lt;/strong&gt;로 되어 있습니다.&lt;br&gt;아래 형태가 모두 문자열이라고 가정하고 new Date를 어떻게 실행시킬 수 있을까, 실행하는 방법을 알아보도록 하겠습니다.    &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;date: {
  $gte: new Date(&amp;quot;날짜&amp;quot;),
  $lte: new Date(&amp;quot;날짜&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;코드&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const dateStrings = obj[key].match(/new Date\(&amp;quot;(.+?)&amp;quot;\)/);
if (dateStrings) {
  obj[key] = new Date(dateStrings[1]);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위처럼 간단하게 정규식을 이용하여 &lt;strong&gt;new Date(&amp;quot;날짜&amp;quot;)&lt;/strong&gt; 함수 내에 있는 &lt;strong&gt;&amp;quot;날짜&amp;quot;&lt;/strong&gt;를 뽑아내면 됩니다.&lt;br&gt;그리고 new Date 함수를 이용하여 실행해주면 됩니다.&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>aggregation date</category>
      <category>date</category>
      <category>date 문자열</category>
      <category>날짜</category>
      <category>날짜 문자열</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/236</guid>
      <comments>https://codiving.tistory.com/236#entry236comment</comments>
      <pubDate>Mon, 21 Oct 2024 19:02:29 +0900</pubDate>
    </item>
    <item>
      <title>[JS,TS] 문자열로 되어 있는 정규식 실행하기</title>
      <link>https://codiving.tistory.com/235</link>
      <description>&lt;h3&gt;문자열로 되어 있는 정규식 실행하기&lt;/h3&gt;
&lt;p&gt;로그를 이용하여 사용자 행동 패턴 분석을 확인할 수 있는 서비스를 구현하고 있습니다.&lt;br&gt;프론트에서 aggregation 명령어를 작성하여 문자열 그대로 서버에 전송하여, 서버에서 문자열을 파싱하여 제대로 된 aggregation 명령어로 변환 후 실행합니다.&lt;br&gt;&lt;br/&gt;&lt;br&gt;이 경우 프론트에서 &lt;strong&gt;new RegExp(&amp;quot;정규식&amp;quot;)&lt;/strong&gt; 함수를 작성하게 되면 문자열이기 때문에 서버에서 제대로 처리를 할 수 없습니다.&lt;br&gt;모든 경우가 아래처럼 &lt;strong&gt;key, value 형태인 객체&lt;/strong&gt;로 되어 있습니다.&lt;br&gt;아래 형태가 모두 문자열이라고 가정하고 new RegExp 어떻게 실행시킬 수 있을까, 실행하는 방법을 알아보도록 하겠습니다.  &lt;/p&gt;
&lt;h3&gt;코드&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const regexs = obj[key].match(/new RegExp\((.+?)\)/);
if (regexs) {
  const regexString = regexs[1];
  obj[key] = new RegExp(regexString);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위처럼 간단하게 정규식을 이용하여 &lt;strong&gt;new RegExp(&amp;quot;정규식&amp;quot;)&lt;/strong&gt; 함수 내에 있는 &lt;strong&gt;&amp;quot;정규식&amp;quot;&lt;/strong&gt;를 뽑아내면 됩니다.&lt;br&gt;그리고 new RegExp 함수를 이용하여 실행해주면 됩니다.&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>aggregation 정규식</category>
      <category>정규식</category>
      <category>정규식 문자열</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/235</guid>
      <comments>https://codiving.tistory.com/235#entry235comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:59:42 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] 구조 분해 할당 관련 규칙</title>
      <link>https://codiving.tistory.com/234</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구조 분해 할당&lt;/b&gt;과 관련된 eslint 규칙에 대해 알아보도록 하겠습니다.&lt;br /&gt;최종 규칙을 보여드리면 아래와 같습니다.&lt;br /&gt;하나씩 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;rules&quot;: {
    &quot;prefer-destructuring&quot;: &quot;off&quot;,
    &quot;no-empty-pattern&quot;: &quot;error&quot;,
    &quot;no-useless-rename&quot;: &quot;error&quot;,
    &quot;no-unused-vars&quot;: &quot;warn&quot;,
     &quot;@typescript-eslint/no-unused-vars&quot;: [
      &quot;error&quot;,
      {
        &quot;args&quot;: &quot;none&quot;,
        &quot;argsIgnorePattern&quot;: &quot;^_&quot;,
        &quot;varsIgnorePattern&quot;: &quot;^_&quot;,
        &quot;ignoreRestSiblings&quot;: true
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;prefer-destructuring&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 분해 할당을 &lt;b&gt;권장&lt;/b&gt;하는 규칙입니다. 옵션으로 객체, 배열에 대해 따로 설정해줄 수 있습니다.&lt;br /&gt;개인적으로 강제할 필요까지는 없다고 생각하여 규칙에 추가하지는 않았습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-unused-vars, @typescript-eslint/no-unused-vars&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 않는 변수에 대해 처리하는 규칙입니다.&lt;br /&gt;상세한 정보는 &lt;a href=&quot;https://codiving.kr/230&quot;&gt;해당 게시글&lt;/a&gt;에서 확인 부탁드립니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-empty-pattern&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;빈 구조 분해 할당&lt;/b&gt; 패턴은 의미가 없기 때문에 불필요한 코드 작성을 방지해줍니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;// bad
const {} = obj;
const [] = arr;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-useless-rename&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 분해 할당 시 &lt;b&gt;동일한 이름으로 속성을 재할당&lt;/b&gt; 하는 것을 방지하는 규칙입니다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;// bad
const { x: x } = obj;

// good
const { x } = obj;
const { x: name } = obj;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>@typescript-eslint/no-unused-vars</category>
      <category>ESLint</category>
      <category>eslint 구조 분해 할당</category>
      <category>no-empty-pattern</category>
      <category>no-unused-vars</category>
      <category>no-useless-rename</category>
      <category>prefer-destructuring</category>
      <category>구조 분해 할당</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/234</guid>
      <comments>https://codiving.tistory.com/234#entry234comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:52:16 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] optional chaining, 옵셔널 체이닝 관련 규칙</title>
      <link>https://codiving.tistory.com/233</link>
      <description>&lt;p&gt;&lt;strong&gt;optional chaining(옵셔널 체이닝, ?.)&lt;/strong&gt;과 관련된 eslint 규칙에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-unsafe-optional-chaining&amp;quot;: &amp;quot;error&amp;quot;,
    &amp;quot;@typescript-eslint/prefer-optional-chain&amp;quot;: &amp;quot;error&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;no-unsafe-optional-chaining&lt;/h3&gt;
&lt;p&gt;옵셔널 체이닝이 &lt;strong&gt;필요 없는 곳에 사용된 위치&lt;/strong&gt;를 찾아주는 규칙입니다.&lt;br&gt;옵셔널 체이닝은 null, undefined가 될 수 있는 곳에 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// bad
const obj = { name: &amp;quot;name&amp;quot; }
console.log(obj?.name)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;@typescript-eslint/prefer-optional-chain&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;옵셔널 체이닝을 권장&lt;/strong&gt;하는 규칙입니다.&lt;br&gt;불필요하게 &amp;amp;&amp;amp; 연산자를 계속해서 사용하는 것보다 옵셔널 체이닝을 이용하여 가독성 있게 구현하도록 도와줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// bad
const msg = error &amp;amp;&amp;amp; error.name &amp;amp;&amp;amp; error.name.message;

// good
const msg = error?.name?.message;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>@typescript-eslint/prefer-optional-chain</category>
      <category>ESLint</category>
      <category>no-unsafe-optional-chaining</category>
      <category>Optional Chaining</category>
      <category>optional chaining eslint</category>
      <category>옵셔널 체이닝</category>
      <category>옵셔널 체이닝 eslint</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/233</guid>
      <comments>https://codiving.tistory.com/233#entry233comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:50:31 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] switch 문</title>
      <link>https://codiving.tistory.com/232</link>
      <description>&lt;p&gt;&lt;strong&gt;switch 문&lt;/strong&gt;과 관련된 eslint 규칙에 대해 알아보도록 하겠습니다.&lt;br&gt;최종 규칙을 보여드리면 아래와 같습니다.&lt;br&gt;하나씩 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;default-case&amp;quot;: &amp;quot;error&amp;quot;,
    &amp;quot;no-fallthrough&amp;quot;: &amp;quot;error&amp;quot;,
    &amp;quot;no-case-declarations&amp;quot;: &amp;quot;error&amp;quot;,
    &amp;quot;consistent-return&amp;quot;: &amp;quot;error&amp;quot;,
    &amp;quot;no-duplicate-case&amp;quot;: &amp;quot;error&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;default-case&lt;/h3&gt;
&lt;p&gt;switch 문에 &lt;strong&gt;default 케이스&lt;/strong&gt;가 존재하도록 강제하는 규칙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// bad
switch (condition) {
  case 1:
    break;
}

// good
switch (condition) {
  case 1:
    break;

  default:
    break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;no-fallthrough&lt;/h3&gt;
&lt;p&gt;case 문에서 &lt;strong&gt;break&lt;/strong&gt; 없이 다음 case로 넘어가는 것을 방지하는 규칙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// bad
switch (condition) {
  case 1:
  case 2:
    break;
}

// good
switch (condition) {
  case 1:
    break;

  case 2:
    break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;no-case-declarations&lt;/h3&gt;
&lt;p&gt;case 문 안에 변수를 선언할 때 &lt;strong&gt;블록을 사용하도록 강제&lt;/strong&gt;하는 규칙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// bad
switch (condition) {
  case 1:
    const name = &amp;quot;name&amp;quot;;
    break;
}

// good
switch (condition) {
  case 1: {
    const name = &amp;quot;name&amp;quot;;
    break;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;consistent-return&lt;/h3&gt;
&lt;p&gt;case 문에서 &lt;strong&gt;반환 값&lt;/strong&gt;을 일관되게 반환하거나, 반환하지 않도록 하는 규칙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// bad
switch (condition) {
  case &amp;quot;A&amp;quot;:
    return &amp;quot;a&amp;quot;;

  case &amp;quot;b&amp;quot;:

  default:
    break;
}

// good
switch (condition) {
  case &amp;quot;A&amp;quot;:
    return &amp;quot;a&amp;quot;;

  case &amp;quot;b&amp;quot;:
    return &amp;quot;b&amp;quot;;

  default:
    return &amp;quot;c&amp;quot;;
    break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;no-duplicate-case&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;동일한 case 문&lt;/strong&gt;을 작성하지 않도록 하는 규칙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// bad
switch (a) {
  case &amp;quot;A&amp;quot;:
    return &amp;quot;a&amp;quot;;

  case &amp;quot;A&amp;quot;:
    return &amp;quot;A&amp;quot;;

  default:
    return &amp;quot;c&amp;quot;;
    break;
}

// good
switch (a) {
  case &amp;quot;A&amp;quot;:
    return &amp;quot;a&amp;quot;;

  case &amp;quot;b&amp;quot;:
    return &amp;quot;b&amp;quot;;

  default:
    return &amp;quot;c&amp;quot;;
    break;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>consistent-return</category>
      <category>default-case</category>
      <category>ESLint</category>
      <category>eslint switch</category>
      <category>no-case-declarations</category>
      <category>no-duplicate-case</category>
      <category>no-fallthrough</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/232</guid>
      <comments>https://codiving.tistory.com/232#entry232comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:48:44 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] 삼항연산자 관련 규칙</title>
      <link>https://codiving.tistory.com/231</link>
      <description>&lt;p&gt;&lt;strong&gt;삼항연산자&lt;/strong&gt;를 사용할 때, 도움이 되는 eslint 규칙에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-unneeded-ternary&amp;quot;: &amp;quot;error&amp;quot;,
    &amp;quot;multiline-ternary&amp;quot;: &amp;quot;off&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;no-unneeded-ternary&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;불필요하게 사용된 삼항연산자&lt;/strong&gt;를 찾아주는 규칙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// bad
const value = condition ? true : false;

// good
const value = Boolean(condition);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;multiline-ternary&lt;/h3&gt;
&lt;p&gt;삼항연산자를 사용할 때, 가독성을 위해 &lt;strong&gt;줄바꿈을 강제&lt;/strong&gt;하는 규칙입니다.&lt;br&gt;저 같은 경우 가독성에 큰 문제를 느끼지 못 하고 있기 때문에 해당 규칙은 사용하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// bad ..?
const value = condition ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;;

// good ..?
const value = condition
? &amp;quot;Yes&amp;quot;
: &amp;quot;No&amp;quot;;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>ESLint</category>
      <category>multiline-ternary</category>
      <category>no-unneeded-ternary</category>
      <category>삼항연산자</category>
      <category>삼항연산자 eslint</category>
      <category>삼항연산자 줄바꿈</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/231</guid>
      <comments>https://codiving.tistory.com/231#entry231comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:47:24 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] 사용하지 않는 변수 찾기</title>
      <link>https://codiving.tistory.com/230</link>
      <description>&lt;p&gt;&lt;strong&gt;사용하지 않는 변수&lt;/strong&gt;와 관련된 eslint 규칙에 대해 알아보도록 하겠습니다.&lt;br&gt;최종 규칙을 보여드리면 아래와 같습니다.&lt;br&gt;하나씩 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-unused-vars&amp;quot;: &amp;quot;off&amp;quot;,
    &amp;quot;@typescript-eslint/no-unused-vars&amp;quot;: [
      &amp;quot;error&amp;quot;,
      {
        &amp;quot;args&amp;quot;: &amp;quot;none&amp;quot;,
        &amp;quot;argsIgnorePattern&amp;quot;: &amp;quot;^_&amp;quot;,
        &amp;quot;varsIgnorePattern&amp;quot;: &amp;quot;^_&amp;quot;,
        &amp;quot;ignoreRestSiblings&amp;quot;: true
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;no-unused-vars&lt;/h3&gt;
&lt;p&gt;JavaScript로 작성된 코드의 경우에만 적용이 됩니다.&lt;/p&gt;
&lt;p&gt;만약 JavaScript 프로젝트만 사용하신다면 아래처럼 작성해주시면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-unused-vars&amp;quot;: &amp;quot;off&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;@typescript-eslint/no-unused-vars&lt;/h3&gt;
&lt;p&gt;TypeScript로 작성된 코드의 경우 적용됩니다.&lt;br&gt;설정되어 있는 JavaScript용 eslint를 off 해주신 다음 사용하시면 됩니다.&lt;br&gt;추가 옵션으로 사용하지 않는 변수에 대해 &lt;strong&gt;prefix _&lt;/strong&gt; 를 사용하여 에러 표시가 되지 않도록 하였습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-unused-vars&amp;quot;: &amp;quot;off&amp;quot;,
    &amp;quot;@typescript-eslint/no-unused-vars&amp;quot;: [
      &amp;quot;error&amp;quot;,
      {
        &amp;quot;args&amp;quot;: &amp;quot;none&amp;quot;,
        &amp;quot;argsIgnorePattern&amp;quot;: &amp;quot;^_&amp;quot;,
        &amp;quot;varsIgnorePattern&amp;quot;: &amp;quot;^_&amp;quot;,
        &amp;quot;ignoreRestSiblings&amp;quot;: true
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>@typescript-eslint/no-unused-vars</category>
      <category>ESLint</category>
      <category>js 사용하지 않는 변수</category>
      <category>no-unused-vars</category>
      <category>ts 사용하지 않는 변수</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/230</guid>
      <comments>https://codiving.tistory.com/230#entry230comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:46:09 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] if 문과 관련된 규칙</title>
      <link>https://codiving.tistory.com/229</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;if 문&lt;/b&gt;과 관련된 eslint 규칙에 대해 알아보도록 하겠습니다.&lt;br /&gt;최종 규칙을 보여드리면 아래와 같습니다.&lt;br /&gt;하나씩 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;rules&quot;: {
    &quot;curly&quot;: [&quot;error&quot;, &quot;all&quot;],
    &quot;no-constant-condition&quot;: &quot;error&quot;,
    &quot;no-lonely-if&quot;: &quot;error&quot;,
    &quot;no-negated-condition&quot;: &quot;warn&quot;,
    &quot;no-else-return&quot;: &quot;warn&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;curly&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문 외에 &lt;b&gt;중괄호&lt;/b&gt;를 사용하는 if, else, for, while 등에 적용되는 규칙입니다.&lt;br /&gt;강제로 중괄호를 작성하게 함으로써 가독성을 높이고 실수를 방지할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// bad
if (condition) statement;

// good
if (condition) {
  statement;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-constant-condition&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 규칙은 &lt;b&gt;상수 값&lt;/b&gt;의 경우 if, while 등 조건식에서 사용하는 것을 금지하는 규칙입니다.&lt;br /&gt;항상 참 또는 거짓이기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;// bad
if (true) {}

// bad
if (false) {}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-lonely-if&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;else 블록 안에 &lt;b&gt;단독으로 if문이 작성&lt;/b&gt;되는 경우를 금지하는 규칙입니다.&lt;br /&gt;그 대신 &lt;b&gt;else-if&lt;/b&gt;를 사용하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// bad
if (condition1) {}
else {
  if (condition2) {}
}

// good
if (condition1) {}
else if (condition2) {}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-negated-condition&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부정 조건문 금지&lt;/b&gt;하는 규칙입니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// bad
if (!isFixed) {}
else {}

// good
if (isFixed) {}
else {}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;no-else-return&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;불필요한 else 사용을 금지&lt;/b&gt;하는 규칙입니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// bad
if (condition) {
  return true;
} else {
  return false;
}

// good
if (condition) {
  return true;
}
return false;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>curly</category>
      <category>ESLint</category>
      <category>eslint if return</category>
      <category>eslint if문</category>
      <category>eslint 조건문</category>
      <category>eslint 중괄호</category>
      <category>no-constant-condition</category>
      <category>no-else-return</category>
      <category>no-lonely-if</category>
      <category>no-negated-condition</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/229</guid>
      <comments>https://codiving.tistory.com/229#entry229comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:44:42 +0900</pubDate>
    </item>
    <item>
      <title>[eslint] 불필요한, 의미없는 코드를 찾기</title>
      <link>https://codiving.tistory.com/228</link>
      <description>&lt;p&gt;코드를 작성하면 불필요하고 의미없는 코드가 생기는 경우가 있습니다.&lt;br&gt;이때 일일이 찾기가 힘든데 &lt;strong&gt;eslint&lt;/strong&gt;를 이용하여 불필요한 표현식을 찾을 수 있습니다.  &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;rules&amp;quot;: {
    &amp;quot;no-unused-expressions&amp;quot;: &amp;quot;error&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;no-unused-expressions&lt;/h3&gt;
&lt;p&gt;아래 코드는 정상적으로 실행되지 않는 불필요한 코드입니다.&lt;br&gt;접근은 가능하지만 기능이 존재하지 않습니다.&lt;br&gt;위 규칙을 사용하면 아래와 같은 코드를 찾아줍니다. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const router = useRouter();
router;

1 + 2;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공유/ESLint</category>
      <category>ESLint</category>
      <category>js 불필요한 코드</category>
      <category>no-unused-expressions</category>
      <category>ts 불필요한 코드</category>
      <category>불필요한 코드</category>
      <category>불필요한 코드 eslint</category>
      <category>의미 없는 코드</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/228</guid>
      <comments>https://codiving.tistory.com/228#entry228comment</comments>
      <pubDate>Mon, 21 Oct 2024 18:41:58 +0900</pubDate>
    </item>
    <item>
      <title>NextJS 페이지 라우터에서 모든 페이지 라우트 확인하는 방법</title>
      <link>https://codiving.tistory.com/227</link>
      <description>&lt;p&gt;NextJS 페이지 라우터에서 모든 페이지 라우트 확인하는 방법.&lt;/p&gt;
&lt;p&gt;페이지 라우터는 &lt;strong&gt;page 디렉터리 구조&lt;/strong&gt;에 따라 자동으로 라우트가 생성됩니다.&lt;br&gt;프로젝트의 &lt;strong&gt;모든 라우트&lt;/strong&gt;를 알아야 하는 필요성이 생겨 구현하였습니다.&lt;/p&gt;
&lt;h3&gt;코드&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;type Page = string;

export const getStaticProps: GetStaticProps&amp;lt;{ pages: Page[] }&amp;gt; = async () =&amp;gt; {
  const pagesDir = path.join(process.cwd(), &amp;#39;pages&amp;#39;);
  const pages = getAllPages(pagesDir);

  return {
    props: {
      pages,
    },
  };
};

function getAllPages(dir: string, basePath: string = &amp;#39;&amp;#39;): Page[] {
  const files = fs.readdirSync(dir);
  const pages: Page[] = [];

  files.forEach((file) =&amp;gt; {
    const fullPath = path.join(dir, file);
    const stat = fs.statSync(fullPath);

    if (stat.isDirectory()) {
      pages.push(...getAllPages(fullPath, `${basePath}/${file}`));
    } else {
      if (file.endsWith(&amp;#39;.js&amp;#39;) || file.endsWith(&amp;#39;.tsx&amp;#39;)) {
        let route = `${basePath}/${file.replace(/\.(js|tsx)$/, &amp;#39;&amp;#39;)}`;
        if (route === &amp;#39;/index&amp;#39;) route = &amp;#39;/&amp;#39;;
        pages.push(route);
      }
    }
  });

  return pages;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;.js, .tsx 확장자로 끝나는 파일만 찾아서 파일명을 뽑아내면 프로젝트에 존재하는 &lt;strong&gt;모든 라우트를 확인&lt;/strong&gt;할 수 있습니다.&lt;br&gt;저는 getStaticProps를 이용하여 해결하였는데 getAllPages 함수만을 이용하셔도 가능합니다. &lt;/p&gt;</description>
      <category>공유/React, Next</category>
      <category>nextjs</category>
      <category>nextjs 라우트</category>
      <category>nextjs 모든 라우트</category>
      <category>nextjs 프로젝트 라우트</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/227</guid>
      <comments>https://codiving.tistory.com/227#entry227comment</comments>
      <pubDate>Wed, 16 Oct 2024 18:33:02 +0900</pubDate>
    </item>
    <item>
      <title>Next에서 특정 페이지에 사용자가 머무는 시간 측정 방법</title>
      <link>https://codiving.tistory.com/226</link>
      <description>&lt;p&gt;구글 애널리틱스(GA)를 이용하여 해결할 수 있습니다. 그러나 &lt;strong&gt;자체적으로 관리&lt;/strong&gt;하기 위해 커스텀 훅을 구현하였습니다.&lt;/p&gt;
&lt;h3&gt;학습 개념&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Next.js 페이지 로드 이벤트&lt;/li&gt;
&lt;li&gt;Next.js 페이지 이탈, 종료 이벤트&lt;/li&gt;
&lt;li&gt;Next.js 새로고침 이벤트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;usePageStayDuration&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;usePageStayDuration&lt;/strong&gt; 훅은 사용자가 현재 페이지에 &lt;strong&gt;머무는 시간을 측정&lt;/strong&gt;하는 기능을 제공합니다.&lt;br&gt;이 훅은 &lt;strong&gt;Next.js의 라우터 이벤트&lt;/strong&gt;를 활용하여 페이지 전환 및 새로 고침 시 머무른 시간을 기록합니다.&lt;br&gt;아래에서 이 훅의 동작 방식과 구현 세부 사항을 설명하겠습니다.&lt;/p&gt;
&lt;h3&gt;코드&lt;/h3&gt;
&lt;p&gt;먼저 구현된 코드를 확인해보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useRouter } from &amp;quot;next/router&amp;quot;;
import { useEffect, useRef } from &amp;quot;react&amp;quot;;

const usePageStayDuration = () =&amp;gt; {
  const router = useRouter();

  const isRefresh = useRef(false);
  const startTime = useRef(Date.now());
  const duration = useRef(0);

  useEffect(() =&amp;gt; {
    const calculateDuration = () =&amp;gt; {
      const endTime = Date.now();
      return (endTime - startTime.current) / 1000;
    };

    const handleRouteChangeStart = () =&amp;gt; {
      if (isRefresh.current) return;
      duration.current += calculateDuration();
    };

    const handleRouteChangeComplete = () =&amp;gt; {
      if (isRefresh.current) return;
      startTime.current = Date.now();
      duration.current = 0;
    };

    const handleBeforeUnload = () =&amp;gt; {
      isRefresh.current = true;
      duration.current += calculateDuration();
    };

    router.events.on(&amp;quot;routeChangeStart&amp;quot;, handleRouteChangeStart);
    router.events.on(&amp;quot;routeChangeComplete&amp;quot;, handleRouteChangeComplete);
    window.addEventListener(&amp;quot;beforeunload&amp;quot;, handleBeforeUnload);

    return () =&amp;gt; {
      router.events.off(&amp;quot;routeChangeStart&amp;quot;, handleRouteChangeStart);
      router.events.off(&amp;quot;routeChangeComplete&amp;quot;, handleRouteChangeComplete);
      window.removeEventListener(&amp;quot;beforeunload&amp;quot;, handleBeforeUnload);
    };
  }, [router.events, startTime]);
};

export default usePageStayDuration;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;상태 관리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;isRefresh&lt;/strong&gt;&lt;br&gt;페이지 새로 고침 여부를 확인하는 참조 변수입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;startTime&lt;/strong&gt;&lt;br&gt;사용자가 페이지에 진입한 시점을 기록합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;duration&lt;/strong&gt;&lt;br&gt;사용자가 페이지에 머문 총 시간을 초 단위로 누적합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;제가 구현한 코드에서는 &lt;strong&gt;렌더링과 무관&lt;/strong&gt;하기 때문에 &lt;strong&gt;ref로 관리&lt;/strong&gt;하였습니다.&lt;br&gt;만약 렌더링에 따라 동작하기 원하면 &lt;strong&gt;state로 변경&lt;/strong&gt;하시면 됩니다.&lt;/p&gt;
&lt;h3&gt;이벤트 핸들러 및 함수 설명&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;routeChangeStart&lt;/strong&gt; 이벤트는 route가 변경되는 시점에 발생하는 이벤트입니다.&lt;br&gt;&lt;strong&gt;routeChangeComplete&lt;/strong&gt; 이벤트는 route 변경이 완료된 시점에 발생하는 이벤트입니다.&lt;br&gt;위 2가지는 Next.js에서 제공해주는 이벤트입니다.&lt;/p&gt;
&lt;p&gt;새로고침 이벤트는 window 객체를 사용하셔야 합니다.&lt;br&gt;&lt;strong&gt;beforeunload&lt;/strong&gt; 이벤트를 사용하시면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;calculateDuration&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;페이지에 머무는 시간을 계산&lt;/strong&gt;합니다. 현재 시간을 가져와서 startTime과의 차이를 계산하여 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;handleRouteChangeStart&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;페이지 전환이 시작될 때 호출&lt;/strong&gt;됩니다. 페이지 새로 고침이 아닌 경우, 현재까지의 머무른 시간을 누적합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;handleRouteChangeComplete&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;페이지 전환이 완료될 때 호출&lt;/strong&gt;됩니다. 새로 고침이 아닌 경우, 새 페이지 진입을 위해 startTime을 현재 시간으로 업데이트하고 duration을 초기화합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;handleBeforeUnload&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;사용자가 페이지를 떠나기 전에 호출&lt;/strong&gt;됩니다. 새로 고침 여부를 확인하고, 현재까지의 머무른 시간을 누적합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;결론&lt;/h3&gt;
&lt;p&gt;Next에서 제공해주는 &lt;strong&gt;라우터 이벤트를 활용&lt;/strong&gt;하여 간단히 구현해보았습니다.&lt;br&gt;사용자의 행동을 분석하는데 도움이 되셨으면 좋겠습니다.&lt;/p&gt;</description>
      <category>공유/React, Next</category>
      <category>next 새로고침 이벤트</category>
      <category>next 페이지 시작 이벤트</category>
      <category>next 페이지 이동 이벤트</category>
      <category>next 페이지 종료 이벤트</category>
      <category>nextjs</category>
      <category>routechangecomplete</category>
      <category>routechangestart</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/226</guid>
      <comments>https://codiving.tistory.com/226#entry226comment</comments>
      <pubDate>Wed, 16 Oct 2024 18:30:02 +0900</pubDate>
    </item>
    <item>
      <title>Typography 스토리 구현하기</title>
      <link>https://codiving.tistory.com/225</link>
      <description>&lt;h2 id=&quot;typography-스토리-구현하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;Typography 스토리 구현하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Storybook 연동이 완료되었으니, 이제 &lt;i&gt;Typography&lt;/i&gt; 컴포넌트의 스토리를 구현해보겠습니다.&lt;br /&gt;&lt;!-- --&gt;기본 Typography 스토리 하나와 모든 &lt;i&gt;variant&lt;/i&gt;를 동시에 보여주는 스토리를 작성할 예정입니다.&lt;/p&gt;
&lt;h3 id=&quot;메타-데이터-작성하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;메타 데이터 작성하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;argTypes&lt;/i&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;i&gt;description&lt;/i&gt;: 해당 prop에 대한 간단한 설명을 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;control&lt;/i&gt;: 테스트할 방법을 지정하여 사용자가 해당 prop을 쉽게 조작할 수 있게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;args&lt;/i&gt;를 이용하여 기본 값을 설정할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;각 props의 이름과 기본값을 적어주면, 스토리에서 기본 상태를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;const meta = {
  title: &quot;Typography&quot;,
  component: Typography,
  parameters: {
    layout: &quot;padded&quot;
  },
  tags: [&quot;autodocs&quot;],
  argTypes: {
    children: {
      control: &quot;text&quot;,
      description: &quot;보여지는 텍스트&quot;
    },
    variant: {
      control: &quot;inline-radio&quot;
    },
    color: {
      control: &quot;inline-radio&quot;
    },
    noWrap: {
      control: &quot;boolean&quot;
    },
    align: {
      control: &quot;inline-radio&quot;
    },
    ...disabledProps
  },
  args: {
    children: &quot;Lorem ipsum dolor, sit amet consectetur adipisicing elit.&quot;,
    align: &quot;inherit&quot;,
    variant: &quot;body1&quot;,
    noWrap: false,
    color: &quot;textPrimary&quot;
  }
} satisfies Meta&amp;lt;typeof Typography&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 아래 이미지와 같은 결과가 나타납니다.&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-23 오후 7.26.37.png&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b19Ifb/btsJJaq27Cd/rip71h10ty5TWFkVxOjtKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b19Ifb/btsJJaq27Cd/rip71h10ty5TWFkVxOjtKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b19Ifb/btsJJaq27Cd/rip71h10ty5TWFkVxOjtKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb19Ifb%2FbtsJJaq27Cd%2Frip71h10ty5TWFkVxOjtKk%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;1057&quot; height=&quot;489&quot; data-filename=&quot;스크린샷 2024-09-23 오후 7.26.37.png&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;489&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h3 id=&quot;스토리-작성하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;스토리 작성하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;메타 데이터 작성이 완료되면, 원하는 스토리를 구현합니다.&lt;br /&gt;&lt;i&gt;name&lt;/i&gt;을 작성하지 않으면 기본적으로 변수 이름이 사용되며, 예를 들어 &lt;i&gt;Typography&lt;/i&gt;라는 이름으로 설정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;export const DefaultTypography: Story = {
  name: &quot;Typography&quot;,
  args: {},
  argTypes: {}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 아래 이미지와 같은 결과가 나타납니다.&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-23 오후 7.26.51.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRUZZa/btsJJdagyRi/bUw6snn8Bl3FNnHTHFeuU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRUZZa/btsJJdagyRi/bUw6snn8Bl3FNnHTHFeuU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRUZZa/btsJJdagyRi/bUw6snn8Bl3FNnHTHFeuU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRUZZa%2FbtsJJdagyRi%2FbUw6snn8Bl3FNnHTHFeuU1%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;287&quot; height=&quot;91&quot; data-filename=&quot;스크린샷 2024-09-23 오후 7.26.51.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;모든 &lt;i&gt;variant&lt;/i&gt;를 동시에 보여주기 위한 스토리도 작성할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;이 경우, 각 &lt;i&gt;variant&lt;/i&gt;를 따로 설정할 필요가 없으므로 해당 항목을 비활성화합니다.&lt;br /&gt;&lt;!-- --&gt;그리고 &lt;i&gt;render&lt;/i&gt; 함수를 사용해 원하는 형태로 렌더링합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export const AllVariantTypography: Story = {
  args: {},
  argTypes: {
    variant: {
      table: {
        disable: true
      }
    }
  },
  render: props =&amp;gt; {
    return (
      &amp;lt;div style={{ display: &quot;flex&quot;, flexDirection: &quot;column&quot; }}&amp;gt;
        {TYPOGRAPHY_VARIANT_LIST.map(variant =&amp;gt; (
          &amp;lt;Typography key={variant} {...props} variant={variant} /&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    );
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 아래 이미지와 같은 결과가 나타납니다.&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-23 오후 7.27.04.png&quot; data-origin-width=&quot;1497&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n6MWZ/btsJInj9g4Z/IDk8JRohJ6biScW3dChy7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n6MWZ/btsJInj9g4Z/IDk8JRohJ6biScW3dChy7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n6MWZ/btsJInj9g4Z/IDk8JRohJ6biScW3dChy7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn6MWZ%2FbtsJInj9g4Z%2FIDk8JRohJ6biScW3dChy7k%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;1497&quot; height=&quot;654&quot; data-filename=&quot;스크린샷 2024-09-23 오후 7.27.04.png&quot; data-origin-width=&quot;1497&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h2 id=&quot;결론&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Typography의 스토리를 간단하게 작성해보았습니다.&lt;br/&gt;최종적으로 1개를 테스트 할 수 있는 것과 모든 variant를 확인할 수 있는 스토리를 구현했습니다.&lt;/p&gt;
&lt;h2 id=&quot;링크&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;링크&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/d754a6a7c95d6073f395029e1827c9f3b9d6397d&quot;&gt;- Typography 스토리 구현&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/tree/d754a6a7c95d6073f395029e1827c9f3b9d6397d&quot;&gt;- 해당 게시글 최종 코드&lt;/a&gt;&lt;/p&gt;</description>
      <category>개인 프로젝트/React 컴포넌트 구현하기</category>
      <category>emotion 스토리북</category>
      <category>nextjs 스토리북</category>
      <category>react</category>
      <category>react 스토리북</category>
      <category>typography 스토리</category>
      <category>typography 스토리북</category>
      <category>스토리북 구현</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/225</guid>
      <comments>https://codiving.tistory.com/225#entry225comment</comments>
      <pubDate>Mon, 23 Sep 2024 19:30:23 +0900</pubDate>
    </item>
    <item>
      <title>Storybook 연동하기</title>
      <link>https://codiving.tistory.com/224</link>
      <description>&lt;div&gt;&lt;h2 id=&quot;storybook-연동하기&quot; class=&quot;v2_h2_title&quot;&gt;Storybook 연동하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;컴포넌트를 쉽게 확인하고 관리할 수 있는 좋은 방법은 Storybook을 사용하는 것입니다.&lt;br&gt;
&lt;!-- --&gt;이번 포스트에서는 &lt;em class=&quot;v2_em&quot;&gt;Next.js 14&lt;/em&gt;와 &lt;em class=&quot;v2_em&quot;&gt;Emotion&lt;/em&gt;을 활용해 Storybook을 연동하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;storybook-설치&quot; class=&quot;v2_h3_title&quot;&gt;Storybook 설치&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;먼저, Storybook을 설치합니다. 아래 명령어를 실행하세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;npx storybook@latest init
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot;&gt;설치가 완료되면 여러 개의 프로토타입 코드와 이미지가 생성됩니다.&lt;br&gt;
&lt;!-- --&gt;이 과정이 끝나면, &lt;em class=&quot;v2_em&quot;&gt;.storybook&lt;/em&gt; 폴더를 제외한 모든 파일을 삭제해 주세요.&lt;br&gt;
&lt;!-- --&gt;단, &lt;em class=&quot;v2_em&quot;&gt;stories&lt;/em&gt; 폴더는 남겨둡니다. 이 폴더에 앞으로 컴포넌트의 Story 파일을 작성할 예정입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/f6e93a2e0561c9b09ac81b1cf9d5d8662711b395&quot;&gt;지금까지 코드 확인하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;emotion-연동&quot; class=&quot;v2_h3_title&quot;&gt;Emotion 연동&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;프로젝트에서 &lt;em class=&quot;v2_em&quot;&gt;Emotion&lt;/em&gt;을 사용 중이라면, Storybook에 Emotion의 &lt;em class=&quot;v2_em&quot;&gt;ThemeProvider&lt;/em&gt;를 연동해야 합니다.&lt;br&gt;
&lt;!-- --&gt;이를 위해 아래 명령어를 실행해 필요한 패키지를 설치합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;npx storybook@latest add @storybook/addon-themes
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot;&gt;이후, &lt;em class=&quot;v2_em&quot;&gt;preview.ts&lt;/em&gt; 파일에 &lt;em class=&quot;v2_em&quot;&gt;GlobalStyles&lt;/em&gt; 컴포넌트가 생성되며, 이에 대한 작업을 진행해야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;해야-할-작업-목록&quot; class=&quot;v2_h3_title&quot;&gt;해야 할 작업 목록&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;em class=&quot;v2_em&quot;&gt;preview.ts&lt;/em&gt; 파일에서 아래 작업을 진행해야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;파일 확장자를 &lt;em class=&quot;v2_em&quot;&gt;.ts&lt;/em&gt;에서 &lt;em class=&quot;v2_em&quot;&gt;.tsx&lt;/em&gt;로 변경합니다.&lt;/li&gt;
&lt;li&gt;&lt;em class=&quot;v2_em&quot;&gt;LIGHT&lt;/em&gt; 및 &lt;em class=&quot;v2_em&quot;&gt;DARK&lt;/em&gt; theme를 연동합니다.&lt;/li&gt;
&lt;li&gt;&lt;em class=&quot;v2_em&quot;&gt;GlobalStyles&lt;/em&gt;로 글로벌 CSS 스타일을 적용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot;&gt;React 컴포넌트가 포함된 파일은 &lt;em class=&quot;v2_em&quot;&gt;.tsx&lt;/em&gt; 확장자를 사용하는 것이 권장되므로 확장자 변경 작업이 필요합니다.&lt;br&gt;
&lt;!-- --&gt;theme을 연동하여야 나중에 storybook에서도 편하게 &lt;em class=&quot;v2_em&quot;&gt;light, dark 모드 테스트&lt;/em&gt;가 가능합니다.&lt;br&gt;
&lt;!-- --&gt;storybook도 독립된 실행환경이기 때문에 &lt;em class=&quot;v2_em&quot;&gt;global css&lt;/em&gt;를 설정해주어야 합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;em class=&quot;v2_em&quot;&gt;preview.tsx&lt;/em&gt;의 최종 코드입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;import { css, Global, ThemeProvider } from &quot;@emotion/react&quot;;
import { withThemeFromJSXProvider } from &quot;@storybook/addon-themes&quot;;
import type { Preview } from &quot;@storybook/react&quot;;
import React from &quot;react&quot;;
import { DARK_THEME, LIGHT_THEME } from &quot;../src/ThemeContext/theme&quot;;

const GlobalStyles = () =&amp;gt; (
  &amp;lt;Global
    styles={css`
      /* css code */
    `}
  /&amp;gt;
);

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i
      }
    }
  },

  decorators: [
    withThemeFromJSXProvider({
      themes: {
        light: LIGHT_THEME,
        dark: DARK_THEME
      },
      defaultTheme: &quot;light&quot;,
      Provider: ThemeProvider,
      GlobalStyles
    })
  ]
};

export default preview;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/5515622bb2605dfc1643692aa7ab315c04b34e75&quot;&gt;지금까지 코드 확인하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고자료&quot; class=&quot;v2_h2_title&quot;&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://storybook.js.org/recipes/@emotion/styled&quot;&gt;Emotion 설치 및 연동 가이드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;링크&quot; class=&quot;v2_h2_title&quot;&gt;링크&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/f6e93a2e0561c9b09ac81b1cf9d5d8662711b395&quot;&gt;storybook 기본 설정 추가&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/5515622bb2605dfc1643692aa7ab315c04b34e75&quot;&gt;storybook에 emotion 연동하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/tree/5515622bb2605dfc1643692aa7ab315c04b34e75&quot;&gt;해당 게시글 최종 코드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;</description>
      <category>개인 프로젝트/React 컴포넌트 구현하기</category>
      <category>emotion theme 스토리북</category>
      <category>emotion 스토리북</category>
      <category>nextjs</category>
      <category>nextjs 14 스토리북</category>
      <category>nextjs 스토리북</category>
      <category>react</category>
      <category>react 스토리북</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/224</guid>
      <comments>https://codiving.tistory.com/224#entry224comment</comments>
      <pubDate>Mon, 23 Sep 2024 19:14:44 +0900</pubDate>
    </item>
    <item>
      <title>Typography 컴포넌트 구현하기</title>
      <link>https://codiving.tistory.com/223</link>
      <description>&lt;div&gt;
&lt;div&gt;
&lt;h1 id=&quot;typography-컴포넌트-구현하기&quot; class=&quot;v2_h1_title&quot;&gt;Typography 컴포넌트 구현하기&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Typography&lt;/i&gt; 컴포넌트는 텍스트를 보여주기 위한 컴포넌트입니다.&lt;br /&gt;&lt;!-- --&gt;일반적으로 HTML에서 텍스트를 표시할 때는 &lt;i&gt;span&lt;/i&gt; 또는 &lt;i&gt;p&lt;/i&gt; 태그를 사용합니다.&lt;br /&gt;&lt;!-- --&gt;제가 구현한 Typography 컴포넌트는 기본적으로 &lt;i&gt;span&lt;/i&gt; 태그를 사용하지만, 사용자가 필요에 따라 &lt;i&gt;p&lt;/i&gt; 태그로 변경할 수 있도록 유연하게 설계되었습니다.&lt;/p&gt;
&lt;h2 id=&quot;typography-구현하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;Typography 구현하기&lt;/h2&gt;
&lt;h3 id=&quot;props&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;props&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;interface TypographyProps {
  color?: TypographyColor;
  align?: &quot;inherit&quot; | &quot;center&quot; | &quot;right&quot; | &quot;left&quot;;
  variant?: TypographyVariant;
  noWrap?: boolean;
  component?: ElementType;
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;모든 props를 일일이 작성할 필요는 없습니다.&lt;br /&gt;&lt;i&gt;실제로 사용할 속성&lt;/i&gt;만 정의하여 관리의 복잡성을 줄이는 것이 더 효율적입니다.&lt;br /&gt;&lt;!-- --&gt;또한, 추가적인 CSS 작업이 필요한 경우를 대비해 &lt;i&gt;style&lt;/i&gt; props도 함께 제공하여 유연한 스타일링이 가능하도록 구현했습니다.&lt;/p&gt;
&lt;h3 id=&quot;색상-variant-처리&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;색상, Variant 처리&lt;/h3&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;const getColor = (theme: Theme, color?: TypographyColor) =&amp;gt; {
  if (!color) return undefined;
  if (color === &quot;textDisabled&quot;) {
    return theme.palette.text.disabled;
  }
  if (color === &quot;textPrimary&quot;) {
    return theme.palette.text.primary;
  }
  if (color === &quot;textSecondary&quot;) {
    return theme.palette.text.secondary;
  }
  return theme.palette[color].main;
};

const getVariant = (theme: Theme, variant?: TypographyVariant) =&amp;gt; {
  if (!variant) return {};
  return theme.typography[variant];
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 코드를 보면 &lt;i&gt;theme의 장점&lt;/i&gt;을 확인할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;미리 정의해둔 &lt;i&gt;theme&lt;/i&gt;을 사용하여 설정된 값을 손쉽게 적용할 수 있어, 일관성 있는 스타일링과 유지 보수가 용이합니다.&lt;/p&gt;
&lt;h3 id=&quot;typography-컴포넌트&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;Typography 컴포넌트&lt;/h3&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;const StyledTypography = styled(&quot;span&quot;)&amp;lt;TypographyProps&amp;gt;(
  ({
    theme,
    color: _color,
    variant: _variant,
    align: textAlign,
    noWrap,
    style
  }) =&amp;gt; {
    const color = getColor(theme, _color);
    const variant = getVariant(theme, _variant);
    return {
      textAlign,
      whiteSpace: noWrap ? &quot;nowrap&quot; : &quot;normal&quot;,
      color,
      ...variant,
      ...style
    };
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;i&gt;span&lt;/i&gt; 엘리먼트를 사용하고 있으며, &lt;i&gt;color&lt;/i&gt;와 &lt;i&gt;variant&lt;/i&gt; 값에 따라 적절한 CSS가 적용됩니다.&lt;br /&gt;&lt;!-- --&gt;이를 통해 다양한 스타일을 유연하게 설정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const Typography = forwardRef&amp;lt;HTMLElement, TypographyProps&amp;gt;(function Typography(
  {
    children,
    color = &quot;textPrimary&quot;,
    align = &quot;inherit&quot;,
    variant = &quot;body1&quot;,
    noWrap = false,
    component: Component = &quot;span&quot;,
    ...props
  }: TypographyProps,
  ref: Ref&amp;lt;HTMLElement&amp;gt;
) {
  return (
    &amp;lt;StyledTypography
      as={Component}
      color={color}
      align={align}
      noWrap={noWrap}
      variant={variant}
      ref={ref}
      {...props}
    &amp;gt;
      {children}
    &amp;lt;/StyledTypography&amp;gt;
  );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;as props&lt;/i&gt;를 사용하면 기본적으로 &lt;i&gt;span&lt;/i&gt;이 아닌, 원하는 태그로 컴포넌트를 변경할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;예를 들어, &lt;i&gt;as=&quot;p&quot;&lt;/i&gt; 값을 주면 &lt;i&gt;p&lt;/i&gt; 태그로 렌더링됩니다.&lt;/p&gt;
&lt;h2 id=&quot;결론&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 간단한 Typography 컴포넌트를 살펴보았습니다.&lt;br /&gt;&lt;!-- --&gt;다음 게시글에서는 이 Typography 컴포넌트를 더 직관적으로 확인할 수 있도록 &lt;i&gt;Storybook&lt;/i&gt;을 셋팅하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;링크&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;링크&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/8aac4f1c4659f05b32b803ef970880b7b13cbde0&quot;&gt;- Typography 구현&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/tree/8aac4f1c4659f05b32b803ef970880b7b13cbde0&quot;&gt;- 해당 게시글 최종 코드&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개인 프로젝트/React 컴포넌트 구현하기</category>
      <category>EMOTION</category>
      <category>emotion typography</category>
      <category>nextjs</category>
      <category>nextjs typography</category>
      <category>react</category>
      <category>react typography</category>
      <category>Typography</category>
      <category>typography 컴포넌트</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/223</guid>
      <comments>https://codiving.tistory.com/223#entry223comment</comments>
      <pubDate>Mon, 23 Sep 2024 18:59:28 +0900</pubDate>
    </item>
    <item>
      <title>Theme 타입, 값 설정 및 적용</title>
      <link>https://codiving.tistory.com/222</link>
      <description>&lt;p class=&quot;v2_p&quot;&gt;프로젝트 내에서 색상, 그림자, break points 등 &lt;em class=&quot;v2_em&quot;&gt;공통된 UI&lt;/em&gt;를 가지기 위해 &lt;em class=&quot;v2_em&quot;&gt;theme을 사용&lt;/em&gt;합니다.&lt;br&gt;
&lt;!-- --&gt;해당 게시글에서는 theme 타입, 값을 생성해보도록 하겠습니다.&lt;br&gt;
&lt;!-- --&gt;그리고 해당 값을 ThemeProvider에 주입하여 프로젝트 어디에서든지 사용할 수 있도록 하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;간단한 내용이라 코드에 대한 설명보다는 왜 해당 작업을 하였는지 설명을 작성하였습니다.&lt;br&gt;
&lt;!-- --&gt;코드는 &lt;em class=&quot;v2_em&quot;&gt;github 링크&lt;/em&gt;를 통해 확인할 수 있도록 하였습니다.&lt;/p&gt;
&lt;h2 id=&quot;theme-타입-값-설정&quot; class=&quot;v2_h2_title&quot;&gt;Theme 타입, 값 설정&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;전반적인 색상을 위한 &lt;em class=&quot;v2_em&quot;&gt;palette&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;글자를 위한 &lt;em class=&quot;v2_em&quot;&gt;typography&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;media query를 위한 &lt;em class=&quot;v2_em&quot;&gt;break points&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;내부적으로 색상 alias를 위한 &lt;em class=&quot;v2_em&quot;&gt;color&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;ltr, rtl direction을 위한 &lt;em class=&quot;v2_em&quot;&gt;direction&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;light mode, dark mode를 위한 &lt;em class=&quot;v2_em&quot;&gt;mode&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot;&gt;간단하게 위 값만 해보도록 하겠습니다.&lt;br&gt;
&lt;!-- --&gt;위 값들은 &lt;em class=&quot;v2_em&quot;&gt;mui 홈페이지&lt;/em&gt;에 있는 값을 참고하여 작성하였습니다.&lt;br&gt;
&lt;!-- --&gt;그리고 아직 dark mode의 값을 추가하지 않았습니다. (light와 동일)&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 값들은 그냥 설정만 해주는 것이기 때문에 아래 palette, typography, 나머지 theme 링크를 참고하시길 바랍니다.&lt;br&gt;
&lt;!-- --&gt;그 중 나머지 theme 링크에 아래 코드가 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;declare module &quot;@emotion/react&quot; {
  export interface Theme {
    mode: Mode;
    breakpoints: BreakPoints;
    direction: Direction;
    palette: Palette;
    typography: Typography;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 코드는 &lt;em class=&quot;v2_em&quot;&gt;emotion 패키지&lt;/em&gt;에 내가 설정한 &lt;em class=&quot;v2_em&quot;&gt;theme 타입&lt;/em&gt;을 알려주는 코드입니다.&lt;br&gt;
&lt;!-- --&gt;저는 index.ts파일에 같이 작성해 주었지만 emotion.d.ts와 같은 파일을 만들어서 따로 작성해주셔도 됩니다.&lt;/p&gt;
&lt;h2 id=&quot;themeprovider-설정&quot; class=&quot;v2_h2_title&quot;&gt;ThemeProvider 설정&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;위에서 작업한 값을 전역적으로 사용하기 위해서는 &lt;em class=&quot;v2_em&quot;&gt;ThemeContext&lt;/em&gt;, &lt;em class=&quot;v2_em&quot;&gt;ThemeProvider&lt;/em&gt; 작업을 해주어야 합니다.&lt;br&gt;
&lt;!-- --&gt;아래 ThemeContext, ThemeProvider 추가 링크를 확인하시면 됩니다.&lt;/p&gt;
&lt;h2 id=&quot;링크&quot; class=&quot;v2_h2_title&quot;&gt;링크&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/ff87d599fd77fdda5ce30be9bf287381c33f02f3&quot;&gt;- palette&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/3d80ac82903ef7a991d2b46a85b8310cba2b997d&quot;&gt;- typography&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/3210e54a39be6a0c34ecfa1e79595d641b87e19c&quot;&gt;- 나머지 theme&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/57c65cad1826a54694ab890908eded2ac5d5fc96&quot;&gt;- ThemeContext 추가&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/71340508a2f4a6be5897d6ebefedbb12812037fe&quot;&gt;- Global font 설정&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/Codiving/codiving-ui/commit/0df9e624617342b5d8aa0979ca92428fa1e5a451&quot;&gt;- ThemeProvider 추가&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/Codiving/codiving-ui/tree/0df9e624617342b5d8aa0979ca92428fa1e5a451&quot;&gt;- 해당 게시글 최종 코드&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;a href=&quot;https://mui.com/material-ui/customization/default-theme/&quot;&gt;- mui theme&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description>
      <category>개인 프로젝트/React 컴포넌트 구현하기</category>
      <category>EMOTION</category>
      <category>emotion theme</category>
      <category>emotion theme type</category>
      <category>emotion theme 타입</category>
      <category>mui theme</category>
      <category>mui theme value</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/222</guid>
      <comments>https://codiving.tistory.com/222#entry222comment</comments>
      <pubDate>Thu, 19 Sep 2024 19:54:55 +0900</pubDate>
    </item>
    <item>
      <title>React(Next) + Emotion로 MUI 따라 만들기</title>
      <link>https://codiving.tistory.com/220</link>
      <description>&lt;div&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;React&lt;/i&gt;로 개발할 때 &lt;i&gt;UI 라이브러리&lt;/i&gt;를 사용하면 빠르게 구현할 수 있지만, &lt;i&gt;커스터마이징&lt;/i&gt;이 복잡하고 시간이 많이 소요되는 단점이 있습니다.&lt;br /&gt;&lt;!-- --&gt;이를 해결하기 위한 &lt;i&gt;headless 컴포넌트&lt;/i&gt; 라이브러리도 있지만, &lt;i&gt;인력과 시간이 충분하다면&lt;/i&gt; 직접 구현하는 것도 좋은 대안이 될 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;사이드 프로젝트로 도전해 보는 것도 의미 있을 것 같아 시작하게 되었습니다.&lt;/p&gt;
&lt;h2 id=&quot;개발-환경&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;개발 환경&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;개발 환경은 &lt;i&gt;NextJS 14 + Emotion&lt;/i&gt;입니다.&lt;br /&gt;&lt;i&gt;CRA&lt;/i&gt; 대신 &lt;i&gt;Next 14&lt;/i&gt;를 사용하고 싶었고, 아직 &lt;i&gt;SSR&lt;/i&gt;에서 정상 동작하지 않습니다.&lt;br /&gt;&lt;!-- --&gt;따라서 &lt;code class=&quot;hljs&quot;&gt;&quot;use client&quot;&lt;/code&gt;를 추가해야 하지만, 익숙한 &lt;i&gt;Emotion&lt;/i&gt;을 계속 사용하기로 했습니다.&lt;/p&gt;
  
  &lt;h2 id=&quot;nextjs14--emotion-개발환경-셋팅하기&quot; class=&quot;v2_h2_title&quot;&gt;NextJS14 + Emotion 개발환경 셋팅하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;NextJS14와 emotion을 이용하여 개발환경을 셋팅해보겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 명령어를 이용하여 nextjs 14 설치하시면 됩니다. &lt;code class=&quot;hljs&quot;&gt;npx create-next-app@latest&lt;/code&gt;&lt;br /&gt;&lt;!-- --&gt;참고로 위 명령어는 &lt;i&gt;latest 버전&lt;/i&gt;을 설치하는 것이기 때문에 버전이 다를 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;제가 사용한 버전은 &lt;i&gt;14.2.9&lt;/i&gt;입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 명령어를 이용하여 &lt;i&gt;emotion&lt;/i&gt; 관련 패키지를 설치해줍니다.&lt;br /&gt;&lt;code class=&quot;hljs&quot;&gt;yarn add @emotion/react @emotion/styled&lt;/code&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 과정만 진행하시면 &lt;i&gt;개발환경은 셋팅&lt;/i&gt;은 끝났습니다.&lt;br /&gt;&lt;!-- --&gt;다음 게시글에서 &lt;i&gt;ThemeContext, ThemeProvider 설정&lt;/i&gt;하는 방법을 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;링크&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;링크&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;nextjs14 설치 및 프로젝트 정리&quot; href=&quot;https://github.com/Codiving/codiving-ui/commit/07f8bf61eb663675935707b5665d1219da0ba9ba&quot;&gt;- nextjs14 설치 및 프로젝트 정리&lt;/a&gt;&lt;br /&gt;&lt;a title=&quot;emotion 패키지 추가&quot; href=&quot;https://github.com/Codiving/codiving-ui/commit/6bb2d07d0bfb8313ac9020a2314564059300c4c6&quot;&gt;- emotion 패키지 추가&lt;/a&gt;&lt;/p&gt;
  
&lt;h2 id=&quot;개발-순서&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;개발 순서&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;개발 순서는 구현하고 싶은 &lt;i&gt;컴포넌트&lt;/i&gt;를 기준으로 진행할 것입니다.&lt;/p&gt;
&lt;/div&gt;</description>
      <category>개인 프로젝트/React 컴포넌트 구현하기</category>
      <category>EMOTION</category>
      <category>Material UI</category>
      <category>MUI</category>
      <category>nextjs</category>
      <category>react</category>
      <category>react 컴포넌트</category>
      <category>react 컴포넌트 구현</category>
      <category>storybook</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/220</guid>
      <comments>https://codiving.tistory.com/220#entry220comment</comments>
      <pubDate>Thu, 19 Sep 2024 19:14:08 +0900</pubDate>
    </item>
    <item>
      <title>react native + expo를 이용한 webview 앱 개발환경</title>
      <link>https://codiving.tistory.com/219</link>
      <description>&lt;div&gt;&lt;h1 id=&quot;react-native--expo를-이용한-webview-앱-개발환경&quot; class=&quot;v2_h1_title&quot;&gt;react native + expo를 이용한 webview 앱 개발환경&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot;&gt;미래의 제가 보기 위해 작성하는 것이므로 간단하게 작성하였습니다.&lt;/p&gt;
&lt;h3 id=&quot;사전-준비&quot; class=&quot;v2_h3_title&quot;&gt;사전 준비&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;expo 회원가입 - &lt;a href=&quot;https://expo.dev/&quot;&gt;회원가입 하러가기&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;명령어&quot; class=&quot;v2_h3_title&quot;&gt;명령어&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;여기서부터는 명령어 나열입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- create-expo-app 명령어를 이용한 프로젝트 설치&lt;br&gt;
&lt;code class=&quot;hljs&quot;&gt;npx create-expo-app my-app&lt;/code&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- eas cli 설치&lt;br&gt;
&lt;code class=&quot;hljs&quot;&gt;npm install -g eas-cli&lt;/code&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- eas login&lt;br&gt;
&lt;code class=&quot;hljs&quot;&gt;eas login&lt;/code&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- eas build:configure
&lt;code class=&quot;hljs&quot;&gt;eas build:configure&lt;/code&gt;&lt;br&gt;
&lt;!-- --&gt;위 작업을 완료하면 &lt;em class=&quot;v2_em&quot;&gt;eas.json&lt;/em&gt; 파일이 생성됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- 해당 &lt;a href=&quot;https://docs.expo.dev/build-reference/apk/&quot;&gt;링크&lt;/a&gt;를 클릭하여 eas.json에 적혀있는 코드를 복붙&lt;br&gt;
&lt;!-- --&gt;기존 &lt;em class=&quot;v2_em&quot;&gt;eas.json&lt;/em&gt; 파일의 &lt;em class=&quot;v2_em&quot;&gt;build&lt;/em&gt;를 교체해주시면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- app 빌드 명령어&lt;br&gt;
&lt;code class=&quot;hljs&quot;&gt;eas build -p android --profile preview&lt;/code&gt;&lt;br&gt;
&lt;!-- --&gt;해당 명령어를 입력하면 앱을 설치할 수 있는 &lt;em class=&quot;v2_em&quot;&gt;링크&lt;/em&gt;가 생성됩니다.&lt;br&gt;
&lt;!-- --&gt;핸드폰에서 링크를 클릭하면 설치해주시면 앱이 설치됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;- react native webview 설치&lt;br&gt;
&lt;code class=&quot;hljs&quot;&gt;npx expo install react-native-webview&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;a href=&quot;https://docs.expo.dev/build/setup/&quot;&gt;expo 홈페이지&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description>
      <category>개인 프로젝트/React 컴포넌트 구현하기</category>
      <category>expo</category>
      <category>expo 웹뷰</category>
      <category>React Native</category>
      <category>react native webview</category>
      <category>리액트 네이티브</category>
      <category>리엑트 네이비트 웹뷰</category>
      <category>웹뷰</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/219</guid>
      <comments>https://codiving.tistory.com/219#entry219comment</comments>
      <pubDate>Thu, 19 Sep 2024 19:06:13 +0900</pubDate>
    </item>
    <item>
      <title>[React] Context API vs Zustand 렌더링 비교</title>
      <link>https://codiving.tistory.com/218</link>
      <description>&lt;h1 id=&quot;context-api-vs-zustand-렌더링-비교&quot; class=&quot;v2_h1_title&quot;&gt;Context API vs Zustand 렌더링 비교&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;React에서 상태 관리를 위한 라이브러리는 다양합니다.&lt;br /&gt;&lt;!-- --&gt;내장된 Context API부터 Redux, MobX, Recoil, Zustand 등 여러 가지가 있습니다.&lt;br /&gt;&lt;!-- --&gt;각 라이브러리의 장단점은 사용자에 따라 다르게 느껴질 수 있지만, 이번 글에서는 &lt;i&gt;Zustand&lt;/i&gt;를 사용한 경험을 바탕으로 &lt;i&gt;렌더링 최적화&lt;/i&gt;에 대해 살펴보려고 합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;많은 글에서 Zustand와 같은 라이브러리를 사용하면 Context API에 비해 렌더링이 더 최적화된다고 언급됩니다.&lt;br /&gt;&lt;!-- --&gt;그러나 실제로 이를 테스트해본 글은 찾기 어렵습니다.&lt;br /&gt;&lt;!-- --&gt;이번 글에서는 실제로 렌더링이 어떻게 최적화되는지 직접 확인해보겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;결과&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;빠르게 결과부터 확인해봅시다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Codiving/context-zustand/commit/0a1c469b3a4e6ee07c3021f71b24b0e46bda6565&quot;&gt;&amp;bull; Context API - 코드&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/Codiving/context-zustand/commit/08f21d949a0e05f083f210294c645d1b085001db&quot;&gt;&amp;bull; Zustand 실패 - 코드&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/Codiving/context-zustand/commit/2e05c918a7b4ac552e9f96532823edb475a8c72d&quot;&gt;&amp;bull; Zustand 성공 - 코드&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;결론은 &lt;i&gt;Zustand를 개발자가 의도한 대로 잘 사용하면 렌더링 최적화가 가능하다&lt;/i&gt;는 것입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 이미지를 참고하세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.gif&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvxmY2/btsJxZizJkC/fDKqDM9fPUM1MFJadJxXbK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvxmY2/btsJxZizJkC/fDKqDM9fPUM1MFJadJxXbK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvxmY2/btsJxZizJkC/fDKqDM9fPUM1MFJadJxXbK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bvxmY2/btsJxZizJkC/fDKqDM9fPUM1MFJadJxXbK/img.gif&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;680&quot; height=&quot;738&quot; data-filename=&quot;1.gif&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Context API와 Zustand를 제대로 사용하지 않으면, 상태와 상관없이 &lt;i&gt;모든 컴포넌트가 다시 렌더링&lt;/i&gt;됩니다.&lt;br /&gt;&lt;!-- --&gt;이는 Zustand를 최적화하지 못했을 때 발생하는 현상입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;왜 그런지 아래 코드를 살펴봅시다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const { age, ageIncrement, ageDecrement } = useStore();
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이 코드는 store에서 전체 상태 값을 반환한 후 구조분해 할당을 통해 사용하는 방식입니다.&lt;br /&gt;&lt;!-- --&gt;여기서 문제는 &lt;i&gt;전체 상태 값&lt;/i&gt;을 반환하기 때문에, 상태 중 하나라도 변경되면 모든 상태가 변경된 것으로 인식하고 컴포넌트를 다시 렌더링하게 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이는 상태를 담고 있는 객체가 변경되었으니 React가 렌더링해야 한다고 판단하는 것입니다.&lt;br /&gt;&lt;!-- --&gt;이 문제를 해결하려면 &lt;i&gt;selector 패턴&lt;/i&gt;을 이용해 원하는 상태만 선택해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;const age = useStore(state =&amp;gt; state.age);
const ageIncrement = useStore(state =&amp;gt; state.ageIncrement);
const ageDecrement = useStore(state =&amp;gt; state.ageDecrement);
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 코드처럼 &lt;i&gt;필요한 상태만 선택&lt;/i&gt;해 가져오면 불필요한 렌더링을 막을 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;아래 이미지를 보면, 상태가 변경되지 않는 한 &lt;i&gt;렌더링이 발생하지 않는&lt;/i&gt; 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.gif&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2eMus/btsJxTCPm3G/8yDKkkdvwAtekQlvkaDvT1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2eMus/btsJxTCPm3G/8yDKkkdvwAtekQlvkaDvT1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2eMus/btsJxTCPm3G/8yDKkkdvwAtekQlvkaDvT1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/2eMus/btsJxTCPm3G/8yDKkkdvwAtekQlvkaDvT1/img.gif&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;680&quot; height=&quot;738&quot; data-filename=&quot;2.gif&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;결론&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Zustand를 사용할 때는 구조분해 할당을 피하고, &lt;i&gt;선택적 상태 구독&lt;/i&gt;을 통해 렌더링을 최적화할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;이를 통해 Context API보다 효율적인 상태 관리를 할 수 있습니다.&lt;/p&gt;</description>
      <category>공유/React, Next</category>
      <category>context api</category>
      <category>context api vs zustand</category>
      <category>react</category>
      <category>react 렌더링</category>
      <category>zustand</category>
      <category>zustand 렌더링</category>
      <category>zustand 렌더링 최적화</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/218</guid>
      <comments>https://codiving.tistory.com/218#entry218comment</comments>
      <pubDate>Tue, 10 Sep 2024 19:19:26 +0900</pubDate>
    </item>
    <item>
      <title>github action을 이용하여 PR시 효율적으로 tsc, eslint 검사하기</title>
      <link>https://codiving.tistory.com/217</link>
      <description>&lt;div&gt;
  &lt;h1 id=&quot;github-action을-이용하여-pr시-효율적으로-tsc-eslint-검사하기&quot; class=&quot;v2_h1_title&quot;&gt;github action을 이용하여 PR시 효율적으로 tsc, eslint 검사하기&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot;&gt;github action을 이용하여 PR시 merge branch &lt;em class=&quot;v2_em&quot;&gt;코드를 검사&lt;/em&gt;할 필요가 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;하고-싶은-것&quot; class=&quot;v2_h3_title&quot;&gt;하고 싶은 것&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;PR 시 merge branch의 &lt;em class=&quot;v2_em&quot;&gt;코드가 정상적인지 검사&lt;/em&gt;할 필요가 있음.&lt;br&gt;
&lt;em class=&quot;v2_em&quot;&gt;monorepo 프로젝트이&lt;/em&gt;기 때문에 &lt;em class=&quot;v2_em&quot;&gt;변경된 모든 프로젝트를 검사&lt;/em&gt;해야함.&lt;/p&gt;
&lt;h3 id=&quot;기존-해결-방법-및-문제점-1&quot; class=&quot;v2_h3_title&quot;&gt;기존 해결 방법 및 문제점 [1]&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;em class=&quot;v2_em&quot;&gt;커밋 메시지 컨벤션&lt;/em&gt;으로 커밋 메시지에 &lt;em class=&quot;v2_em&quot;&gt;변경된 파일의 프로젝트 명을 작성&lt;/em&gt;하게 하였습니다.&lt;br&gt;
&lt;!-- --&gt;그럼 해당 커밋에서 &lt;em class=&quot;v2_em&quot;&gt;어떠한 프로젝트의 파일이 변경&lt;/em&gt;되었는지 파악할 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;해당 정보를 이용하여 변경된 파일의 프로젝트를 &lt;em class=&quot;v2_em&quot;&gt;tsc, eslint를 수행&lt;/em&gt;하여 문제를 해결하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;그러나 위 방법은 너무 &lt;em class=&quot;v2_em&quot;&gt;오랜 시간이 소요&lt;/em&gt;된다는 문제가 있었습니다.&lt;br&gt;
&lt;!-- --&gt;만약 커밋을 3개하는 경우 프로젝트 tsc, eslint를 총 3번 하는 것이기 때문에 오랜 시간이 소요되었습니다.&lt;br&gt;
&lt;!-- --&gt;또한 해당 작업을 수행할 때 동안은 코드 수정이 불가능하다는 단점도 있었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 작업을 PR 요청 보낼 때, 1번만 수행하면 되겠다는 생각이 들어 &lt;em class=&quot;v2_em&quot;&gt;github action&lt;/em&gt;으로 해결해보았습니다.&lt;/p&gt;
&lt;h3 id=&quot;기존-해결-방법-및-문제점-2&quot; class=&quot;v2_h3_title&quot;&gt;기존 해결 방법 및 문제점 [2]&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;첫번째는 커밋 메시지마다 검사를 하였다면 이제 PR 요청 시 &lt;em class=&quot;v2_em&quot;&gt;한번만 tsc, eslint 작업&lt;/em&gt;을 하는 것입니다.&lt;br&gt;
&lt;!-- --&gt;동일하게 &lt;em class=&quot;v2_em&quot;&gt;변경된 프로젝트&lt;/em&gt;만 tsc, eslint를 작업해야 하기 때문에 &lt;em class=&quot;v2_em&quot;&gt;PR 레이블&lt;/em&gt;에 모노레포에 있는 프로젝트 이름을 모두 적어주었습니다.&lt;br&gt;
&lt;!-- --&gt;PR 문서가 생성, 수정 등 이벤트가 들어올 때마다 &lt;em class=&quot;v2_em&quot;&gt;레이블을 확인&lt;/em&gt;하여 해당 프로젝트에 대해 &lt;em class=&quot;v2_em&quot;&gt;tsc, eslint가 수행&lt;/em&gt;되었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 방식은 나쁘지 않았습니다. 그러나 &lt;em class=&quot;v2_em&quot;&gt;PR 레이블에 모든 프로젝트를 적어주어야 한다는 점&lt;/em&gt;이 불편하였습니다.&lt;br&gt;
&lt;!-- --&gt;또한, 개발자가 변경된 프로젝트의 &lt;em class=&quot;v2_em&quot;&gt;레이블 이름을 선택하지 않거나 잘못 선택한 경우&lt;/em&gt; 제대로 동작하지 않았습니다.&lt;br&gt;
&lt;!-- --&gt;마지막으로 eslint의 경우 경우 변경된 파일만 수행하면 되는데 &lt;em class=&quot;v2_em&quot;&gt;해당 프로젝트를 모두 수행하여 시간이 오래 걸리는 점&lt;/em&gt;이 문제가 되었습니다.&lt;/p&gt;
&lt;h3 id=&quot;최종-해결-방법-3&quot; class=&quot;v2_h3_title&quot;&gt;최종 해결 방법 [3]&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 방식과 동일하게 &lt;em class=&quot;v2_em&quot;&gt;github action&lt;/em&gt;으로 문제를 해결하였습니다.&lt;br&gt;
&lt;!-- --&gt;그러나 이번에는 목표를 정하고 접근을 하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;목표&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;레이블에 있는 프로젝트 명 삭제 및 변경된 파일의 프로젝트 명 가져오기.&lt;/li&gt;
&lt;li&gt;변경된 파일만 eslint 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 2가지 목표를 달성하는 것은 생각보다 간단하였습니다.&lt;/p&gt;
&lt;h3 id=&quot;레이블에-있는-프로젝트-명-삭제-및-변경된-파일의-프로젝트-명-가져오기&quot; class=&quot;v2_h3_title&quot;&gt;레이블에 있는 프로젝트 명 삭제 및 변경된 파일의 프로젝트 명 가져오기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;불편하게 매번 변경된 프로젝트를 레이블로 선택해야 하는 점을 개선하고 싶었습니다.&lt;br&gt;
&lt;em class=&quot;v2_em&quot;&gt;tj-actions/changed-files&lt;/em&gt;을 이용하면 매우 쉽게 &lt;em class=&quot;v2_em&quot;&gt;변경된 파일명&lt;/em&gt;을 가져올 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;추출된 파일명을 분석하여 &lt;em class=&quot;v2_em&quot;&gt;변경된 프로젝트 이름&lt;/em&gt;을 확인할 수 있었습니다.
따라서 &lt;em class=&quot;v2_em&quot;&gt;변경된 프로젝트만 tsc를 수행&lt;/em&gt;할 수 있었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;- name: Check for changed ts files
  if: steps.changed-files.outputs.any_changed == 'true'
  env:
    TS_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
  run: |
    for file in ${TS_CHANGED_FILES}; do
      echo &quot;$file was changed&quot;
    done

- name: Identify packages with changed files
  run: |
    PACKAGES=()
    for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
      if [[ ! &quot; ${PACKAGES[@]} &quot; =~ &quot; main &quot; &amp;amp;&amp;amp; &quot;$file&quot; == packages/main/* ]]; then
        PACKAGES+=(&quot;main&quot;)
      elif [[ ! &quot; ${PACKAGES[@]} &quot; =~ &quot; sub1 &quot; &amp;amp;&amp;amp; &quot;$file&quot; == packages/sub1/* ]]; then
        PACKAGES+=(&quot;sub1&quot;)
      elif [[ ! &quot; ${PACKAGES[@]} &quot; =~ &quot; sub2 &quot; &amp;amp;&amp;amp; &quot;$file&quot; == packages/sub2/* ]]; then
        PACKAGES+=(&quot;sub2&quot;)
      fi
      if [[ ${#PACKAGES[@]} -eq 3 ]]; then
        break
      fi
    done
    echo &quot;UNIQUE_PACKAGES=${PACKAGES[@]}&quot; &amp;gt;&amp;gt; $GITHUB_ENV

- name: Run tsc
  id: tsc-check
  run: |
    IFS=' ' read -r -a PACKAGES &amp;lt;&amp;lt;&amp;lt; &quot;${{ env.UNIQUE_PACKAGES }}&quot;
    for package in &quot;${PACKAGES[@]}&quot;; do
      echo &quot;Type-checking package: $package&quot;
      OUTPUT=$(yarn &quot;$package&quot; tsc 2&amp;gt;&amp;amp;1)
    done
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;em class=&quot;v2_em&quot;&gt;Identify packages with changed files&lt;/em&gt;를 보시면 &lt;em class=&quot;v2_em&quot;&gt;모노레포 프로젝트 이름&lt;/em&gt;이 if문에 있는 것을 확인할 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;프로젝트 이름을 if문에 작성해주시면 됩니다.&lt;br&gt;
&lt;!-- --&gt;PACKAGES 크기가 프로젝트 개수만큼 되었으면 강제로 for문을 종료하였습니다.&lt;/p&gt;
&lt;h3 id=&quot;변경된-파일만-eslint-수행&quot; class=&quot;v2_h3_title&quot;&gt;변경된 파일만 eslint 수행&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;eslint가 생각보다 오랜 시간이 소요됩니다.&lt;br&gt;
&lt;!-- --&gt;따라서 변경된 파일에 대해서만 eslint를 수행하고 싶었습니다.&lt;br&gt;
&lt;em class=&quot;v2_em&quot;&gt;tj-actions/eslint-changed-files&lt;/em&gt;를 이용하면 간단하게 &lt;em class=&quot;v2_em&quot;&gt;변경된 파일만 eslint 작업&lt;/em&gt;을 할 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;그러나 Next14로 테스트 하고 있었는데 eslint 9 버전으로 계속 에러가 발생하였습니다.&lt;br&gt;
&lt;!-- --&gt;따라서 Next 프로젝트를 생성할 때, eslint 버전이 8로 설정되어 있는 것을 확인하고 &lt;em class=&quot;v2_em&quot;&gt;eslint 8&lt;/em&gt;을 사용하는 버전으로 해당 작업을 수행하였습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Run&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;eslint&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;changed&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;files&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tj-actions/eslint-changed-files@v23.2.0&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;file_extensions:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
      **/*.ts
      **/*.tsx
&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;extra_args:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;--max-warnings=0&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;token:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;secrets.ESLINT_TOKEN&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;em class=&quot;v2_em&quot;&gt;file_extensions&lt;/em&gt;을 이용하여 변경된 &lt;em class=&quot;v2_em&quot;&gt;.ts, .tsx 파일만 확인&lt;/em&gt;하였습니다.&lt;br&gt;
&lt;!-- --&gt;댓글이 달리기 위해서는 &lt;em class=&quot;v2_em&quot;&gt;token&lt;/em&gt;이 필요한 것 같아 추가해주었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 2가지 방법으로 불편했던 점을 모두 개선하였습니다.&lt;/p&gt;&lt;/div&gt;</description>
      <category>공유/Git, Github</category>
      <category>ESLint</category>
      <category>github</category>
      <category>Github Action</category>
      <category>tsc</category>
      <category>깃헙 액션</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/217</guid>
      <comments>https://codiving.tistory.com/217#entry217comment</comments>
      <pubDate>Tue, 3 Sep 2024 20:10:11 +0900</pubDate>
    </item>
    <item>
      <title>[HTML, CSS] flex에서 margin 이용하여 할 수 있는 것</title>
      <link>https://codiving.tistory.com/216</link>
      <description>&lt;h1 id=&quot;css-flex에서-margin-이용하여-할-수-있는-것&quot; class=&quot;v2_h1_title&quot;&gt;[HTML, CSS] flex에서 margin 이용하여 할 수 있는 것&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;제목을 무엇으로 해야할 지 몰라서 위처럼 지었습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;.container {
    display: flex;
    gap: 4px
}

.item {
    width: 100px;
    background-color: lightblue;
    padding: 8px;
}

&amp;lt;div class=&quot;container&quot;&amp;gt;
    &amp;lt;div class=&quot;item&quot;&amp;gt;Item1&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;item&quot;&amp;gt;Item2&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;item&quot;&amp;gt;Item3&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAAJSg/btsJb6wg9Fe/tIKIOQXDCKvkM55XjzKnZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAAJSg/btsJb6wg9Fe/tIKIOQXDCKvkM55XjzKnZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAAJSg/btsJb6wg9Fe/tIKIOQXDCKvkM55XjzKnZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAAJSg%2FbtsJb6wg9Fe%2FtIKIOQXDCKvkM55XjzKnZk%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;1109&quot; height=&quot;122&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;!-- --&gt;위와 같은 구조가 있다고 해봅시다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;여기서 아래 이미지를 구현하려면 어떻게 할 수 있을까요?&lt;br /&gt;&lt;!-- --&gt;아래 이미지는 생각보다 많이 사용하는 &lt;i&gt;레이아웃 구조&lt;/i&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsgzAH/btsJbfOcduq/bR7ZnNSfoOkg8AYMkAKnjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsgzAH/btsJbfOcduq/bR7ZnNSfoOkg8AYMkAKnjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsgzAH/btsJbfOcduq/bR7ZnNSfoOkg8AYMkAKnjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsgzAH%2FbtsJbfOcduq%2FbR7ZnNSfoOkg8AYMkAKnjk%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;1109&quot; height=&quot;127&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;div로-처리&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;div로 처리&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;처음 생각드는 방법은 &lt;i&gt;item1, 2&lt;/i&gt;를 &lt;i&gt;div&lt;/i&gt;로 한번 더 감싸았을 것 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;.container {
    display: flex;
    gap: 4px;
    justify-content: space-between;
}

.wrap {
    display: flex;
    gap: 4px;
}

.item {
    width: 100px;
    background-color: lightblue;
    padding: 8px;
}

&amp;lt;div class=&quot;container&quot;&amp;gt;
    &amp;lt;div class=&quot;wrap&quot;&amp;gt;
        &amp;lt;div class=&quot;item&quot;&amp;gt;Item1&amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;item&quot;&amp;gt;Item2&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;item&quot;&amp;gt;Item3&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPs0BP/btsJbKtkcmL/zbZOGjMzkOlaWHMssLT2ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPs0BP/btsJbKtkcmL/zbZOGjMzkOlaWHMssLT2ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPs0BP/btsJbKtkcmL/zbZOGjMzkOlaWHMssLT2ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPs0BP%2FbtsJbKtkcmL%2FzbZOGjMzkOlaWHMssLT2ck%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;1109&quot; height=&quot;127&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 처리할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;그러나 이렇게 하면 추가해야하는 코드가 많이 있습니다.&lt;br /&gt;&lt;!-- --&gt;이건 너무 불편한 것 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;margin-left-이용&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;margin-left 이용&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;margin-left&lt;/i&gt;를 이용하시면 간단하게 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;.container {
    display: flex;
    gap: 4px
}

.item {
    width: 100px;
    background-color: lightblue;
    padding: 8px;
}

.ml-auto {
    margin-left: auto
}

&amp;lt;div class=&quot;container&quot;&amp;gt;
    &amp;lt;div class=&quot;item&quot;&amp;gt;Item1&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;item&quot;&amp;gt;Item2&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;item ml-auto&quot;&amp;gt;Item3&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boxSq3/btsJbkosZXJ/IXPJry7uFv1sA5QCiRw8Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boxSq3/btsJbkosZXJ/IXPJry7uFv1sA5QCiRw8Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boxSq3/btsJbkosZXJ/IXPJry7uFv1sA5QCiRw8Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboxSq3%2FbtsJbkosZXJ%2FIXPJry7uFv1sA5QCiRw8Rk%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;1109&quot; height=&quot;127&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 &lt;i&gt;margin-left: auto&lt;/i&gt; 를 이용하면 간단하게 처리할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 방법으로 &lt;i&gt;마크업 구조&lt;/i&gt;를 더 깔끔하게 작성할 수 있습니다.&lt;/p&gt;</description>
      <category>공유/HTML, CSS</category>
      <category>Flex</category>
      <category>flex margin</category>
      <category>margin</category>
      <category>마크업</category>
      <category>마크업 구조</category>
      <category>마크업 구조 잘 짜는 법</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/216</guid>
      <comments>https://codiving.tistory.com/216#entry216comment</comments>
      <pubDate>Thu, 22 Aug 2024 19:40:02 +0900</pubDate>
    </item>
    <item>
      <title>글로벌 웹 서비스를 다시 만든다면 실수하지 않을 것들</title>
      <link>https://codiving.tistory.com/215</link>
      <description>&lt;div&gt;&lt;h1 id=&quot;글로벌-웹-서비스를-다시-만든다면-실수하지-않을-것들&quot; class=&quot;v2_h1_title&quot;&gt;글로벌 웹 서비스를 다시 만든다면 실수하지 않을 것들&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot;&gt;다국가를 지원하는 &lt;em class=&quot;v2_em&quot;&gt;글로벌 웹 서비스&lt;/em&gt;를 구현하면 생각하지 못 한 많은 문제를 겪을 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;아래 게시글은 만약 제가 &lt;em class=&quot;v2_em&quot;&gt;처음부터 다시 개발한다면 어떻게 할까?&lt;/em&gt; 라는 생각을 가지고 작성하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;하나의 국가만 지원한다면 상관없지만 &lt;em class=&quot;v2_em&quot;&gt;다국가를 지원&lt;/em&gt;하는 경우 한번 읽어보시는 것도 좋을 것 같습니다.&lt;br&gt;
&lt;!-- --&gt;제가 정답이라는 것은 아니고 &lt;em class=&quot;v2_em&quot;&gt;이러한 방법도 있다&lt;/em&gt; 라는 생각으로 읽어주시면 감사하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;다국어-처리&quot; class=&quot;v2_h2_title&quot;&gt;다국어 처리&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;페이지, 기능 등 여러 단위로 &lt;em class=&quot;v2_em&quot;&gt;다국어 파일을 관리&lt;/em&gt;할 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;위처럼 진행하면 처음에 구현할 떄는 문제가 없을 수 있습니다.&lt;br&gt;
&lt;!-- --&gt;그러나 점점 서비스가 커지고 페이지, 기능이 많아지면 &lt;em class=&quot;v2_em&quot;&gt;동일한 뜻&lt;/em&gt;을 가진 로케일 키가 &lt;em class=&quot;v2_em&quot;&gt;기하급수적으로 증가&lt;/em&gt;합니다.&lt;br&gt;
&lt;!-- --&gt;따라서 제가 생각할 때는 동일한 뜻을 가진 단어가 있으면 &lt;em class=&quot;v2_em&quot;&gt;하나의 로케일로 관리&lt;/em&gt;하는 것이 좋아 보입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;이유는 페이지, 기능 별로 파일을 분리하여 관리하였을 때 &lt;em class=&quot;v2_em&quot;&gt;VOC에 대응&lt;/em&gt;하기 힘들었습니다.&lt;br&gt;
&lt;!-- --&gt;예를들어 '배'를 '사과'로 변경해주세요. 라고 요청이 들어왔다고 가정해봅시다.&lt;br&gt;
&lt;!-- --&gt;만약 페이지, 기능 별로 파일을 분리하여 관리하였다면 &lt;em class=&quot;v2_em&quot;&gt;각 파일마다 수정&lt;/em&gt;을 해주어야 할 것입니다.&lt;br&gt;
&lt;!-- --&gt;그러나 하나의 로케일로 관리를 하였다면 &lt;em class=&quot;v2_em&quot;&gt;'배'만 '사과'로 변경&lt;/em&gt;하면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;더 자세히 확인하고 싶으시면 아래 링크를 확인해보세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://codiving.kr/181&quot; title=&quot;다국어 관리 방법&quot;&gt;• 다국어 관리 방법&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;문자열-비교는-정규화-진행-후-하자&quot; class=&quot;v2_h2_title&quot;&gt;문자열 비교는 정규화 진행 후 하자&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;문자열 비교는 &lt;em class=&quot;v2_em&quot;&gt;정규화&lt;/em&gt;를 진행 후 하는 것이 좋습니다.&lt;br&gt;
&lt;!-- --&gt;실제로 사용자가 보는 텍스트는 동일하지만 &lt;em class=&quot;v2_em&quot;&gt;컴퓨터에 다루는 값&lt;/em&gt;이 다를 수 있기 때문입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;ũ, ũ 해당 2개의 문자는 같아 보이지만 실제로는 다른 값입니다.&lt;br&gt;
&lt;!-- --&gt;위와 같은 경우를 방지하기 위해 &lt;em class=&quot;v2_em&quot;&gt;정규화&lt;/em&gt;를 진행하여 문자열 비교를 해야합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;더 자세히 확인하고 싶으시면 아래 링크를 확인해보세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://codiving.kr/43&quot; title=&quot;베트남 문자열 비교하기&quot;&gt;• 베트남 문자열 비교하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;date-객체-다루기--썸머타임&quot; class=&quot;v2_h2_title&quot;&gt;Date 객체 다루기 (+ 썸머타임)&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;가장 힘들었던 &lt;em class=&quot;v2_em&quot;&gt;Date 객체&lt;/em&gt;를 다루는 내용입니다.&lt;br&gt;
&lt;!-- --&gt;도움이 되었으면 좋겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;썸머타임&quot; class=&quot;v2_h3_title&quot;&gt;썸머타임&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;처음 배포했을 때는 &lt;em class=&quot;v2_em&quot;&gt;썸머타임&lt;/em&gt;에 대한 문제가 없었습니다.&lt;br&gt;
&lt;!-- --&gt;생각도 못 했지만 다행히 국가들이 썸머타임을 적용하지 않았기 때문입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;그러나 이집트에서 다시 &lt;em class=&quot;v2_em&quot;&gt;썸머타임을 적용&lt;/em&gt;하고 있어 이때부터 문제가 발생하였습니다.&lt;br&gt;
&lt;!-- --&gt;개발 당시 썸머타임을 생각하지 못 했기 때문에 하루가 &lt;em class=&quot;v2_em&quot;&gt;24시간&lt;/em&gt;이라고 생각하고 구현했던 것들이 제대로 동작하지 않았습니다.&lt;br&gt;
&lt;!-- --&gt;썸머타임을 적용하는 시점에는 &lt;em class=&quot;v2_em&quot;&gt;23시간&lt;/em&gt;이기 때문입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;대표적인 예시로 &lt;em class=&quot;v2_em&quot;&gt;주 단위 캘린더&lt;/em&gt;를 구현할 때 문제가 발생하였습니다.&lt;br&gt;
&lt;!-- --&gt;주 중 썸머타임 시작 날짜의 &lt;em class=&quot;v2_em&quot;&gt;시작 시간이 01시&lt;/em&gt;이기 때문에 00시의 자리에 01시가 존재하는 문제가 발생하였습니다.&lt;br&gt;
&lt;!-- --&gt;저처럼 위와 같은 문제를 겪지 않으시길 바랍니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;그리고 대부분 Date 객체를 그대로 사용하는 것이 아닌 &lt;em class=&quot;v2_em&quot;&gt;라이브러리를 사용&lt;/em&gt;하실 것으로 예상됩니다.&lt;br&gt;
&lt;!-- --&gt;해당 라이브러리에서 &lt;em class=&quot;v2_em&quot;&gt;썸머타임을 제공&lt;/em&gt;하는 지 꼭 확인하시는 것을 추천 드립니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;제가 사용하였던 &lt;em class=&quot;v2_em&quot;&gt;moment, dayjs&lt;/em&gt;는 모두 지원하고 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;타임존&quot; class=&quot;v2_h3_title&quot;&gt;타임존&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;시간은 &lt;em class=&quot;v2_em&quot;&gt;고유한 값&lt;/em&gt;이기 떄문에 어떠한 타임존에서 사용하더라도 문제가 없습니다.&lt;br&gt;
&lt;!-- --&gt;해당 타임존의 &lt;em class=&quot;v2_em&quot;&gt;시간으로 변경&lt;/em&gt;되어 보여지기 때문입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;해당 부분은 &lt;em class=&quot;v2_em&quot;&gt;하루 기준으로 특정 작업&lt;/em&gt;을 할 때 문제가 많이 생겼습니다.&lt;br&gt;
&lt;!-- --&gt;예를들어 &lt;em class=&quot;v2_em&quot;&gt;하루에 1개만 생성&lt;/em&gt;이 가능한 기능이 있다.&lt;br&gt;
&lt;!-- --&gt;위와 같은 경우는 &lt;em class=&quot;v2_em&quot;&gt;타임존을 변경&lt;/em&gt;하게 되면 시작 날짜가 달라지기 때문에 &lt;em class=&quot;v2_em&quot;&gt;하루를 계산하는 기준&lt;/em&gt;이 달라집니다.&lt;br&gt;
&lt;!-- --&gt;시간은 절대적이지만 한국과 미국의 시작 &lt;em class=&quot;v2_em&quot;&gt;시간은 상대적으로 다르기 때문&lt;/em&gt;입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;제 생각에는 위와 같은 기능이 없는 게 최고입니다...&lt;br&gt;
&lt;!-- --&gt;그러나 어쩔 수 없이 위와 같은 기능이 있는 경우 &lt;em class=&quot;v2_em&quot;&gt;타임존을 명확하게 지정&lt;/em&gt;하는 것이 좋아보입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;가벼운 기능인 경우 &lt;em class=&quot;v2_em&quot;&gt;프론트에서 서버로&lt;/em&gt; 타임존을 넘겨주어 서버에서 해당 타임존을 설정해서 계산하면 됩니다.&lt;br&gt;
&lt;!-- --&gt;그러나 꼭 무조건 1개여야 한다. 이러한 경우는 해당 서비스의 &lt;em class=&quot;v2_em&quot;&gt;타임존을 명확하게 하나로 지정&lt;/em&gt;하는 것이 좋아보입니다.&lt;/p&gt;
&lt;h2 id=&quot;rtl은-적당히-지원하자&quot; class=&quot;v2_h2_title&quot;&gt;RTL은 적당히 지원하자&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;이집트와 같이 LTR이 아니라 &lt;em class=&quot;v2_em&quot;&gt;RTL인 국가를 처리&lt;/em&gt;해야 하는 경우가 있습니다.&lt;br&gt;
&lt;!-- --&gt;그러나 RTL을 완벽하게 처리해주기는 쉽지 않습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;대표적인 예시가 &lt;em class=&quot;v2_em&quot;&gt;50%, %50&lt;/em&gt; 이런 것들입니다.&lt;br&gt;
&lt;!-- --&gt;문자와 단위, 숫자 이런 것들이 섞여버리게 되면 단순히 *direction: 'rtl'*로는 해결되지 않습니다.&lt;br&gt;
&lt;em class=&quot;v2_em&quot;&gt;일일이 처리&lt;/em&gt;를 해주어야 합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;그러나 위와 같은 것들은 일단 배포하고 &lt;em class=&quot;v2_em&quot;&gt;나중에 신경써도 되는 내용&lt;/em&gt;인 것 같습니다.&lt;br&gt;
&lt;!-- --&gt;추후 알게된 사실인데 대부분의 웹 사이트들이 LTR로 되어 있기 때문에 RTL을 사용하는 국가도 &lt;em class=&quot;v2_em&quot;&gt;큰 불편함 없이 사용&lt;/em&gt;한다고 합니다.&lt;br&gt;
&lt;!-- --&gt;실제로 이집트에서 운영되고 있는 사이트만 보아도 모든 사이트들이 완벽하게 지원하고 있지는 않습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;따라서 나중에 VOC가 많이 접수되면 &lt;em class=&quot;v2_em&quot;&gt;고도화 작업&lt;/em&gt;을 하는 것이 좋아보입니다.&lt;br&gt;
&lt;!-- --&gt;추가로 React pdf 라이브러리 중 rtl을 제대로 지원해주는 것은 없는 것 같습니다... ㅠㅠ&lt;/p&gt;
&lt;h2 id=&quot;동아라비아-서아라비아-숫자&quot; class=&quot;v2_h2_title&quot;&gt;동아라비아, 서아라비아 숫자&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;우리가 아는 숫자는 &lt;em class=&quot;v2_em&quot;&gt;0, 1, ..., 9&lt;/em&gt; 입니다.&lt;br&gt;
&lt;!-- --&gt;혹시 &lt;em class=&quot;v2_em&quot;&gt;٠,١,٢,٣,٤,٥,٦,٧,٨,٩&lt;/em&gt; 를 알고 계신가요?&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;우리가 알고 있는 0~9는 &lt;em class=&quot;v2_em&quot;&gt;서아라비아 숫자&lt;/em&gt;입니다.&lt;br&gt;
&lt;!-- --&gt;위에 적혀 있는 문자로 되어 있는 것은 &lt;em class=&quot;v2_em&quot;&gt;동아라비아 숫자 중 아랍 문자&lt;/em&gt;입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;그러나 많이 사용되는 서아라비아 숫자가 사용하기 편하겠죠?&lt;br&gt;
&lt;!-- --&gt;따라서 가장 베스트는 해당 국가 법인과 이야기 하여 &lt;em class=&quot;v2_em&quot;&gt;서아라비아 숫자만 사용&lt;/em&gt;하는 것입니다.&lt;br&gt;
&lt;!-- --&gt;만약 이것이 안된다면 &lt;em class=&quot;v2_em&quot;&gt;동아라비아 숫자를 서아라비아 숫자로 변환하는 함수&lt;/em&gt;를 구현하여 사용하셔야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://codiving.kr/212&quot; title=&quot;서아라비아, 동아라비아 숫자 변환하기&quot;&gt;• 서아라비아, 동아라비아 숫자 변환하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;</description>
      <category>공유/기타</category>
      <category>글로벌 웹서비스</category>
      <category>다국가 웹 서비스</category>
      <category>다국가 웹 서비스 처리</category>
      <category>다국어 처리</category>
      <category>웹서비스</category>
      <category>이집트 웹서비스</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/215</guid>
      <comments>https://codiving.tistory.com/215#entry215comment</comments>
      <pubDate>Thu, 22 Aug 2024 19:23:02 +0900</pubDate>
    </item>
    <item>
      <title>[dayjs] DayJS를 이용하여 날짜 다루기</title>
      <link>https://codiving.tistory.com/214</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;[dayjs] DayJS를 이용하여 날짜 다루기&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;날짜 라이브러리 중에서 아직까지 &lt;i&gt;moment의 다운로드 수&lt;/i&gt;가 상당히 높습니다.&lt;br /&gt;&lt;!-- --&gt;그러나 그 뒤를 빠르게 따라오는 라이브러리가 &lt;i&gt;dayjs&lt;/i&gt; 입니다.&lt;br /&gt;&lt;!-- --&gt;사용방법도 매우 유사합니다. 간단하고 빠르게 한번 알아보도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;설명보다는 &lt;i&gt;코드 위주&lt;/i&gt;로 작성해두겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;dayjs 라이브러리 설치하는 것은 패스하도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;dayjs-년월일-시분초-값-가져오기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;dayjs 년월일 시분초 값 가져오기&lt;/h2&gt;
&lt;h3 id=&quot;각-함수를-이용하여-가져오기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;각 함수를 이용하여 가져오기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 방식은 &lt;i&gt;년, 월, 일, 시, 분, 초&lt;/i&gt; 각각의 &lt;i&gt;값이 필요&lt;/i&gt;할 때 사용하면 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;const dayjs = require(&quot;dayjs&quot;);

const dj = dayjs();

const year = dj.year();
const month = dj.month();
const date = dj.date();

const hour = dj.hour();
const minute = dj.minute();
const second = dj.second();

console.log(`${year}년 ${month}월 ${date}일 ${hour}:${minute}:${second}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tSpsV/btsJbkorQx9/hecSBGCXYRUtWWE41Cpft1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tSpsV/btsJbkorQx9/hecSBGCXYRUtWWE41Cpft1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tSpsV/btsJbkorQx9/hecSBGCXYRUtWWE41Cpft1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtSpsV%2FbtsJbkorQx9%2FhecSBGCXYRUtWWE41Cpft1%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;502&quot; height=&quot;92&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에서 확인할 수 있듯이 &lt;i&gt;해당하는 함수&lt;/i&gt;를 사용하면 쉽게 값을 가져올 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;format-함수를-이용하여-출력하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;format 함수를 이용하여 출력하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;각각의 값이 궁금한게 아니라 &lt;i&gt;년, 월, 일, 시, 분, 초를 표기&lt;/i&gt;하고 싶은 경우는 어떨까요?&lt;br /&gt;&lt;!-- --&gt;위와 같은 경우는 굳이 각각의 함수를 호출할 필요가 없습니다.&lt;br /&gt;&lt;i&gt;format 함수&lt;/i&gt;를 이용하여 간단하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const dayjs = require(&quot;dayjs&quot;);

const dj = dayjs();

console.log(dj.format(&quot;YYYY년 MM월 DD일 HH:mm:ss&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZYBRY/btsJdkmvECz/98oSXhZAhkZYdBrrnzjUi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZYBRY/btsJdkmvECz/98oSXhZAhkZYdBrrnzjUi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZYBRY/btsJdkmvECz/98oSXhZAhkZYdBrrnzjUi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZYBRY%2FbtsJdkmvECz%2F98oSXhZAhkZYdBrrnzjUi0%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;514&quot; height=&quot;96&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 &lt;i&gt;format 함수&lt;/i&gt;를 이용하면 간단하게 확인할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;1번 방식과 약간의 &lt;i&gt;차이점&lt;/i&gt;은 존재합니다.&lt;br /&gt;&lt;!-- --&gt;1번 결과와 다르게 2번 결과에서는 &lt;i&gt;자리 수&lt;/i&gt;에 맞게 앞에 &lt;i&gt;0&lt;/i&gt;을 붙여줍니다.&lt;br /&gt;&lt;!-- --&gt;(format에서 2자리로 명시해주어서 그렇습니다.)&lt;br /&gt;&lt;!-- --&gt;1번에서도 동일하게 동작하고 싶으면 &lt;i&gt;padStart 함수&lt;/i&gt;를 사용하시면 됩니다.&lt;/p&gt;
&lt;h2 id=&quot;format-함수-알아보기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;format 함수 알아보기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에서도 살펴보았듯이 &lt;i&gt;format 함수&lt;/i&gt;를 사용하면 쉽게 &lt;i&gt;날짜를 출력&lt;/i&gt;할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;format 함수를 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;년월일-시분초-ampm-표시&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;년월일 시분초 AM/PM 표시&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const dayjs = require(&quot;dayjs&quot;);

const dj = dayjs();

console.log(`년 : ${dj.format(&quot;YYYY&quot;)}`);
console.log(`월 : ${dj.format(&quot;MM&quot;)}`);
console.log(`일 : ${dj.format(&quot;DD&quot;)}`);
console.log(`시 : ${dj.format(&quot;HH&quot;)} - 24시긴 기준`);
console.log(`시 : ${dj.format(&quot;hh&quot;)} - 12시간 기준`);
console.log(`분 : ${dj.format(&quot;mm&quot;)}`);
console.log(`초 : ${dj.format(&quot;ss&quot;)}`);
console.log(`AM/PM : ${dj.format(&quot;A&quot;)} - 대문자`);
console.log(`am/pm : ${dj.format(&quot;a&quot;)} - 소문자`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I225x/btsJddnzi5i/QkOebvy8gIPNXXdl5Qxp9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I225x/btsJddnzi5i/QkOebvy8gIPNXXdl5Qxp9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I225x/btsJddnzi5i/QkOebvy8gIPNXXdl5Qxp9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI225x%2FbtsJddnzi5i%2FQkOebvy8gIPNXXdl5Qxp9k%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;526&quot; height=&quot;328&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; 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;HH : 24시간 기준 hour&lt;/li&gt;
&lt;li&gt;hh : 12시간 기준 hour&lt;/li&gt;
&lt;li&gt;A : 대문자 AM/PM&lt;/li&gt;
&lt;li&gt;a : 소문자 AM/PM&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;요일-날짜-표시하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;요일, 날짜 표시하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;월요일, 일요일 등 &lt;i&gt;날짜를 표시&lt;/i&gt;하고 싶은 경우가 있습니다.&lt;br /&gt;&lt;!-- --&gt;아래 코드를 확인해봅시다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const dayjs = require(&quot;dayjs&quot;);

const dj = dayjs();

console.log(`${dj.format(&quot;ddd&quot;)}`);
console.log(`${dj.format(&quot;dddd&quot;)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kYFBJ/btsJcXyuthn/3QhTH4YCJVO5xq2tqKYrLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kYFBJ/btsJcXyuthn/3QhTH4YCJVO5xq2tqKYrLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kYFBJ/btsJcXyuthn/3QhTH4YCJVO5xq2tqKYrLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkYFBJ%2FbtsJcXyuthn%2F3QhTH4YCJVO5xq2tqKYrLk%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;500&quot; height=&quot;124&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; 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;ddd : 축약형 요일&lt;/li&gt;
&lt;li&gt;dddd : 전체 요일&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;기타-token&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;기타 token&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 token들은 &lt;i&gt;공식 홈페이지&lt;/i&gt;에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Format&quot; href=&quot;https://day.js.org/docs/en/display/format&quot;&gt;&amp;bull; Format&lt;/a&gt;&lt;br /&gt;&lt;a title=&quot;AdvancedFormat&quot; href=&quot;https://day.js.org/docs/en/plugin/advanced-format&quot;&gt;&amp;bull; AdvancedFormat&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;대부분 Format으로 해결하실 수 있을 것이라고 생각됩니다.&lt;br /&gt;&lt;!-- --&gt;그러나 좀 더 상세히 다루기 위할 땐 AdvancedFormat을 확인하여 사용하시면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;AdvancedFormat을 사용하실 땐 advancedFormat를 꼭 extend 해주셔야 합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;var advancedFormat = require(&quot;dayjs/plugin/advancedFormat&quot;);
// import advancedFormat from 'dayjs/plugin/advancedFormat' // ES 2015

dayjs.extend(advancedFormat);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;localized-format&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;Localized format&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;LocalizedFormat를 사용하실 땐 localizedFormat를 꼭 extend 해주셔야 합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;var localizedFormat = require(&quot;dayjs/plugin/localizedFormat&quot;);
// import localizedFormat from 'dayjs/plugin/localizedFormat' // ES 2015

dayjs.extend(localizedFormat);

dayjs().format(&quot;L LT&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 지역에 맞게 format을 해주는 기능입니다.&lt;br /&gt;&lt;!-- --&gt;예를들어 format 함수에 L을 넣으면 해당 국가에 맞게 년월일이 표시됩니다.&lt;br /&gt;&lt;!-- --&gt;아래 코드를 확인해봅시다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// default
const dayjs = require(&quot;dayjs&quot;);
const localizedFormat = require(&quot;dayjs/plugin/localizedFormat&quot;);
dayjs.extend(localizedFormat);

const dj = dayjs();

console.log(`${dj.format(&quot;L&quot;)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PjdKT/btsJcUhpsIW/jZvYk2SrRcow5lZvNgyWt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PjdKT/btsJcUhpsIW/jZvYk2SrRcow5lZvNgyWt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PjdKT/btsJcUhpsIW/jZvYk2SrRcow5lZvNgyWt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPjdKT%2FbtsJcUhpsIW%2FjZvYk2SrRcow5lZvNgyWt0%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;500&quot; height=&quot;94&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아직 학습하지는 않았지만 아래 코드는 &lt;i&gt;한국어로 변경&lt;/i&gt;해둔 상태입니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 한국어
const dayjs = require(&quot;dayjs&quot;);
const updateLocale = require(&quot;dayjs/plugin/updateLocale&quot;);
require(&quot;dayjs/locale/ko&quot;);

const localizedFormat = require(&quot;dayjs/plugin/localizedFormat&quot;);
dayjs.extend(localizedFormat);
dayjs.extend(updateLocale);
dayjs.locale(&quot;ko&quot;);

const dj = dayjs();

console.log(`${dj.format(&quot;L&quot;)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm5tFJ/btsJdzjqBQ5/3ZsGt0TNKMGKML3Fhi1Kl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm5tFJ/btsJdzjqBQ5/3ZsGt0TNKMGKML3Fhi1Kl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm5tFJ/btsJdzjqBQ5/3ZsGt0TNKMGKML3Fhi1Kl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm5tFJ%2FbtsJdzjqBQ5%2F3ZsGt0TNKMGKML3Fhi1Kl1%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;510&quot; height=&quot;98&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 결과를 보시면 알 수 있듯이 각 &lt;i&gt;지역화&lt;/i&gt;에 따라 &lt;i&gt;년월일 표기법이 다르게&lt;/i&gt; 나타납니다.&lt;br /&gt;&lt;!-- --&gt;따라서 &lt;i&gt;다국가 처리&lt;/i&gt;를 위해서는 해당 기능 사용하시면 간단하게 처리하실 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에서는 간단하게 L만 확인해보았습니다.&lt;br /&gt;&lt;!-- --&gt;더 다양한 &lt;i&gt;localized format&lt;/i&gt;을 확인하고 싶으시면 아래 링크를 확인해보세요.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;localized-formats&quot; href=&quot;https://day.js.org/docs/en/display/format#localized-formats&quot;&gt;&amp;bull; localized-formats&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;언어-변경하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;언어 변경하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;바로 위에서 &lt;i&gt;한국어로 변경&lt;/i&gt;을 하였습니다.&lt;br /&gt;&lt;!-- --&gt;한국어 외에도 많은 언어로 변경할 수 있는데 알아보도록 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const dayjs = require(&quot;dayjs&quot;);
const updateLocale = require(&quot;dayjs/plugin/updateLocale&quot;);
require(&quot;dayjs/locale/ko&quot;); // 원하는 언어
dayjs.extend(updateLocale);
dayjs.locale(&quot;ko&quot;); // 언어 설정

const dj = dayjs();

console.log(`${dj.format(&quot;dddd&quot;)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpZHUl/btsJdyLAKYh/KP2lSmsDCYOIratmEgCjkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpZHUl/btsJdyLAKYh/KP2lSmsDCYOIratmEgCjkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpZHUl/btsJdyLAKYh/KP2lSmsDCYOIratmEgCjkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpZHUl%2FbtsJdyLAKYh%2FKP2lSmsDCYOIratmEgCjkk%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;516&quot; height=&quot;86&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;코드에 &lt;i&gt;주석처리&lt;/i&gt;를 해두었습니다.&lt;br /&gt;&lt;!-- --&gt;require로 원하는 locale을 가져온 후 &lt;i&gt;dayjs.locale 함수로 설정&lt;/i&gt;하시고, &lt;i&gt;updateLocale을 extend&lt;/i&gt; 해주시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const dayjs = require(&quot;dayjs&quot;);
const updateLocale = require(&quot;dayjs/plugin/updateLocale&quot;);
require(&quot;dayjs/locale/ja&quot;);
dayjs.extend(updateLocale);
dayjs.locale(&quot;ja&quot;);

const dj = dayjs();

console.log(`${dj.format(&quot;dddd&quot;)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HosLT/btsJcknmKGl/3Kae8A7dx4PL362kNbuPtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HosLT/btsJcknmKGl/3Kae8A7dx4PL362kNbuPtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HosLT/btsJcknmKGl/3Kae8A7dx4PL362kNbuPtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHosLT%2FbtsJcknmKGl%2F3Kae8A7dx4PL362kNbuPtk%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;502&quot; height=&quot;88&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;일본어&lt;/i&gt;로 변경 해보았는데 잘됩니다.&lt;br /&gt;&lt;!-- --&gt;위처럼 간단하게 &lt;i&gt;언어 변경&lt;/i&gt;하실 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;변경-가능한-언어-확인하는-방법&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;변경 가능한 언어 확인하는 방법&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;언어 변경하는 방법을 알았으니 &lt;i&gt;어떤 언어로 변경이 가능&lt;/i&gt;한지만 알면됩니다.&lt;br /&gt;&lt;i&gt;node_modules&amp;gt;dayjs&amp;gt;locale&amp;gt;&lt;/i&gt; 경로에 들어가셔서 확인하시면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;결과 이미지 - 라스트&lt;/p&gt;
&lt;h2 id=&quot;dayjs-날짜-조작하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;dayjs 날짜 조작하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;날짜를 +1 day, -1 day, 주의 시작과 끝 등 &lt;i&gt;조작하고 싶은 경우&lt;/i&gt;가 있습니다.&lt;br /&gt;&lt;!-- --&gt;이러한 것도 간단하게 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;특정-기준에서-더하기-빼기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;특정 기준에서 더하기, 빼기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;년,월,일,시,분,초 등 특정 unit으로 &lt;i&gt;시간을 더하고 뺼&lt;/i&gt; 수 있습니다.&lt;br /&gt;&lt;i&gt;add, subtract 함수&lt;/i&gt;를 이용하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;const FORMAT = &quot;YYYY MM DD, HH:mm:ss&quot;;

const dayjs = require(&quot;dayjs&quot;);
const dj = dayjs();

console.log(`현재 시간 : ${dj.format(FORMAT)}`);
console.log();
console.log(`1년 더하기 : ${dj.add(1, &quot;year&quot;).format(FORMAT)}`);
console.log(`1년 빼기 : ${dj.subtract(1, &quot;year&quot;).format(FORMAT)}`);
console.log();
console.log(`1달 더하기 : ${dj.add(1, &quot;month&quot;).format(FORMAT)}`);
console.log(`1달 빼기 : ${dj.subtract(1, &quot;month&quot;).format(FORMAT)}`);
console.log();
console.log(`1일 더하기 : ${dj.add(1, &quot;date&quot;).format(FORMAT)}`);
console.log(`1일 빼기 : ${dj.subtract(1, &quot;date&quot;).format(FORMAT)}`);
console.log();
console.log(`1시간 더하기 : ${dj.add(1, &quot;hour&quot;).format(FORMAT)}`);
console.log(`1시간 빼기 : ${dj.subtract(1, &quot;hour&quot;).format(FORMAT)}`);
console.log();
console.log(`1분 더하기 : ${dj.add(1, &quot;minute&quot;).format(FORMAT)}`);
console.log(`1분 빼기 : ${dj.subtract(1, &quot;minute&quot;).format(FORMAT)}`);
console.log();
console.log(`1초 더하기 : ${dj.add(1, &quot;second&quot;).format(FORMAT)}`);
console.log(`1초 빼기 : ${dj.subtract(1, &quot;second&quot;).format(FORMAT)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUtzXB/btsJbjpy5E1/BKEiShiHyaaKOVmsNsXbDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUtzXB/btsJbjpy5E1/BKEiShiHyaaKOVmsNsXbDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUtzXB/btsJbjpy5E1/BKEiShiHyaaKOVmsNsXbDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUtzXB%2FbtsJbjpy5E1%2FBKEiShiHyaaKOVmsNsXbDK%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;624&quot; height=&quot;588&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;만약 여러 작업을 하고 싶으면 &lt;i&gt;이어서(chaining) 작성&lt;/i&gt;하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;const FORMAT = &quot;YYYY MM DD, HH:mm:ss&quot;;

const dayjs = require(&quot;dayjs&quot;);
const dj = dayjs();

console.log(`현재 시간 : ${dj.format(FORMAT)}`);
console.log();
console.log(
  `+1년, -1달, +3시간 : ${dj
    .add(1, &quot;year&quot;)
    .subtract(-1, &quot;month&quot;)
    .add(3, &quot;hour&quot;)
    .format(FORMAT)}`
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brPP8G/btsJbdW7jd1/5m7kayc1KT45ALys3O8BO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brPP8G/btsJbdW7jd1/5m7kayc1KT45ALys3O8BO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brPP8G/btsJbdW7jd1/5m7kayc1KT45ALys3O8BO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrPP8G%2FbtsJbdW7jd1%2F5m7kayc1KT45ALys3O8BO0%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;634&quot; height=&quot;160&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;특정-기준으로-시작과-끝-계산&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;특정 기준으로 시작과 끝 계산&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;년/월/주/하루 등을 기준으로 &lt;i&gt;시작과 끝&lt;/i&gt;을 계산해야하는 경우가 있습니다.&lt;br /&gt;&lt;!-- --&gt;이럴 땐 &lt;i&gt;startOf, endOf 함수&lt;/i&gt;를 사용하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;const FORMAT = &quot;YYYY MM DD, HH:mm:ss&quot;;

const dayjs = require(&quot;dayjs&quot;);
const dj = dayjs();

console.log(`현재 시간 : ${dj.format(FORMAT)}`);
console.log();
console.log(`년도 시작 : ${dj.startOf(&quot;year&quot;).format(FORMAT)}`);
console.log(`년도 끝 : ${dj.endOf(&quot;year&quot;).format(FORMAT)}`);
console.log();
console.log(`월 시작 : ${dj.startOf(&quot;month&quot;).format(FORMAT)}`);
console.log(`월 끝 : ${dj.endOf(&quot;month&quot;).format(FORMAT)}`);
console.log();
console.log(`주 시작 : ${dj.startOf(&quot;week&quot;).format(FORMAT)}`);
console.log(`주 끝 : ${dj.endOf(&quot;week&quot;).format(FORMAT)}`);
console.log();
console.log(`하루 시작 : ${dj.startOf(&quot;day&quot;).format(FORMAT)}`);
console.log(`하루 끝 : ${dj.endOf(&quot;day&quot;).format(FORMAT)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E8NIX/btsJb7hEAyP/1wOfDh9CqfntOeyWTwT9z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E8NIX/btsJb7hEAyP/1wOfDh9CqfntOeyWTwT9z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E8NIX/btsJb7hEAyP/1wOfDh9CqfntOeyWTwT9z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE8NIX%2FbtsJb7hEAyP%2F1wOfDh9CqfntOeyWTwT9z0%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;606&quot; height=&quot;432&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;timezone-다루기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;timezone 다루기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;timezone 다루기는 &lt;i&gt;중복된 내용&lt;/i&gt;이어서 아래 링크를 참고 부탁드리겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;dayjs timezone 다루기&quot; href=&quot;https://codiving.kr/183&quot;&gt;&amp;bull; dayjs timezone 다루기&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;정리&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;dayjs로 날짜 표기, 조작 등 여러가지를 알아보았습니다.&lt;br /&gt;&lt;!-- --&gt;사실 위 개념까지만 알아도 날짜를 다루는 데 큰 문제는 없습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; 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;/li&gt;
&lt;li&gt;custom format 만들기&lt;/li&gt;
&lt;li&gt;AM, PM 텍스트 변경하기&lt;/li&gt;
&lt;li&gt;일요일~월요일 텍스트 변경하기&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;정도가 있습니다.&lt;br /&gt;&lt;!-- --&gt;아래 링크를 확인하시면 쉽게 구현하실 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;dayjs customization&quot; href=&quot;https://day.js.org/docs/en/customization/customization&quot;&gt;&amp;bull; dayjs customization&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Format&quot; href=&quot;https://day.js.org/docs/en/display/format&quot;&gt;&amp;bull; Format&lt;/a&gt;&lt;br /&gt;&lt;a title=&quot;AdvancedFormat&quot; href=&quot;https://day.js.org/docs/en/plugin/advanced-format&quot;&gt;&amp;bull; AdvancedFormat&lt;/a&gt;&lt;br /&gt;&lt;a title=&quot;localized-formats&quot; href=&quot;https://day.js.org/docs/en/display/format#localized-formats&quot;&gt;&amp;bull; localized-formats&lt;/a&gt;&lt;br /&gt;&lt;a title=&quot;dayjs timezone 다루기&quot; href=&quot;https://codiving.kr/183&quot;&gt;&amp;bull; dayjs timezone 다루기&lt;/a&gt;&lt;br /&gt;&lt;a title=&quot;dayjs customization&quot; href=&quot;https://day.js.org/docs/en/customization/customization&quot;&gt;&amp;bull; dayjs customization&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>dayjs</category>
      <category>dayjs locale</category>
      <category>dayjs timezone</category>
      <category>dayjs 다루기</category>
      <category>dayjs 로케일</category>
      <category>dayjs 언어 변경</category>
      <category>dayjs 타임존</category>
      <category>dayjs 한국어</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/214</guid>
      <comments>https://codiving.tistory.com/214#entry214comment</comments>
      <pubDate>Thu, 22 Aug 2024 19:18:22 +0900</pubDate>
    </item>
    <item>
      <title>SSR로 동작하는 지 확인하는 방법</title>
      <link>https://codiving.tistory.com/213</link>
      <description>&lt;h1 id=&quot;ssr로-동작하는-지-확인하는-방법&quot; class=&quot;v2_h1_title&quot;&gt;SSR로 동작하는 지 확인하는 방법&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;SEO&lt;/i&gt;에 도움이 되기 위해서는 CSR보다는 &lt;i&gt;SSR을 활용&lt;/i&gt;하는 것이 좋습니다.&lt;br /&gt;&lt;!-- --&gt;그런데 지금 페이지가 정상적으로 &lt;i&gt;SSR로 동작&lt;/i&gt;하는 것을 어떻게 확인할 수 있을까요?&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;현재 프로젝트가 어떻게 동작하는 지 &lt;i&gt;정확하게 이해&lt;/i&gt;하고 있으면 어디까지가 SSR이고 아닌 지 파악할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;그러나 모르는 경우 내가 이 부분은 SSR, 이 부분은 CSR이라고 생각하고 구현을 했을 때,&lt;br /&gt;&lt;!-- --&gt;내가 &lt;i&gt;원하는 방향으로 동작&lt;/i&gt;하는 것을 확인할 필요가 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 게시글은 크롬 기준으로 작성되어 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;ssr-동작-확인하는-방법&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;SSR 동작 확인하는 방법&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;SSR 동작을 확인하는 방법은 매우 간단합니다.&lt;br /&gt;&lt;i&gt;JavaScript 동작&lt;/i&gt;을 없애면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 사이트는 제가 &lt;i&gt;사이드 프로젝트&lt;/i&gt;로 진행하고 있는 사이트입니다.&lt;br /&gt;&lt;a title=&quot;다이싸 이동하기&quot; href=&quot;https://www.game.codiving.kr&quot;&gt;&amp;bull; 다이싸 이동하기&lt;/a&gt;&lt;br /&gt;&lt;!-- --&gt;검색에 도움되게 하기 위해 게임마다 &lt;i&gt;설명을 추가&lt;/i&gt;하고 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;제가 원하는 동작은 아래와 같습니다.&lt;br /&gt;&lt;!-- --&gt;SSR : 게임에 대한 설명&lt;br /&gt;&lt;!-- --&gt;CSR : 실제 게임&lt;br /&gt;&lt;!-- --&gt;입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 JavaScript 동작을 없앴을 때 게임 설명이 표시되면 해당 부분은 &lt;i&gt;SSR로 동작&lt;/i&gt;하고 있는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;F12를 눌러 &lt;i&gt;개발자 모드에 진입&lt;/i&gt;하여 설정 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;117&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfMLm2/btsIXYx9gUp/zYAl3mbDkmkMEBDhtMKfkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfMLm2/btsIXYx9gUp/zYAl3mbDkmkMEBDhtMKfkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfMLm2/btsIXYx9gUp/zYAl3mbDkmkMEBDhtMKfkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfMLm2%2FbtsIXYx9gUp%2FzYAl3mbDkmkMEBDhtMKfkK%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;547&quot; height=&quot;117&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;117&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Debugger -&amp;gt; Disable JavaScript 클릭&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dorzmK/btsIXHDgmnq/JEumAL4zjbfh1VlNCkIjo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dorzmK/btsIXHDgmnq/JEumAL4zjbfh1VlNCkIjo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dorzmK/btsIXHDgmnq/JEumAL4zjbfh1VlNCkIjo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdorzmK%2FbtsIXHDgmnq%2FJEumAL4zjbfh1VlNCkIjo1%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;544&quot; height=&quot;664&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 하면 현재 웹 사이트에서 &lt;i&gt;JavaScript를 비활성화&lt;/i&gt; 한 것입니다. 이 상태로 게임을 접속해보겠습니다.&lt;br /&gt;&lt;!-- --&gt;아래 링크를 확인해보면 제가 예상한대로 설명이 잘 나옵니다.&lt;br /&gt;&lt;a title=&quot;다이싸 게임&quot; href=&quot;https://www.game.codiving.kr/game/flag&quot;&gt;&amp;bull; 다이싸 게임&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-08 오후 12.49.34.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/57BLI/btsIYuwzoLw/fGN3g75hBEGCDWOYQfNGh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/57BLI/btsIYuwzoLw/fGN3g75hBEGCDWOYQfNGh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/57BLI/btsIYuwzoLw/fGN3g75hBEGCDWOYQfNGh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F57BLI%2FbtsIYuwzoLw%2FfGN3g75hBEGCDWOYQfNGh0%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;500&quot; height=&quot;555&quot; data-filename=&quot;스크린샷 2024-08-08 오후 12.49.34.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;i&gt;SSR이 정상적으로 동작&lt;/i&gt;하고 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;게임은 시작 버튼을 클릭해야 시작이 되는 데 버튼이 클릭되지 않으니 &lt;i&gt;CSR로 동작&lt;/i&gt;하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;결론&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;본인이 개발한 것이 SSR, CSR로 동작하는 지 의문이 생기면 &lt;i&gt;JavaScript를 비활성화&lt;/i&gt;해서 테스트 해보시길 바랍니다.&lt;/p&gt;</description>
      <category>공유/기타</category>
      <category>CSR</category>
      <category>csr 동작 확인 방법</category>
      <category>csr 확인 방법</category>
      <category>SSR</category>
      <category>ssr 동작 확인 방법</category>
      <category>ssr 확인 방법</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/213</guid>
      <comments>https://codiving.tistory.com/213#entry213comment</comments>
      <pubDate>Thu, 8 Aug 2024 12:52:25 +0900</pubDate>
    </item>
    <item>
      <title>[JS/TS] 서아라비아, 동아라비아 숫자 변환하기</title>
      <link>https://codiving.tistory.com/212</link>
      <description>&lt;div&gt;&lt;p class=&quot;v2_p&quot;&gt;[JS/TS] 서아라비아, 동아라비아 숫자 변환하기&lt;/p&gt;
&lt;h2 id=&quot;숫자-종류-확인하기&quot; class=&quot;v2_h2_title&quot;&gt;숫자 종류 확인하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;숫자에도 종류가 있는 것을 아시나요?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서아라비아 : 0~9&lt;/li&gt;
&lt;li&gt;동아라비아 : ٠,١,٢,٣,٤,٥,٦,٧,٨,٩ (아랍 문자)&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot;&gt;입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;따라서 이집트와 같이 동아라비아 숫자를 사용하는 국가의 경우 따로 &lt;strong class=&quot;v2_strong&quot;&gt;변환 작업이 필요&lt;/strong&gt;합니다.&lt;br&gt;
&lt;!-- --&gt;편의상 서아라비아 숫자는 &lt;strong class=&quot;v2_strong&quot;&gt;아라비아&lt;/strong&gt; 숫자, 동아라비아 숫자는 &lt;strong class=&quot;v2_strong&quot;&gt;아랍 숫자&lt;/strong&gt;라고 표기하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;아라바이-아랍-숫자-변환하기&quot; class=&quot;v2_h2_title&quot;&gt;아라바이, 아랍 숫자 변환하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;변환하는 방법은 간단합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;const ARABIC_TO_ARABIA: { [index: number]: string } = {
  1: &quot;١&quot;,
  2: &quot;٢&quot;,
  3: &quot;٣&quot;,
  4: &quot;٤&quot;,
  5: &quot;٥&quot;,
  6: &quot;٦&quot;,
  7: &quot;٧&quot;,
  8: &quot;٨&quot;,
  9: &quot;٩&quot;,
  0: &quot;٠&quot;,
};

const ARABIA_TO_ARABIC: { [index: string]: string } = {
  &quot;١&quot;: &quot;1&quot;,
  &quot;٢&quot;: &quot;2&quot;,
  &quot;٣&quot;: &quot;3&quot;,
  &quot;٤&quot;: &quot;4&quot;,
  &quot;٥&quot;: &quot;5&quot;,
  &quot;٦&quot;: &quot;6&quot;,
  &quot;٧&quot;: &quot;7&quot;,
  &quot;٨&quot;: &quot;8&quot;,
  &quot;٩&quot;: &quot;9&quot;,
  &quot;٠&quot;: &quot;0&quot;,
};

const convertArabicToArabia = (string: string) =&amp;gt; {
  return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
    return ARABIA_TO_ARABIC[match];
  });
};

const convertArabiaToArabic = (string: string) =&amp;gt; {
  return string.replace(/\d/g, function (match) {
    return ARABIC_TO_ARABIA[Number(match)];
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot;&gt;위처럼 &lt;strong class=&quot;v2_strong&quot;&gt;정규식&lt;/strong&gt;을 사용해주시면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;strong class=&quot;v2_strong&quot;&gt;convertArabiaToArabic&lt;/strong&gt; 함수는 &lt;strong class=&quot;v2_strong&quot;&gt;아랍 숫자&lt;/strong&gt;를 &lt;strong class=&quot;v2_strong&quot;&gt;아라비아 숫자&lt;/strong&gt;로 변환하는 함수입니다.&lt;br&gt;
&lt;strong class=&quot;v2_strong&quot;&gt;convertArabiaToArabic&lt;/strong&gt; 함수는 &lt;strong class=&quot;v2_strong&quot;&gt;아라비아 숫자&lt;/strong&gt;를 &lt;strong class=&quot;v2_strong&quot;&gt;아랍 숫자&lt;/strong&gt;로 변환하는 함수입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;상황에 맞게 사용하시면 됩니다.&lt;/p&gt;&lt;/div&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>동아라비아 숫자</category>
      <category>서아라비아 숫자</category>
      <category>아라비아 숫자</category>
      <category>아랍 숫자</category>
      <category>이집트 숫자</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/212</guid>
      <comments>https://codiving.tistory.com/212#entry212comment</comments>
      <pubDate>Fri, 2 Aug 2024 19:00:54 +0900</pubDate>
    </item>
    <item>
      <title>[JS/TS] TTS, Text to Speech 구현하기</title>
      <link>https://codiving.tistory.com/211</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;JavaScript(TypeScript)를 이용하여 &lt;i&gt;Text를 Voice로 변환&lt;/i&gt;하는 방법을 알아보도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;다른 라이브러리를 사용하지 않고 구현이 가능합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;코드는 TypeScript로 구현하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;tts-구현&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;TTS 구현&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const onSpeakToText = (text: string) =&amp;gt; {
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.lang = 'ko-KR';
  window.speechSynthesis.speak(utterance);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;text를 &lt;i&gt;한국어&lt;/i&gt;로 읽어주는 함수를 구현하였습니다.&lt;/p&gt;
&lt;h3 id=&quot;tts-속도-조절하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;TTS 속도 조절하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;읽는 속도&lt;/i&gt;를 빠르게 또는 느리게 조절도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const onSpeakToText = (text: string, rate: number) =&amp;gt; {
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.lang = 'ko-KR';
  utterance.rate = rate;
  window.speechSynthesis.speak(utterance);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 &lt;i&gt;rate 값&lt;/i&gt;을 1미만으로 주면 느리게, 1초과로 주면 빠르게 읽어줍니다.&lt;/p&gt;
&lt;h3 id=&quot;tts-다른-언어-사용하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;TTS 다른 언어 사용하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;한국어 외 다른 언어로 텍스트를 읽도록 할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;현재 함수에는 &lt;i&gt;lang 값&lt;/i&gt;이 'ko-KR'로 고정이 되어 있는데 이 값을 변경해주면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;어떠한 값을 넣을 수 있는 지 확인하는 방법은 간단합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const synth = window.speechSynthesis;
console.log(synth.getVoices())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNbaH1/btsISr1UEsU/67I0pr45tBEAsHynvEfPb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNbaH1/btsISr1UEsU/67I0pr45tBEAsHynvEfPb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNbaH1/btsISr1UEsU/67I0pr45tBEAsHynvEfPb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNbaH1%2FbtsISr1UEsU%2F67I0pr45tBEAsHynvEfPb1%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;651&quot; height=&quot;502&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 코드를 실행하면 &lt;i&gt;실행 가능한 voices&lt;/i&gt;를 얻을 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;여기에 있는 &lt;i&gt;lang 값&lt;/i&gt;을 확인하여 넣으시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const onSpeakToText = (text: string, rate: number, lang: string) =&amp;gt; {
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.lang = lang;
  utterance.rate = rate;
  window.speechSynthesis.speak(utterance);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;결론&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;간단하게 text를 읽어주는 &lt;i&gt;onSpeakToText&lt;/i&gt; 함수를 구현해보았습니다.&lt;br /&gt;&lt;!-- --&gt;라이브러리를 사용하지 않고 쉽게 구현이 가능합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에서 최종적으로 구현한 함수는 확장성이 없습니다. 실제로 사용하실 때는 &lt;i&gt;함수 오버로딩&lt;/i&gt;을 이용하여 여러 args를 받을 수 있도록 수정하면 좋을 것 같습니다.&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>tts 구현</category>
      <category>tts 속도 조절</category>
      <category>tts 언어 변경</category>
      <category>자바스크립트 tts</category>
      <category>타입스크립트 tts</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/211</guid>
      <comments>https://codiving.tistory.com/211#entry211comment</comments>
      <pubDate>Fri, 2 Aug 2024 12:30:43 +0900</pubDate>
    </item>
    <item>
      <title>VSCode 엔터 키, 정규식 검색하기</title>
      <link>https://codiving.tistory.com/210</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Visual Studio Code 내에서 코드내에서 &lt;i&gt;엔터 키&lt;/i&gt;를 검색하고 싶었습니다.&lt;br /&gt;&lt;i&gt;정규식&lt;/i&gt;을 이용하면 쉽게 해결할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;우선 정규식을 검색할 수 있도록 &lt;i&gt;설정&lt;/i&gt;해줍니다.&lt;br /&gt;&lt;!-- --&gt;검색 아이콘을 클릭한 후 제일 오른쪽에 있는 &lt;i&gt;* 모양&lt;/i&gt;을 클릭해줍니다.&lt;br /&gt;&lt;!-- --&gt;해당 모드가 &lt;i&gt;정규식 검색 모드&lt;/i&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-02 오전 9.35.39.png&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTEGrb/btsIRmtpUgN/kZuDf3fZy2KLmkdi2AIk51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTEGrb/btsIRmtpUgN/kZuDf3fZy2KLmkdi2AIk51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTEGrb/btsIRmtpUgN/kZuDf3fZy2KLmkdi2AIk51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTEGrb%2FbtsIRmtpUgN%2FkZuDf3fZy2KLmkdi2AIk51%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;872&quot; height=&quot;176&quot; data-filename=&quot;스크린샷 2024-08-02 오전 9.35.39.png&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그 다음 엔터를 검색하고 싶으면 &lt;i&gt;\n\n를 입력&lt;/i&gt;하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-02 오전 9.35.10.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkAUoy/btsIS6JFmK8/nR35YWFZ2mcKrMPlLspF9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkAUoy/btsIS6JFmK8/nR35YWFZ2mcKrMPlLspF9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkAUoy/btsIS6JFmK8/nR35YWFZ2mcKrMPlLspF9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkAUoy%2FbtsIS6JFmK8%2FnR35YWFZ2mcKrMPlLspF9K%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;868&quot; height=&quot;188&quot; data-filename=&quot;스크린샷 2024-08-02 오전 9.35.10.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 입력하시면 모든 &lt;i&gt;엔터 키&lt;/i&gt;를 검색하실 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;주의해아할 점은 개발 시 &lt;i&gt;들여쓰기&lt;/i&gt;를 한다는 점입니다.&lt;br /&gt;&lt;!-- --&gt;아래 예시를 확인해봅시다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// tmp.ts file
{
  @Query(() =&amp;gt; Boolean)
  getUser() {}

  @Mutation(() =&amp;gt; Boolean)
  updateUser() {}

  @Query(() =&amp;gt; Boolean)
  getUser() {}

  @Log()
  @Mutation(() =&amp;gt; Boolean)
  updateUser() {}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;제가 검색하고 싶었던 것은 &lt;i&gt;Mutation이면서 Log 데코레이터가 없는 함수&lt;/i&gt;였습니다.&lt;br /&gt;&lt;!-- --&gt;만약 정규식을 사용하지 않으면 &lt;i&gt;@Mutation을 검색&lt;/i&gt;하여 &lt;i&gt;일일이 확인&lt;/i&gt;하여야 합니다.&lt;br /&gt;&lt;!-- --&gt;그러나 정규식을 사용하면 쉽게 검색할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; 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;i&gt;\n\n @Mutation&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;실패 : &lt;i&gt;\n\n@Mutation&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;개발 코드는 들여쓰기가 되어 있기 때문에 해당 부분도 처리를 해주어야 합니다.&lt;br /&gt;&lt;!-- --&gt;위 부분만 신경쓰셔서 검색하시면 됩니다.&lt;/p&gt;</description>
      <category>공유/기타</category>
      <category>Visual Studio Code</category>
      <category>visual studio code 정규식</category>
      <category>vsCode</category>
      <category>vscode 엔터 검색</category>
      <category>vscode 정규식 검색</category>
      <category>정규식 검색</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/210</guid>
      <comments>https://codiving.tistory.com/210#entry210comment</comments>
      <pubDate>Fri, 2 Aug 2024 12:30:04 +0900</pubDate>
    </item>
    <item>
      <title>headless 컴포넌트 도입</title>
      <link>https://codiving.tistory.com/209</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 &lt;i&gt;material-ui&lt;/i&gt;를 사용하였습니다.&lt;br /&gt;&lt;!-- --&gt;그 이유와 왜 지금은 사용하지 않는 지?&lt;br /&gt;&lt;!-- --&gt;그리고 어떻게 하다 &lt;i&gt;headless 컴포넌트를 도입&lt;/i&gt;하게 되었는 지 적어보려고 합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;headless에 대한 설명은 따로 없습니다.&lt;/p&gt;
&lt;h2 id=&quot;material-ui를-사용한-이유&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;material-ui를 사용한 이유&lt;/h2&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;li&gt;시간 부족&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;프로젝트 초창기&lt;/i&gt;에는 &lt;i&gt;material-ui를 사용&lt;/i&gt;하여 개발을 했었습니다.&lt;br /&gt;&lt;!-- --&gt;팀원들은 웹 개발하던 사람들이 아니었어서 react는 물론 &lt;i&gt;css도 처음 접하는 사람&lt;/i&gt;들이었습니다.&lt;br /&gt;&lt;!-- --&gt;따라서 그 당시 유명한 &lt;i&gt;material-ui를 이용&lt;/i&gt;하여 빠르게 구현을 하였습니다.&lt;/p&gt;
&lt;h2 id=&quot;material-ui를-벗어난-이유&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;material-ui를 벗어난 이유&lt;/h2&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;li&gt;자체적으로 구현하고 싶은 욕망&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 기획, 디자인이 고도화 되며 컴포넌트를 &lt;i&gt;커스텀하게 변경&lt;/i&gt;해야하는 필요가 생겼습니다.&lt;br /&gt;&lt;!-- --&gt;이것은 매우 불편하고 어느 정도의 &lt;i&gt;한계가 분명&lt;/i&gt;하다고 생각이 들었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;지금은 mui에서 style이 없는 컴포넌트를 제공해주는 것 같으나&lt;br /&gt;&lt;!-- --&gt;그 당시만 해도 &lt;i&gt;className을 이용하여 변경&lt;/i&gt;을 해주어야 했습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;팀원들의 실력도 성장하였고 자체적인 &lt;i&gt;컴포넌트를 구현하고 싶은 욕구&lt;/i&gt;들이 생겨 하나씩 바꾸어 나갔습니다.&lt;/p&gt;
&lt;h2 id=&quot;material-ui를-벗어나서-생긴-불편함&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;material-ui를 벗어나서 생긴 불편함&lt;/h2&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;li&gt;완성도 있게 구현하기가 생각보다 어려움&lt;/li&gt;
&lt;li&gt;코드가 복잡해짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;당연히 하나 하나씩 구현을 하려고 하니 힘들었습니다.&lt;br /&gt;&lt;!-- --&gt;시간도 많이 들고 생각하지 않아도 되는 로직을 생각하는게 힘들었지만, 미션을 깨는 듯한 재미가 있어 개발 자체는 재미있었습니다. 구현을 하면 할수록 material-ui 개발자들이 대단하다고 느껴졌습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그러나 위 문제는 별 게 아니었습니다.&lt;br /&gt;&lt;!-- --&gt;어떠한 이유 때문에 디자인이 변경되면 수정하기가 힘든 경우가 있었습니다.&lt;br /&gt;&lt;!-- --&gt;대부분의 경우는 쉽게 가능했지만 &lt;i&gt;Select, Table 처럼 복잡한 컴포넌트&lt;/i&gt;의 경우 힘이 들었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그 이유를 확인해보니 &lt;i&gt;하나의 컴포넌트를 완성형&lt;/i&gt;으로 만들고 그 안에 &lt;i&gt;많은 로직&lt;/i&gt;이 들어가 있었습니다.&lt;br /&gt;&lt;!-- --&gt;예를들어 type에 따라서 동작이 &lt;i&gt;single, multiple, groupSingle, groupMultiple&lt;/i&gt; 과 같이 &lt;i&gt;독립된 동작을 구현&lt;/i&gt;해야했습니다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;&amp;lt;Select type=&quot;single&quot; list={[]} /&amp;gt;
&amp;lt;Select type=&quot;multiple&quot; list={[]} /&amp;gt;
&amp;lt;Select type=&quot;groupSingle&quot; list={[]} /&amp;gt;
&amp;lt;Select type=&quot;groupMultiple&quot; list={[]} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 &lt;i&gt;1개의 컴포넌트를 4개의 컴포넌트처럼 사용&lt;/i&gt;을 하였습니다. 위 4개를 하나의 컴포넌트에서 동작이 가능하도록 하니 &lt;i&gt;복잡도가 높아졌고 수정 시 코드를 확인할 때 어려움&lt;/i&gt;이 있었습니다.&lt;br /&gt;&lt;!-- --&gt;또 &lt;i&gt;수정할 떄 외부의 코드는 최대한 변경 없이 구현&lt;/i&gt;하려고 하니 더 복잡했습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이때 우연찮게 &lt;i&gt;headless 컴포넌트&lt;/i&gt;에 대해 알게됩니다.&lt;/p&gt;
&lt;h2 id=&quot;headless-컴포넌트&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;headless 컴포넌트&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 무슨 말인지 정확히 이해가 가지 않았습니다.&lt;br /&gt;&lt;!-- --&gt;그리고 우연히 개발 중 저희 코드를 보니 &lt;i&gt;headless 컴포넌트로 구현했으면 이러한 문제가 없었겠구나&lt;/i&gt; 라는 생각을 하게되었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;간단하게 설명하면 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;&amp;lt;Select&amp;gt;
  &amp;lt;SelectItem&amp;gt;&amp;lt;/SelectItem&amp;gt;
  &amp;lt;SelectItem&amp;gt;&amp;lt;/SelectItem&amp;gt;
  &amp;lt;SelectItem&amp;gt;&amp;lt;/SelectItem&amp;gt;
&amp;lt;/Select&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Select, SelectItem 컴포넌트를 조합&lt;/i&gt;해서 내가 원하는 타입의 Select 컴포넌트를 구현하면 됩니다.&lt;br /&gt;&lt;!-- --&gt;다시 말하면 위 조합으로 &lt;i&gt;직접 single, multiple, groupSingle, groupMultiple 을 구현&lt;/i&gt;하여 사용하면 됩니다.&lt;br /&gt;&lt;!-- --&gt;추가로 위 4가지 타입이 아닌 다른 Select 컴포넌트가 필요하더라도 &lt;i&gt;쉽게 구현&lt;/i&gt;할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;디자인이 바뀌어도 &lt;i&gt;쉽게 css를 수정&lt;/i&gt;할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;모든 컴포넌트는 아니지만 위처럼 &lt;i&gt;변경 발생하면 수정하기 어렵거나&lt;/i&gt;,&lt;br /&gt;&lt;i&gt;코드 복잡도가 높은 컴포넌트&lt;/i&gt;를 headless 컴포넌트로 변경하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에서 언급했듯이 모든 컴포넌트가 아닌 &lt;i&gt;특정 컴포넌트만 headless로 변경&lt;/i&gt;하였습니다.&lt;br /&gt;&lt;!-- --&gt;상황에 맞게 구현하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;제 글을 보면 뭐가 좋은 지 이해가 안되실 것 같아 보았던 유튜브 링크를 첨부했습니다.&lt;br /&gt;&lt;!-- --&gt;해당 영상을 보아도 저처럼 뭐가 좋은 지 이해가 안되실 수 있는데 그럼 일단 넘어가시면 됩니다.&lt;br /&gt;&lt;!-- --&gt;언젠간 필요하게 될 때 다시 보시면 좋을 것 같습니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;toss&quot; href=&quot;https://www.youtube.com/watch?v=fR8tsJ2r7Eg&amp;amp;pp=ygUP7Yag7IqkIGhlYWRsZXNz&quot;&gt;&amp;bull; toss&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/기타</category>
      <category>Headless</category>
      <category>headless component</category>
      <category>헤드리스</category>
      <category>헤드리스 컴포넌트</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/209</guid>
      <comments>https://codiving.tistory.com/209#entry209comment</comments>
      <pubDate>Wed, 31 Jul 2024 18:12:16 +0900</pubDate>
    </item>
    <item>
      <title>[개선] 프로젝트 분리하여 빌드 속도 개선</title>
      <link>https://codiving.tistory.com/208</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;[개선] 프로젝트 분리하여 빌드 속도 개선&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 게시글은 이전에 제가 &lt;i&gt;빌드를 개선&lt;/i&gt;했던 방법 중 한가지를 소개하기 위해 작성했습니다.&lt;br /&gt;&lt;!-- --&gt;너무 예전에 진행하였어서 동일한 환경이 아니고 &lt;i&gt;비슷한 환경을 억지로 만들어서 진행&lt;/i&gt;했습니다.&lt;/p&gt;
&lt;h2 id=&quot;프로젝트-개선&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;프로젝트 개선&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;프로젝트를 개선&lt;/i&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;/li&gt;
&lt;li&gt;코드 스플릿&lt;/li&gt;
&lt;li&gt;빌드 속도 개선&lt;/li&gt;
&lt;li&gt;사용하는 패키지를 더 좋은 것으로 변경&lt;/li&gt;
&lt;li&gt;등등등&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 &lt;i&gt;빌드 속도를 개선&lt;/i&gt;한 사례를 작성해보도록 하겠습니다.&lt;br /&gt;&lt;i&gt;NextJS&lt;/i&gt; 기준으로 작성되어 있으나 다른 프레임워크에서도 비슷하게 할 수 있을 것 같습니다.&lt;/p&gt;
&lt;h2 id=&quot;번들-사이즈-측정-방법&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;번들 사이즈 측정 방법&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;NextJS에서 &lt;i&gt;번들 사이즈 측정&lt;/i&gt;하는 방법은 간단합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;next.config.js &lt;/i&gt;파일에서 아래 설정을 해줍니다.&lt;br /&gt;&lt;i&gt;ANALYZE 값이 true&lt;/i&gt;인 경우만 분석도구를 실행하기 위함입니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

// ...

module.exports = withBundleAnalyzer(nextConfig);
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;터미널에 아래 명령어를 입력해줍니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ANALYZE=true yarn build
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;사긴이 지나면 빌드가 되고 &lt;i&gt;결과 페이지가 표시&lt;/i&gt;될 것입니다.&lt;/p&gt;
&lt;h2 id=&quot;번들-사이즈-분석-및-개선하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;번들 사이즈 분석 및 개선하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;번들 사이즈 분석 결과는 아래와 같습니다.&lt;br /&gt;&lt;!-- --&gt;편의상 결과 이미지는 stat, parsed, gzip 중 &lt;i&gt;stat만 작성&lt;/i&gt;하도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;최종 값은 하단에 적어두겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;stat-1.png&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sM211/btsIzdwtVHz/h6KZF9eQ8w3iF7TP02U2TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sM211/btsIzdwtVHz/h6KZF9eQ8w3iF7TP02U2TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sM211/btsIzdwtVHz/h6KZF9eQ8w3iF7TP02U2TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsM211%2FbtsIzdwtVHz%2Fh6KZF9eQ8w3iF7TP02U2TK%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;389&quot; height=&quot;231&quot; data-filename=&quot;stat-1.png&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;stat-2.png&quot; data-origin-width=&quot;1251&quot; data-origin-height=&quot;927&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehJgzw/btsIzKtINsW/EtqdpLp0HPYKKeEBHKJryk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehJgzw/btsIzKtINsW/EtqdpLp0HPYKKeEBHKJryk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehJgzw/btsIzKtINsW/EtqdpLp0HPYKKeEBHKJryk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehJgzw%2FbtsIzKtINsW%2FEtqdpLp0HPYKKeEBHKJryk%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;1251&quot; height=&quot;927&quot; data-filename=&quot;stat-2.png&quot; data-origin-width=&quot;1251&quot; data-origin-height=&quot;927&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;보시면 &lt;i&gt;babylonjs&lt;/i&gt; 관련 패키지가 &lt;i&gt;엄청 큰 것&lt;/i&gt;을 확인하실 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;거의 프로젝트의 &lt;i&gt;50%를 담당&lt;/i&gt;하고 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;babylonjs를 사용은 해야하는데 어떻게 사용하면서 개선할 수 있을가요?&lt;br /&gt;&lt;!-- --&gt;저희는 &lt;i&gt;서비스를 분리&lt;/i&gt;하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;웹에서 3D 파일을 보여주기 위해 babylonjs를 사용하고 있었습니다.&lt;br /&gt;&lt;i&gt;3D 파일을 볼 수 있는 서비스&lt;/i&gt;를 하나 더 만들었습니다.&lt;br /&gt;&lt;!-- --&gt;그리고 &lt;i&gt;쿼리 파라미터&lt;/i&gt;로 값을 넘겨 해당 서비스에서 파일을 볼 수 있도록 하였습니다.&lt;br /&gt;&lt;!-- --&gt;그 결과 메인 프로젝트에서는 &lt;i&gt;babylonjs 관련 패키지를 모두 삭제&lt;/i&gt;하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;다시 번들 사이즈를 분석하였습니다.&lt;br /&gt;&lt;!-- --&gt;(node_modules 관련 이미지를 지웠는지 없어서 올리지 못 하였습니다...)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;stat-3.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEssRF/btsIy0xpEU6/Jc8tganM4IkozmjPEcRyNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEssRF/btsIy0xpEU6/Jc8tganM4IkozmjPEcRyNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEssRF/btsIy0xpEU6/Jc8tganM4IkozmjPEcRyNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEssRF%2FbtsIy0xpEU6%2FJc8tganM4IkozmjPEcRyNk%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;768&quot; height=&quot;512&quot; data-filename=&quot;stat-3.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;파일 사이즈가 엄청 줄어든 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;결과&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;정량적인 빌드 속도가 기억이 나지는 않습니다.&lt;br /&gt;&lt;!-- --&gt;그러나 그 당시 &lt;i&gt;체감상 40%&lt;/i&gt; 정도는 빨라졌던 것으로 기억합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; 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;i&gt;stat&lt;/i&gt; : 59.52MB -&amp;gt; 30.05MB (약 49.51%)&lt;/li&gt;
&lt;li&gt;&lt;i&gt;parsed&lt;/i&gt; : 26.51MB -&amp;gt; 13.57MB (약 48.81%)&lt;/li&gt;
&lt;li&gt;&lt;i&gt;gzip&lt;/i&gt; : 6.51MB -&amp;gt; 3.67MB (약 43.63%)&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 게시글에서는 꼭 &lt;i&gt;코드 스필릿&lt;/i&gt;이나 &lt;i&gt;캐싱, 특정 패키지 변경&lt;/i&gt;이 아닌 &lt;i&gt;다른 방법으로도 개선&lt;/i&gt;할 수 있다는 것을 전달하고 싶었습니다.&lt;br /&gt;&lt;!-- --&gt;실제로 빌드 속도 뿐만이 아니라 서비스가 분리되어 &lt;i&gt;배포와 버전 관리&lt;/i&gt;도 따로 할 수 있었습니다.&lt;/p&gt;</description>
      <category>공유/기타</category>
      <category>nextjs 빌드 속도 개선</category>
      <category>빌드 속도 개선</category>
      <category>프로젝트 개선</category>
      <category>프로젝트 개선 방법</category>
      <category>프로젝트 분리</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/208</guid>
      <comments>https://codiving.tistory.com/208#entry208comment</comments>
      <pubDate>Mon, 15 Jul 2024 18:35:04 +0900</pubDate>
    </item>
    <item>
      <title>[Next] Next14 모노레포에서 로컬 페키지, 모듈 가져와서 사용하는 방법</title>
      <link>https://codiving.tistory.com/207</link>
      <description>&lt;h1 id=&quot;next14-모노레포에서-로컬-페키지-모듈-가져와서-사용하는-방법&quot; class=&quot;v2_h1_title&quot;&gt;Next14 모노레포에서 로컬 페키지, 모듈 가져와서 사용하는 방법&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트를 &lt;i&gt;Next 12에서 14로 업그레이드&lt;/i&gt;를 하였습니다.&lt;br /&gt;&lt;!-- --&gt;그러나 아래와 같은 &lt;i&gt;에러 메시지&lt;/i&gt;가 발생하며 실행되지 않았습니다.&lt;br /&gt;&lt;!-- --&gt;찾아보니 Next14에서 설정이 조금 변경이 되었습니다.&lt;br /&gt;&lt;!-- --&gt;한번 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;발생한-에러와-해결-방법&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;발생한 에러와 해결 방법&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;next-transpile-modules - could not find default CSS rule, CSS imports may not work
next-transpile-modules - could not find default SASS rule, SASS imports may not work
next-transpile-modules - could not find default CSS rule, CSS imports may not work
next-transpile-modules - could not find default SASS rule, SASS imports may not work
next-transpile-modules - could not find default CSS rule, CSS imports may not work
next-transpile-modules - could not find default SASS rule, SASS imports may not work
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-11 오전 10.36.56.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XLfpp/btsIxGp8GvC/kXnk9QjfLciEIjGtKQmC31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XLfpp/btsIxGp8GvC/kXnk9QjfLciEIjGtKQmC31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XLfpp/btsIxGp8GvC/kXnk9QjfLciEIjGtKQmC31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXLfpp%2FbtsIxGp8GvC%2FkXnk9QjfLciEIjGtKQmC31%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;1186&quot; height=&quot;174&quot; data-filename=&quot;스크린샷 2024-07-11 오전 10.36.56.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;하나씩 설정을 건드려보니 &lt;i&gt;next.config.js&lt;/i&gt;에서 제대로 동작하지 않는 것을 확인했습니다.&lt;br /&gt;&lt;!-- --&gt;저희는 모노레포를 사용하고 있고 특정 프로젝트에서는 다른 프로젝트를 모듈로 가져와서 사용하고 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;12 버전에서는 &lt;i&gt;withTM&lt;/i&gt;으로 다른 모듈을 가져와서 사용했었는데,&lt;br /&gt;&lt;!-- --&gt;14 버전에서는 &lt;i&gt;transpilePackages&lt;/i&gt;로 변경이 된 것이었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 경로를 예를들어 아래와 같다고 가정하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@project/utils
@project/date
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;next12&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;Next12&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;const withTM = require(&quot;next-transpile-modules&quot;)([
  &quot;@project/utils&quot;,
  &quot;@project/date&quot;
]);

const nextConfig = {
  reactStrictMode: true,
};

module.exports = withTM(nextConfig);

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;next14&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;Next14&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;const nextConfig = {
  reactStrictMode: true,
  transpilePackages: [&quot;@project/utils&quot;, &quot;@project/date&quot;],
};

module.exports = nextConfig;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 변경해주니 &lt;i&gt;정상 동작&lt;/i&gt;하였습니다.&lt;br /&gt;&lt;!-- --&gt;Next12에서 Next14로 업그레이드 시 참고하시길 바랍니다.&lt;br /&gt;&lt;!-- --&gt;확인해보지는 않았지만 &lt;i&gt;마이그레이션 문서&lt;/i&gt;에 있을 것 같습니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;transpilePackages&quot; href=&quot;https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages&quot;&gt;&amp;bull; transpilePackages&lt;/a&gt;&lt;/p&gt;</description>
      <category>에러 모음/React</category>
      <category>Next 14</category>
      <category>next 14 모노레포</category>
      <category>next 14 모노레포 로컬 모듈</category>
      <category>next 14 모노레포 로컬 패키지</category>
      <category>next 14 업그레이드</category>
      <category>next transpilepackages</category>
      <category>nextjs 14</category>
      <category>transpilepackages</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/207</guid>
      <comments>https://codiving.tistory.com/207#entry207comment</comments>
      <pubDate>Fri, 12 Jul 2024 08:54:13 +0900</pubDate>
    </item>
    <item>
      <title>[Next] 14버전에서 Link와 a를 같이 사용하는 방법</title>
      <link>https://codiving.tistory.com/206</link>
      <description>&lt;h1 id=&quot;next-14버전에서-link와-a를-같이-사용하는-방법&quot; class=&quot;v2_h1_title&quot;&gt;[Next] 14버전에서 Link와 a를 같이 사용하는 방법&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;NextJS 14&lt;/i&gt;버전에서 &lt;i&gt;Link&lt;/i&gt;와 &lt;i&gt;a element&lt;/i&gt;를 같이 사용하고 싶은 경우가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;Link href={route} passHref&amp;gt;
  &amp;lt;a href={route}&amp;gt;{name}&amp;lt;/a&amp;gt;
&amp;lt;/Link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 코드를 작성하였는데 아래 &lt;i&gt;에러가 발생&lt;/i&gt;하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-11 오후 12.15.29.png&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drW6qL/btsIvx9vbft/VORpdEyFkvkrm8q6AD39V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drW6qL/btsIvx9vbft/VORpdEyFkvkrm8q6AD39V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drW6qL/btsIvx9vbft/VORpdEyFkvkrm8q6AD39V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrW6qL%2FbtsIvx9vbft%2FVORpdEyFkvkrm8q6AD39V1%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;943&quot; height=&quot;114&quot; data-filename=&quot;스크린샷 2024-07-11 오후 12.15.29.png&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;Error: Invalid &amp;lt;Link&amp;gt; with &amp;lt;a&amp;gt; child. Please remove &amp;lt;a&amp;gt; or use &amp;lt;Link legacyBehavior&amp;gt;.
Learn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해결방법 1&lt;br /&gt;&lt;i&gt;a element를 삭제&lt;/i&gt;한다.&lt;br /&gt;&lt;!-- --&gt;그러나 이 방법은 제가 원하는 방법이 아니었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;Link href={route} passHref &amp;gt;
  &amp;lt;a href={route}&amp;gt;{name}&amp;lt;/a&amp;gt;
&amp;lt;/Link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해결방법 2&lt;br /&gt;&lt;i&gt;legacyBehavior props를 추가&lt;/i&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;Link href={route} passHref legacyBehavior&amp;gt;
  &amp;lt;a href={route}&amp;gt;{name}&amp;lt;/a&amp;gt;
&amp;lt;/Link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해결방법 2로 수정하였습니다.&lt;/p&gt;</description>
      <category>에러 모음/React</category>
      <category>legacybehavior</category>
      <category>nextjs</category>
      <category>nextjs 14</category>
      <category>nextjs a</category>
      <category>NextJS Link</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/206</guid>
      <comments>https://codiving.tistory.com/206#entry206comment</comments>
      <pubDate>Fri, 12 Jul 2024 08:50:33 +0900</pubDate>
    </item>
    <item>
      <title>VSCode에서 특정 단축키가 안돼요.</title>
      <link>https://codiving.tistory.com/205</link>
      <description>&lt;h1 id=&quot;vscode에서-특정-단축키가-안돼요&quot; class=&quot;v2_h1_title&quot;&gt;VSCode에서 특정 단축키가 안돼요.&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;어느 순간부터 &lt;i&gt;닫은 탭을 다시 오픈하는 단축키&lt;/i&gt;가 되지 않았습니다.&lt;br /&gt;&lt;!-- --&gt;저는 &lt;i&gt;탭을 닫고 열고 하는 습관&lt;/i&gt;이 있습니다.&lt;br /&gt;&lt;!-- --&gt;그래서 닫은 탭을 열어주는 위 단축키가 안되는 것은 개발하는 데 너무 신경이 쓰여 해결했습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; 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;윈도우 : crtl + shift + t&lt;/li&gt;
&lt;li&gt;맥 : command + shift + t&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;vscode-단축키-확인하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;VSCode 단축키 확인하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;안되는 이유는 해당 단축키의 &lt;i&gt;설정이 잘못 되어 있을 확률&lt;/i&gt;이 높습니다.&lt;br /&gt;&lt;!-- --&gt;아니면 &lt;i&gt;중복되어 충돌&lt;/i&gt; 나는 경우도 있습니다.&lt;br /&gt;&lt;!-- --&gt;어떠한 경우인지 확인해보도록 하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;VSCode에서 아래 키를 입력해줍시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도우 : crtl + shift + p&lt;/li&gt;
&lt;li&gt;맥 : command + shift + p&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래와 같은 입력창이 표시되는데 &lt;i&gt;Preference: Open Keyboard Shortcuts&lt;/i&gt;를 입력해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIgxnm/btsIwSxOGO9/BdEdOKn7FcpiFQhBZuWkwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIgxnm/btsIwSxOGO9/BdEdOKn7FcpiFQhBZuWkwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIgxnm/btsIwSxOGO9/BdEdOKn7FcpiFQhBZuWkwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIgxnm%2FbtsIwSxOGO9%2FBdEdOKn7FcpiFQhBZuWkwk%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;1228&quot; height=&quot;918&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그리고 내가 &lt;i&gt;확인하고 싶은 단축키&lt;/i&gt;를 입력합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도우 : crtl + shift + t&lt;/li&gt;
&lt;li&gt;맥 : command + shift + t&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 이미지를 보면 &lt;i&gt;동일한 단축키에 2개의 설정&lt;/i&gt;이 되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pHgkN/btsIwNKjxcJ/CyZDQKUI3Gab3nnP4snrP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pHgkN/btsIwNKjxcJ/CyZDQKUI3Gab3nnP4snrP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pHgkN/btsIwNKjxcJ/CyZDQKUI3Gab3nnP4snrP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpHgkN%2FbtsIwNKjxcJ%2FCyZDQKUI3Gab3nnP4snrP0%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;1324&quot; height=&quot;306&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;!-- --&gt;테일윈드와 관련된 설정을 지워주도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;혹시 모르니 VSCode를 껏다가 &lt;i&gt;재실행&lt;/i&gt; 해주면 잘 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;높은 확률로 다른 단축키도 모두 &lt;i&gt;동일한 방법&lt;/i&gt;으로 해결하실 수 있을 것입니다.&lt;/p&gt;</description>
      <category>공유/기타</category>
      <category>vsCode</category>
      <category>vscode 단축키</category>
      <category>vscode 단축키 안돼요</category>
      <category>vscode 단축키 안됨</category>
      <category>vscode 단축키 확인방법</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/205</guid>
      <comments>https://codiving.tistory.com/205#entry205comment</comments>
      <pubDate>Fri, 12 Jul 2024 08:47:27 +0900</pubDate>
    </item>
    <item>
      <title>Dead Code 찾기, 사용하지 않는 파일, 변수 찾기</title>
      <link>https://codiving.tistory.com/204</link>
      <description>&lt;h1 id=&quot;javascritp-dead-code-찾기-사용하지-않는-파일-변수-찾기&quot; class=&quot;v2_h1_title&quot;&gt;Dead Code 찾기, 사용하지 않는 파일, 변수 찾기&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;JavaScript Dead Code 찾기&lt;br /&gt;&lt;!-- --&gt;JavaScript 사용하지 않는 파일 찾기&lt;br /&gt;&lt;!-- --&gt;JavaScript 사용하지 않는 변수 찾기&lt;br /&gt;&lt;!-- --&gt;JavaScript 사용하지 않는 export 찾기&lt;/p&gt;
&lt;h2 id=&quot;dead-code-란&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;Dead Code 란&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Dead Code&lt;/i&gt; 이름 그대로 &lt;i&gt;죽은 코드&lt;/i&gt;입니다.&lt;br /&gt;&lt;!-- --&gt;즉, &lt;i&gt;실행되지 않는 코드&lt;/i&gt;를 의미합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 코드를 계속 두는 것은 좋은 &lt;i&gt;개발환경&lt;/i&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;/li&gt;
&lt;li&gt;빌드 속도 우려&lt;/li&gt;
&lt;li&gt;import 시 불편함&lt;/li&gt;
&lt;li&gt;파일, 코드 찾을 때 불편함&lt;/li&gt;
&lt;li&gt;코드 읽을 때 불편함&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;등이 있을 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;knip&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;knip&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;knip&lt;/i&gt; 패키지를 사용하면 &lt;i&gt;dead code&lt;/i&gt;를 빠르게 찾아줍니다.&lt;br /&gt;&lt;!-- --&gt;knip을 활용하여 어떻게 &lt;i&gt;dead code&lt;/i&gt;를 &lt;i&gt;삭제&lt;/i&gt;하는 지 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;knip-설치&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;knip 설치&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;npm init @knip/config
pnpm create @knip/config
yarn create @knip/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;사용하는 &lt;i&gt;패키지 매니저&lt;/i&gt;에 맞게 설치하시면 됩니다.&lt;br /&gt;&lt;!-- --&gt;자세한 설정이나 설명은 아래 참고문헌에 있는 &lt;i&gt;knip 링크&lt;/i&gt;를 확인해보세요.&lt;/p&gt;
&lt;h3 id=&quot;knip-사용하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;knip 사용하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 경로에서 아래 명령어를 이용하시면 됩니다.&lt;br /&gt;&lt;!-- --&gt;저는 제 프로젝트에서 실행해보도록 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;npx knip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_1.png&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4uDeC/btsIszNmrVA/k1aIloGF5iNrQ36H4Ymoyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4uDeC/btsIszNmrVA/k1aIloGF5iNrQ36H4Ymoyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4uDeC/btsIszNmrVA/k1aIloGF5iNrQ36H4Ymoyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4uDeC%2FbtsIszNmrVA%2Fk1aIloGF5iNrQ36H4Ymoyk%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;786&quot; height=&quot;868&quot; data-filename=&quot;edited_1.png&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에 보시면 &lt;i&gt;사용하지 않는 파일, 변수들에 대한 정보&lt;/i&gt;들이 나와있습니다.&lt;br /&gt;&lt;!-- --&gt;잘못 되었을 수도 있으니 &lt;i&gt;직접 확인하신 후 지우시는 것&lt;/i&gt;을 추천 드립니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;저 같은 경우 사용하지 않는 파일은 &lt;i&gt;command 명령어&lt;/i&gt;로 다 지웠습니다.&lt;br /&gt;&lt;!-- --&gt;나머지는 &lt;i&gt;직접 파일 하나 하나 확인&lt;/i&gt;하면서 삭제하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;잘 되었는지 확인하기 위해 마지막에 &lt;i&gt;tsc로 한번 더 확인&lt;/i&gt;하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LvJ1Y/btsItFFVnqW/OutDpFlptee1oq86apL5r1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LvJ1Y/btsItFFVnqW/OutDpFlptee1oq86apL5r1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LvJ1Y/btsItFFVnqW/OutDpFlptee1oq86apL5r1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLvJ1Y%2FbtsItFFVnqW%2FOutDpFlptee1oq86apL5r1%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;158&quot; height=&quot;34&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 결과 이미지를 보면 &lt;i&gt;약 14,000줄을 삭제&lt;/i&gt;한 것을 확인할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;삭제하면서 쓰레기를 정리하는 기분이 들어 재미있게 지웠습니다.&lt;/p&gt;
&lt;h3 id=&quot;주의점&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;주의점&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;knip을 100% 신뢰할 수는 없는 것 같습니다.&lt;br /&gt;&lt;!-- --&gt;script 에서 사용하는 것일 수도 있고 잘못된 결과를 출력할 수도 있기 때문입니다.&lt;br /&gt;&lt;!-- --&gt;그리고 삭제하면서 다른 &lt;i&gt;사이드 이펙트&lt;/i&gt;가 생길 확률이 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;꼭 직접 &lt;i&gt;확인하시면서 지우는 것을 추천&lt;/i&gt; 드립니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;코드를 구현하다보면 사용하지 않는 코드나 파일이 생깁니다.&lt;br /&gt;&lt;!-- --&gt;즉시 지우면 좋겠지만 나도 모르게 남겨두는 경우가 많이 있습니다.&lt;br /&gt;&lt;!-- --&gt;knip을 사용하여 편하고 빠르게 지우시길 바랍니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;knip&quot; href=&quot;https://knip.dev/&quot;&gt;&amp;bull; knip&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/기타</category>
      <category>dead code</category>
      <category>knip</category>
      <category>데드 코드</category>
      <category>사용하지 않는 export</category>
      <category>사용하지 않는 변수 찾기</category>
      <category>사용하지 않는 코드 찾기</category>
      <category>사용하지 않는 타입 찾기</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/204</guid>
      <comments>https://codiving.tistory.com/204#entry204comment</comments>
      <pubDate>Wed, 10 Jul 2024 13:17:50 +0900</pubDate>
    </item>
    <item>
      <title>[개발자 도구] 개발자 도구 콘솔에서 console.log를 믿으면 안되는 이유</title>
      <link>https://codiving.tistory.com/203</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;[개발자 도구] 개발자 도구 콘솔에서 console.log를 믿으면 안되는 이유&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;개발을 공부하는 친구가 이런 질문을 하였습니다.&lt;br /&gt;&lt;!-- --&gt;console.log를 찍었는데 &lt;i&gt;값이 이상해, 값이 자꾸 바뀌어&lt;/i&gt;.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;듣자마자 어떠한 문제인지 알고 가르쳐주었습니다.&lt;br /&gt;&lt;!-- --&gt;어떠한 문제였을까요?&lt;br /&gt;&lt;!-- --&gt;같이 확인해봅시다.&lt;/p&gt;
&lt;h2 id=&quot;consolelog&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;console.log&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;웹 개발자라면 F12를 눌러 &lt;i&gt;개발자 도구&lt;/i&gt;를 사용합니다.&lt;br /&gt;&lt;i&gt;console 탭&lt;/i&gt;에서 &lt;i&gt;로깅해둔 결과&lt;/i&gt;물을 보고 디버깅을 하는 경우도 있을 것입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이때 &lt;i&gt;주의해야 하는 점&lt;/i&gt;이 있습니다.&lt;br /&gt;&lt;i&gt;원시 값&lt;/i&gt;은 괜찮으나 &lt;i&gt;참조 값&lt;/i&gt;일 때 문제가 발생합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 코드를 확인해봅시다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const obj = { a: 1 };
console.log(obj);
obj.b = 2;
console.log(obj);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEDKde/btsIsBYJqbP/IeulZSuTPiKc5hv1alw1VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEDKde/btsIsBYJqbP/IeulZSuTPiKc5hv1alw1VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEDKde/btsIsBYJqbP/IeulZSuTPiKc5hv1alw1VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEDKde%2FbtsIsBYJqbP%2FIeulZSuTPiKc5hv1alw1VK%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;175&quot; height=&quot;126&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 결과 이미지를 보시면 뭔가 이상하다는 것을 확인하시 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;첫번째 console.log에서 객체를 열어보면 &lt;i&gt;b 키 값이 포함&lt;/i&gt;되어 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이것은 &lt;i&gt;참조하고 있던 객체가 변경&lt;/i&gt;이 되어서 그렇습니다.&lt;br /&gt;&lt;!-- --&gt;C, C++의 &lt;i&gt;포인터&lt;/i&gt;라고 생각하시면 편합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;따라서 그 떄의 객체 값을 정확하게 알고 싶으시면 &lt;i&gt;JSON.stringify&lt;/i&gt;를 사용하셔야 합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const obj = { a: 1 };
console.log(JSON.stringify(obj, null, 2));
obj.b = 2;
console.log(JSON.stringify(obj, null, 2));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;194&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRUfm1/btsIuak1WAR/ekokB55xQyOCXikGjw6kUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRUfm1/btsIuak1WAR/ekokB55xQyOCXikGjw6kUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRUfm1/btsIuak1WAR/ekokB55xQyOCXikGjw6kUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRUfm1%2FbtsIuak1WAR%2FekokB55xQyOCXikGjw6kUK%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;194&quot; height=&quot;99&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;194&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그럼 위처럼 정상적으로 &lt;i&gt;해당 시점의 값&lt;/i&gt;을 보여주고 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;배열도 참조 값&lt;/i&gt;이므로 동일합니다.&lt;br /&gt;&lt;!-- --&gt;빠르게 결과만 살펴보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;const arr = [1];
console.log(arr);
arr.push(2);
console.log(arr)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;194&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3WUWI/btsItR0l2ly/Nq8QKkwhNK13qy5pkAcXC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3WUWI/btsItR0l2ly/Nq8QKkwhNK13qy5pkAcXC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3WUWI/btsItR0l2ly/Nq8QKkwhNK13qy5pkAcXC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3WUWI%2FbtsItR0l2ly%2FNq8QKkwhNK13qy5pkAcXC0%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;194&quot; height=&quot;158&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;194&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이 경우도 &lt;i&gt;JSON.stringify&lt;/i&gt;를 사용하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const arr = [1];
console.log(JSON.stringify(arr, null, 2));
arr.push(2);
console.log(JSON.stringify(arr, null, 2))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;199&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRpUUx/btsIt4ZtwVg/lZKXtskCDqVwE1Q6EYZcKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRpUUx/btsIt4ZtwVg/lZKXtskCDqVwE1Q6EYZcKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRpUUx/btsIt4ZtwVg/lZKXtskCDqVwE1Q6EYZcKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRpUUx%2FbtsIt4ZtwVg%2FlZKXtskCDqVwE1Q6EYZcKk%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;199&quot; height=&quot;100&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;199&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;정리하자면 &lt;i&gt;참조 값 변수&lt;/i&gt;를 출력하면 &lt;i&gt;값이 변할&lt;/i&gt; 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;따라서 해당 시점의 값을 정확히 보고 싶으면 &lt;i&gt;JSON.stringify 를 사용&lt;/i&gt;해야 합니다.&lt;/p&gt;</description>
      <category>에러 모음/기타</category>
      <category>console.log</category>
      <category>console.log 값이 바뀌어요</category>
      <category>console.log 값이 이상해요</category>
      <category>console.log 객체 값 변함</category>
      <category>console.log 배열 값 변함</category>
      <category>console.log 주의점</category>
      <category>console.log 참조 값</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/203</guid>
      <comments>https://codiving.tistory.com/203#entry203comment</comments>
      <pubDate>Wed, 10 Jul 2024 13:11:35 +0900</pubDate>
    </item>
    <item>
      <title>[Next] dynamic, lazy import 시 발생하는 에러</title>
      <link>https://codiving.tistory.com/202</link>
      <description>&lt;h1 id=&quot;next-dynamic-lazy-import-시-발생하는-에러&quot; class=&quot;v2_h1_title&quot;&gt;[Next] dynamic, lazy import 시 발생하는 에러&lt;/h1&gt;
&lt;h2 id=&quot;하고자-한-것&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;하고자 한 것&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;icon을 볼 수 있는 페이지를 구현하려고 했습니다.&lt;br /&gt;&lt;!-- --&gt;로직 순서는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[Server] icon이 있는 폴더를 읽는다.&lt;/li&gt;
&lt;li&gt;[Server] .tsx 확장자만 필터링하여 배열로 데이터를 만든다.&lt;/li&gt;
&lt;li&gt;[Front] 서버에서 받은 데이터를 활용하여 렌더링을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;서버에서 데이터를 생성 후 프론트에게 전달하면 프론트에서 &lt;i&gt;lazy import&lt;/i&gt;로 렌더링 하는 순간 읽으려고 하였습니다.&lt;br /&gt;&lt;!-- --&gt;그 이유는 &lt;i&gt;icon을 추가할 때마다 ICONS 배열을 변경&lt;/i&gt;하는 것은 불필요한 작업이라고 생각했기 때문입니다.&lt;/p&gt;
&lt;h3 id=&quot;서버에서-만든-데이터-구조&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;서버에서 만든 데이터 구조&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;const ICONS = [
    {
        name: &quot;AddIcon&quot;,
        src: &quot;../../icon/AddIcon&quot;
    },
    ...
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;에러가-발생하는-프론트-코드&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;에러가 발생하는 프론트 코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;{ICONS.map(icon =&amp;gt; {
    const Component = dynamic(() =&amp;gt; import(`${icon.src}`), { ssr: false });
    return &amp;lt;div key={icon.src}&amp;gt;{&amp;lt;Component /&amp;gt;}&amp;lt;/div&amp;gt;
})}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;에러가-발생하지-않는-프론트-코드&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;에러가 발생하지 않는 프론트 코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;{ICONS.map(icon =&amp;gt; {
    const Component = dynamic(() =&amp;gt; import(`../../icon/AddIcon`), { ssr: false });
    return &amp;lt;div key={icon.src}&amp;gt;{&amp;lt;Component /&amp;gt;}&amp;lt;/div&amp;gt;
})}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 2개의 차이는 &lt;i&gt;경로를 변수로 할당&lt;/i&gt;하는지 아닌지의 차이입니다.&lt;br /&gt;&lt;!-- --&gt;그럼 &lt;i&gt;에러 메시지&lt;/i&gt;와 &lt;i&gt;발생 이유&lt;/i&gt;를 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;에러-메시지&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;에러 메시지&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;에러 메시지는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;error - uncaughtException: TypeError: Cannot read properties of null (reading 'match')
at Object.fn (/Users/codiving/test-project/node_modules/next-transpile-modules/src/next-transpile-modules.js:70:40)
at /Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:117581:37
at Array.some (&amp;lt;anonymous&amp;gt;)
at Object.fn (/Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:117581:25)
at execRule (/Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:117340:22)
at execRule (/Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:117365:10)
at execRule (/Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:117360:6)
at Object.exec (/Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:117379:6)
at /Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:58350:35
at /Users/codiving/test-project/node_modules/next/dist/compiled/webpack/bundle5.js:57973:11
(node:52267) [DEP_WEBPACK_MODULE_ISSUER] DeprecationWarning: Module.issuer: Use new ModuleGraph API
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db
Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FBd6l/btsIsxhKd3T/dHHn5kS8R4s5fsRTTKxKPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FBd6l/btsIsxhKd3T/dHHn5kS8R4s5fsRTTKxKPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FBd6l/btsIsxhKd3T/dHHn5kS8R4s5fsRTTKxKPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFBd6l%2FbtsIsxhKd3T%2FdHHn5kS8R4s5fsRTTKxKPk%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;1696&quot; height=&quot;588&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;너무 이상하여 &lt;i&gt;NextJS 공식 홈페이지&lt;/i&gt;를 다시 보니 아래처럼 나와있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FWkJ0/btsIuFZcOZY/omf80XDXdPOWOceGmvdFDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FWkJ0/btsIuFZcOZY/omf80XDXdPOWOceGmvdFDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FWkJ0/btsIuFZcOZY/omf80XDXdPOWOceGmvdFDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFWkJ0%2FbtsIuFZcOZY%2Fomf80XDXdPOWOceGmvdFDK%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;653&quot; height=&quot;168&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;애초에 제가 &lt;i&gt;잘못 사용&lt;/i&gt;하고 있었습니다.&lt;br /&gt;&lt;i&gt;경로를 명확하게 작성&lt;/i&gt;하라고 합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;저처럼 쓸모 없는 삽질을 하지 마시라고 에러 코드도 모두 작성해두었습니다.&lt;br /&gt;&lt;!-- --&gt;(검색에 걸리기 위해)&lt;/p&gt;</description>
      <category>에러 모음/React</category>
      <category>import error</category>
      <category>nextjs</category>
      <category>nextjs dynamic import</category>
      <category>nextjs import</category>
      <category>nextjs lazy import</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/202</guid>
      <comments>https://codiving.tistory.com/202#entry202comment</comments>
      <pubDate>Wed, 10 Jul 2024 13:07:26 +0900</pubDate>
    </item>
    <item>
      <title>[회고록] dental-notation 패키지 npm 배포</title>
      <link>https://codiving.tistory.com/201</link>
      <description>&lt;div&gt;&lt;h1 id=&quot;typescript-프로젝트-npm-배포-후기&quot; class=&quot;v2_h1_title&quot;&gt;dental-notation 패키지 npm 배포&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot;&gt;치과에서 &lt;em class=&quot;v2_em&quot;&gt;치아를 표기&lt;/em&gt;하는 대표적인 방법이 3개 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FDI&lt;/li&gt;
&lt;li&gt;Universal&lt;/li&gt;
&lt;li&gt;Palmer&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot;&gt;각 국가마다 사용하는 표기법은 다르다.&lt;br&gt;
&lt;!-- --&gt;치과 관련 서비스를 구현하다보니 &lt;em class=&quot;v2_em&quot;&gt;치아 번호를 편하게 다룰 수 있는 라이브러리&lt;/em&gt;가 있으면 좋겠다고 생각했다.&lt;br&gt;
&lt;!-- --&gt;그래서 간단하게 구현하여 &lt;em class=&quot;v2_em&quot;&gt;npm에 배포&lt;/em&gt;해보았다.&lt;/p&gt;
&lt;h2 id=&quot;기능&quot; class=&quot;v2_h2_title&quot;&gt;기능&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;기능은 총 2개 구현했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 치아 &lt;em class=&quot;v2_em&quot;&gt;notation 별 변환&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;치아 번호를 &lt;em class=&quot;v2_em&quot;&gt;문자열로 변환&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;notation-변환&quot; class=&quot;v2_h3_title&quot;&gt;Notation 변환&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;국가별로 치아를 나타내는 방법은 &lt;em class=&quot;v2_em&quot;&gt;모두 다르다&lt;/em&gt;.&lt;br&gt;
&lt;!-- --&gt;위 문제를 해결하기 위해 국가별로 &lt;em class=&quot;v2_em&quot;&gt;DB에 다르게 저장&lt;/em&gt;하고 &lt;em class=&quot;v2_em&quot;&gt;코드에서 분기처리&lt;/em&gt;를 한다는 것은 쉽지 않다.&lt;br&gt;
&lt;!-- --&gt;따라서 국가와 상관없이 &lt;em class=&quot;v2_em&quot;&gt;DB에 1가지 notation 값만 저장&lt;/em&gt;을 해둔다.&lt;br&gt;
&lt;!-- --&gt;그리고 나라별로 보여줄 때 해당 &lt;em class=&quot;v2_em&quot;&gt;notation으로 변환&lt;/em&gt;하여 보여주는 방식을 사용한다.&lt;br&gt;
&lt;!-- --&gt;위 방식으로 구현하려면 각 &lt;em class=&quot;v2_em&quot;&gt;notation별 변환하는 함수&lt;/em&gt;가 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;치아-번호-문자열로-변환&quot; class=&quot;v2_h3_title&quot;&gt;치아 번호 문자열로 변환&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot;&gt;치아 번호도 &lt;em class=&quot;v2_em&quot;&gt;연속되는 숫자나 문자&lt;/em&gt;가 있다.&lt;br&gt;
&lt;!-- --&gt;이걸 하나씩 보여주면 문자열이 굉징히 길어진다.&lt;br&gt;
&lt;!-- --&gt;이걸 짧게 표시하는 방법이 각 notation 마다 다른데 이걸 구현하였다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot;&gt;숫자로 표현하자면 아래 방식과 동일하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1, 2, 3, 4, 5 : 1~5&lt;/li&gt;
&lt;li&gt;1, 2, 4, 5, 7, 8, 9 : 1~2, 4~5, 7~9&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;배운점&quot; class=&quot;v2_h2_title&quot;&gt;배운점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;tsup을 활용하여 빠르게 TypeScript 라이브러리를 번들하는 방법&lt;/li&gt;
&lt;li&gt;TypeScript 오버라이딩 활용 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot;&gt;위 중에서 2번째 &lt;em class=&quot;v2_em&quot;&gt;TypeScript 오버라이딩 활용&lt;/em&gt;하는 방법을 배운 것이 많은 도움이 되었다.&lt;/p&gt;
&lt;h2 id=&quot;결과물&quot; class=&quot;v2_h2_title&quot;&gt;결과물&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/dental-notation&quot; title=&quot;npm dental-notation&quot;&gt;• npm dental-notation&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://dental-notation-docs.vercel.app/&quot; title=&quot;dental-notation-docs&quot;&gt;• dental-notation-docs&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description>
      <category>회고/회고록</category>
      <category>dental-notation</category>
      <category>FDI</category>
      <category>palmer</category>
      <category>Universal</category>
      <category>자바스크립트 치아</category>
      <category>자바스크립트 치아 표기</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/201</guid>
      <comments>https://codiving.tistory.com/201#entry201comment</comments>
      <pubDate>Wed, 10 Jul 2024 13:03:39 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 만 나이 계산하는 방법</title>
      <link>https://codiving.tistory.com/200</link>
      <description>&lt;div&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;[JavaScript] 만 나이 계산하는 방법&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;만 나이&lt;/i&gt;를 계산해야하는 경우가 생겨 간단하게 구현하였습니다.&lt;br /&gt;&lt;!-- --&gt;사실 0세 미만의 나이는 없지만 예외처리는 따로 하지 않고 &lt;i&gt;0으로 반환&lt;/i&gt;하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;로직은 간단하여 설명은 생락하도록 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const getFullAge = (dob: Date, target: Date = new Date()) =&amp;gt; {
  const dobYear = dob.getFullYear();
  const dobMonth = dob.getMonth() + 1;
  const dobDate = dob.getDate();

  const targetYear = target.getFullYear();
  const targetMonth = target.getMonth() + 1;
  const targetDate = target.getDate();

  const age = targetYear - dobYear;

  // age가 음수인 경우도 0으로 처리
  if (age &amp;lt;= 0) return 0;
  if (targetMonth &amp;lt; dobMonth) return age - 1;
  if (targetMonth === dobMonth &amp;amp;&amp;amp; targetDate &amp;lt; dobDate) return age - 1;
  return age;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>javascript 만 나이</category>
      <category>javascript 만 나이 계산</category>
      <category>만 나이 계산</category>
      <category>만 나이 계산기</category>
      <category>자바스크립트 만 나이</category>
      <category>자바스크립트 만 나이 계산</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/200</guid>
      <comments>https://codiving.tistory.com/200#entry200comment</comments>
      <pubDate>Wed, 10 Jul 2024 13:00:13 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 배열 리스트를 객체로 그룹화 하는 방법</title>
      <link>https://codiving.tistory.com/199</link>
      <description>&lt;h1 id=&quot;javascript-배열-리스트를-객체로-그룹화-하는-방법&quot; class=&quot;v2_h1_title&quot;&gt;[JavaScript] 배열 리스트를 객체로 그룹화 하는 방법&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 &lt;i&gt;GroupSelect 컴포넌트&lt;/i&gt;를 구현하였습니다.&lt;br /&gt;&lt;!-- --&gt;props로 list를 넘겨주면 해당 컴포넌트 내에서 특정 &lt;i&gt;type을 기준으로 그룹핑&lt;/i&gt;을 하였습니다.&lt;br /&gt;&lt;!-- --&gt;이 부분이 불편하여 찾아보다가 &lt;i&gt;좋은 방법을 발견&lt;/i&gt;하여 게시글을 작성합니다.&lt;/p&gt;
&lt;h2 id=&quot;기존-방식-1&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;기존 방식&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const groupBy = (array, type) =&amp;gt; {
  return array.reduce((x, y) =&amp;gt; {
    (x[y[type]] = x[y[type]] || []).push(y);

    return x;
  }, {});
};

const users = [
  {
    name: &quot;강호동&quot;,
    gender: &quot;man&quot;
  },
  {
    name: &quot;이효리&quot;,
    gender: &quot;woman&quot;
  },
  {
    name: &quot;이수근&quot;,
    gender: &quot;man&quot;
  },
  {
    name: &quot;김희선&quot;,
    gender: &quot;woman&quot;
  },
  {
    name: &quot;김아중&quot;,
    gender: &quot;woman&quot;
  },
  {
    name: &quot;김흥국&quot;,
    gender: &quot;man&quot;
  }
];


const { man, woman } = groupBy(users, &quot;gender&quot;);
console.log(&quot;# man  : &quot;, man);
console.log(&quot;# woman  : &quot;, woman);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-02 오후 7.01.38.png&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnpgZj/btsIldPKlN8/ttDF9B64xtlQpiaqvqJkBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnpgZj/btsIldPKlN8/ttDF9B64xtlQpiaqvqJkBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnpgZj/btsIldPKlN8/ttDF9B64xtlQpiaqvqJkBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnpgZj%2FbtsIldPKlN8%2FttDF9B64xtlQpiaqvqJkBk%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;500&quot; height=&quot;192&quot; data-filename=&quot;스크린샷 2024-07-02 오후 7.01.38.png&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 방식이 사용하던 방식을 간단하게 작성한 코드입니다.&lt;br /&gt;&lt;!-- --&gt;TypeScript로 할 때 &lt;i&gt;type만 잘 정의&lt;/i&gt;해준다면 문제는 없습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그러나 굳이 위처럼 &lt;i&gt;groupBy 함수를 구현하지 않고&lt;/i&gt; 간단하게 사용할 수 있는 방법이 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;objectgroupby-사용하기-node-21-이상-1&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;Object.groupBy 사용하기 (node 21 이상)&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Object.groupBy&lt;/i&gt; 를 사용하면 간단하게 배열을 그룹핑 할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;아래 코드를 보시면 바로 이해가 가능하실 것입니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;const users = [
  {
    name: &quot;강호동&quot;,
    gender: &quot;man&quot;
  },
  {
    name: &quot;이효리&quot;,
    gender: &quot;woman&quot;
  },
  {
    name: &quot;이수근&quot;,
    gender: &quot;man&quot;
  },
  {
    name: &quot;김희선&quot;,
    gender: &quot;woman&quot;
  },
  {
    name: &quot;김아중&quot;,
    gender: &quot;woman&quot;
  },
  {
    name: &quot;김흥국&quot;,
    gender: &quot;man&quot;
  }
];

const { man, woman } = Object.groupBy(users, ({ gender }) =&amp;gt;
  gender === &quot;man&quot; ? &quot;man&quot; : &quot;woman&quot;
);

console.log(&quot;# man  : &quot;, man);
console.log(&quot;# woman  : &quot;, woman);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-02 오후 7.01.38.png&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxGeMb/btsIlCaF7Nv/kJ4CksU0BtTUtgfcOQo69k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxGeMb/btsIlCaF7Nv/kJ4CksU0BtTUtgfcOQo69k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxGeMb/btsIlCaF7Nv/kJ4CksU0BtTUtgfcOQo69k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxGeMb%2FbtsIlCaF7Nv%2FkJ4CksU0BtTUtgfcOQo69k%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;500&quot; height=&quot;192&quot; data-filename=&quot;스크린샷 2024-07-02 오후 7.01.38.png&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;비교해준 기준으로 그룹핑&lt;/i&gt;이 되어 return이 된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;주의점-1&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;주의점&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 함수를 사용하실 때 아래와 같은 &lt;i&gt;에러&lt;/i&gt;를 확인하실 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;TypeError: Object.groupBy is not a function
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-02 오후 7.03.17.png&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OQKx6/btsIkBQ9TW4/DpGuzvpXpwSgZOuDtzTWok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OQKx6/btsIkBQ9TW4/DpGuzvpXpwSgZOuDtzTWok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OQKx6/btsIkBQ9TW4/DpGuzvpXpwSgZOuDtzTWok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOQKx6%2FbtsIkBQ9TW4%2FDpGuzvpXpwSgZOuDtzTWok%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;500&quot; height=&quot;112&quot; data-filename=&quot;스크린샷 2024-07-02 오후 7.03.17.png&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 에러가 발생하는 이유는 사용하는 &lt;i&gt;node 버전이 낮아서&lt;/i&gt; 그럴 확률이 높습니다.&lt;br /&gt;&lt;i&gt;node 21 이상&lt;/i&gt;부터 사용할 수 있기 때문에 그 점 참고하시면 좋을 것 같습니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Object.groupBy&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy&quot;&gt;&amp;bull; Object.groupBy&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>javascript 객체 그룹핑</category>
      <category>javascript 객체 그룹화</category>
      <category>javascript 배열 그룹핑</category>
      <category>javascript 배열 그룹화</category>
      <category>object.groupby</category>
      <category>object.groupby is not a function</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/199</guid>
      <comments>https://codiving.tistory.com/199#entry199comment</comments>
      <pubDate>Tue, 2 Jul 2024 19:11:18 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 불변성을 유지하며 배열 특정 인덱스 요소 변경하기</title>
      <link>https://codiving.tistory.com/198</link>
      <description>&lt;h1 id=&quot;javascript-불변성을-유지하며-배열-특정-인덱스-요소-변경하기&quot; class=&quot;v2_h1_title&quot;&gt;[JavaScript] 불변성을 유지하며 배열 특정 인덱스 요소 변경하기&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;React, JavaScript&lt;/i&gt; 할 것 없이 원본을 유지하는 &lt;i&gt;불변성&lt;/i&gt;은 중요한 개념입니다.&lt;br /&gt;&lt;i&gt;React&lt;/i&gt;에서 배열 &lt;i&gt;불변성&lt;/i&gt; 유지하여 변경하기 편한 벙법을 알아보겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 배열이 있고 &lt;i&gt;2번째 인덱스의 값&lt;/i&gt;인 3을 6으로 변경해보도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;다시 말하지만 3을 찾아 변경하는 것이 아닌 &lt;i&gt;2번째 인덱스의 값을 변경&lt;/i&gt;하는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const origin = [1, 2, 3, 4, 5];
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;기존-원본-배열-유지하는-방법&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;기존 원본 배열 유지하는 방법&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const origin = [1, 2, 3, 4, 5];

// map 사용하기
const copied1 = origin.map((el, index) =&amp;gt; index === 2 ? 6 : el);

// ... 문법 사용하기
const copied2 = [...origin];
copied2[2] = 6;

// slice 사용하기
const copied3 = origin.slice();
copied3[2] = 6;

console.log(&quot;origin : &quot;, origin);
console.log(&quot;copied1 : &quot;, copied1);
console.log(&quot;copied2 : &quot;, copied2);
console.log(&quot;copied3 : &quot;, copied3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-02 오후 6.30.02.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpW1Yc/btsIjMMAsEk/ZuM3yqeKmkEqINhencJEg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpW1Yc/btsIjMMAsEk/ZuM3yqeKmkEqINhencJEg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpW1Yc/btsIjMMAsEk/ZuM3yqeKmkEqINhencJEg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpW1Yc%2FbtsIjMMAsEk%2FZuM3yqeKmkEqINhencJEg0%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;400&quot; height=&quot;121&quot; data-filename=&quot;스크린샷 2024-07-02 오후 6.30.02.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;모두 정상적으로 출력이 되었습니다.&lt;/p&gt;
&lt;h2 id=&quot;최신-javascript-문법-with-사용하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;최신 JavaScript 문법 with 사용하기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;with&lt;/i&gt;를 사용하면 간단하게 구현이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;const origin = [1, 2, 3, 4, 5];
const copied = origin.with(2, 6);
// origin.with(index 번호, 값)

console.log(&quot;origin : &quot;, origin);
console.log(&quot;copied : &quot;, copied);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2024-07-02 오후 6.30.23.png&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uBV9Z/btsIks08DfR/OSBpqMFbqhWfkmhlDV8kk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uBV9Z/btsIks08DfR/OSBpqMFbqhWfkmhlDV8kk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uBV9Z/btsIks08DfR/OSBpqMFbqhWfkmhlDV8kk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuBV9Z%2FbtsIks08DfR%2FOSBpqMFbqhWfkmhlDV8kk0%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;400&quot; height=&quot;97&quot; data-filename=&quot;edited_스크린샷 2024-07-02 오후 6.30.23.png&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;React에서 &lt;i&gt;with&lt;/i&gt;를 사용하면 위처럼 &lt;i&gt;배열 불변성&lt;/i&gt;을 손쉽게 할 수 있습니다.&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>javascript</category>
      <category>react 배열</category>
      <category>react 배열 불변성</category>
      <category>자바스크립트</category>
      <category>자바스크립트 배열</category>
      <category>자바스크립트 배열 with</category>
      <category>자바스크립트 배열 내장 함수</category>
      <category>자바스크립트 배열 특정 요소 변경</category>
      <category>자바스크립트 특정 요소 변경</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/198</guid>
      <comments>https://codiving.tistory.com/198#entry198comment</comments>
      <pubDate>Tue, 2 Jul 2024 18:36:13 +0900</pubDate>
    </item>
    <item>
      <title>[npm] 배포 후 검색이 안돼요</title>
      <link>https://codiving.tistory.com/197</link>
      <description>&lt;div&gt;
&lt;h1 id=&quot;npm-배포-후-검색이-안돼요&quot; class=&quot;v2_h1_title&quot;&gt;npm 배포 후 검색이 안돼요&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;npm 배포 후 &lt;i&gt;검색이 안되는 경우&lt;/i&gt;가 있습니다.&lt;br /&gt;&lt;!-- --&gt;제 경우도 검색이 똑바로 되지 않는 문제가 있었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때 아래 &lt;i&gt;명령어&lt;/i&gt;로 제대로 &lt;i&gt;배포가 되었는지 확인&lt;/i&gt;하는 것이 좋습니다.&lt;br /&gt;&lt;!-- --&gt;package-name에 배포하신 package 명을 작성하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm show package-name
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 package-name이 js-array 라고 하시면&lt;br /&gt;&lt;!-- --&gt;아래처럼 하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm show package-name
npm show js-array
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;실제로 배포 후 검색이 될 때까지 &lt;i&gt;수분에서 수시간이 소요&lt;/i&gt;되는 것 같습니다.&lt;br /&gt;&lt;!-- --&gt;위 정보만 잘 표시가 된다면 &lt;i&gt;충분히 기다리시면 될 것&lt;/i&gt;입니다.&lt;/p&gt;
&lt;/div&gt;</description>
      <category>공유/Node, NPM</category>
      <category>npm package 검색</category>
      <category>npm package 검색 안됨</category>
      <category>npm package 배포</category>
      <category>npm show</category>
      <category>npm 배포</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/197</guid>
      <comments>https://codiving.tistory.com/197#entry197comment</comments>
      <pubDate>Mon, 1 Jul 2024 19:12:04 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] JavaScript에서 Array, Set으로 집합 연산하기</title>
      <link>https://codiving.tistory.com/196</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;[JavaScript] JavaScript에서 Array, Set으로 집합 연산하기&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;JavaScript에서 집합을 다룰 때 배열을 많이 사용했습니다.&lt;br /&gt;&lt;!-- --&gt;해당 게시글에서는 아래 &lt;i&gt;집합 연산&lt;/i&gt;을 구현해볼 것입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT에게 먼저 &lt;i&gt;집합 연산&lt;/i&gt;을 물어보니 아래처럼 답변을 해주었습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;합집합 (Union)&lt;/i&gt;: A, B 함수는 두 집합의 합집합을 반환합니다.&lt;br /&gt;&lt;i&gt;교집합 (Intersection)&lt;/i&gt;: A, B 함수는 두 집합의 교집합을 반환합니다.&lt;br /&gt;&lt;i&gt;차집합 (Difference)&lt;/i&gt;: A, B 함수는 첫 번째 집합에서 두 번째 집합을 뺀 차집합을 반환합니다.&lt;br /&gt;&lt;i&gt;대칭 차집합 (Symmetric Difference)&lt;/i&gt;: A, B 함수는 두 집합의 대칭 차집합을 반환합니다.&lt;br /&gt;&lt;i&gt;부분집합 (Subset)&lt;/i&gt;: A, B 함수는 첫 번째 집합이 두 번째 집합의 부분집합인지 확인합니다.&lt;br /&gt;&lt;i&gt;상위집합 (Superset)&lt;/i&gt;: A, B 함수는 첫 번째 집합이 두 번째 집합의 상위집합인지 확인합니다.&lt;br /&gt;&lt;i&gt;서로소 (Disjoint)&lt;/i&gt;: A, B 함수는 두 집합이 서로소인지 확인합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위에 적혀있는 연산을 &lt;i&gt;배열로 구현&lt;/i&gt;해보고 &lt;i&gt;Set을 사용&lt;/i&gt;하면 얼마나 간단하게 구현할 수 있는 지 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;배열로-구현하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;배열로 구현하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;간단하게 &lt;i&gt;차집합, 교집합, 대칭 차집합&lt;/i&gt;을 구현해보겠습니다.&lt;br /&gt;&lt;!-- --&gt;배열로 구현을 하여도 위처럼 쉽게 구현이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 차집합 (Difference)
const getDifference = (arr1, arr2) =&amp;gt; {
  return arr1.filter(item =&amp;gt; !arr2.includes(item));
};

// 교집합 (Intersection)
const getIntersection = (arr1, arr2) =&amp;gt; {
  return arr1.filter(item =&amp;gt; arr2.includes(item));
};

// 대칭 차집합 (Symmetric Difference)
const getExclusiveOr = (arr1, arr2) =&amp;gt; {
  return arr1
    .filter(item =&amp;gt; !arr2.includes(item))
    .concat(arr2.filter(el =&amp;gt; !arr1.includes(el)));
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;set으로-구현하기---1&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;Set으로 구현하기 - 1&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT에게 구현을 부탁하니 &lt;i&gt;Set으로 구현&lt;/i&gt;을 해주었습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 합집합 (Union)
function union(setA, setB) {
    return new Set([...setA, ...setB]);
}

// 교집합 (Intersection)
function intersection(setA, setB) {
    return new Set([...setA].filter(x =&amp;gt; setB.has(x)));
}

// 차집합 (Difference)
function difference(setA, setB) {
    return new Set([...setA].filter(x =&amp;gt; !setB.has(x)));
}

// 대칭 차집합 (Symmetric Difference)
function symmetricDifference(setA, setB) {
    let diffA = new Set([...setA].filter(x =&amp;gt; !setB.has(x)));
    let diffB = new Set([...setB].filter(x =&amp;gt; !setA.has(x)));
    return new Set([...diffA, ...diffB]);
}

// 부분집합 (Subset)
function isSubset(setA, setB) {
    return [...setA].every(x =&amp;gt; setB.has(x));
}

// 상위집합 (Superset)
function isSuperset(setA, setB) {
    return [...setB].every(x =&amp;gt; setA.has(x));
}

// 서로소 (Disjoint)
function isDisjoint(setA, setB) {
    return [...setA].every(x =&amp;gt; !setB.has(x));
}

// 예제 사용법
let setA = new Set([1, 2, 3]);
let setB = new Set([3, 4, 5]);

console.log(&quot;Union:&quot;, union(setA, setB));
console.log(&quot;Intersection:&quot;, intersection(setA, setB));
console.log(&quot;Symmetric Difference:&quot;, symmetricDifference(setA, setB));
console.log(&quot;Difference (A - B):&quot;, difference(setA, setB));
console.log(&quot;Difference (B - A):&quot;, difference(setB, setA));
console.log(&quot;Is Subset (A &amp;sube; B):&quot;, isSubset(setA, setB));
console.log(&quot;Is Superset (A &amp;supe; B):&quot;, isSuperset(setA, setB));
console.log(&quot;Is Disjoint:&quot;, isDisjoint(setA, setB));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byDkfT/btsIkucvK3q/wDZ2W3qHGlt7p9026zfE11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byDkfT/btsIkucvK3q/wDZ2W3qHGlt7p9026zfE11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byDkfT/btsIkucvK3q/wDZ2W3qHGlt7p9026zfE11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyDkfT%2FbtsIkucvK3q%2FwDZ2W3qHGlt7p9026zfE11%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;662&quot; height=&quot;319&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;set으로-구현하기---2&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;Set으로 구현하기 - 2&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그러나 Chat GPT가 가르쳐준 내용이 최선일까요?&lt;br /&gt;&lt;!-- --&gt;나쁘지는 않지만 Chat GPT보다 &lt;i&gt;더 빠르고 간단하게 사용&lt;/i&gt;할 수 있는 방법이 있습니다.&lt;br /&gt;&lt;!-- --&gt;사실 위 모든 내용은 &lt;i&gt;Set의 내장함수&lt;/i&gt;로 존재합니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;let setA = new Set([1, 2, 3]);
let setB = new Set([3, 4, 5]);

console.log(&quot;Union:&quot;, setA.union(setB));
console.log(&quot;Intersection:&quot;, setA.intersection(setB));
console.log(&quot;Symmetric Difference:&quot;, setA.symmetricDifference(setB));
console.log(&quot;Difference (A - B):&quot;, setA.difference(setB));
console.log(&quot;Difference (B - A):&quot;, setB.difference(setA));
console.log(&quot;Is Subset (A &amp;sube; B):&quot;, setA.isSubsetOf(setB));
console.log(&quot;Is Superset (A &amp;supe; B):&quot;, setA.isSupersetOf(setB));
console.log(&quot;Is Disjoint:&quot;, setA.isDisjointFrom(setB));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJnne/btsIicLonFX/stjGn34eF8obqefZM3yX01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJnne/btsIicLonFX/stjGn34eF8obqefZM3yX01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJnne/btsIicLonFX/stjGn34eF8obqefZM3yX01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJnne%2FbtsIicLonFX%2FstjGn34eF8obqefZM3yX01%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;664&quot; height=&quot;317&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;함수를 구현할 필요조차 없이 &lt;i&gt;내장함수로 처리&lt;/i&gt;할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;New JavaScript Set methods&quot; href=&quot;https://developer.mozilla.org/en-US/blog/javascript-set-methods/&quot;&gt;&amp;bull; New JavaScript Set methods&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/JavaScript, TypeScript</category>
      <category>javascript set</category>
      <category>javascript 배열</category>
      <category>javascript 중복 제거</category>
      <category>javascript 집합</category>
      <category>javascript 집합 연산</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/196</guid>
      <comments>https://codiving.tistory.com/196#entry196comment</comments>
      <pubDate>Mon, 1 Jul 2024 19:09:41 +0900</pubDate>
    </item>
    <item>
      <title>[emotion] You may have forgotten to import it. 에러 해결하기</title>
      <link>https://codiving.tistory.com/195</link>
      <description>&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;[emotion] You may have forgotten to import it. 에러 해결하기&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;React에서 &lt;i&gt;styled-component&lt;/i&gt;나 &lt;i&gt;emotion&lt;/i&gt;으로 개발할 때 아래 에러가 나타날 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;해결 방법은 &lt;i&gt;styled-component github issue&lt;/i&gt;에서 찾았습니다.&lt;/p&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;Error: You are trying to create a styled element with an undefined component.
You may have forgotten to import it.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMc582/btsIiTLfD0u/GBemNudoHQ7PV8JYQxPWk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMc582/btsIiTLfD0u/GBemNudoHQ7PV8JYQxPWk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMc582/btsIiTLfD0u/GBemNudoHQ7PV8JYQxPWk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMc582%2FbtsIiTLfD0u%2FGBemNudoHQ7PV8JYQxPWk0%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;983&quot; height=&quot;555&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;github issue&lt;/i&gt;로 보아 &lt;i&gt;styled-component&lt;/i&gt;는 아래 에러가 표시되는 것 같습니다.&lt;/p&gt;
&lt;pre class=&quot;vhdl&quot;&gt;&lt;code&gt;Uncaught Error: Cannot create styled-component for component: undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;문제점-및-해결&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;문제점&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;문제점&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;기존 코드는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;const StyledIcon = styled(Icon)(() =&amp;gt; ({
  paddingRight: 4,
}));
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;사실 코드는 문제가 없습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;정확하지는 않지만 제가 &lt;i&gt;추측&lt;/i&gt;하기로는 &lt;i&gt;파일 로드 순서에 영향&lt;/i&gt;을 받는 것 같습니다.&lt;br /&gt;&lt;!-- --&gt;tsx 파일이 로드될 때 StyledIcon 컴포넌트가 로드되는 시점이 Icon 컴포넌트가 로드되는 시점보다 더 빠른 것입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;다시 말하면 &lt;i&gt;Icon이 없는데 Icon을 사용&lt;/i&gt;하는 것입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 의견은 제 &lt;i&gt;단순한 추측&lt;/i&gt;이고 실제로는 간단한 버그일 수도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;해결&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const StyledIcon = styled((props: any) =&amp;gt; &amp;lt;Icon {...props} /&amp;gt;)(() =&amp;gt; ({
  paddingRight: 4,
}));
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 &lt;i&gt;props를 넘겨주는 방식&lt;/i&gt;으로 하면 간단하게 해결이 됩니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;github issue&quot; href=&quot;https://github.com/styled-components/styled-components/issues/1449&quot;&gt;&amp;bull; github issue&lt;/a&gt;&lt;/p&gt;</description>
      <category>에러 모음/기타</category>
      <category>emotion 에러</category>
      <category>import 에러</category>
      <category>styled-component 에러</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/195</guid>
      <comments>https://codiving.tistory.com/195#entry195comment</comments>
      <pubDate>Mon, 1 Jul 2024 19:00:29 +0900</pubDate>
    </item>
    <item>
      <title>[npm] 프로젝트 배포하기</title>
      <link>https://codiving.tistory.com/194</link>
      <description>&lt;h1 id=&quot;npm-프로젝트-배포하기&quot; class=&quot;v2_h1_title&quot;&gt;[npm] 프로젝트 배포하기&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;구현한 함수와 타입을 &lt;i&gt;npm에 배포&lt;/i&gt;해보겠습니다.&lt;br /&gt;&lt;!-- --&gt;그전에 npmjs 사이트에 접속하셔서 &lt;i&gt;회원가입&lt;/i&gt;부터 하셔야 합니다.&lt;br /&gt;&lt;!-- --&gt;해당 부분은 생략하도록 하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;회원가입 링크 : &lt;a title=&quot;이동하기&quot; href=&quot;https://www.npmjs.com/signup&quot;&gt;이동하기&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;회원가입이 완료&lt;/i&gt;되었다는 가정하에 배포 해보도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;npm에-배포하기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;npm에 배포하기&lt;/h2&gt;
&lt;h3 id=&quot;npm-로그인&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;npm 로그인&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;아래 작업들을 진행하기 앞서 &lt;i&gt;npm에 로그인&lt;/i&gt;을 먼저 해줍니다.&lt;br /&gt;&lt;!-- --&gt;아래 명령어를 입력하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm login
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그럼 &lt;i&gt;홈페이지로 이동&lt;/i&gt;하게 되는데 &lt;i&gt;로그인&lt;/i&gt;을 하시면 됩니다. 로그인이 정상적으로 되었는 지 확인하는 방법은 터미널에 아래 명령어를 입력해보세요.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm whoami
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXA08r/btsIkcXEs8D/YD9JT9WHNteeTBO16IB8Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXA08r/btsIkcXEs8D/YD9JT9WHNteeTBO16IB8Z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXA08r/btsIkcXEs8D/YD9JT9WHNteeTBO16IB8Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXA08r%2FbtsIkcXEs8D%2FYD9JT9WHNteeTBO16IB8Z1%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;860&quot; height=&quot;64&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;본인 계정이 표시&lt;/i&gt;되면 정상적으로 로그인이 된 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;패키지-이름-정하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;패키지 이름 정하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그 다음으로 해야할 것이 &lt;i&gt;패키지의 이름&lt;/i&gt;을 정하는 것입니다.&lt;br /&gt;&lt;!-- --&gt;제일 중요하면서도 어려운 것입니다.&lt;br /&gt;&lt;!-- --&gt;그러나 이번에는 테스트를 위한 패키지 명이니 대충 지어주도록 하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;ts-function&lt;/i&gt;으로 진행하려고 했지만 이미 존재하여 &lt;i&gt;ts-function-for-test&lt;/i&gt; 로 진행하도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;존재하는 패키지인지 아닌지 확인하는 간단한 방법이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;npm info 패키지 이름
npm info ts-function-for-test
npm info react
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;react를 입력한 경우 아래처럼 &lt;i&gt;정보&lt;/i&gt;가 나타납니다.&lt;br /&gt;&lt;!-- --&gt;이 경우 해당 패키지 이름을 사용할 수 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2018&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dV8ugS/btsIihy7hM7/VS12H1CRg5sPpWMErRgYi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dV8ugS/btsIihy7hM7/VS12H1CRg5sPpWMErRgYi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dV8ugS/btsIihy7hM7/VS12H1CRg5sPpWMErRgYi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdV8ugS%2FbtsIihy7hM7%2FVS12H1CRg5sPpWMErRgYi1%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;2018&quot; height=&quot;794&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2018&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그러나 ts-function-for-test의 경우 아래처럼 &lt;i&gt;404로 출력&lt;/i&gt;됩니다.&lt;br /&gt;&lt;!-- --&gt;이 경우 해당 패키지 이름을 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Sjuj/btsIjK1nWCf/gFQIHJ8MU7PvQf541u2VWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Sjuj/btsIjK1nWCf/gFQIHJ8MU7PvQf541u2VWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Sjuj/btsIjK1nWCf/gFQIHJ8MU7PvQf541u2VWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Sjuj%2FbtsIjK1nWCf%2FgFQIHJ8MU7PvQf541u2VWK%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;1678&quot; height=&quot;280&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;readme-파일--packagejson-작성하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;README 파일 &amp;amp; package.json 작성하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 부분을 자세하게 확인하고 싶으시면 &lt;i&gt;다른 블로그나 홈페이지를 확인&lt;/i&gt;하시는 것을 추천드립니다.&lt;br /&gt;&lt;!-- --&gt;이 게시글에서는 간단하게 알아보겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;README 파일에는 간단하게 &lt;i&gt;해당 패키지의 정보&lt;/i&gt;를 적어주시면 됩니다.&lt;br /&gt;&lt;i&gt;package.json&lt;/i&gt;에 아래 내용을 추가 및 변경 하였습니다.&lt;br /&gt;&lt;!-- --&gt;전체 코드는 하단 github을 참고해주세요.&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;ts-function-for-test&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;description&quot;: &quot;Test project for npm publish&quot;,
  &quot;keywords&quot;: [
    &quot;test&quot;
  ],
  &quot;repository&quot;: {
    &quot;url&quot;: &quot;https://github.com/Codiving/ts-function&quot;
  },
  &quot;author&quot;: &quot;Codiving&quot;,
  &quot;license&quot;: &quot;MIT&quot;,
  ...
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;패키지-배포하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;패키지 배포하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;패키지를 배포하시기 전&lt;/i&gt;에 꼭 저번 게시글에서 보았던 &lt;i&gt;build&lt;/i&gt;를 하셔야 합니다.&lt;br /&gt;&lt;!-- --&gt;그렇지 않으면 정상적으로 배포가 되지 않을 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;아래 명령어를 이용하여 배포하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm publish
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;패키지-정상-배포된-것-확인하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;패키지 정상 배포된 것 확인하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;패키지를 배포하자마자 검색이 되지 않을 수도 있습니다.&lt;br /&gt;&lt;i&gt;짧게는 몇 분, 길게는 수시간을 소요&lt;/i&gt;될 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그러나 확인할 수 있는 방법은 많이 있습니다.&lt;br /&gt;&lt;!-- --&gt;정상적으로 배포가 된 경우 &lt;i&gt;성공 메일이 수신&lt;/i&gt;이 됩니다.&lt;br /&gt;&lt;!-- --&gt;또한 검색만 안되는 것이지 &lt;i&gt;직접 url로 접속&lt;/i&gt;이 가능합니다.&lt;br /&gt;&lt;!-- --&gt;npm install 패키지 이름으로 &lt;i&gt;설치도 가능&lt;/i&gt;합니다.&lt;br /&gt;&lt;!-- --&gt;npm info 패키지 이름으로 &lt;i&gt;정보를 확인&lt;/i&gt;할 수도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;버전-업데이트-하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;버전 업데이트 하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;코드가 수정된 경우 &lt;i&gt;버전을 업데이트를 하고 배포&lt;/i&gt;를 해야합니다.&lt;br /&gt;&lt;!-- --&gt;코드 수정이 완료가 되었다면 아래 명령어를 입력하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;npm version patch
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 &lt;i&gt;package.json&lt;/i&gt; 파일에 있는 &lt;i&gt;version이 변경&lt;/i&gt;된 것을 확인할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;그 다음 배포와 동일한 명령어를 사용하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm publish
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;패키지-삭제하기&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;패키지 삭제하기&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;배포한 패키지를 삭제&lt;/i&gt;하고 싶은 경우가 있습니다.&lt;br /&gt;&lt;i&gt;72시간 내에 진행&lt;/i&gt;을 하셔야 쉽게 삭제가 가능합니다.&lt;br /&gt;&lt;!-- --&gt;이후 삭제는 &lt;i&gt;메일로 문의&lt;/i&gt;를 해야 가능할 것 같습니다.&lt;br /&gt;&lt;!-- --&gt;npm에서는 삭제보단 &lt;i&gt;deprecate를 권장&lt;/i&gt;하고 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;명령어는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;npm unpublish 패키지 이름@버전
npm unpublish ts-function-for-test@0.0.1

npm unpublish 패키지 이름 -f
npm unpublish ts-function-for-test -f
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼하면 &lt;i&gt;정상적으로 삭제&lt;/i&gt;가 가능합니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 npm 배포, 버전 업데이트 및 삭제를 알아보았습니다.&lt;br /&gt;&lt;!-- --&gt;모두 본인의 프로젝트를 잘 배포하시길 바랍니다.&lt;/p&gt;

&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;혹시 따라오지 못 하신 분은 아래 github 주소를 확인해보세요.&lt;br /&gt;해당 소스코드 : &lt;a title=&quot;이동하기&quot; href=&quot;https://github.com/Codiving/ts-function/commit/d092087473d3edba47b4b7caa0a33f212d07bd00&quot;&gt;이동하기&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/Node, NPM</category>
      <category>npm patch</category>
      <category>npm publish</category>
      <category>npm unpublish</category>
      <category>npm version patc</category>
      <category>npm 배포</category>
      <category>npm 재배포</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/194</guid>
      <comments>https://codiving.tistory.com/194#entry194comment</comments>
      <pubDate>Mon, 1 Jul 2024 18:56:57 +0900</pubDate>
    </item>
    <item>
      <title>[npm] TypeScript 함수 및 타입 배포 준비</title>
      <link>https://codiving.tistory.com/193</link>
      <description>&lt;h1 id=&quot;npm-typescript-함수-및-타입-배포-준비&quot; class=&quot;v2_h1_title&quot;&gt;[npm] TypeScript 함수 및 타입 배포 준비&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 &lt;i&gt;구현한 함수와 타입을 빌드&lt;/i&gt;해보도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;추가로 배포하기 전 &lt;i&gt;정상 동작하는 지 테스트 하는 방법&lt;/i&gt;도 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;tsup-설치-및-설정&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;tsup 설치 및 설정&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;빠르고 간단하게 번들링을 해주는 &lt;i&gt;tsup package&lt;/i&gt; 를 이용해보도록 하겠습니다.&lt;br /&gt;&lt;!-- --&gt;아래 명령어를 이용하여 설치해주시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;q&quot;&gt;&lt;code&gt;npm install tsup --save-dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;최상단에 &lt;i&gt;tsup.config.ts&lt;/i&gt; 파일을 생성 후 아래 코드를 복사 붙여넣기 합니다.&lt;br /&gt;&lt;!-- --&gt;tsup에 관한 &lt;i&gt;설정 값&lt;/i&gt;입니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;속성에 대한 자세한 정보는 하단 &lt;i&gt;참고문헌 링크&lt;/i&gt;를 확인해보세요.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;// tsup.config.ts

import { defineConfig } from &quot;tsup&quot;;

export default defineConfig({
  format: [&quot;cjs&quot;, &quot;esm&quot;],
  entry: [&quot;./src/index.ts&quot;],
  dts: true,
  shims: true,
  skipNodeModulesBundle: true,
  clean: true
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;빌드를 위해 &lt;i&gt;package.json을 수정&lt;/i&gt;해주겠습니다.&lt;br /&gt;&lt;!-- --&gt;아래 코드를 추가(merge)해줍니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;기존에 작성된 package.json 코드를 &lt;i&gt;대체하는 것이 아닌 추가&lt;/i&gt;를 해주셔야 합니다.&lt;br /&gt;&lt;!-- --&gt;최종 package.json 코드는 하단 github 링크에서 확인하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;main&quot;: &quot;./dist/index.js&quot;,
&quot;module&quot;: &quot;./dist/index.mjs&quot;,
&quot;types&quot;: &quot;./dist/index.d.ts&quot;,
&quot;scripts&quot;: {
  &quot;build&quot;: &quot;tsup&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이제 빌드만 하면 됩니다.&lt;br /&gt;&lt;!-- --&gt;아래 명령어를 입력하여 빌드를 합니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6eOs/btsIhRlvRhm/UFfA47jq4ChxkjwhzuQeo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6eOs/btsIhRlvRhm/UFfA47jq4ChxkjwhzuQeo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6eOs/btsIhRlvRhm/UFfA47jq4ChxkjwhzuQeo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6eOs%2FbtsIhRlvRhm%2FUFfA47jq4ChxkjwhzuQeo1%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;500&quot; height=&quot;250&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;build가 정상적으로 실행이 되었다면 &lt;i&gt;dist 파일이 생성&lt;/i&gt;되었을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5a6pU/btsIhc4Tm2w/kkPeXvMhQHJFceIvAKVVD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5a6pU/btsIhc4Tm2w/kkPeXvMhQHJFceIvAKVVD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5a6pU/btsIhc4Tm2w/kkPeXvMhQHJFceIvAKVVD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5a6pU%2FbtsIhc4Tm2w%2FkkPeXvMhQHJFceIvAKVVD0%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;500&quot; height=&quot;190&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;배포-전-테스트&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;배포 전 테스트&lt;/h3&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 함수와 타입을 npm 배포 전 정상 동작하는 지 확인하고 싶을 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;이 경우 &lt;i&gt;dist 폴더의 위치&lt;/i&gt;를 확인합니다.&lt;br /&gt;&lt;i&gt;pwd 명령어&lt;/i&gt;를 사용하면 현재 위치를 확인할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;pwd 명령어 결과를 &lt;i&gt;/Users/codiving/ts-function&lt;/i&gt; 이라고 가정하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;테스트를 위한 프로젝트를 생성 후 아래 명령어를 입력해줍니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install {pwd 결과}
npm install /Users/codiving/ts-function
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 설치 명령어를 입력 후 해당 함수들이 잘 import 되는 지 확인하시면 됩니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 tsup package를 이용하여 &lt;i&gt;프로젝트 빌드&lt;/i&gt;를 해보았습니다.&lt;br /&gt;&lt;!-- --&gt;추가로 npm 배포 전 &lt;i&gt;빌드된 파일을 테스트&lt;/i&gt; 하는 방법도 확인해보았습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;혹시 따라오지 못 하신 분은 아래 github 주소를 확인해보세요.&lt;br/&gt;해당 소스코드 : &lt;a title=&quot;이동하기&quot; href=&quot;https://github.com/Codiving/ts-function/commit/5fe2f111aea688051faba8f2f20fa9f104f4079c&quot;&gt;이동하기&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;다음 게시글에서는 &lt;i&gt;npm에 배포&lt;/i&gt;를 해보겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;참고문헌&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;tsup&quot; href=&quot;https://tsup.egoist.dev/&quot;&gt;&amp;bull; tsup&lt;/a&gt;&lt;/p&gt;</description>
      <category>공유/Node, NPM</category>
      <category>npm publish</category>
      <category>npm 배포하기</category>
      <category>ts 프로젝트 빌드</category>
      <category>tsup</category>
      <category>typescript</category>
      <category>typescript build</category>
      <category>타입스크립트</category>
      <category>타입스크립트 빌드</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/193</guid>
      <comments>https://codiving.tistory.com/193#entry193comment</comments>
      <pubDate>Fri, 28 Jun 2024 19:08:59 +0900</pubDate>
    </item>
    <item>
      <title>[npm] TypeScript 함수 구현 및 타입 생성</title>
      <link>https://codiving.tistory.com/192</link>
      <description>&lt;h1 id=&quot;npm-typescript-함수-구현-및-타입-생성&quot; class=&quot;v2_h1_title&quot;&gt;[npm] TypeScript 함수 구현 및 타입 생성&lt;/h1&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 &lt;i&gt;TypeScript 함수를 구현&lt;/i&gt;하고 &lt;i&gt;타입을 생성&lt;/i&gt;해보겠습니다.&lt;br /&gt;&lt;!-- --&gt;그리고 내가 &lt;i&gt;원하는 함수와 타입만 export&lt;/i&gt; 하는 것까지 해보도록 하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;구현해볼 함수는 특정 상품 중에서 &lt;i&gt;입력된 가격과 이름을 가진 상품을 찾는 함수&lt;/i&gt;입니다.&lt;br /&gt;&lt;!-- --&gt;예시를 위한 함수입니다.&lt;/p&gt;
&lt;h2 id=&quot;폴더-구조-변경&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;폴더 구조 변경&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;먼저 개발할 때 필요한 &lt;i&gt;폴더 및 파일을 생성&lt;/i&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;i&gt;src 폴더&lt;/i&gt; 생성&lt;/li&gt;
&lt;li&gt;&lt;i&gt;src 폴더&lt;/i&gt; 내 &lt;i&gt;findProduct.ts&lt;/i&gt; , &lt;i&gt;compareName.ts&lt;/i&gt; , &lt;i&gt;comparePrice.ts&lt;/i&gt; , &lt;i&gt;types.ts&lt;/i&gt; , &lt;i&gt;index.ts&lt;/i&gt; 파일 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위 작업을 마치시면 아래 이미지처럼 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-06-27 오후 10.30.09.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0ZERN/btsIhVBkJsp/JkMxlD2lLKGtYsWOEYknn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0ZERN/btsIhVBkJsp/JkMxlD2lLKGtYsWOEYknn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0ZERN/btsIhVBkJsp/JkMxlD2lLKGtYsWOEYknn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0ZERN%2FbtsIhVBkJsp%2FJkMxlD2lLKGtYsWOEYknn0%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;400&quot; height=&quot;180&quot; data-filename=&quot;스크린샷 2024-06-27 오후 10.30.09.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;함수-구현&quot; class=&quot;v2_h3_title&quot; data-ke-size=&quot;size23&quot;&gt;함수 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Product type 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// src/type.ts

export interface Product {
  id: number;
  name: string;
  price: number;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;compareName 함수 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;// src/compareName.ts

import { Product } from &quot;./type&quot;;

export function compareName(product: Product, name: string) {
  return product.name === name;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;comparePrice 함수 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;// src/comparePrice.ts

import { Product } from &quot;./type&quot;;

export function comparePrice(product: Product, price: number) {
  return product.price === price;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;findProduct 함수 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// src/findPrice.ts

import { compareName } from &quot;./compareName&quot;;
import { comparePrice } from &quot;./comparePrice&quot;;
import { Product } from &quot;./type&quot;;

export function findProduct(
  products: Product[],
  targetPrice: number,
  targetName: string
): Product | undefined {
  return products.find(
    product =&amp;gt;
      comparePrice(product, targetPrice) &amp;amp;&amp;amp; compareName(product, targetName)
  );
}

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;npm에 올리고 싶은 함수와 타입 export&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// src/index.ts

import { findProduct } from &quot;./findProduct&quot;;
import { Product } from &quot;./type&quot;;

export { findProduct };
export type { Product };

&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;함수는 너무 간단하니 설명은 생략하겠습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;src/index.ts&lt;/i&gt; 를 보시면 내가 &lt;i&gt;export 하기 원하는 함수와 타입만 구현&lt;/i&gt;해주었습니다.&lt;br /&gt;&lt;!-- --&gt;위 프로젝트 내에서는 &lt;i&gt;compareName&lt;/i&gt;, &lt;i&gt;comparePrice&lt;/i&gt; 함수를 &lt;i&gt;import 하여 사용&lt;/i&gt;할 수 있습니다.&lt;br /&gt;&lt;!-- --&gt;그러나 해당 &lt;i&gt;package를 배포한 후 설치한 사람&lt;/i&gt;들은 위 함수는 import 못 하고 &lt;i&gt;src/index.ts&lt;/i&gt;에 있는 &lt;i&gt;함수와 타입만 import가 가능&lt;/i&gt;합니다.&lt;/p&gt;
&lt;h2 id=&quot;구현한-함수-실행해보기&quot; class=&quot;v2_h2_title&quot; data-ke-size=&quot;size26&quot;&gt;구현한 함수 실행해보기&lt;/h2&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;ts 파일을 실행하기 위해서는 &lt;i&gt;ts-node 명령어&lt;/i&gt;를 사용하여야 합니다.&lt;br /&gt;&lt;!-- --&gt;저 같은 경우 &lt;i&gt;src/index.ts 파일에서 테스트&lt;/i&gt;를 하였습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { findProduct } from &quot;./findProduct&quot;;
import { Product } from &quot;./type&quot;;

const products: Product[] = [
  { id: 1, name: &quot;Keyboard&quot;, price: 50 },
  { id: 2, name: &quot;Mouse&quot;, price: 30 },
  { id: 3, name: &quot;Monitor&quot;, price: 200 }
];

const product = findProduct(products, 30, &quot;Mouse&quot;);

console.log(&quot;# product : &quot;, product);

export { findProduct };
export type { Product };
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 간단하게 코드를 구현한 후 터미널에 아래 명령어를 입력하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;ts-node src/index.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-06-27 오후 10.42.27.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vHXPH/btsIgOXDMi1/WMJtA3WDcPfKzWeSfktqR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vHXPH/btsIgOXDMi1/WMJtA3WDcPfKzWeSfktqR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vHXPH/btsIgOXDMi1/WMJtA3WDcPfKzWeSfktqR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvHXPH%2FbtsIgOXDMi1%2FWMJtA3WDcPfKzWeSfktqR1%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;500&quot; height=&quot;84&quot; data-filename=&quot;스크린샷 2024-06-27 오후 10.42.27.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 정상적으로 동작하는 것을 확인하였습니다.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 &lt;i&gt;TypeScript 함수 구현 및 타입 생성&lt;/i&gt;을 하였습니다.&lt;br /&gt;&lt;!-- --&gt;혹시 따라오지 못 하신 분은 아래 github 주소를 확인해보세요.&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;해당 소스코드 : &lt;a title=&quot;이동하기&quot; href=&quot;https://github.com/Codiving/ts-function/commit/63874a978b38ee8209d7bc039c6b4e01b220f4bd&quot;&gt;이동하기&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;v2_p&quot; data-ke-size=&quot;size16&quot;&gt;다음 게시글에서는 &lt;i&gt;타입스크립트 프로젝트 배포 전 빌드&lt;/i&gt;을 해보겠습니다.&lt;/p&gt;</description>
      <category>공유/Node, NPM</category>
      <category>npm publish</category>
      <category>npm 배포하기</category>
      <category>npm 원하는 type export</category>
      <category>npm 원하는 타입 export</category>
      <category>npm 원하는 함수 export</category>
      <category>typescript</category>
      <category>타입스크립트</category>
      <author>구하천포</author>
      <guid isPermaLink="true">https://codiving.tistory.com/192</guid>
      <comments>https://codiving.tistory.com/192#entry192comment</comments>
      <pubDate>Fri, 28 Jun 2024 18:58:09 +0900</pubDate>
    </item>
  </channel>
</rss>