<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Don't be afraid of challenges</title>
    <link>https://asd72621.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 13 May 2026 15:14:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>초아롱</managingEditor>
    <image>
      <title>Don't be afraid of challenges</title>
      <url>https://tistory1.daumcdn.net/tistory/6417014/attach/be402ebad00f43679169238f98728bc9</url>
      <link>https://asd72621.tistory.com</link>
    </image>
    <item>
      <title>Next.js + 스토리북 활용하기</title>
      <link>https://asd72621.tistory.com/123</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스토리북이란 ?&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;UI 컴포넌트와 페이지를 독립적으로 생성하여, 직접 접근하기 어려운 state나 엣지 케이스를 애플리케이션 전체를 살피지 않고도 쉽게 개발하고 공유하도록 해주는 도구&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시작하기&lt;/h4&gt;
&lt;pre id=&quot;code_1738222573753&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx storybook@latest init&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;수정한 storybook 코드 업데이트하기&lt;/h4&gt;
&lt;pre id=&quot;code_1738222590464&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx storybook@latest upgrade&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;framework가 안깔려져있으면&lt;/h4&gt;
&lt;pre id=&quot;code_1738222598123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev @storybook/nextjs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지해서 폴더 구조를 보면 .storybook이라는 폴더가 생긴다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zWpn2/btsL1j1ezYM/qlcdjXzbnhjUViUfoHfItk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zWpn2/btsL1j1ezYM/qlcdjXzbnhjUViUfoHfItk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zWpn2/btsL1j1ezYM/qlcdjXzbnhjUViUfoHfItk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzWpn2%2FbtsL1j1ezYM%2FqlcdjXzbnhjUViUfoHfItk%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;260&quot; height=&quot;156&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1738222908009&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import type { StorybookConfig } from &quot;@storybook/nextjs&quot;;

const config: StorybookConfig = {
  stories: [&quot;../src/**/*.mdx&quot;, &quot;../src/**/*.stories.@(js|jsx|mjs|ts|tsx)&quot;],

  addons: [
    &quot;@storybook/addon-onboarding&quot;,
    &quot;@storybook/addon-essentials&quot;,
    &quot;@chromatic-com/storybook&quot;,
    &quot;@storybook/addon-interactions&quot;,
    &quot;@storybook/addon-mdx-gfm&quot;
  ],

  framework: {
    name: &quot;@storybook/nextjs&quot;,
    options: {},
  },

  staticDirs: [&quot;..\\public&quot;],

  docs: {},

  typescript: {
    reactDocgen: &quot;react-docgen-typescript&quot;
  }
};
export default config;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에는 이렇게 하라고 했는데, 위의 명령어를 다 하고나니까 이미 다 완료되어있었음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.storybook/main.ts&lt;/p&gt;
&lt;pre id=&quot;code_1738222627425&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { StorybookConfig } from '@storybook/nextjs';
 
const config: StorybookConfig = {
  // ...
  // framework: '@storybook/react-webpack5',   Remove this
  framework: '@storybook/nextjs', //   Add this
};
 
export default config;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1738222651253&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { StorybookConfig } from '@storybook/nextjs';
 
const config: StorybookConfig = {
  // ...
  addons: [
    // ...
    //   These can both be removed
    // 'storybook-addon-next',
    // 'storybook-addon-next-router',
  ],
};
 
export default config;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;storybook 실행하기&lt;/h4&gt;
&lt;pre id=&quot;code_1738222705944&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm run storybook&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하면 이렇게 나올 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;1759&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vjLEm/btsL1DMmFJj/kAkyb6jUmGaL3nCRxjyl4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vjLEm/btsL1DMmFJj/kAkyb6jUmGaL3nCRxjyl4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vjLEm/btsL1DMmFJj/kAkyb6jUmGaL3nCRxjyl4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvjLEm%2FbtsL1DMmFJj%2FkAkyb6jUmGaL3nCRxjyl4K%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;1079&quot; height=&quot;1759&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;1759&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stories 여기를 잘 살펴야하는데 이 폴더가 우리가 사용할 storybook이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;241&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vr2lZ/btsL2kL2BQH/8Eg10KxDlU9Way8dOYO23K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vr2lZ/btsL2kL2BQH/8Eg10KxDlU9Way8dOYO23K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vr2lZ/btsL2kL2BQH/8Eg10KxDlU9Way8dOYO23K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvr2lZ%2FbtsL2kL2BQH%2F8Eg10KxDlU9Way8dOYO23K%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;241&quot; height=&quot;263&quot; data-origin-width=&quot;241&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 .stories.ts 파일 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 들어&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1738223119225&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const meta: Meta&amp;lt;typeof Button&amp;gt; = {
  title: &quot;Components/Button&quot;,
  component: Button,
  argTypes: {
    intent: {
      control: { type: &quot;select&quot; },
      options: [&quot;primary&quot;, &quot;secondary&quot;, &quot;danger&quot;, &quot;default&quot;],
    },
    size: {
      control: { type: &quot;select&quot; },
      options: [&quot;sm&quot;, &quot;md&quot;, &quot;lg&quot;],
    },
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;meta : 스토리의 메타 정보를 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;title : &quot;Components/Button&quot; : 현재 스토리의 제목을 정의한다. 이는 Storybook에서 스토리의 위치 및 구조를 결정하는데 사용된다. / 로 스토리 간 계층 구조를 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;component: Button : 현재 스토리에서 사용되는 컴포넌트를 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argTypes: { ... } : 컴포넌트의 인자(Props)를 직접 조작할 수 있도록 설정할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;export const Primary = { ... };&lt;/h2&gt;
&lt;pre id=&quot;code_1738223129470&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const Primary: Story = {
  args: {
    children: &quot;Primary Button&quot;,
    intent: &quot;primary&quot;,
    size: &quot;md&quot;,
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Primary 라는 스토리를 정읳나다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;args :{ ... } : Primary 스토리의 기본 인자를 정의한다. 즉, Button 컴포넌트에 전달되는 Props값이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argTypes Control Types&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Boolean : 사용자에게 체크박스를 제공하며, true/false 값을 토글 할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예) { control : { type : &quot;boolean&quot; } }&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Text : 문자열을 입력할 수 있는 텍스트 필드를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예) { control : { type : &quot;text&quot; } }&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. Number : 숫자를 입력 할 수 있는 필드를 제공한다. 최솟값,최댓값,스텝(증감 단위)를 옵션으로 설정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'number', min: 0, max: 100, step: 5 } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4.Color : 색상 선택기를 제공한다. 사용자가 컴포넌트의 색상을 쉽게 변경 할 수 있게 해준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'color' } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5. Object : 객체를 JSON 형태로 입력 할 수 있는 텍스트 영역을 제공한다. 복잡한 객체 구조를 props로 전달할 때 유용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'object' } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;6. Select : 사용자가 목록에서 여러 옵션 중 하나를 선택할 수 있게 하는 드롭다운 메뉴를 제공한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'select', options: ['Option 1', 'Option 2'] } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;7. Radio : 여러 옵션 중 하나를 선택할 수&amp;nbsp; 있는 라디오 버튼을 제공한다. select와 유사하지만 UI가 다르다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'radio', options: ['Option 1', 'Option 2'] } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;8. InlineRadio : 라디오 버튼을 인라인(수평)으로 표시한다. 선택옵션을 옆으로 나열한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'inline-radio', options: ['Option 1', 'Option 2'] } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;9. Check : 사용자가 여러 옵션을 선택할 수 있는 체크박스 그룹을 제공한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'check', options: ['Option 1', 'Option 2'] } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;10. InlineCheck : 체크박스를 인라인(수평)으로 표시한다. 여러 옵션을 옆으로 나열한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'inline-check', options: ['Option 1', 'Option 2'] } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;11. Range : 슬라이더를 통해 숫자 값을 선택할 수 있는 컨트롤을 제공한다. 최솟값, 최댓값,스텝을 설정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'range', min: 0, max: 100, step: 5 } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;12. Date : 날짜를 선택할 수 있는 달력 UI를 제공한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'date' } }&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;13. File : 파일을 업로드할 수 있는 입력 필드를 제공한다. 허용하는 파일 타입을 설정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예) &lt;b&gt;{ control: { type: 'file', accept: '.png,.jpg' } }&lt;/b&gt; &lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;mdx 파일&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;storybook 세팅을 할 때 이런 질문들을 받을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCmHDu/btsL1VzgLMK/VklGkniv0DKnxUH5HvDRh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCmHDu/btsL1VzgLMK/VklGkniv0DKnxUH5HvDRh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCmHDu/btsL1VzgLMK/VklGkniv0DKnxUH5HvDRh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCmHDu%2FbtsL1VzgLMK%2FVklGkniv0DKnxUH5HvDRh1%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;543&quot; height=&quot;350&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yes를 하다보면 mdx파일이 생기는데, MDX는 Markdown 기반의 문서를 확장한 것으로, React 컴포넌트를 포함할 수 있는 마크다운 문서 형식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDX파일은 Javascript와 JSX코드를 포함하여 더 동적이고 인터렉티브 한 콘텐츠를 작성할 수 있게 해준다. 주로 문서, 블로그 포스트, 프로젝트의 사용설명서 등을 작성할 때 사용된다. 스토리북 환경은 자체적으로 실행되기 때문에 특정 라이브러리를 사용한 컴포넌트를 스토리로 만드려할 때 오류가 발생할 수 있다. 그럴 때 다른 문서화 프레임워크를 사용하지 않고 mdx 형식을 이용해 스토리북 환경 내에서 문서를 확인할 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;899&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejlE1P/btsL24BVzYJ/0Ol7rsunFGlkA3qemdwG0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejlE1P/btsL24BVzYJ/0Ol7rsunFGlkA3qemdwG0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejlE1P/btsL24BVzYJ/0Ol7rsunFGlkA3qemdwG0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejlE1P%2FbtsL24BVzYJ%2F0Ol7rsunFGlkA3qemdwG0k%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;1887&quot; height=&quot;899&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;899&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출처)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스토리북 공식문서 :&amp;nbsp;&lt;a href=&quot;https://storybook.js.org/docs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://storybook.js.org/docs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타 블로그 참고 : &lt;a href=&quot;https://velog.io/@pyotato/NextjsStorybook-%EC%A0%81%EC%9A%A9%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@pyotato/NextjsStorybook-%EC%A0%81%EC%9A%A9%EA%B8%B0&lt;/a&gt;, &lt;a href=&quot;https://lasbe.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lasbe.tistory.com/196&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹서비스</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/123</guid>
      <comments>https://asd72621.tistory.com/123#entry123comment</comments>
      <pubDate>Thu, 30 Jan 2025 17:00:22 +0900</pubDate>
    </item>
    <item>
      <title>[트러블 슈팅] 데이터 업데이트 후 바로 업데이트된 데이터 불러오기</title>
      <link>https://asd72621.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;'수정'을 거친 후 데이터베이스가 업데이트 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 업데이트된 데이터를 다시 브라우저에 불러와야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 수행할 수 있는 몇 가지 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. tanstack query (권장)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tanstack query는 클라이언트에서 서버 데이터를 쉽게 관리하고 갱신하는데 유용한 라이브러리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 사용하면 데이터가 변경된 후 자동으로 데이터를 새로 불러오거나, 수정된 데이터를 서버에서 가져와 UI 갱신 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 장점 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- refetching : 데이터가 수정되면 해당 데이터를 자동으로 다시 불러오거나, 수동으로 refetch 트리거하여 최신 데이터 반영&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- optimistic updates : 데이터 수정 요청이 서버에 성공하기 전에 UI에서 미리 예상 결과를 보여주어, UX 개선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 캐시 관리 : 요청 결과는 캐시에 저장되어, 동일한 요청에 대해 불필요한 네트워크 요청을 줄일 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. window.reload() (비권장)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지를 새로 고침하여 모든 상태를 초기화하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 수정 후 서버에서 변경된 데이터를 반영한 뒤, 페이지를 새로 고침하여 데이터를 다시 로드하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, html,css, javascript 렌더 트리를 만드는 것부터 처음부터 다시 한다는 것이다..&lt;/p&gt;
&lt;pre id=&quot;code_1736927325103&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const handleSave = async () =&amp;gt; {
  // 데이터 수정 로직
  await axios.put('/url', data);

  // 수정 후 페이지 새로 고침
  window.location.reload();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 상태관리 라이브러리 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 중앙에서 관리하여 상태변경을 통해 UI 자동으로 갱신&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. polling (주기적 요청)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기적으로 서버에 요청을 보내 데이터 변경 여부를 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- setInterval을 사용해 일정 간격으로 API 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 새로운 데이터가 있으면 상태 업데이트&lt;/p&gt;
&lt;pre id=&quot;code_1736926996400&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  const interval = setInterval(async () =&amp;gt; {
    const response = await fetch(&quot;/api/data&quot;);
    const updatedData = await response.json();
    setData(updatedData); // 상태 업데이트
  }, 5000); // 5초 간격

  return () =&amp;gt; clearInterval(interval); // 컴포넌트 unmount 시 정리
}, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이런 방식이면 사용자가 이 페이지에 있을 때 n초 마다 계속 API 호출을 보내기 때문에 서버 터질듯..&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. websocket 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간으로 서버에서 변경 사항을 클라이언트로 푸시하기 위해 Websocket 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라이언트가 서버와 WebSocket 연결 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버에서 데이터 변경이 발생하면 WebSocket을 통해 클라이언트에 알림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라이언트는 알림을 받고 데이터를 다시 fetch하거나 UI를 업데이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. SSE(Server-Sent Events) 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE는 서버가 클라이언트로 단방향 스트림 데이터를 보낼 수 있는 기술로, WebSocket보다 구현 간단&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라이언트가 서버로 SSE 연결 요청&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버에서 데이터가 변경될 때 이벤트를 푸시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라이언트는 이벤트를 받고 UI 갱신&lt;/p&gt;
&lt;pre id=&quot;code_1736926959887&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const eventSource = new EventSource(&quot;/api/updates&quot;);

eventSource.onmessage = (event) =&amp;gt; {
  const updatedData = JSON.parse(event.data);
  setData(updatedData); // 상태 업데이트
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7. mutation callback 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 업데이트할 때, 업데이트 요청을 보낸 후 성공 시 클라이언트에서 상태를 직접 갱신하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터 수정 API 호출 후, 성공 콜백에서 상태 업데이트&lt;/p&gt;
&lt;pre id=&quot;code_1736927062816&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const handleUpdate = async (newData) =&amp;gt; {
  const response = await fetch(&quot;/api/update&quot;, {
    method: &quot;POST&quot;,
    body: JSON.stringify(newData),
  });

  if (response.ok) {
    setData((prevData) =&amp;gt; ({
      ...prevData,
      ...newData,
    }));
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조사하면서 드는 생각은 웬만하면 tanstack query나 상태관리 라이브러리를 쓰는게 낫고, 이 둘다 사용하지 않는 프로젝트에선 props drilling으로 즉 7. mutation callback 을 사용하는 방법 밖에 없을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인은 socket으로도 가능하다는 것을 이번 블로그 포스팅을 하면서 처음 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주말에 websocket도 좀 공부해봐야 할 것 같다.&lt;/p&gt;</description>
      <category>nextjs</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/122</guid>
      <comments>https://asd72621.tistory.com/122#entry122comment</comments>
      <pubDate>Wed, 15 Jan 2025 16:52:30 +0900</pubDate>
    </item>
    <item>
      <title>쿼리 파라미터를 이용한 검색로직 구현</title>
      <link>https://asd72621.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2025-01-09 저녁, 인턴업무를 수행하던 와중 검색, 필터링 구현을 시작하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 그냥 데이터를 다 받은 다음 프론트 단에서 데이터를 slice하고 그랬었는데 몇 달전 내배캠에서 최종프로젝트를 같이하던 팀원분이 검색로직을 맡았었는데 searchParam을 이용하여 로직구현을 하던것을 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그땐 시간도 없고 내 업무 처리하느라 바빠서 저게 뭐지 ..? 하고 넘겼었는데 이제 본인이 그 업무를 맡게 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 관련 블로그를 찾아보니 url에 쿼리스트링을 조작하여 상태정보를 저장할 수 있는 방법을 보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 쿼리를 이용하여 검색로직을 구현해야하는 것에 대해 굳이..? 라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 검색창에 검색을 하고 필터링을 직접 조작하면 되는 것이 아닌가라고 생각했었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느날 게임을 하다가 필터링이 계속 검색할때마다 초기화되는 현상을 맞닥들였는데 이때 마다 계속 필터링 버튼을 클릭하고 검색하고 반복하니 사용자 경험이 떨어진다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 마찬가지로 웹사이트에서도 똑같이 사용자가 입력한 값을 어디다가 저장하여 계속 그 데이터를 이용한다면 UX에 매우 도움이 될 것이라 생각하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 관련 블로그를 참고하여 parameter를 이용한 검색로직을 구현해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 먼저 url에 받아서 쓰기 까지 가능한 URLSearchParams을 조작하는 파일이 필요하다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고한 블로그에선&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;background-color: #f6f8fa; color: #24292e; text-align: start;&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;searchParams: Object.fromEntries(searchParams)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 썼는데 이러면 객체형태로 됬었나 ..? 아무튼 데이터 구성이 내 로직과 맞지 않아 에러가 발생했었기 때문에 나는 그냥 searchParams 통째로 받아왔다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736670939059&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client';

import { usePathname, useRouter, useSearchParams } from 'next/navigation';

type NewParamsType = { [key: string]: string };

const useCustomSearchParams = () =&amp;gt; {
  const router = useRouter();
  const pathname = usePathname();
  const _searchParams = useSearchParams();
  const searchParams = new URLSearchParams(_searchParams.toString());

  const setNewParams = (newParams: NewParamsType) =&amp;gt; {
    for (const [key, value] of Object.entries(newParams)) {
      if (value) searchParams.set(key, value);
      else searchParams.delete(key);
    }
    return searchParams.toString();
  };

  const setSearchParams = (newParams: NewParamsType) =&amp;gt; {
    return router.push(`${pathname}?${setNewParams(newParams)}`);
  };

  return { searchParams, setSearchParams };
};

export default useCustomSearchParams;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://gyyeom.tistory.com/148&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gyyeom.tistory.com/148&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Search 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 구현 시스템이라서 다는 못보여주고 본인이 구현한 검색로직만 가져와봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 짜면 url에 검색입력값, 필터링값이 들어와서 자동적으로 필터링된 검색값에 해당하는 데이터를 보여줄 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1736671165611&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function SamplePage() {
  const { searchParams, setSearchParams } = useCustomSearchParams();
  const [data, setData] = useState&amp;lt;Data[]&amp;gt;([]);

  const searchQuery = searchParams.get('search') || '';
  const order = searchParams.get('order') === 'true';

  useEffect(() =&amp;gt; {
    // 문서 불러오기 API 호출
    const getDataAPI = async () =&amp;gt; {
      try {
        const response = await axios.get('데이터 불러오는 url');

        setData(response.data);
      } catch (error) {
        console.error('문서 가져오기 실패:', error);
      }
    };
    getDataAPI();
  }, [searchQuery, order]);
  
  const applyFilters = (data: any[]) =&amp;gt; {
    let filteredData= data.filter((item) =&amp;gt; item !== null);

    // 검색 필터링
    if (searchQuery) {
      filteredData = filteredData.filter((item) =&amp;gt;
        item.name.toLowerCase().includes(searchQuery.toLowerCase()),
      );
    }

    // 정렬 필터링
    filteredData.sort((a, b) =&amp;gt; {
     //필터링 로직
    });

    return filteredData;
  };

  const filteredData = applyFilters(documents);

  // 검색 처리
  const handleSearch = (e: FormEvent) =&amp;gt; {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);
    const name = formData.get('name') as string;
    setSearchParams({ search: name });
  };

  // 정렬 처리
  const handleOrder = () =&amp;gt; {
    setSearchParams({ order: (!order).toString() });
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;검색 바가 있는 컴포넌트
      /&amp;gt;

      &amp;lt;div&amp;gt;
        {filteredData.map((item) =&amp;gt; (
          &amp;lt;Card
            key={item.id}
            item={item}
            onClick={() =&amp;gt; handleDocManager(item.id)}
          /&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;

    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 위에 조건들 말고도 더 조건이 있었는데 현재 백엔드와의 연결에 문제점을 겪고 있어서 이후는 리펙토링을 하면서 다시 짜야할 것같다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색로직을 구현하면서 겪은 문제점은 역시 useEffect으로 검색값, 필터링조건 등등이 변경될 때마다 useEffect가 실행되게 해야하는데 무한루프에 빠지게 되어버린 것이다. 그래서 메모이제이션을 사용하는 등의 방법으로 바꿔봤지만 여전히 안됐었는데 지금은 괜찮은데 나중에 다른 값들이 나오면 문제점이 생긴다. 이부분도 천천히 알아본 후 관련 블로그를 개시할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색로직을 구현하면서 좋아진 점은 불필요한 상태관리 변수가 확 줄어든다는 점이다. 상태정보를 검색 parameter에 담아 두니까 상태변수를 선언할 필요가 없어 코드양이 확 줄어들어 가독성 향상에도 도움을 주었다. 이부분의 장점이 너무커서 앞으로도 이 방법을 애용하고 더 깊게 공부해볼 생각이다.&lt;/p&gt;</description>
      <category>nextjs</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/121</guid>
      <comments>https://asd72621.tistory.com/121#entry121comment</comments>
      <pubDate>Sun, 12 Jan 2025 17:51:19 +0900</pubDate>
    </item>
    <item>
      <title>pnpm이란</title>
      <link>https://asd72621.tistory.com/120</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. pnpm이란 ?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pnpm이란 패키지 관리 도구로, 빠르고 &lt;b&gt;효율적인 의존성 설치&lt;/b&gt;를 제공한다. npm이나 yarn과 비슷한 기능을 제공하지만, &lt;b&gt;의존성을 하드링크&lt;/b&gt;하여 더 적은 디스크 공간을 사용하고 속도 또한 빠르다. 이는 특히 &lt;b&gt;모노레포 프로젝트&lt;/b&gt;나 대규모 프로젝트에서 효율적이다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모노레포 란 ?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 관리 시스템에서 두 개 이상의 프로젝트 코드가 동일한 저장소에 저장되는 소프트웨어 개발 전략&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 주요 기능과 특징&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;하드 링크 저장&lt;/b&gt; : pnpm은 모든 패키지를 전역 저장소에 설치하고, 하드 링크를 통해 프로젝트에 연결한다. 이 덕분에 중복된 패키지 설치를 방지하고 디스크 공간을 절약할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 설치 속도&lt;/b&gt; : pnpm은 의존성의 중복 다운로드를 방치하고, 네트워크 트래픽을 줄여 npm보다 빠르게 설치할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모노레포 지원&lt;/b&gt; : pnpm은 여러 패키지를 하나의 저장소에서 효율적으로 관리할 수 있다는 점에서 모노레포 프로젝트 구조에서 강력한 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Lock 파일 사용&lt;/b&gt; : &lt;i&gt;pnpm-lock.yaml&lt;/i&gt; 파일을 통해 의존성 버전을 고정하여, 일관된 빌드 환경을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하드 링크란 ?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 파일의 복사본을 만든다고 생각하면 된다. 소프트 링크와의 차이점은 원본을 지워도 하드 링크 파일은 실행이 되며, 내용이 보존된다는 것이다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 설치 및 기본 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.1. pnpm 설치&lt;/p&gt;
&lt;pre id=&quot;code_1735282608433&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// pnpm 설치
npm install -g pnpm

// pnpm을 통한 프로젝트 초기화
pnpm init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.2. 패키지 설치&lt;/p&gt;
&lt;pre id=&quot;code_1735282624030&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 패키지 설치
pnpm add &amp;lt;package_name&amp;gt;

// 개발 의존성으로 패키지 설치
pnpm add &amp;lt;package_name&amp;gt; -D&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.3 기존 프로젝트 pnpm으로 전환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 npm이나 yarn 프로젝트는 &lt;i&gt;pnpm import&lt;/i&gt; 명령어를 사용하여 pnpm으로 전환할 수 있고, &lt;i&gt;package-lock.json&lt;/i&gt; 또는 &lt;i&gt;yarn.lock&lt;/i&gt; 파일을 &lt;i&gt;pnpm-lock.yaml &lt;/i&gt;로 변환할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1735282704133&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm import&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 주요 사용 예제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1. 의존성 관리 예제&lt;/p&gt;
&lt;pre id=&quot;code_1735282750640&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 의존성 설치
pnpm add axios

// 패키지 제거
pnpm remove axios&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.2 스크립트 실행 예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pnpm을 통해 &lt;i&gt;package.json&lt;/i&gt;의 스크립트를 실행할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1735283080229&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// package.json 파일
{
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node index.js&quot;,
    &quot;build&quot;: &quot;tsc&quot;
  }
}

// 스크립트 실행
pnpm run start
pnpm run build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.3 모노레포 프로젝트 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pnpm은 모노레포에서 &lt;i&gt;workspace&lt;/i&gt; 기능을 제공한다. 예를들어, &lt;i&gt;packages&lt;/i&gt; 폴더 내에 여러 패키지를 관리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1735283122040&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// pnpm-workspace.yaml
packages:
  - 'packages/*'
  // packages 디렉토리 안의 모든 패키지를 워크스페이스로 인식합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 장단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;빠른 설치 속도&lt;/b&gt; : 하드 링크 방식으로 패키지를 공유하여 설치 속도가 빠르다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공간 절약&lt;/b&gt; : 전역 패키지 저장소를 사용해 디스크 공간을 절약한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모노레포 최적화&lt;/b&gt; : 대규모 프로젝트의 모노레포 구조에 적합하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;엄격한 의존성 관리&lt;/b&gt; : 종속 패키지의 설치 및 관리를 명확히 하여, 의존성 충돌 문제를 줄인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;호환성 문제&lt;/b&gt; : 일부 패키지가 pnpm의 엄격한 의존성 구조를 지원하지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 곡선&lt;/b&gt; : 기존 npm, yarn과는 설치 방식이 달라 익숙해지기 까지 시간이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기존 프로젝트 전환 시 설정 필요&lt;/b&gt; : npm에서 pnpm으로 전환 시 lock 파일을 변환하거나 추가 설정 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. npm, yarn과 비교&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;pnpm&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;npm&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;yarn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;속도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;빠름&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;보통&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;의존성 설치 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;하드 링크&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;복사&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;하드링크 (pnp 지원)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;모노레포 지원&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;강력한 지원&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;제한적 지원&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;강력한 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;디스크 공간 절약&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;전역 캐시 저장소 사용&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;모든 프로젝트에 복사&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;전역 캐시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;명령어 호환성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;npm과 유사하지만 추가 기능 있음&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기본&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;npm과 유사하지만 추가 기능 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>웹서비스</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/120</guid>
      <comments>https://asd72621.tistory.com/120#entry120comment</comments>
      <pubDate>Fri, 27 Dec 2024 16:11:39 +0900</pubDate>
    </item>
    <item>
      <title>git issue, kanbanboard, milestone, task card</title>
      <link>https://asd72621.tistory.com/119</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이슈 템플릿 작성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGCYEX/btsLAXPXWvx/DySB5EPgU4z25uGw4PJpuk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGCYEX/btsLAXPXWvx/DySB5EPgU4z25uGw4PJpuk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGCYEX/btsLAXPXWvx/DySB5EPgU4z25uGw4PJpuk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bGCYEX/btsLAXPXWvx/DySB5EPgU4z25uGw4PJpuk/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;1437&quot; height=&quot;939&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;칸반보드 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blSoDw/btsLA5NTKdY/P6ZyLq6K06FjyKdc0eGkbk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blSoDw/btsLA5NTKdY/P6ZyLq6K06FjyKdc0eGkbk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blSoDw/btsLA5NTKdY/P6ZyLq6K06FjyKdc0eGkbk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/blSoDw/btsLA5NTKdY/P6ZyLq6K06FjyKdc0eGkbk/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;1437&quot; height=&quot;939&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;마일스톤 작성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정표 역할, 이슈라고 불리는 태스크 카드들을 그룹화하는데 사용된다. 마일스톤에 연결된 태스크 카드들이 완료되면, 각 마일스톤의 진행상황을 업데이트 할 수 있다. 이러한 마일스톤의 기능은 관련 이슈들의 추적 및 진행상황을 한눈에 파악하는데 큰 장점을 제공한다. 스프린트나 단계별 구현에 관련된 유사 이슈들을 개별적으로 추적하는 대신, 마일스톤을 사용하여 관리 과정을 간소화 할 수 있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oUgT9/btsLxZBT8zG/KnvpgqAija5qNXCkpOBGK1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oUgT9/btsLxZBT8zG/KnvpgqAija5qNXCkpOBGK1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oUgT9/btsLxZBT8zG/KnvpgqAija5qNXCkpOBGK1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/oUgT9/btsLxZBT8zG/KnvpgqAija5qNXCkpOBGK1/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;1437&quot; height=&quot;939&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 카드 활용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github에서 태스크 카드를 활용하여 프로젝트를 효율적으로 관리할 수 있다. 먼저, 이슈 카드를 통해 프로젝트의 세부 사항을 명확하게 정의하고 할당할 수 있다. 이는 작업의 우선순위를 결정하는데 큰 도움이 된다. 또한, 각 이슈의 진행상황을 실시간으로 추적할 수 있어, 팀원 간의 소통을 강화하는데 기여한다. 마일스톤과 연동하면 전체 프로젝트의 진행상태를 한눈에 파악할 수 있다. 칸반 보드를 사용하면 진행중, 대기중, 완료된 태스크를 시각적으로 구분하여 관리할 수 있다. 마지막으로, 이슈 템플릿을 활용하면 일관된 정보구조로 이슈를 생성할 수 있다. 이는 빠르고 효율적인 이슈 해결에 기여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 시작 시 `In Progress`, 작업 완료 후 리퀘스트를 생성하고 `Review` 컬럼으로 이동, 코드 리뷰 후 승인되면 `Done` 컬럼으로 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;Assigness&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;해당 태스크를 맡은 사람을 지정, assign yourself를 누르면 자신의 태스크로 만들 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;Labels&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;태스크 카드에 라벨링을 할 수 있다. E: 1h 와 같이 예상 소요시간을 라벨로 만들어 꼭 라벨링 해야한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;Projects&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Projects를 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;Milestone&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;해당 태스크의 기한을 정함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lables 태그들&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 152px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;bug&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 16px;&quot;&gt;something isn't working&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;documentation&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;improvements or additions to documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;b&gt;duplicate&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;the issue or pull request already exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;good first issue&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;good for newcomers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;help wanted&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;extra attention is needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #99cefa;&quot;&gt;&lt;b&gt;enhancement&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;new feature or request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;invalid&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;this doesn't seem right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #ffc1c8;&quot;&gt;&lt;b&gt;question&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;further information is requested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;&lt;b&gt;wontfix&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;this will not be worked on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyFXa1/btsLAAgknGv/dQggc5xQueF8FZAxR8uzI0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyFXa1/btsLAAgknGv/dQggc5xQueF8FZAxR8uzI0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyFXa1/btsLAAgknGv/dQggc5xQueF8FZAxR8uzI0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cyFXa1/btsLAAgknGv/dQggc5xQueF8FZAxR8uzI0/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;1437&quot; height=&quot;939&quot; data-filename=&quot;Animation.gif&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/119</guid>
      <comments>https://asd72621.tistory.com/119#entry119comment</comments>
      <pubDate>Fri, 27 Dec 2024 11:43:35 +0900</pubDate>
    </item>
    <item>
      <title>git 명령어</title>
      <link>https://asd72621.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 레포지토리와 연결할 유저 정보 설정&lt;/p&gt;
&lt;pre id=&quot;code_1735264491462&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git config --global user.name &quot;[firstname lastname]&quot;
# 버전 히스토리를 식별할 때 사용할 이름을 설정합니다.

$ git config --global user.email &amp;ldquo;[valid-email]&amp;rdquo;
# 각 기록과 연결할 이메일 주소를 설정합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;도움말 보기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;help 명령어를 이용하여 각 명령어 및 옵션의 기능에 대해 살펴 볼 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264523118&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git help --all
# git에서 제공하는 모든 명령어를 볼 수 있습니다.

$ git [command] -help
# 특정 command에서 사용할 수 있는 모든 옵션을 볼 수 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;세팅 및 초기화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레포지토리를 초기화하거나 존재하는 레포지토리를 클론할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264611519&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git init
# 현재 디렉토리를 기준으로 Git 저장소가 생성됩니다.

$ git clone [url]
# URL을 통해 리모트 레포지토리를 로컬 레포지토리에 복제합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Stage &amp;amp; Commit&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이지 영역을 이용하여 커밋할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264666997&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git status
# 다음 커밋을 위해 현재 디렉토리에서 수정된 파일을 확인할 수 있습니다.

$ git add [file]
# 다음 커밋을 위해 파일을 추가(스테이지)합니다. (stage)

$ git reset [file]
# 추가한 파일을 언스테이징합니다. 변경 사항은 유지됩니다.

$ git diff
# 스테이지되지 않은 변경 사항을 보여줍니다.

$ git diff --staged
# 스테이지했지만 커밋하지 않은 변경 사항을 보여줍니다.

$ git commit -m &amp;ldquo;[descriptive message]&amp;rdquo;
# 스테이지된 컨텐츠를 메시지와 함께 커밋합니다. (스냅샷 생성)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Branch &amp;amp; Merge&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 내역을 브랜치로 분리해 컨텍스트를 변경, 통합할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264718979&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git branch
# 브랜치 목록을 보여줍니다. * 표시로 현재 작업중인 브랜치를 확인할 수 있습니다.

$ git branch [branch-name]
# 현재 커밋에서 새로운 브랜치를 생성합니다.

$ git checkout [branch-name]
# 다른 브랜치로 전환합니다. -b 옵션으로 브랜치를 생성하고 전환할 수 있습니다.

$ git merge [branch-name]
# 현재 브랜치에 특정 브랜치의 히스토리를 병합합니다.

$ git log
# 현재 브랜치의 모든 커밋 히스토리를 보여줍니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;비교 및 검사&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그 및 변경 사항을 검사할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264763711&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git log branchB..branchA
# 브랜치B에 없는 브랜치A의 모든 커밋 히스토리를 보여줍니다.

$ git log --follow [file]
# 해당 파일의 변경 사항이 담긴 모든 커밋을 표시합니다. (파일 이름 변경도 표시)

$ git diff branchB...branchA
# 브랜치A에 있지만 브랜치B에 없는 것의 변경 내용(diff)을 표시합니다. (branch간 상태 비교)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;공유 및 업데이트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 레포지토리의 업데이트 사항을 검색하여 로컬 레포지토리를 업데이트 할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264842125&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote add [alias] [url]
# url을 통해 특정 리모트 레포지토리를 별칭으로 추가합니다.

$ git fetch [alias]
# 별칭으로 추가한 리모트 레포지토리에 있는 모든 브랜치 및 데이터를 로컬로 가져옵니다.

$ git merge [alias]/[branch]
# 리모트 브랜치를 현재 작업중인 브랜치와 병합하여 최신 상태로 만들 수 있습니다.

$ git push [alias] [branch]
# 로컬 브랜치의 커밋을 리모트 브랜치로 전송합니다.

$ git pull
# 리모트 레포지토리의 정보를 가져와 자동으로 로컬 브랜치에 병합합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;히스토리 수정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치 또는 커밋을 수정하거나 커밋 히스토리를 지울 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264898523&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git rebase [branch]
# 특정 브랜치의 분기 이후 커밋을 현재 작업중인 브랜치에 반영합니다.

$ git reset --hard [commitish]
# 특정 커밋 전으로 돌아가며 스테이지된 변경 사항을 모두 지웁니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;임시 저장&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치를 전환하기 위해 변경되었거나 추적중인 파일을 임시로 저장 할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1735264954054&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git stash
# 수정하거나 스테이지된 변경사항을 스택에 임시 저장하고 현재 작업 내역에서 지웁니다.

$ git stash list
# 스택에 임시 저장된 변경사항의 목록을 보여줍니다.

$ git stash apply
# 스택에 임시 저장된 변경사항을 다시 현재 작업 내역에 적용합니다.

$ git stash pop
# 스택에 임시 저장된 변경사항을 다시 현재 작업 내역에 적용하고 스택에서 삭제합니다.

$ git stash drop
# 스택에 임시 저장된 변경사항을 삭제합니다.&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로젝트</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/118</guid>
      <comments>https://asd72621.tistory.com/118#entry118comment</comments>
      <pubDate>Fri, 27 Dec 2024 11:03:45 +0900</pubDate>
    </item>
    <item>
      <title>[Jest] 파일 구조 &amp;amp; 사용법</title>
      <link>https://asd72621.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;describe&lt;/b&gt;&quot; argument( name, fn) : 여러 관련 테스트를 &lt;span style=&quot;color: #ee2323;&quot;&gt;그룹화&lt;/span&gt;하는 블록을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;it&lt;/b&gt;&quot; same as test argument ( name,fn, timeout) : &lt;span style=&quot;color: #ee2323;&quot;&gt;개별 테스트&lt;/span&gt;를 수행하는 곳. 각 테스트를 작은 문장처럼 설명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;expect&lt;/b&gt;&quot; : expect 함수는 값을 테스트할 때마다 사용, expect 함수 혼자서는 거의 사용하지 않으며 matcher와 함께 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;matcher&lt;/b&gt;&quot; : &lt;span style=&quot;color: #ee2323;&quot;&gt;다른 방법&lt;/span&gt;으로 값을 테스트 하도록 &quot;matcher&quot;를 사용&lt;/p&gt;
&lt;pre id=&quot;code_1735205594779&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test('two plus two is four', ()=&amp;gt;{
	expect(2+2).toBe(4) // 여기서 toBe가 matcher
});

test('two plus two is not five', ()=&amp;gt;{
	expect(2+2).not.toBe(5) // 여기서 toBe가 matcher
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;render&lt;/b&gt;&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM에 컴포넌트를 렌더링하는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자로 렌더링할 React 컴포넌트가 들어감&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Return은 RTL에서 제공하는 쿼리 함수와 기타 유틸리티 함수를 담고 있는 객체를 리턴 (Destructuring&amp;nbsp; 문법으로 원하는 쿼리함수만 얻어올 수 있다) ===&amp;gt; 소스 코드가 복잡해지면 비추천, screen 객체를 사용해야한다. 왜냐하면 사용해야할 쿼리가 많아질 수록 코드가 복잡해질 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1735205907603&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// v1
test('renders learn react link', ()=&amp;gt;{
	render(&amp;lt;App /&amp;gt;);
    const linkElement = screen.getByText(/lean react/i);
    expect(linkElement).toBeInTheDocument();
});

// v2
test('renders learn react link', ()=&amp;gt;{
	const { getByText } = render(&amp;lt;App /&amp;gt;);
    const linkElement = screen.getByText(/lean react/i);
    expect(linkElement).toBeInTheDocument();
});&lt;/code&gt;&lt;/pre&gt;</description>
      <category>웹서비스</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/117</guid>
      <comments>https://asd72621.tistory.com/117#entry117comment</comments>
      <pubDate>Thu, 26 Dec 2024 20:07:54 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Library</title>
      <link>https://asd72621.tistory.com/116</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RTL (React Testing Library)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Testing Library는 React 구성요소 작업을 위한 API를 추가하여 DOM Testing Library 위에 구축된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM Testing Library란 DOM 노드(Node)를 테스트 하기 위한 매우 가벼운 솔루션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRA로 생성된 프로젝트는 즉시 React Testing Library를 지원하지만 그렇지 않은 경우 밑의 npm을 통해 추가해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1735204169480&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev @testing/library/react&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTL은 에어비엔비에서 만든 Enzyme을 대처하는 솔루션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enzyme이 React 개발자에게 React 구성 요소의 내부를 테스트할 수 있는 유틸리티를 제공하는 동안 RTL은 한 걸음 물러서서 &quot;React 구성요소를 테스트하여 REact 구성요소를 완전히 신뢰하는 방법&quot;에 대해 질문한다. &lt;span style=&quot;color: #ee2323;&quot;&gt;구성 요소의 구현 세부 정보를 테스트하는 대신 RTL 개발자를 React 애플리케이션의 사용자 입장에 둔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enzyme -&amp;gt; 구현 주도 테스트 (Implementation Driven Test)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Testing Library -&amp;gt; 행위 주도 테스트 (Behavior Driven Test)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DOM 이란 ?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Document Object Model의 약자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Document : 문서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object : 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model : 모델&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM(문서 객체 모델)은 XML, HTML 문서의 각 항목을 계층으로 표현하여 생성, 변형 삭제 할 수 있도록 돕는 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;HTML 요소들의 구조화된 표현&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;DOM은 HTML이 브라우저의 렌더링 엔진에 의해 분석되고 분석이 모두 끝나고 난 HTML 파일이 DOM이다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;HTML은 화면에 보이고자 하는 모양과 구조를 문서로 만들어서 단순 텍스트로 구성되어있으며 DOM은 HTML 문서의 내용과 구조가 객체 모델로 변화되어 다양한 프로그램에서 사용될 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;HTML 문서가 유효하지않게 작성됐을 때는 브라우저가 올바르게 교정해주며, DOM은 자바스크립트에 의해 수정될 수 있따. 하지만 HTML은 수정되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;웹 페이지 빌드 과정 (Critical Rendering Path CRP)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 서버에서 페이지에 대한 HTML 응답을 받고 화면에 표시하기 전에 여러 단계가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저가 HTML 문서를 읽고, 스타일을 입히고 뷰포트에 표시하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0KHum/btsLAtOSuL6/XgvErEincirjsc2YFdOiH0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0KHum/btsLAtOSuL6/XgvErEincirjsc2YFdOiH0/img.jpg&quot; data-alt=&quot;https://dimension85.com/glossary/critical-render-path&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0KHum/btsLAtOSuL6/XgvErEincirjsc2YFdOiH0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0KHum%2FbtsLAtOSuL6%2FXgvErEincirjsc2YFdOiH0%2Fimg.jpg&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;760&quot; height=&quot;490&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://dimension85.com/glossary/critical-render-path&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 렌더링은 HTML, CSS, JS 등의 웹 페이지 자원을 브라우저가 화면에 그리는 과정&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;HTML 파싱 후 DOM 트리 구축&lt;/li&gt;
&lt;li&gt;CSS 파싱 후 CSSOM 트리 구축&lt;/li&gt;
&lt;li&gt;JS 실행 ( HTML 파싱 중 JS 코드 있으면 파싱 중단)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt; HTML 렌더링 중에 Javascript가 실행되면 렌더링이 멈추는 이유 &lt;/b&gt;&lt;/li&gt;
&lt;li&gt;렌더링엔진이 HTML 파싱을 하면서 한줄 한줄씩 DOM 트리를 생성하는데 Javscript를 만나게 되면 DOM 생성을 중단시키고 JS엔진에 &lt;span style=&quot;background-color: #fbf3db;&quot; data-token-index=&quot;1&quot;&gt;제어권&lt;/span&gt;을 넘기는데, JS파싱이 끝난후 다시 제어권을 받게되면 중단된부분부터 다시 HTML 파싱을 재개하여 DOM 트리를 생성합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;DOM,CSSOM 트리 조합해서 렌더트리 구축&lt;/li&gt;
&lt;li&gt;뷰포인트 기반으로 렌더트리의 각 노드가 가지는 정확한 위치와 크기 계산 (Layout)&lt;/li&gt;
&lt;li&gt;계산한 위치/크기 기반으로 화면에 그림 (Paint)&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>웹서비스</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/116</guid>
      <comments>https://asd72621.tistory.com/116#entry116comment</comments>
      <pubDate>Thu, 26 Dec 2024 18:25:47 +0900</pubDate>
    </item>
    <item>
      <title>prisma가 무엇인가</title>
      <link>https://asd72621.tistory.com/115</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;prisma란 ?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트와 타입스크립트 커뮤니티에서 주목받고 있는 차세대 ORM(Object Relational Mapping) 프레임워크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스와 상호작용하는 응용 애플리케이션을 개발할 때, 프로그래머가 직접 SQL을 작성하지 않아도 되므로, 개발 생산성을 높여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;효과적인 데이터 모델링을 위한 간단하지만 강력한 자체적인 스키마 문법을 제공하고 이 스키마를 통해 DB 마이그레이션과 클라이언트 코드 생성을 완전히 자동으로 생성해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자입장에서는 데이터베이스에 접근하기 위해 작성해야하는 코드가 비약적으로 줄어들고 스키마 파일 하나만 관리하면 되기때문에 유지보수 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;prisma 설치&lt;/h4&gt;
&lt;pre id=&quot;code_1735192637535&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i -D prisma&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;prisma command&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 119px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style7&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;init&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;set up Prisma for your app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;generate&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Generate artifacts ( e.g Prisma client)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;db&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Manage your database schema and lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;migrate&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Migrate your database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;studio&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Browse your data with Prisma Studio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;validate&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Validate your Prisma schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;format&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Format your Prisma schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735192939521&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  Set up a new Prisma project
  $ prisma init

  Generate artifacts (e.g. Prisma Client)
  $ prisma generate

  Browse your data
  $ prisma studio

  Create migrations from your Prisma schema, apply them to the database, generate artifacts (e.g. Prisma Client)
  $ prisma migrate dev

  Pull the schema from an existing database, updating the Prisma schema
  $ prisma db pull

  Push the Prisma schema state to the database
  $ prisma db push

  Validate your Prisma schema
  $ prisma validate

  Format your Prisma schema
  $ prisma format&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735193185351&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; npx prisma init --datasource-provider sqlite&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// prisma/prisma.schema&lt;/p&gt;
&lt;pre id=&quot;code_1735193234297&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 마이그레이션 실행&lt;/p&gt;
&lt;pre id=&quot;code_1735193284161&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx prisma migrate dev --name init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// prisma/prisma.schema&lt;/p&gt;
&lt;pre id=&quot;code_1735193314935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;datasource db {
  provider = &quot;sqlite&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// .env&lt;/p&gt;
&lt;pre id=&quot;code_1735193325097&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DATABASE_URL=&quot;file:./dev.db&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//prisma/migrations/20230226182528_init/migration.sql&lt;/p&gt;
&lt;pre id=&quot;code_1735193359193&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- CreateTable
CREATE TABLE &quot;User&quot; (
    &quot;id&quot; INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    &quot;email&quot; TEXT NOT NULL,
    &quot;name&quot; TEXT
);

-- CreateIndex
CREATE UNIQUE INDEX &quot;User_email_key&quot; ON &quot;User&quot;(&quot;email&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹서비스</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/115</guid>
      <comments>https://asd72621.tistory.com/115#entry115comment</comments>
      <pubDate>Thu, 26 Dec 2024 16:24:19 +0900</pubDate>
    </item>
    <item>
      <title>투포인터 알고리즘</title>
      <link>https://asd72621.tistory.com/114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에 순차적으로 접근해야할 때 &lt;b&gt;두 개의 점 위치를 기록하면서 처리&lt;/b&gt;하는 알고리즘&lt;br /&gt;1차원 배열에서 각자 다른 원소를 가리키고 있는 2개의 포인터를 조작하면서 원하는 값을 찾을 때 까지 탐색하는 알고리즘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[동작방식]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보통 left, right / start, end 등의 포인터 변수 생성&lt;/li&gt;
&lt;li&gt;처음에는 start = end = 0이고 두 포인터 관계는 start &amp;lt;= end&lt;/li&gt;
&lt;li&gt;현재 부분 합이 target과 같다면 카운트&lt;/li&gt;
&lt;li&gt;현재 부분 합이 target보다 작다면 end++;&lt;/li&gt;
&lt;li&gt;현재 부분 합이 target보다 크거나 같다면 start++;&lt;/li&gt;
&lt;li&gt;모든 경우를 확인할 때 까지 3~5 과정 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[예시 : 프로그래머스 Lv.2 구명보트]&lt;/p&gt;
&lt;pre id=&quot;code_1734592574946&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(people, limit) {
    var answer = 0;
    people.sort((a,b)=&amp;gt; a-b);
    let left = 0;
    let right = people.length -1;
    while(left &amp;lt;= right){
        if(people[left] + people[right] &amp;lt;= limit) left++;
        right--;
        answer++;
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[예시 : 프로그래머스 Lv.2 연속된 부분 수열의 합]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차 풀이 : 실패&lt;/p&gt;
&lt;pre id=&quot;code_1734592946053&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution (sequence,k){
     let check = sequence.indexOf(k);
    
     if(check &amp;gt; -1) return [check,check];
    
     for(let i =0; i&amp;lt; sequence.length; i++){
        
         for(let j =i+1; j&amp;lt; sequence.length; j++){
             let sum = sequence.slice(i,j).reduce((prev,cur)=&amp;gt; prev+= cur);
             if(sum == k) return [i, j-1];
         }
     }
     
     return -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 풀이: 성공&lt;/p&gt;
&lt;pre id=&quot;code_1734592668389&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(sequence, k) {
    let [left,right] = [0,0];
    let sum = sequence[0];
    let ret = [0, sequence.length];
    
    while(left &amp;lt; sequence.length){
        if(sum &amp;lt; k &amp;amp;&amp;amp; right &amp;lt; sequence.length){
            sum += sequence[++right];
        }else if(sum == k &amp;amp;&amp;amp; right -left &amp;lt; ret[1] -ret[0]){
            ret = [left,right];
            sum += sequence[++right];
        }else sum -= sequence[left++];
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>이것이 코딩테스트다</category>
      <author>초아롱</author>
      <guid isPermaLink="true">https://asd72621.tistory.com/114</guid>
      <comments>https://asd72621.tistory.com/114#entry114comment</comments>
      <pubDate>Thu, 19 Dec 2024 17:12:25 +0900</pubDate>
    </item>
  </channel>
</rss>